机器视觉课程设计
对圆盘形零件圆心与直径和矩形零件长与宽尺寸测量
学生学院机电学院
专业班级
学号
学生
指导教师
2015年 1 月 20 日
目录
1 问题描述 (5)
1.1 基本目标 (5)
1.2 基本要求 (5)
2 程序及其算法 (5)
2.1 检测与计算圆半径的程序 (5)
2.2 检测与计算矩形长和宽的程序 (8)
2.2.1 打开摄像头程序 (8)
2.2.2 保存拍摄的照片程序 (9)
2.2.3 读取拍摄到的图片(读取文名字CurrentImage.jpg的图片) (9)
2.2.4 检测边上的点和计算长和高的函数 (9)
2.2.5 老师写的显示图片的函数 (13)
3 运行结果 (14)
4 小结 (15)
1 问题描述
1.1 基本目标
显示一图片(包含一个矩形或一个圆),测量矩形的长宽或圆的直径。完成得及格分,扩展有加分!
1.2 基本要求
“机器视觉”考试结果要求独立在计算机上完成,建议使用Visual C++和OpenCV 实现一个具有视觉捕捉、图像显示、尺寸测量等功能的对话框程序,其中必须完成对圆盘形零件圆心与直径和矩形零件长与宽尺寸测量容。在教师提供的基本框架程序基础上,修改、补充界面和功能。
2 程序及其算法
2.1 检测与计算圆半径的程序
思路:从图片中间横扫取点得M_Point[0],M_Point[1](x坐标相加除2的圆心的x 坐标)中间纵向取点得M_Point[2],M_Point[3](y坐标相加除2的圆心的y坐标)圆上四个点到圆心的距离(半径)取平均值,输出为半径。
要求图片
显示图片
程序如下:
double c_DialogTeclarn::f_MakeCircle(double e_dThreshold)
{
if(NULL==m_pIplImageSource)
{return 0;}
//定义变量存图像的宽,高,行像素
int q_iWidth=m_pIplImageSource->width;
int q_iHeight=m_pIplImageSource->height;
int q_iWidthStep=m_pIplImageSource->widthStep;
uchar *q_pchDataImage=(uchar *)m_pIplImageSource->imageData;
CvPoint M_Point[4]={};//存放检索出的四个点循环检索
//01纵扫
for(int Cycle_Y=1;Cycle_Y { if(e_dThreshold>q_pchDataImage[q_iWidth/2+q_iWidthStep*(Cycle_Y-1)]) { M_Point[0].x=q_iWidth/2; M_Point[0].y=Cycle_Y; } } for(int Cycle_Y=q_iHeight;Cycle_Y>1;Cycle_Y--) { if(e_dThreshold>q_pchDataImage[q_iWidth/2+q_iWidthStep*(Cycle_Y-1)]) { M_Point[1].x=q_iWidth/2; M_Point[1].y=Cycle_Y; } } for(int Cycle_X=1;Cycle_X { if(e_dThreshold>q_pchDataImage[Cycle_X+q_iWidthStep*(q_iHeight/2-1)]) { M_Point[2].x=Cycle_X; M_Point[2].y=q_iHeight/2; } } for(int Cycle_X=q_iWidth;Cycle_X>1;Cycle_X--) { if(e_dThreshold>q_pchDataImage[Cycle_X+q_iWidthStep*(q_iHeight/2-1)]) { M_Point[3].x=Cycle_X; M_Point[3].y=q_iHeight/2; } } for (int i=0;i<4;i++)//画边界圆 {cvCircle(m_pIplImageSource,M_Point[i],8,cvScalarAll(127),2);} int Ridius=0,clear=0; int circle_y=abs(M_Point[1].y+M_Point[0].y)/2; int circle_x=abs(M_Point[3].x+M_Point[2].x)/2; float c=0,j=0; for (int i=0;i<4;i++) { j=(M_Point[i].x-circle_x)*(M_Point[i].x-circle_x)+(M_Point[i].y-circle_y )*(M_Point[i].y-circle_y); c=c+sqrt(j); } Ridius=c/4; //在窗口中显示长和高 char ch1[10],ch2[10]; itoa(Ridius,ch1,10); itoa(clear,ch2,10); SetDlgItemText(IDC_LONG,ch1); SetDlgItemText(IDC_SHORT,ch2); CWnd *e_pCWndPicture=this->GetDlgItem(ID_PICTURE_DISPLAY); f_ControlShowImage(m_pIplImageDisplay,e_pCWndPicture,m_pIplImageSource); cvSaveImage(_T(".//Result_Image.jpg"),m_pIplImageSource); return 0; } 2.2 检测与计算矩形长和宽的程序 思路:这个程序主要包括:打开摄像头---保存图片(拍照)---加载刚刚保存的图片---找出点并计算长和高四个按钮,每一个按钮包含下面的一段代码,拍摄的图片保存为CurrentImage.jpg,这里事先保存了一同名的图片,所以可以直接点击Load Image 读取。 2.2.1 打开摄像头程序 void c_DialogTeclarn::OnBnClickedButton OpenCamera() { CvCapture *q_pCvCapture=cvCreateCameraCapture(0); if(NULL==q_pCvCapture) { return; } IplImage *q_pIplImageCapture=cvQueryFrame(q_pCvCapture); if(NULL==q_pIplImageCapture) { return; } if(NULL!=m_pIplImageSource) { cvReleaseImage(&m_pIplImageSource); m_pIplImageSource=NULL; } m_pIplImageSource=cvCloneImage(q_pIplImageCapture); cvReleaseCapture(&q_pCvCapture); CWnd *e_pCWndPicture=this->GetDlgItem(ID_PICTURE_DISPLAY); f_ControlShowImage(m_pIplImageDisplay,e_pCWndPicture,m_pIplImageSource); } 注:绿色的为检测是否成功打开摄像头,蓝色的是读取摄像头拍摄的图片到存,后面的语句是让图片在mfc窗口显示出来。 2.2.2 保存拍摄的照片程序 void c_DialogTeclarn::OnBnClickedButton SaveImage() { // TODO: 在此添加控件通知处理程序代码 cvSaveImage(_T(".//CurrentImage.jpg"),m_pIplImageSource); } 2.2.3 读取拍摄到的图片(读取文名字CurrentImage.jpg的图片) void c_DialogTeclarn::OnBnClickedButton LoadImage() { // TODO: 在此添加控件通知处理程序代码 if(NULL!=m_pIplImageSource) { cvReleaseImage(&m_pIplImageSource); m_pIplImageSource=NULL; } m_pIplImageSource=cvLoadImage(_T(".//CurrentImage.jpg"),0); CWnd *e_pCWndPicture=this->GetDlgItem(ID_PICTURE_DISPLAY); f_ControlShowImage(m_pIplImageDisplay,e_pCWndPicture,m_pIplImageSource); } 2.2.4 检测边上的点和计算长和高的函数 思路是:第一步找到边上的点,设定一个变量e_dThreshold值为127,通过循环让它与每一个像素的像素值进行比较。如果检测到一个点的像素值比127小,则这个点可能就是需要的点(黑色=0,白色=255)如果每个像素都要比较运算太多,所以上边两个点的检测是沿着图片长的的2/5分处和3/5分处向下检测,下边的点是沿着1/2处向上检测。第二步是用找到6个点的坐标算出三角形的面积,然后除以底边边长得到高。求三角形的面积用的是行列式的值等于三角形面积的2倍 程序如下: double c_DialogTeclarn::f_MakeRectangleWidth(double e_dThreshold) { // TODO: 在此添加控件通知处理程序代码 //定义一些点和变量 int q_iWidth=m_pIplImageSource->width; int q_iHeight=m_pIplImageSource->height; int q_iWidthStep=m_pIplImageSource->widthStep; uchar *q_pchDataImage=(uchar *)m_pIplImageSource->imageData; int q_iXLeftTop=2*q_iWidth/5; int q_iXRightTop=3*q_iWidth/5; int q_iXBottom=q_iWidth/2; int q_iYLeftTop=2*q_iHeight/5; int q_iYLeftBottom=3*q_iHeight/5; int q_iYRightMid=q_iHeight/2; CvPoint q_CvPointLeftTop,q_CvPointRightTop,q_CvPointBottom; CvPoint q_CvPointLeft_Top,q_CvPointRight_Mid,q_CvLeft_Bottom; //求上下两边上的点,3个循环 for(int q_iCycleHeight=0;q_iCycleHeight { if(e_dThreshold>q_pchDataImage[q_iCycleHeight*q_iWidthStep+q_iXLeftTop]) { q_CvPointLeftTop.x=q_iXLeftTop; q_CvPointLeftTop.y=q_iCycleHeight; break; } } for(int q_iCycleHeight=0;q_iCycleHeight { if(e_dThreshold>q_pchDataImage[q_iCycleHeight*q_iWidthStep+q_iXRightTop]) { q_CvPointRightTop.x=q_iXRightTop; q_CvPointRightTop.y=q_iCycleHeight; break; } } for(int q_iCycleHeight=q_iHeight-1;q_iCycleHeight>=0;q_iCycleHeight--) { if(e_dThreshold>q_pchDataImage[q_iCycleHeight*q_iWidthStep+q_iXBottom]) { q_CvPointBottom.x=q_iXBottom; q_CvPointBottom.y=q_iCycleHeight; break; } } //求左右两边上的点,3个循环 for(int q_iCycleWidth=0;q_iCycleWidth { if(e_dThreshold>q_pchDataImage[q_iCycleWidth+q_iWidthStep*(q_iYLeftTop-1)]) { q_CvPointLeft_Top.x=q_iCycleWidth; q_CvPointLeft_Top.y=q_iYLeftTop; break; } } for(int q_iCycleWidth=0;q_iCycleWidth { if(e_dThreshold>q_pchDataImage[q_iCycleWidth+q_iWidthStep*(q_iYLeftBottom-1)]) { q_CvLeft_Bottom.x=q_iCycleWidth; q_CvLeft_Bottom.y=q_iYLeftBottom; break; } } for(int q_iCycleWidth=q_iHeight-1;q_iCycleWidth>=0;q_iCycleWidth--) { if(e_dThreshold>q_pchDataImage[q_iCycleWidth+q_iWidthStep*(q_iYRightMid-1)]) { q_CvPointRight_Mid.x=q_iCycleWidth; q_CvPointRight_Mid.y=q_iYRightMid; break; } } //在检测到的点上画圆,只是为了更好看到找点的情况 cv Circle(m_pIplImageSource,q_CvPointLeftTop,5,cvScalarAll(127),2); cvCircle(m_pIplImageSource,q_CvPointRightTop,5,cvScalarAll(127),2); cvCircle(m_pIplImageSource,q_CvPointBottom,5,cvScalarAll(127),2); cvCircle(m_pIplImageSource,q_CvPointLeft_Top,5,cvScalarAll(127),2); cvCircle(m_pIplImageSource,q_CvLeft_Bottom,5,cvScalarAll(127),2); cvCircle(m_pIplImageSource,q_CvPointRight_Mid,5,cvScalarAll(127),2); //显示画圆后的图片 CWnd *e_pCWndPicture=this->GetDlgItem(ID_PICTURE_DISPLAY); f_Control ShowImage(m_pIplImageDisplay,e_pCWndPicture,m_pIplImageSource); //计算长和高的像素值,三角形3点的坐标构成行列式,行列式的值=2*面积,行列式 的值/底边=高 CvMat Ma; int HIGH,WIDE; float Area; int a=q_CvPointLeftTop.x-q_CvPointRightTop.x; int b=q_CvPointLeftTop.y-q_CvPointRightTop.y; double c=a*a+b*b; //定义行列式 double arr[9]={q_CvPointLeftTop.x,q_CvPointLeftTop.y,1,q_CvPointRightTop.x,q_CvPointRightTop. y,1,q_CvPointBottom.x,q_CvPointBottom.y,1}; cvInitMatHeader(&Ma,3,3,CV_64FC1,arr); Area=abs(cvDet(&Ma)); HIGH=Area/sqrt(c); int A=q_CvPointLeft_Top.x-q_CvLeft_Bottom.x; int B=q_CvPointLeft_Top.y-q_CvLeft_Bottom.y; double C=A*A+B*B; //定义行列式 Double ARR[9]={q_CvPointLeft_Top.x,q_CvPointLeft_Top.y,1,q_CvLeft_Bottom.x,q_CvLeft_Bottom.y,1,q_ CvPointRight_Mid.x,q_CvPointRight_Mid.y,1}; cvInitMatHeader(&Ma,3,3,CV_64FC1,ARR); Area=abs(cvDet(&Ma)); WIDE=Area/sqrt(C); //在窗口中显示长和高 char ch1[10],ch2[10]; itoa(HIGH,ch1,10); itoa(WIDE,ch2,10); SetDlgItemText(IDC_LONG,ch1); SetDlgItemText(IDC_SHORT,ch2); cvSaveImage(_T(".//Result_Image.jpg"),m_pIplImageSource); return 0; } //调用上面的函数进行检测点和计算长和高的值,点击第四个按钮会调用这段程序,当里面的函数调用时,它会调用上面那段函数 void c_DialogTeclarn::OnBnClickedButton Rectangle() { // TODO: 在此添加控件通知处理程序代码 f_MakeRectangleWidth(); } 2.2.5 老师写的显示图片的函数 void c_DialogTeclarn::f_ControlShowImage(IplImage *&e_pIplImageShow,CWnd *e_pCWndControl, IplImage *e_pIplImageSource) { if((NULL==e_pCWndControl)||(NULL==e_pIplImageSource)) { return; } CRect q_CRectControl; e_pCWndControl->GetClientRect(&q_CRectControl); if(NULL!=e_pIplImageShow) { cvReleaseImage(&e_pIplImageShow); e_pIplImageShow=NULL; } e_pIplImageShow=cvCreateImage(cvSize(q_CRectControl.Width(),q_CRectControl.Height()), IPL_DEPTH_8U, e_pIplImageSource->nChannels); cvResize(e_pIplImageSource,e_pIplImageShow); HDC q_HDCControl=e_pCWndControl->GetDC()->GetSafeHdc(); unsigned int q_piBuffer[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256]; BITMAPINFO *e_pBITMAPINFODisplay=(BITMAPINFO *)q_piBuffer; BITMAPINFOHEADER *e_pBITMAPINFOHEADERDisplay=&(e_pBITMAPINFODisplay->bmiHeader); memset(e_pBITMAPINFOHEADERDisplay,0,sizeof(*e_pBITMAPINFOHEADERDisplay)); e_pBITMAPINFOHEADERDisplay->biSize=sizeof(BITMAPINFOHEADER); e_pBITMAPINFOHEADERDisplay->biWidth=e_pIplImageShow->width; e_pBITMAPINFOHEADERDisplay->biHeight=-e_pIplImageShow->height; e_pBITMAPINFOHEADERDisplay->biPlanes=1; e_pBITMAPINFOHEADERDisplay->biBitCount=8*e_pIplImageShow->nChannels; e_pBITMAPINFOHEADERDisplay->biCompression=BI_RGB; RGBQUAD* palette=e_pBITMAPINFODisplay->bmiColors; if(8==e_pBITMAPINFOHEADERDisplay->biBitCount) { for(int q_iCycle=0;q_iCycle<256;q_iCycle++) { palette[q_iCycle].rgbBlue=(BYTE)q_iCycle; palette[q_iCycle].rgbGreen=(BYTE)q_iCycle; palette[q_iCycle].rgbRed=(BYTE)q_iCycle; palette[q_iCycle].rgbReserved=(BYTE)0; } } StretchDIBits(q_HDCControl, q_CRectControl.left, q_CRectControl.top, e_pIplImageShow->width, e_pIplImageShow->height, q_CRectControl.left, q_CRectControl.top, e_pIplImageShow->width, e_pIplImageShow->height, e_pIplImageShow->imageData, e_pBITMAPINFODisplay, DIB_RGB_COLORS, SRCCOPY); } 3 运行结果 运行结果图形如下: 4 小结 通过本次设计,让我很好的锻炼了理论联系实际,与具体项目、课题相结合开发、设计产品的能力。既让我们懂得了怎样把理论应用于实际,又让我们懂得了在实践中遇到的问题怎样用理论去解决。 在本次设计中,我们还需要大量的以前没有学到过的知识,于是图书馆和INTERNET 成了我们很好的助手。在查阅资料的过程中,我们要判断优劣、取舍相关知识,不知不觉中我们查阅资料的能力也得到了很好的锻炼。我们学习的知识是有限的,在以后的工作中我们肯定会遇到许多未知的领域,这方面的能力便会使我们受益非浅。 在设计过程中,总是遇到这样或那样的问题。有时发现一个问题的时候,需要做大量的工作,花大量的时间才能解决。自然而然,我的耐心便在其中建立起来了。为以后的工作积累了经验,增强了信心。 检测图片--圆 拍摄图片--圆 拍摄图片--矩形 检测图片--矩形