[图像识别] 1、如何识别一个指针式的时种的时间?

本文涉及的产品
图像搜索,7款服务类型 1个月
简介:


 

目录

一、算法基本原理

1、图片预处理

2、找表盘

3、找指针

4、指针映射

5、求时间

二、算法流程图

三、程序关键函数说明

1Canny

2HoughCircles

3HoughLines2

4MyLine

5平面几何相关函数

四、运行结果

五、实验中遇到的主要问题及解决方法:

1、在处理速度方面

2、去除其他圆的影响

3、霍夫找到的直线转换为夹角表示的线段

六、实验中的缺陷和不足


 

一、算法基本原理

时钟识别,顾名思义:就是根据一张带有钟表的图片识别出钟表上所展示的时间。对于这个问题我把它分为四步处理:

  • 1、 图片预处理

  由于一张图片中包含的圆圈和直线的信息量较大,直接进行霍夫处理要浪费大量的运算量,而且也不利于去除各种干扰。于是这里所谓的预处理就是利用canny算法先对原图进行边缘提取,一方面滤掉部分干扰,另一方面将原图转换为边缘图后只剩下主要线条信息,有利于加快处理。实验证明,用此方法有用信息丢失较少,可以采用!

  •  2、 找表盘

  找表盘主要运用霍夫找圆,但是由于一张图里会找出很多圆,因此关键还是滤掉其他有影响的圆!这里采用的方法是找出所有用霍夫找到的圆中半径最大且整个圆都在当前图片中的那个圆作为表盘。实践证明,用这种方法能够较为准确的去除其他圆的影响并准确找到表盘所对应的圆(对了,这里的钟默认为圆形的表盘的指针式钟)。

  • 3、 找指针

  找指针主要运用霍夫找线段,但是可想而知一张图里除了指针外一定还存在其他线段,而这些线段会对指针识别造成影响。因此我采用判断线段到圆心的距离小于一定的距离才默认为指针。然后对获取求得的线段进行转换为我定义的用斜率表示的线段的形式。

  • 4、 指针映射

  由于上面求得的线段并不是严格上的指针,而是很多线段,但是如果仔细观察会发现虽然并不是严格的对应关系,但是他们会出现成簇的分组效果!即:多条线段会聚集在某一个指针左右,而这些线段的斜率和预求得指针的斜率相差不大,我正是利用它的这个特点首先根据指针与X轴正方向的夹角从小到大进行排序,然后每次遇到相邻两个线段的夹角存在较大的跳变就进行切割,最后所有的线段被分割为一些组,再利用一个特殊的公式重新计算这组所代表指针的长度:

                         Le[num-1]=Le_ping[num-1]*0.2+Le_max[num-1]*0.8;

  •  5、 求时间

  通过上面四步的计算我们已经能够将线段分为三组或者两组,这样再根据长短来和时分秒三个或者时分指针进行对应,再根据夹角就能求出对应的时间。

 

二、算法流程图

 

 

三、程序关键函数说明

1、 Canny

void cvCanny( const CvArr* image,CvArr* edges,double threshold1,double threshold2, int aperture_size=3 );

说明: 开放计算机视觉(OpenCV)库库函数之一,用于对图像的边缘检测(采用canny算法)。

  • image 输入单通道图像(可以是彩色图像)对于多通道的图像可以用cvCvtColor()修改。
  • edges 输出的边缘图像 ,也是单通道的,但是是黑白的
  • threshold1 第一个阈值
  • threshold2 第二个阈值
  • aperture_size Sobel 算子内核大小

附加说明: 函数 cvCanny 采用 Canny 算法发现输入图像的边缘而且在输出图像中标识这些边缘。threshold1和threshold2 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。

 

2、 HoughCircles

CvSeq *cvHoughCircles(CvArr *image,void *circle_storage,int method,double dp,double min_dist,double param1,double param2,int min_radius,int max_radius);

说明:该函数用Hough变换在二值图像中中寻找圆,成功时返回CvSeq指针。

  • image:输入8bit(灰度)图像,其内容可被函数所改变
  • circle_storage:检测到的圆存储仓,可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回)或者是包含圆参数的特殊类型的具有单行/单列的CV_32FC3型矩阵(CvMat*). 矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的圆。如果 circle_storage 是矩阵,而实际圆的数目超过矩阵尺寸,那么最大可能数目的圆被返回,每个圆由三个浮点数表示:圆心坐标(x,y)和半径.).
  • method:Hough 变换方式,目前只支持CV_HOUGH_GRADIENT, which is basically 21HT, described in [Yuen03].
  • dp:寻找圆弧圆心的累计分辨率,这个参数允许创建一个比输入图像分辨率低的累加器。(这样做是因为有理由认为图像中存在的圆会自然降低到与图像宽高相同数量的范畴)。如果dp设置为1,则分辨率是相同的;如果设置为更大的值(比如2),累加器的分辨率受此影响会变小(此情况下为一半)。dp的值不能比1小。
  • min_dist:该参数是让算法能明显区分的两个不同圆之间的最小距离。
  • param1:用于Canny的边缘阀值上限,下限被置为上限的一半。
  • param2:累加器的阀值。
  • min_radius:最小圆半径。
  • max_radius:最大圆半径。

 

3、 HoughLines2

CvSeq* cvHoughLines2(CvArr* image,void* line_storage,int mehtod,double rho,double theta,int threshold,double param1 =0,double param2 =0);

说明: 此函数是opencv图像变换函数中的一个,主要用来访问霍夫变换的两个算法———标准霍夫变换(SHT)和累计概率霍夫变换(PPHT)。

  • Image:输入 8-比特、单通道 (二值) 图像,当用CV_HOUGH_PROBABILISTIC方法检测的时候其内容会被函数改变。
  • line_storage:检测到的线段存储仓. 可以是内存存储仓 (此种情况下,一个线段序列在存储仓中被创建,并且由函数返回),或者是包含线段参数的特殊类型(见下面)的具有单行/单列的矩阵(CvMat*)。矩阵头为函数所修改,使得它的 cols/rows 将包含一组检测到的线段。如果 line_storage 是矩阵,而实际线段的数目超过矩阵尺寸,那么最大可能数目的线段被返回(线段没有按照长度、可信度或其它指标排序).
  • method
  • Hough 变换变量,是下面变量的其中之一:

CV_HOUGH_STANDARD - 传统或标准 Hough 变换. 每一个线段由两个浮点数 (ρ, θ) 表示,其中 ρ 是直线与原点 (0,0) 之间的距离,θ 线段与 x-轴之间的夹角。因此,矩阵类型必须是 CV_32FC2 type.

CV_HOUGH_PROBABILISTIC - 概率 Hough 变换(如果图像包含一些长的线性分割,则效率更高). 它返回线段分割而不是整个线段。每个分割用起点和终点来表示,所以矩阵(或创建的序列)类型是 CV_32SC4.

CV_HOUGH_MULTI_SCALE - 传统 Hough 变换的多尺度变种。线段的编码方式与 CV_HOUGH_STANDARD 的一致。

  • Rho:与像素相关单位的距离精度
  • Theta:弧度测量的角度精度
  • Threshold:阈值参数。如果相应的累计值大于 threshold, 则函数返回这条线段.
  • param1:第一个方法相关的参数:

对传统 Hough 变换,不使用(0).

对概率 Hough 变换,它是最小线段长度.

对多尺度 Hough 变换,它是距离精度 rho 的分母 (大致的距离精度是 rho 而精确的应该是 rho / param1 ).

  • param2:第二个方法相关参数:

对传统 Hough 变换,不使用 (0).

对概率 Hough 变换,这个参数表示在同一条直线上进行碎线段连接的最大间隔值(gap), 即当同一条直线上的两条碎线段之间的间隔小于param2时,将其合二为一。

对多尺度 Hough 变换,它是角度精度 theta 的分母 (大致的角度精度是 theta 而精确的角度应该是 theta / param2).

 

4、 MyLine

复制代码
 1 //-----------------------------------------------------------------------------
 2 class MyLine{
 3 public:
 4     int id;//编号
 5     int k;//倾斜角[0-360)
 6     int l;//长度
 7 public:
 8     MyLine(int ID=0,int K=0,int L=0){id=ID,k=K,l=L;}//构造函数
 9     bool operator<(const MyLine &A){return k<A.k;}//重定义小于号
10     void print(){printf("id: %3d  k: %3d°  l: %3d\n",id,k,l);}//输出函数
11 };//自定义直线
12 //-----------------------------------------------------------------------------
复制代码

 

5、 平面几何相关函数

复制代码
 1 //-----------------------------------------------------------------------------
 2 //平面几何相关函数http://www.cnblogs.com/zjutlitao/p/3243883.html
 3 //-----------------------------------------------------------------------------
 4 #define eps 0.0000000001
 5 #define PI acos(-1.0)
 6 int dcmp(double x){
 7     if(fabs(x)<eps)return 0;
 8     else return x<0 ? -1:1;
 9 }
10 double Dot(Point A,Point B){return A.x*B.x+A.y*B.y;}//向量点积
11 double Length(Point A){return sqrt(Dot(A,A));}//向量模长
12 double Cross(Point A,Point B){return A.x*B.y-A.y*B.x;}//向量叉积
13 double Angle(Point A,Point B){return acos(Dot(A,B)/Length(A)/Length(B));}//求向量的夹角
14 double DistanceToLine(Point P,Point A,Point B)//点到直线的距离
15 {
16     Point v1=B-A,v2=P-A;
17     return fabs(Cross(v1,v2))/Length(v1);//如果不加绝对值是带有方向的距离
18 }
19 double DistancetoSegment(Point P,Point A,Point B){//点到线段的距离
20     if(A==B)return Length(P-A);
21     Point v1=B-A,v2=P-A,v3=P-B;
22     if(dcmp(Dot(v1,v2))<0)return  Length(v2);
23     else if(dcmp(Dot(v1,v3))>0)return Length(v3);
24     else return fabs(Cross(v1,v2))/Length(v1);
25 }
26 //-----------------------------------------------------------------------------
复制代码

 

四、运行结果

PS:由于篇幅有限,这里就不把全部的图片列出了~

 

五、实验中遇到的主要问题及解决方法:

1、在处理速度方面:

如果直接用原图做霍夫变换计算量巨大而且干扰特别多,这里我先用canny进行边缘提取预处理,然后再进行运算就解决了上述问题。但是用霍夫变换的图不能在上面绘制霍夫找到的直线或圆,结果就要转换为BGR彩图,才能进行可视化显示~具体的两个操作为: 

  • Canny(src, temp, 10, 140, 3);//提取边缘(如果不边缘提取就会浪费巨大时间)
  • cvtColor(temp, dst, CV_GRAY2BGR);//将边缘提取的灰度图转换为BGR图便于画线

 

2、去除其他圆的影响:

由于用霍夫找圆会找到比较多的圆,如何在这些圆中找出和表盘最相近的一个呢?这里采用了比较巧妙的一个方法:

复制代码
 1 //储存检测圆的容器  
 2 std::vector<Vec3f> circles; 
 3 //调用Hough变换检测圆  
 4 //参数为:待检测图像,检测结果,检测方法(这个参数唯一),累加器的分辨率,两个圆间的距离,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径  
 5 HoughCircles(temp,circles,CV_HOUGH_GRADIENT,2,50,200,100,100,300);  
 6 //找出圆盘(因为最大的不一定是的,所以加了几个限制条件)
 7 int pos=0;
 8 int max=-1;
 9 for(size_t i = 0; i < circles.size(); i++ )
10 {  
11     Vec3f f=circles[i];
12     if(f[2]>max && f[0]+f[2]<temp.rows && f[0]-f[2]>=0 && f[1]+f[2]<temp.cols && f[1]-f[2]>0)
13     {
14         max=f[2];
15         pos=i;
16     }
17 } 
18 Point center(circles[pos][0],circles[pos][1]);//找到的圆心
19 int   radius= circles[pos][2];//找到的半径
20 circle(dst,center,radius,Scalar(255),2); 
复制代码

如上面所示:遍历所有霍夫找的圆,记录其中半径最大的且满足整个圆在图像内的那个,作为目标圆,这样就巧妙地找出了我们需要的那个圆~

 

3、霍夫找到的直线转换为夹角表示的线段:

因为接下来要根据夹角进行分组,所以这里要把霍夫找到的直线进行转换,这里我自己定义一个MyLine的类,用于保存一条线段,该线段形式为夹角和长度,其中转换关系为:

复制代码
 1 list<MyLine> list_MyLine;
 2 vector<Vec4i> lines2;//线段检测
 3 HoughLinesP(temp, lines2, 1, CV_PI/180, 50, 50, 10 );
 4 for( size_t i = 0; i < lines2.size(); i++ )
 5 {
 6     Vec4i l = lines2[i];
 7     Point A(l[0], l[1]),B(l[2], l[3]);
 8     if(DistancetoSegment(center,A,B)<30)//根据圆心到指针的距离阈值滤掉其他线段
 9     {
10         bool down=(A.y+B.y-2*center.y>0);//判断长的在过圆心的水平线上部还是下部
11         if(A.x==B.x){//斜率为无穷的情况
12             list_MyLine.push_back(MyLine(i,90+(down?180:0),Length(Point(A.x-B.x,A.y-B.y))));
13         }else if(A.y==B.y){//水平的情况
14             list_MyLine.push_back(MyLine(i,A.x+B.x-2*center.x>0 ? 0:180,Length(Point(A.x-B.x,A.y-B.y))));
15         }else{
16             if(down){
17                 if(A.y>center.y)
18                     list_MyLine.push_back(MyLine(i,360-atan2(A.y-B.y,A.x-B.x)*180/PI,Length(Point(A.x-B.x,A.y-B.y))));
19                 else 
20                     list_MyLine.push_back(MyLine(i,360-atan2(B.y-A.y,B.x-A.x)*180/PI,Length(Point(A.x-B.x,A.y-B.y))));
21             }else{
22                 if(A.y<center.y)
23                     list_MyLine.push_back(MyLine(i,abs(atan2(A.y-B.y,A.x-B.x)*180/PI),Length(Point(A.x-B.x,A.y-B.y))));
24                 else 
25                     list_MyLine.push_back(MyLine(i,abs(atan2(B.y-A.y,B.x-A.x)*180/PI),Length(Point(A.x-B.x,A.y-B.y))));
26             }
27         }
28         line(dst,A,B, Scalar(0,0,i*20+40), 2, CV_AA);
29     }    
30 }
复制代码

 

六、实验中的缺陷和不足

  虽然用我这种识别方法可以快速有效的识别很多时钟,但是也存在一些特殊情况无法处理,比如:1、圆心不准导致计算出错;2、影子出现导致出现诡异指针;3、另类指针导致霍夫求得的直线不能准确描述指针;4、非圆形的表盘根本Hold不住;5、当存在其他干扰时表盘找不准;6、指针后半部分影响导致误判为另一个指针;7、指针长度检测错误导致时分秒针分配错误…等情况,虽然采用一些限制条件可以去除其中一二个错误,但是当算法向这方面偏的时候,又会导致另一些识别好的情况出现错误。最后总结一句:用图像识别不能求普遍性解决问题,我们应该根据具体的问题,在限定条件下进行研究,否则将永远满足不了需求!  

 

 

链接:

本文链接:http://www.cnblogs.com/zjutlitao/p/4187476.html

文档下载:http://pan.baidu.com/s/1i3koenr

工程下载:http://pan.baidu.com/s/1jGst6lC

无法识别:http://pan.baidu.com/s/1o6t7rnG

更多精彩:http://www.cnblogs.com/zjutlitao/p/4125085.html

GitHub链接: https://github.com/beautifulzzzz/OpenCV-Clock-Identification



本文转自beautifulzzzz博客园博客,原文链接:http://www.cnblogs.com/zjutlitao/p/4187476.html,如需转载请自行联系原作者

相关文章
|
4月前
|
机器学习/深度学习 人工智能 算法
【坚果识别】果实识别+图像识别系统+Python+计算机课设+人工智能课设+卷积算法
坚果识别系统,使用Python语言进行开发,通过TensorFlow搭建卷积神经网络算法模型,对10种坚果果实('杏仁', '巴西坚果', '腰果', '椰子', '榛子', '夏威夷果', '山核桃', '松子', '开心果', '核桃')等图片数据集进行训练,得到一个识别精度较高的模型文件,让后使用Django搭建Web网页端界面操作平台,实现用户上传一张坚果图片 识别其名称。
54 0
|
JSON 人工智能 Java
Java视觉智能识别
JavaVision是一个基于Java开发的全能视觉智能识别项目,不仅实现PaddleOCR-V4、YoloV8物体识别、人脸识别、以图搜图等核心功能,还可以轻松扩展到其他领域,如语音识别、动物识别、安防检查等。这使得JavaVision成为一个全面解决多种场景需求的自适应平台。
378 0
|
机器学习/深度学习 传感器 编解码
【图像识别】基于hog特征的机器学习交通标识识别附matlab代码
【图像识别】基于hog特征的机器学习交通标识识别附matlab代码
|
人工智能 文字识别 计算机视觉
阿里妈妈图像识别刷新世界最好成绩,识别虚假推广信息准确率超95%
阿里妈妈图像团队的OCR(图中文字识别)技术刷新了ICDAR Robust Reading竞赛数据集的全球最好成绩,并大幅超越第二名。
10223 2
|
机器学习/深度学习 计算机视觉
【图像识别】白天鹅黑天鹅灰天鹅?卷积神经网络帮你识别
本文将通过一系列的天鹅图片来解释卷积神经网络(CNN)的概念,并使用CNN在常规多层感知器神经网络上处理图像。
1780 0
|
11天前
|
机器学习/深度学习 人工智能 测试技术
深度学习在图像识别中的应用与挑战
本文探讨了深度学习技术,尤其是卷积神经网络(CNN)在图像识别任务中的最新进展和面临的主要挑战。通过分析不同的网络架构、训练技巧以及优化策略,文章旨在提供一个全面的概览,帮助研究人员和实践者更好地理解和应用这些技术。
45 9
|
8天前
|
机器学习/深度学习 人工智能 算法
深度学习在图像识别中的应用与挑战
本文探讨了深度学习技术在图像识别领域的应用,重点分析了卷积神经网络(CNN)的工作原理及其在处理图像数据方面的优势。通过案例研究,展示了深度学习如何提高图像识别的准确性和效率。同时,文章也讨论了当前面临的主要挑战,包括数据不足、过拟合问题以及计算资源的需求,并提出了相应的解决策略。
|
3天前
|
机器学习/深度学习 自然语言处理 算法
深度学习在图像识别中的应用
本文将探讨深度学习在图像识别领域的应用,包括卷积神经网络(CNN)的原理、架构以及在图像识别中的优势。通过实例分析,我们将展示如何构建和训练一个深度学习模型来处理图像识别任务,并讨论其在实际场景中的应用潜力。
21 15
|
3天前
|
机器学习/深度学习 数据采集 边缘计算
深度学习在图像识别中的应用与挑战
本文深入探讨了深度学习在图像识别领域的应用,并分析了当前面临的主要挑战。通过具体案例和数据分析,展示了深度学习技术如何推动图像识别的边界,同时指出了数据质量、模型泛化能力及计算资源等方面的限制因素。文章旨在为研究人员和从业者提供一个关于深度学习在图像识别中应用现状与未来发展方向的全面视角。
|
11天前
|
机器学习/深度学习 人工智能 计算机视觉
探索深度学习在图像识别中的突破与挑战##
本文深入探讨了深度学习技术在图像识别领域的最新进展,重点分析了卷积神经网络(CNN)作为核心技术的演变历程,从LeNet到AlexNet,再到VGG、ResNet等先进架构的创新点。不同于传统摘要形式,本文摘要旨在通过一系列关键里程碑事件,勾勒出深度学习推动图像识别技术飞跃的轨迹,同时指出当前面临的主要挑战,如模型泛化能力、计算资源依赖性及数据偏见问题,为读者提供一个宏观且具体的发展脉络概览。 ##
38 7
下一篇
无影云桌面