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

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

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 }

结果如下图所示:

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 }

结果如下图所示:

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 }

这里模板图像:

结果:

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

时间: 2024-08-27 21:57:58

PCB上圆形焊盘检测三种方法的相关文章

xcode 上 crash 调试的三种方法

最近有新人问crash调试方法,简介记录如下: 模拟器调试 打开控制台查看输出日志 显示出错的行数 显示出错的函数iOS Crash跟踪 真机调试 首先修改真机调试的 bundle ID,使代码可以进行真机调试,连接真机.然后增加 Exception Breakpoint,运行程序,断点将打印出出错的位置和函数名. 运行在真机 连接真机,选择windows->devices->选择连接的真机;运行程序,控制台将输出所有运行在真机上的log. 选择按钮"View Device Logs

关于上一篇鼠标移到按钮时的“按下”效果的三种方法

上一篇博文中,关于按钮按下的效果回过头研究了下,总结了如下三种方法,只写出关键样式: 1.相对定位 1 input.button{ 2 3 position:relative; //用相对定位 4 } 5 6 input.button:hover{ 7 top:2px;//鼠标移动到此top增加2px 8 } 2.主要利用外边距这个属性,鼠标移动到按钮位置时,按钮上外边距增加2px,下外边距减少2px(相当于走出去2px又退回来2px),就可以达到按下效果,如果只是单独写margin-top:2

网络超时检测的三种方法

作者:于老师,华清远见嵌入式学院讲师. 网络通信中,很多操作会使得进程阻塞,这时我们要设定时间,到时间后强制返回,避免进程在没有数据的情况下无限阻塞 这里我们总结一下网络超时检测的三种方法: 通过setsockopt设置套接字属性SO_RCVTIMEO struct timeval t = {5, 0}           if  (setsockopt(listenfd, SOL_SOCKET, SO_RCVTIMEO, &t, sizeof(t)) == -1) {             

JavaScript高级程序设计--对象创建的三种方法

创建对象的三种方法: 1.工厂模式 工厂模式是软件工程领域广为人知的设计模式,这种模式抽象了创建具体对象的过程.下面是使用工厂函数创建对象的的一个例子. 2.构造函数: 从上面的例子中,我们看到构造函数与工厂函数不同之处: 1.没有显式的创建对象 2.直接将属性和方法赋给了this对象,没有return语句 另外,函数名Person使用了首字母大写.(这是一个惯例,构造函数始终都应该以一个大写字母开头,而非构造函数应该以一个小写字母开头.) 使用构造函数来创建对象,意味着你可以获取对象的类型.这

Java实现ping功能的三种方法

Java实现ping功能的三种方法 检测设备的运行状态,有的是使用ping的方式来检测的.所以需要使用java来实现ping功能. 为了使用java来实现ping的功能,有人推荐使用java的 Runtime.exec()方法来直接调用系统的Ping命令,也有人完成了纯Java实现Ping的程序,使用的是Java的NIO包(native io, 高效IO包).但是设备检测只是想测试一个远程主机是否可用.所以,可以使用以下三种方式来实现: 1.Jdk1.5的InetAddresss方式 自从Jav

7.1 安装软件包的三种方法7.2 rpm包介绍 7.3 rpm工具用法 7.4 yum工具用法7.5 yum搭建本地仓库

- 7.1 安装软件包的三种方法 - 7.2 rpm包介绍 - 7.3 rpm工具用法 - 7.4 yum工具用法 - 7.5 yum搭建本地仓库 - 扩展 1. yum保留已经安装过的包 http://www.360doc.com/content/11/0218/15/4171006_94080041.shtml 2. 搭建局域网yum源 http://ask.apelearn.com/question/7627 # 7.1 安装软件包的三种方法 - windows的.exe文件实际上是一种二

二十一、安装软件包的三种方法、RPM包介绍、rpm工具用法、yum工具用法、yum搭建本地仓库

一.安装软件包的三种方法 rpm工具.yum工具.源码包 RPM工具:是以一种数据库记录的方式将需要的套件安装到Linux主机的一套管理程序.就是说,你的Linux系统中有一个关于RPM的数据库,它记录了安装的包以及包与包之间的依赖关系.RPM包是预先在Linux机器上编译并打包的文件,安装快捷.缺点:安装环境必须与编译时的环境一致或相当,包与包之间存在着相互依赖的情况,卸载包时需先把依赖的包卸载.若依赖的包是系统必需的,就不能卸载这个包,否则系统崩溃. yum工具:优势:可联网下载所需要的RP

安装软件包的三种方法 、rpm包介绍、rpm工具用法、yum工具用法、yum搭建本地仓库安装软件包的

安装软件包的三种方法rpm包类似于windows的exe文件.rpm工具可以安装rpm包,安装路径和文件名都是固定好的,不需要过多的设置.yum也可以安装rpm包,比rpm工具好的一点是支持自动安装依赖的包.源码包主要是源代码,需要谁用编译器编译成可执行的文件.·····rpm包介绍首先我们要把光盘连接到linux系统进行挂载,打开vmware运行虚拟机,然后右键右下角的光盘的图标然后点连接,然后在系统里挂载,挂载点可以自己选,我们使用命令:mount /dev/cdrom /mnt/把光盘挂载

Java实现ping功能的三种方法及Linux的区分

前大半部份转自:https://blog.csdn.net/futudeniaodan/article/details/52317650 检测设备的运行状态,有的是使用ping的方式来检测的.所以需要使用java来实现ping功能. 为了使用java来实现ping的功能,有人推荐使用java的 Runtime.exec()方法来直接调用系统的Ping命令,也有人完成了纯Java实现Ping的程序,使用的是Java的NIO包(native io, 高效IO包).但是设备检测只是想测试一个远程主机是