PCB上圆形焊盘检测三种方法

鄙人初学opencv,在论坛上有人在做PCB板圆形焊盘检测,自己尝试用三种方法来检测圆形焊盘,下面为检测图像:

PCB上圆形焊盘检测三种方法_第1张图片

1、轮廓和霍夫圆变换

 1 void main()
 2 {
 3     IplImage* src=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 }

结果如下图所示:

PCB上圆形焊盘检测三种方法_第2张图片

PCB上圆形焊盘检测三种方法_第3张图片

2、采用质心标准圆检测:

  1 #include "Stafx.h"
  2 void main()
  3 {
  4     IplImage* src=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 }

结果如下图所示:

PCB上圆形焊盘检测三种方法_第4张图片

PCB上圆形焊盘检测三种方法_第5张图片

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上圆形焊盘检测三种方法_第6张图片

这三种方法的精度还是不够,需要进一步努力。

你可能感兴趣的:(方法)