首页 > 代码库 > PCB上圆形焊盘检测三种方法
PCB上圆形焊盘检测三种方法
鄙人初学opencv,在论坛上有人在做PCB板圆形焊盘检测,自己尝试用三种方法来检测圆形焊盘,下面为检测图像:
1、轮廓和霍夫圆变换
1 void main() 2 { 3 IplImage* src=http://www.mamicode.com/cvLoadImage("1.jpg",CV_LOAD_IMAGE_UNCHANGED);//三通道的彩色图像 4 IplImage* dst=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1); 5 cvCvtColor(src,dst,CV_RGB2GRAY); 6 for (int i=0;i<dst->height;i++) //反色 7 { 8 uchar* ptr=(uchar*)(dst->imageData+i*dst->widthStep); 9 for (int j=0;j<dst->width;j++)10 {11 ptr[j]=255-ptr[j];12 }13 }14 cvSmooth(dst,dst,CV_MEDIAN,11);15 cvThreshold(dst,dst,200,255,CV_THRESH_TOZERO);16 //创建核,结构元素原点非常重要,17 //以5*5的核为例,中心点在(2,2)位置,改为(1,1)后,画出整个轮廓向左上角平移18 IplConvKernel* myModel=cvCreateStructuringElementEx(5,5,2,2,CV_SHAPE_ELLIPSE);19 /*cvErode(dst,dst,myModel,3);*/20 /*cvDilate(dst,dst,myModel,2);*/21 /*cvMorphologyEx(dst,dst,NULL,NULL,CV_MOP_OPEN,1);*/22 cvMorphologyEx(dst,dst,NULL,myModel,CV_MOP_OPEN,4);23 /*cvMorphologyEx(dst,dst,NULL,NULL,CV_MOP_CLOSE,1);*/24 25 //////////////////////////////////////////////////////////////////////////26 /*27 * 从上面的二值图可以看出,对于圆提取影响较大的是矩形28 * 而矩形和圆分开,可以用圆形度特征分开,实践证明该方法不可行29 * 另一种思路:比较矩形和圆轮廓的外接矩形,利用长与宽的差值作为特征来实现30 */31 //利用圆形度来检测32 //for (CvSeq* c=contour;c!=NULL;c=c->h_next)33 //{34 // //实验表明圆形度不可用35 // //计算轮廓面积36 // double area=cvContourArea(c,CV_WHOLE_SEQ);37 // //计算轮廓周长38 // double length=cvArcLength(c,CV_WHOLE_SEQ,-1);39 // std::cout<<area<<std::endl;40 // //计算圆形度41 // double e=4*CV_PI*abs(area)/(length*length);42 // 43 // //判断轮廓是否为圆44 // if (e>=0.885)45 // {46 // cvDrawContours(dst,c,cvScalarAll(255),cvScalarAll(255),10,1);47 // }48 //}49 CvMemStorage* storage=cvCreateMemStorage();50 CvSeq* results=cvHoughCircles(dst,storage,CV_HOUGH_GRADIENT,2.6,10,10,30);51 for (int i=0;i<results->total;i++)52 {53 float* p=(float*)cvGetSeqElem(results,i);54 if (p[2]>19||p[2]<12)55 continue;56 std::cout<<p[2]<<std::endl;57 CvPoint2D32f pt=cvPoint2D32f(p[0],p[1]);58 cvCircle(src,cvPointFrom32f(pt),cvRound(p[2]),CV_RGB(255,0,0));/**/59 }60 cvShowImage("2",src);61 cvWaitKey();62 }
结果如下图所示:
2、采用质心标准圆检测:
1 #include "Stafx.h" 2 void main() 3 { 4 IplImage* src=http://www.mamicode.com/cvLoadImage("1.jpg",CV_LOAD_IMAGE_UNCHANGED);//三通道的彩色图像 5 IplImage* dst=cvCreateImage(cvSize(src->width,src->height),IPL_DEPTH_8U,1); 6 cvCvtColor(src,dst,CV_RGB2GRAY); 7 for (int i=0;i<dst->height;i++) //反色 8 { 9 uchar* ptr=(uchar*)(dst->imageData+i*dst->widthStep); 10 for (int j=0;j<dst->width;j++) 11 { 12 ptr[j]=255-ptr[j]; 13 } 14 } 15 cvSmooth(dst,dst,CV_MEDIAN,11); 16 cvThreshold(dst,dst,200,255,CV_THRESH_TOZERO); 17 //创建核,结构元素原点非常重要, 18 //以5*5的核为例,中心点在(2,2)位置,改为(1,1)后,画出整个轮廓向左上角平移 19 IplConvKernel* myModel=cvCreateStructuringElementEx(5,5,2,2,CV_SHAPE_ELLIPSE); 20 /*cvErode(dst,dst,myModel,3);*/ 21 /*cvDilate(dst,dst,myModel,2);*/ 22 /*cvMorphologyEx(dst,dst,NULL,NULL,CV_MOP_OPEN,1);*/ 23 cvMorphologyEx(dst,dst,NULL,myModel,CV_MOP_OPEN,4); 24 /*cvMorphologyEx(dst,dst,NULL,NULL,CV_MOP_CLOSE,1);*/ 25 26 ////////////////////////////////////////////////////////////////////////// 27 /* 28 * 从上面的二值图可以看出,对于圆提取影响较大的是矩形 29 * 而矩形和圆分开,可以用圆形度特征分开,实践证明该方法不可行 30 * 另一种思路:比较矩形和圆轮廓的外接矩形,利用长与宽的差值作为特征来实现 31 */ 32 //利用圆形度来检测 33 //for (CvSeq* c=contour;c!=NULL;c=c->h_next) 34 //{ 35 // //实验表明圆形度不可用 36 // //计算轮廓面积 37 // double area=cvContourArea(c,CV_WHOLE_SEQ); 38 // //计算轮廓周长 39 // double length=cvArcLength(c,CV_WHOLE_SEQ,-1); 40 // std::cout<<area<<std::endl; 41 // //计算圆形度 42 // double e=4*CV_PI*abs(area)/(length*length); 43 // 44 // //判断轮廓是否为圆 45 // if (e>=0.885) 46 // { 47 // cvDrawContours(dst,c,cvScalarAll(255),cvScalarAll(255),10,1); 48 // } 49 //} 50 CvMemStorage* storage=cvCreateMemStorage(); 51 //寻找轮廓 52 CvSeq* contour=NULL; 53 CvContourScanner scanner=cvStartFindContours(dst,storage,sizeof(CvContour), 54 CV_RETR_EXTERNAL,CV_CHAIN_APPROX_SIMPLE,cvPoint(0,0)); 55 CvSeq* sq=NULL; 56 do 57 { 58 sq=cvFindNextContour(scanner); 59 if (sq==NULL) break; 60 //计算外接矩形 61 CvRect rect=cvBoundingRect(sq,0); 62 //只有 63 int cha=rect.width-rect.height; 64 if (cha>5) 65 cvSubstituteContour(scanner,NULL); 66 } while (sq!=0); 67 sq=cvEndFindContours(&scanner); 68 //画轮廓二值图 69 std::vector<CvPoint2D32f> central; //存放质心 70 std::vector<int> label; 71 /************************************************************************/ 72 /* 从图上可以分析,该图中圆的半径大小只有两种形式,两种标准圆 73 可想而知,两种圆的半径是确定的,可以事先确定两种标准圆的半径的大小, 74 通过轮廓的二值图中,每个轮廓周长进行聚类分析,确定阈值为90,周长低于90 75 为小圆,半径为14个像素;周长高于90为小圆,半径为18个像素 76 */ 77 /************************************************************************/ 78 for (sq;sq!=NULL;sq=sq->h_next) 79 { 80 //寻找质心 81 CvMoments moment; 82 cvContourMoments(sq,&moment); 83 double m00=moment.m00; 84 double m10=moment.m10; 85 double m01=moment.m01; 86 CvPoint2D32f point=cvPoint2D32f(m10/m00,m01/m00); 87 central.push_back(point); //将每个轮库所得到质心保存下来,压入容器中 88 //计算轮廓周长 89 double length=cvArcLength(sq,CV_WHOLE_SEQ,-1); 90 if (length>90) 91 label.push_back(1); //大圆标签为1 92 else label.push_back(0); //小圆标签为0 93 cvDrawContours( dst, sq, cvScalarAll(255), cvScalarAll(0), -1, CV_FILLED, 8); 94 } 95 //腐蚀掉残留的轮廓 96 cvMorphologyEx(dst,dst,NULL,NULL,CV_MOP_OPEN,1); 97 printf("质心点的数目:%d",central.size()); 98 99 cvShowImage("1",dst);100 /*cvWaitKey();*/101 //将圆画出来102 std::vector<CvPoint2D32f>::iterator it;103 std::vector<int>::iterator flag=label.begin(); //标签开始处104 for (it=central.begin();it!=central.end(),flag!=label.end();it++,flag++)105 {106 if(*flag==1)107 cvCircle(src,cvPointFrom32f(*it),18,CV_RGB(255,0,0)); //画大圆108 if(*flag==0)109 cvCircle(src,cvPointFrom32f(*it),14,CV_RGB(255,255,0)); //画小圆110 cvCircle(src,cvPointFrom32f(*it),2,CV_RGB(0,255,0)); //画圆心111 }112 cvShowImage("2",src);113 cvWaitKey();114 }
结果如下图所示:
3、第三种方法:多目标模板匹配算法
1 //模板匹配法 2 CvPoint cvGetNextMinLoc(IplImage*,IplImage*,CvPoint,double); 3 void main() 4 { 5 IplImage* image=cvLoadImage("1.jpg",CV_LOAD_IMAGE_UNCHANGED); 6 //将已经创建模板图像,图像的宽与高均为30,图像中央为一个半径18的三通道 7 //圆,作为模板 8 IplImage* templ=cvLoadImage("2.jpg",CV_LOAD_IMAGE_UNCHANGED); 9 int rwidth=image->width-templ->width+1;10 int rheight=image->height-templ->height+1;11 IplImage* result=cvCreateImage(cvSize(rwidth,rheight),IPL_DEPTH_32F,1);12 std::vector<std::string> str; //容器用来存放窗口的名称13 str.push_back("1");14 str.push_back("2");15 cvMatchTemplate(image,templ,result,CV_TM_CCORR_NORMED);16 /************************************************************************/17 /* 模板匹配函数cvMatchTemplate依次计算模板与待测图片的重叠区域的相似度,18 并将结果存入映射图像result当中,也就是说result图像中的每一个点的值依次19 代表了一次相似度比较结果20 */21 /************************************************************************/22 double min_val; //相似度最小值23 double max_val; //相似度最大值24 CvPoint min_loc; //相似度最小值所对应的坐标25 CvPoint max_loc; //相似度最大值所对应的坐标26 cvMinMaxLoc(result,&min_val,&max_val,&min_loc,&max_loc,NULL);27 std::cout<<"相似度最小值:"<<min_val<<std::endl<<"相似度最大值:"<<max_val<<std::endl;28 printf("相似度最小值位置坐标:(%d, %d)\n",min_loc.x,min_loc.y);29 printf("相似度最大值位置坐标:(%d, %d)\n",max_loc.x,max_loc.y);30 /************************************************************************/31 /*32 这里有个奇怪的问题,CV_TM_CCORR_NORMED该匹配算法,相似度最大值为最好匹配,33 首先,我通过两种方式进行对比:34 第一种,通过在输入图像中截屏方式获取模板图像,但是匹配的话,需要将相似度最大值位置坐标35 作为画矩形框的左上角的坐标;36 第二种,通过在opencv中直接画出目标图像,保存为模板图像,但是匹配的话,需要将相似度最小值位置坐标37 作为画矩形框的左上角的坐标38 而本次用的是第二种方式39 */40 /************************************************************************/41 cvRectangle(image,min_loc,cvPoint(min_loc.x+templ->width,min_loc.y+templ->height),CV_RGB(255,0,0));42 int count=35;43 CvPoint newmin_loc=min_loc;44 //寻找下一个相似度较低的45 while(count--)46 {47 newmin_loc=cvGetNextMinLoc(result,templ,newmin_loc,max_val);48 //画出目标图像的外接矩形49 cvRectangle(image,newmin_loc,cvPoint(newmin_loc.x+templ->width,newmin_loc.y+templ->height),CV_RGB(255,0,0));50 }51 cvShowImage(str[0].c_str(),result);52 cvShowImage(str[1].c_str(),image);53 cvWaitKey();54 }55 //需找下一个匹配点56 CvPoint cvGetNextMinLoc(IplImage* result,IplImage* templ,CvPoint min_loc, double max_val)57 {58 int startX=min_loc.x;59 int startY=min_loc.y;60 int endX=min_loc.x+templ->width;61 int endY=min_loc.y+templ->height;62 //防止越界63 if(endX>result->width)64 endX=result->width;65 if(endY>result->height)66 endY=result->height;67 //将最小的相似度换成最大值,从而剔除最小的,重新寻找最小的68 for(int i=startX;i<endX;i++)69 for(int j=startY;j<endY;j++)70 {71 cvSetReal2D(result,j,i,max_val);72 }73 CvPoint newmax_loc;74 CvPoint newmin_loc;75 double newmax_val;76 double newmin_val;77 cvMinMaxLoc(result,&newmin_val,&newmax_val,&newmin_loc,&newmax_loc,NULL);78 return newmin_loc;79 }
这里模板图像:
结果:
这三种方法的精度还是不够,需要进一步努力。
PCB上圆形焊盘检测三种方法
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。