├── 304575391ec6f143e33b4a0fb7e103b6.png ├── 69fa287aa876f1e5c7655dca7b0066b1.png ├── 873142f36ff61e7311152a4bcf1be551.png ├── README.md ├── _1_png_dd005b86ad6ac.py ├── _2_png_e4276e2f67c3b.py ├── _3_png_caccab15864be.py ├── _4_png_873142f36ff61.py ├── _7_预处理.py ├── ae6201400f58b55112f5fd50ac9b0a50.png ├── c5d3cf10244086df302f6ed32ce0bdca.png ├── caccab15864bece0746680b93322a8b3.png ├── d91d24225f9d575a5528bc5c251aaa7b.png ├── dd005b86ad6ac12faf4e15a029b207d8.png ├── e4276e2f67c3b5d1d1f61b3937d742e0.png └── python.py /304575391ec6f143e33b4a0fb7e103b6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/304575391ec6f143e33b4a0fb7e103b6.png -------------------------------------------------------------------------------- /69fa287aa876f1e5c7655dca7b0066b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/69fa287aa876f1e5c7655dca7b0066b1.png -------------------------------------------------------------------------------- /873142f36ff61e7311152a4bcf1be551.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/873142f36ff61e7311152a4bcf1be551.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |  2 | # 1.背景 3 | 指针式机械表盘具有安装维护方便、结构简单、防电磁干扰等诸多优点, 目前广泛应用于工矿企业、能源及计量等部门。随着仪表数量的增加及精密仪表技术的发展,人工判读已经不能满足实际应用需求。随着计算机技术和图像处理技术的不断发展,指针式机械表自动读表技术应运而生。该技术提高了表盘识别的自动化程度及实时性,将代替传统工业仪表的读取方式得到广泛应用。 4 | # 2.国内外研究现状 5 | **识别对象的型号**:HCDL821-YB 避雷在线监测装置 6 | ![识别对象.png](c5d3cf10244086df302f6ed32ce0bdca.png) 7 | 8 | 9 | 10 | **识别难点**: 11 | 1.内表盘很深,导致内表盘面阴影严重,给内椭圆的识别增加难度。 12 | 2.电表外轮廓反光严重,如果采用点光源照明,会产生亮斑和眩光现象。 13 | 3.电表内表盘反光严重,导致不同角度颜色不同,不利于阈值的设置。 14 | 4.电表外轮廓经过打磨圆角处理,导致识别的椭圆外轮廓精度下降。 15 | 16 | **表盘特点**:刻度盘和指针具有颜色,而且不同的颜色区间代表不同的刻度值范围。 17 | # 3.算法的特点 18 | 1.可以识别不同光照条件下的表盘:强光、正常光、弱光、点光源、平行光源等。 19 | 2.可以识别不同拍摄角度的表盘:正面、左斜侧、右斜侧、前斜侧、后斜侧等。 20 | 3.可以识别不同距离的表盘:近距离拍摄、中距离拍摄、远距离拍摄等。 21 | 4.可以识别带干扰颜色的场景:红色桌子、白色墙纸、绿色桶、蓝色盆子等。 22 | 5.可以识别不同尺寸、像素的表盘照片。 23 | 6.识别效率高:整个程序运行时间在10秒以内,而传统的椭圆检测程序就需要一分钟以上。 24 | # 4.[算法流程图](https://afdian.net/item?plan_id=0ce112e45d8d11ed88e452540025c377) 25 | ![算法流程图.png](ae6201400f58b55112f5fd50ac9b0a50.png) 26 | 27 | # 5.算法过程可视化 28 | ![运行过程可视化.png](304575391ec6f143e33b4a0fb7e103b6.png) 29 | 30 | # 6.初步处理 31 | **preliminary_pretreatment()** 32 | 该函数进行表盘大致位置的找寻,该函数的亮点是一个参数自调整的cv.HoughCircles()。 33 | 其中param2参数会根据找寻圆的结果进行自动调整。(param2是检测阶段圆心的累加阈值,它越小,可以检测到更多的假圆。它越大,能通过检测的圆越少且更加接近完美的圆。) 34 | 35 | ```python 36 | circles, param, x, y, r = [ ], 50, 0, 0, 0 37 | while 1: 38 | circles = cv.HoughCircles(pre, cv.HOUGH_GRADIENT, 1, 20, param1=100, param2=param, minRadius=100, maxRadius=300) 39 | if circles is None: 40 | param = param - 5 41 | continue 42 | circles = np.uint16(np.around(circles)) 43 | for i in circles[0 , : ]: 44 | if i[2] > r and i[2] < width / 2: 45 | r = i[2] 46 | x = i[0] 47 | y = i[1] 48 | break 49 | 50 | ``` 51 | # 7.预处理 52 | **pretreatment()** 53 | [参考该博客提出的pretreatment()函数](https://mbd.pub/o/bread/Yp6UmZ5y),是图像预处理步骤,对图像进行灰度化、高斯滤波降噪、卷积模糊、边缘检测、形态学闭变换。 54 | 55 | 56 | ```python 57 | gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY) 58 | img_thresh = cv.GaussianBlur(gray, (5, 5), 0) 59 | kernel = np.ones((5, 5), np.float32) / 25 60 | img_thresh = cv.filter2D(img_thresh, -1, kernel) 61 | edges = cv.Canny(img_thresh, 50, 150, apertureSize=3) 62 | Matrix = np.ones((2, 2), np.uint8) 63 | img_edge = cv.morphologyEx(edges, cv.MORPH_CLOSE, Matrix) 64 | ``` 65 | ![1.png](dd005b86ad6ac12faf4e15a029b207d8.png) 66 | 67 | **findEllipse()** 68 | 该函数主要通过cv.fitEllipse()函数来拟合椭圆,再对拟合出的多条椭圆进行多条件的筛选,其中一个重要的筛选条件就是根据初步预处理center()函数得到的大致范围。 69 | 70 | ```python 71 | contours, hierarchy = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) 72 | X, Y, ma, MA,angle = 0, 0, 0, 0, 0 73 | height, width, channels = img_copy.shape 74 | 75 | for ind, cont in enumerate(contours): 76 | if (len(cont) > 5): 77 | (X0, Y0), (MA0, ma0), angle0 = cv.fitEllipse(cont) 78 | if ma0 < min(width,height) and MA0 < max(width,height) and distance(X0, Y0, x, y) < 1 / 2 * r and ma0 > ma and MA0 > MA(等): 79 | X, Y, MA, ma, angle = X0, Y0, MA0, ma0, angle0 80 | ``` 81 | ![2.png](e4276e2f67c3b5d1d1f61b3937d742e0.png) 82 | 83 | # 8.透视变换纠正拍摄角度 84 | **findvertex()** 85 | 86 | ```python 87 | points = [] 88 | img1 = np.zeros((img_copy.shape[0], img_copy.shape[1]), dtype=np.uint8) 89 | cv.ellipse(img1, (int(X), int(Y)), (int(MA / 2), int(ma / 2)), angle, 0, 360, (255, 255, 255), 2) 90 | img2 = np.zeros((img_copy.shape[0], img_copy.shape[1]), dtype=np.uint8) 91 | cv.line(img2, (int(X - math.cos(angle) * ma), int(Y + math.sin(angle) * ma)), 92 | (int(X + math.cos(angle) * ma), int(Y - math.sin(angle) * ma)), (255, 255, 255), 1) 93 | cv.line(img2, (int(X + math.sin(angle) * MA), int(Y + math.cos(angle) * MA)), 94 | (int(X - math.sin(angle) * MA), int(Y - math.cos(angle) * MA)), (255, 255, 255), 1) 95 | for i in range(img_copy.shape[0]): 96 | for j in range(img_copy.shape[1]): 97 | if img1[i, j] > 0 and img2[i, j] > 0: 98 | points.append((j, i)) 99 | point = list([]) 100 | n = points[0][0] 101 | for i in range(len(points)): 102 | if abs(points[i][0] - n) > 2: 103 | point.append(points[i]) 104 | n = points[i][0] 105 | point.append(points[0]) 106 | img3 = np.zeros((img_copy.shape[0], img_copy.shape[1]), dtype=np.uint8) 107 | cv.ellipse(img3, (int(X), int(Y)), (int(MA / 2), int(ma / 2)), angle, 0, 360, (255, 255, 255), -1) 108 | for i in range(img_copy.shape[0]): 109 | for j in range(img_copy.shape[1]): 110 | if img3[i, j] == 0: 111 | img_copy[i,j] = 255 112 | order = [] 113 | order.append(point[np.argmin(point, axis=0)[1]]) 114 | order.append(point[np.argmax(point, axis=0)[1]]) 115 | order.append(point[np.argmin(point, axis=0)[0]]) 116 | order.append(point[np.argmax(point, axis=0)[0]]) 117 | return img_copy,order 118 | ``` 119 | ![3.png](caccab15864bece0746680b93322a8b3.png) 120 | 121 | **perspective_transformation()** 122 | 透视变换指两个平面之间中心投影变换,该函数通过透视变换来矫正拍摄角度,减小刻度盘的圆度误差。 123 | 124 | ```python 125 | w = min(img_copy.shape[0], img_copy.shape[1]) 126 | pts1 = np.float32([[point[0][0], point[0][1]], [point[1][0], point[1][1]], 127 | [point[2][0], point[2][1]],[point[3][0], point[3][1]]]) 128 | pts2 = np.float32([[w / 2, 0], [w / 2, w], [0, w / 2], [w, w / 2]]) 129 | M = cv.getPerspectiveTransform(pts1, pts2) 130 | dst = cv.warpPerspective(img_copy, M, (w, w)) 131 | ``` 132 | ![4.png](873142f36ff61e7311152a4bcf1be551.png) 133 | 134 | # 9.仿射变换使刻度盘水平 135 | **alignment()** 136 | 仿射变换是一种二维坐标到二维坐标之间的线性变换,它保持了二维图形的“平直性”(直线经过变换之后依然是直线)和“平行性”(二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。 137 | 138 | ```python 139 | x0, y0,xlen,ylen = farpoint(point_k,point_k[-1]),x0 - point_k[-1][0],y0 - point_k[-1][1] 140 | deg = math.degrees(rad) 141 | image_center = tuple(np.array(img_copy.shape)[:2] / 2) 142 | rot_mat = cv.getRotationMatrix2D(image_center, deg, 1) 143 | dst_copy = cv.warpAffine(img_copy, rot_mat, img_copy.shape[:2], flags=cv.INTER_LINEAR) 144 | output = cv.warpAffine(output, rot_mat, output.shape[:2], flags=cv.INTER_LINEAR) 145 | ``` 146 | ![5.png](d91d24225f9d575a5528bc5c251aaa7b.png) 147 | 148 | # 10.读取表盘刻度 149 | farpoint()和nearpoint()#找出刻度盘中所有分界点。 150 | points2ciecle()#来根据刻度盘上任意三个点找出刻度盘圆弧的圆心坐标以及半径 151 | cal_ang()#对三个点构成的角度进行输出 152 | 根据刻度盘的每一个分界点对刻度盘进行分区(分为0、1、2、3四个部分),最后根据前面得到的两个角度以及指针所处的分区得出最终指针在刻度盘的读数,从而完成整个电表表盘的识别。 153 | 154 | ![6.png](69fa287aa876f1e5c7655dca7b0066b1.png) 155 | 156 | # 11.参考文献 157 | 参考博客[《Python基于OpenCV的指针式表盘检测系统》](https://mbd.pub/o/qunma/work) 158 | 参考博客[《基于OpenCV的指针式表盘检测系统》](https://s.xiaocichang.com/s/5090fe) 159 | 160 | --- 161 | #### 如果您需要更详细的【源码和环境部署教程】,除了通过【系统整合】小节的链接获取之外,还可以通过邮箱以下途径获取: 162 | #### 1.请先在GitHub上为该项目点赞(Star),编辑一封邮件,附上点赞的截图、项目的中文描述概述(About)以及您的用途需求,发送到我们的邮箱 163 | #### sharecode@yeah.net 164 | #### 2.我们收到邮件后会定期根据邮件的接收顺序将【完整源码和环境部署教程】发送到您的邮箱。 165 | #### 【免责声明】本文来源于用户投稿,如果侵犯任何第三方的合法权益,可通过邮箱联系删除。 -------------------------------------------------------------------------------- /_1_png_dd005b86ad6ac.py: -------------------------------------------------------------------------------- 1 | ![1.png](dd005b86ad6ac12faf4e15a029b207d8.png) 2 | 3 | **findEllipse()** 4 | 该函数主要通过cv.fitEllipse()函数来拟合椭圆,再对拟合出的多条椭圆进行多条件的筛选,其中一个重要的筛选条件就是根据初步预处理center()函数得到的大致范围。 5 | -------------------------------------------------------------------------------- /_2_png_e4276e2f67c3b.py: -------------------------------------------------------------------------------- 1 | ![2.png](e4276e2f67c3b5d1d1f61b3937d742e0.png) 2 | 3 | # 8.透视变换纠正拍摄角度 4 | **findvertex()** 5 | -------------------------------------------------------------------------------- /_3_png_caccab15864be.py: -------------------------------------------------------------------------------- 1 | ![3.png](caccab15864bece0746680b93322a8b3.png) 2 | 3 | **perspective_transformation()** 4 | 透视变换指两个平面之间中心投影变换,该函数通过透视变换来矫正拍摄角度,减小刻度盘的圆度误差。 5 | -------------------------------------------------------------------------------- /_4_png_873142f36ff61.py: -------------------------------------------------------------------------------- 1 | ![4.png](873142f36ff61e7311152a4bcf1be551.png) 2 | 3 | # 9.仿射变换使刻度盘水平 4 | **alignment()** 5 | 仿射变换是一种二维坐标到二维坐标之间的线性变换,它保持了二维图形的“平直性”(直线经过变换之后依然是直线)和“平行性”(二维图形之间的相对位置关系保持不变,平行线依然是平行线,且直线上点的位置顺序不变)。 6 | -------------------------------------------------------------------------------- /_7_预处理.py: -------------------------------------------------------------------------------- 1 | # 7.预处理 2 | **pretreatment()** 3 | [参考该博客提出的pretreatment()函数](https://mbd.pub/o/bread/Yp6UmZ5y),是图像预处理步骤,对图像进行灰度化、高斯滤波降噪、卷积模糊、边缘检测、形态学闭变换。 4 | 5 | -------------------------------------------------------------------------------- /ae6201400f58b55112f5fd50ac9b0a50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/ae6201400f58b55112f5fd50ac9b0a50.png -------------------------------------------------------------------------------- /c5d3cf10244086df302f6ed32ce0bdca.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/c5d3cf10244086df302f6ed32ce0bdca.png -------------------------------------------------------------------------------- /caccab15864bece0746680b93322a8b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/caccab15864bece0746680b93322a8b3.png -------------------------------------------------------------------------------- /d91d24225f9d575a5528bc5c251aaa7b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/d91d24225f9d575a5528bc5c251aaa7b.png -------------------------------------------------------------------------------- /dd005b86ad6ac12faf4e15a029b207d8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/dd005b86ad6ac12faf4e15a029b207d8.png -------------------------------------------------------------------------------- /e4276e2f67c3b5d1d1f61b3937d742e0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/qunshansj/opencv-python-pointer-dial-detection/20f8d124a8c5ec267a2f5855dc4dee1ff46ea57b/e4276e2f67c3b5d1d1f61b3937d742e0.png -------------------------------------------------------------------------------- /python.py: -------------------------------------------------------------------------------- 1 | python 2 | x0, y0,xlen,ylen = farpoint(point_k,point_k[-1]),x0 - point_k[-1][0],y0 - point_k[-1][1] 3 | deg = math.degrees(rad) 4 | image_center = tuple(np.array(img_copy.shape)[:2] / 2) 5 | rot_mat = cv.getRotationMatrix2D(image_center, deg, 1) 6 | dst_copy = cv.warpAffine(img_copy, rot_mat, img_copy.shape[:2], flags=cv.INTER_LINEAR) 7 | output = cv.warpAffine(output, rot_mat, output.shape[:2], flags=cv.INTER_LINEAR) 8 | --------------------------------------------------------------------------------