首页 > 代码库 > 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上圆形焊盘检测三种方法