目录
一、前言二、使用方法1.安装相关的库2.运行 三、方法说明MeterDetection类说明类参数主函数self.ImgCutCircle() 截取表盘区域,滤除背景self.ContoursFilter() 对轮廓进行筛选self.FitNumLine()轮廓拟合直线self.getIntersectionPoints()获取刻度线交点self.FitPointerLine()拟合指针直线段读数 总结一、前言
第一个版本的指针式仪表识别读数程序写得有点乱,有空重新整理下代码,写成了类MeterDetection进行封装。
原始指针仪表识别地址
基于深度学习方法的指针识别
程序地址:指针式仪表读数源码github
数据集为coco.zip,在github中
记得点个star
二、使用方法
1.安装相关的库
pip install -r requirments.txt
numpy==1.19.5opencv_python==4.5.5.64
2.运行
python main.py
<main.py>
from MeterClass import *if __name__ =="__main__": #多张图片,修改输入文件夹# imglist=glob.glob('input/*.jpg') # for imgpath in imglist: #A=MeterDetection(imgpath)#A.Readvalue()#一张图片imgpath='images/1.jpg'A=MeterDetection(imgpath) #创建类对象readValue=A.Readvalue()#调用类方法
三、方法说明
MeterDetection类说明
<MeterClass.py>
类参数
定义了类中的相关参数
class MeterDetection:def __init__(self,path):self.imageName=path.split('/')[-1].split('.')[0]self.outputPath=os.getcwd()+'/outputs/'self.image=cv2.imread(path)self.circleimg=Noneself.panMask=None #霍夫圆检测切割的表盘图片self.poniterMask =None#指针图片self.numLineMask=None #刻度线图片self.centerPoint=None #中心点[x,y]self.farPoint=None#指针端点[x,y]self.zeroPoint=None #起始点[x,y]self.r=None #半径self.divisionValue=100/360 #分度值self.makeFiledir()self.markZeroPoint()
主函数
调用类中其他的方法进行仪表读数
def Readvalue(self):try:self.ImgCutCircle()self.ContoursFilter()self.FitNumLine()self.getIntersectionPoints()self.FitPointerLine()v1=[self.zeroPoint[0]-self.centerPoint[0],self.centerPoint[1]-self.zeroPoint[1]]v2=[self.farPoint[0]-self.centerPoint[0],self.centerPoint[1]-self.farPoint[1]]theta=Functions.GetClockAngle(v1,v2)readValue=self.divisionValue*thetaprint(theta,readValue)return readValueexcept Exception as e:# 写一个exceptprint("程序错误:",e)
self.ImgCutCircle() 截取表盘区域,滤除背景
def ImgCutCircle(self):#截取表盘区域,滤除背景img=self.imagedst = cv2.pyrMeanShiftFiltering(img, 10, 100)cimage = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)circles = cv2.HoughCircles(cimage, cv2.HOUGH_GRADIENT, 1, 80, param1=100, param2=20, minRadius=80, maxRadius=0)circles = np.uint16(np.around(circles)) # 把类型换成整数r_1 = circles[0, 0, 2]c_x = circles[0, 0, 0]c_y = circles[0, 0, 1]circle = np.ones(img.shape, dtype="uint8")circle = circle * 255cv2.circle(circle, (c_x, c_y), int(r_1), 0, -1)bitwiseOr = cv2.bitwise_or(img, circle)cv2.imwrite(self.outputPath+self.imageName + '_1_imgCutCircle.jpg' , bitwiseOr)self.cirleData = [r_1, c_x, c_y]self.panMask=bitwiseOrreturn bitwiseOr
self.ContoursFilter() 对轮廓进行筛选
def ContoursFilter(self):#对轮廓进行筛选""":funtion : 提取刻度线,指针:param a: 高斯滤波 GaussianBlur,自适应二值化adaptiveThreshold,闭运算:param b: 轮廓寻找 findContours,:return:lineSet,new_needleset"""r_1, c_x, c_y = self.cirleDataimg = self.image.copy()# cv2.circle(img, (c_x, c_y), 20, (23, 28, 28), -1)img = cv2.GaussianBlur(img, (3, 3), 0)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)binary = cv2.adaptiveThreshold(~gray, 255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, -10)# cv2.circle(binary, (c_x, c_y), int(r_1*0.5), (0, 0, 0),5)# 闭运算# kernel = np.ones((3, 3), np.uint8)#膨胀# dilation = cv2.dilate(binary, kernel, iterations=1)# kernel2 = np.ones((3, 3), np.uint8)#腐蚀# erosion = cv2.erode(dilation, kernel2, iterations=1)#轮廓查找,根据版本不同,返回参数不同if cv2.__version__ >'4.0.0':contours, hier = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)else:aa,contours, hier = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)cntset = [] # 刻度线轮廓集合cntareas = [] # 刻度线面积集合needlecnt = [] # 指针轮廓集合needleareas = [] # 指针面积集合radiusLength = [r_1 * 0.6, r_1 * 1] # 半径范围# cv2.drawContours(img, contours, -1, (255, 90, 60), 2)# cv2.imwrite(self.outputPath+self.imageName + '_2_----numLineMask.jpg' , img)localtion = []for cnt in contours:rect = cv2.minAreaRect(cnt)# print(rect)#(中心点坐标,(宽度,高度),旋转的角度)= = recta, (w, h), c = rect w = int(w)h = int(h)''' 满足条件:“长宽比例”,“面积”'''if h == 0 or w == 0:passelse:dis = Functions.Disttances((c_x, c_y), a)# if (radiusLength[0] < dis and radiusLength[1] > dis):if (radiusLength[0] < dis and radiusLength[1] > dis):#矩形筛选if h / w > 4 or w / h > 4:localtion.append(dis)cntset.append(cnt)cntareas.append(w * h)else:if w > r_1 / 2 or h > r_1 / 2:needlecnt.append(cnt)needleareas.append(w * h)cntareas = np.array(cntareas)areasMean = Functions.couputeMean(cntareas) # 中位数,上限区new_cntset = []# 面积for i, cnt in enumerate(cntset):if (cntareas[i] <= areasMean * 1.5 and cntareas[i] >= areasMean * 0.8):new_cntset.append(cnt)self.r = np.mean(localtion)mask = np.zeros(img.shape[0:2], np.uint8)self.poniterMask = cv2.drawContours(mask, needlecnt, -1, (255, 255, 255), -1) # 生成掩膜mask = np.zeros(img.shape[0:2], np.uint8)self.numLineMask = cv2.drawContours(mask, new_cntset, -1, (255, 255, 255), -1) # 生成掩膜cv2.imwrite(self.outputPath+self.imageName + '_2_numLineMask.jpg' , self.numLineMask)cv2.imwrite(self.outputPath+self.imageName + '_3_poniterMask.jpg' , self.poniterMask)# for cnt in needlecnt:#cv2.fillConvexPoly(mask,cnt , 255)self.new_cntset=new_cntsetreturn new_cntset
self.FitNumLine()轮廓拟合直线
def FitNumLine(self):""" 轮廓拟合直线"""lineSet = [] # 拟合线集合img=self.image.copy()for cnt in self.new_cntset:rect = cv2.minAreaRect(cnt)# 获取矩形四个顶点,浮点型box = cv2.boxPoints(rect)box = np.int0(box)cv2.polylines(img, [box], True, (0, 255, 0), 1) # picoutput = cv2.fitLine(cnt, 2, 0, 0.001, 0.001)k = output[1] / output[0]k = round(k[0], 2)b = output[3] - k * output[2]b = round(b[0], 2)x1 = 1x2 = img.shape[0]y1 = int(k * x1 + b)y2 = int(k * x2 + b)# cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 1)#lineSet:刻度线拟合直线数组,k斜率 blineSet.append([k, b]) # 求中心点的点集[k,b]cv2.imwrite(self.outputPath+self.imageName + '_4_fitNumLine.jpg' , img)self.lineSet=lineSetreturn lineSet
self.getIntersectionPoints()获取刻度线交点
def getIntersectionPoints(self):#获取刻度线交点img = self.imagelineSet=self.lineSetw, h, c = img.shapepoint_list = []xlist=[]ylist=[]if len(lineSet) > 2:# print(len(lineSet))np.random.shuffle(lineSet)lkb = int(len(lineSet) / 2)kb1 = lineSet[0:lkb]kb2 = lineSet[lkb:(2 * lkb)]# print('len', len(kb1), len(kb2))kb1sample = random.sample(kb1, int(len(kb1) / 2))kb2sample = random.sample(kb2, int(len(kb2) / 2))else:kb1sample = lineSet[0]kb2sample = lineSet[1]for i, wx in enumerate(kb1sample):# for wy in kb2:for wy in kb2sample:k1, b1 = wxk2, b2 = wy# print('kkkbbbb',k1[0],b1[0],k2[0],b2[0])# k1-->[123]try:if (b2 - b1) == 0:b2 = b2 - 0.1if (k1 - k2) == 0:k1 = k1 - 0.1x = (b2 - b1) / (k1 - k2)y = k1 * x + b1x = int(round(x))y = int(round(y))except:x = (b2 - b1 - 0.01) / (k1 - k2 + 0.01)y = k1 * x + b1x = int(round(x))y = int(round(y))# x,y=solve_point(k1, b1, k2, b2)if x < 0 or y < 0 or x > w or y > h:break# point_list.append([x, y])xlist.append(x)ylist.append(y)# cv2.circle(img, (x, y), 2, (122, 22, 0), 2)# print('point_list',point_list)cx=int(np.mean(xlist))cy=int(np.mean(ylist))self.centerPoint=[cx,cy]cv2.circle(img, (cx, cy), 2, (0, 0, 255), 2)cv2.imwrite(self.outputPath+self.imageName + '_5_IntersectionPoints.jpg' , img)return img
self.FitPointerLine()拟合指针直线段
def FitPointerLine(self):#拟合指针直线段img =self.poniterMaskorgin_img=self.image.copy()# kernel = np.ones((3, 3), np.uint8)# mask = cv2.dilate(img, kernel, iterations=1)# img = cv2.erode(mask, kernel, iterations=1)lines = cv2.HoughLinesP(img, 1, np.pi / 180, 100, minLineLength=int(self.r / 2), maxLineGap=2)# nmask = np.zeros(img.shape, np.uint8)# lines = mential.findline(self=0, cp=[x, y], lines=lines)# print('lens', len(lines))dmax=0pointerLine=[]#最长的线段为指针for line in lines:x1, y1, x2, y2 = line[0]d1=Functions.Disttances((x1, y1),(x2, y2))if(d1>dmax):dmax=d1pointerLine=line[0]x1, y1, x2, y2 = pointerLined1=Functions.Disttances((x1, y1),(self.centerPoint[0],self.centerPoint[1]))d2=Functions.Disttances((x2, y2),(self.centerPoint[0],self.centerPoint[1]))if d1 > d2:self.farPoint = [x1, y1]else:self.farPoint = [x2, y2]cv2.line(orgin_img, (x1, y1), (x2, y2), 20, 1, cv2.LINE_AA)cv2.circle(orgin_img,(self.farPoint[0],self.farPoint[1]), 2, (0, 0, 255),2)cv2.imwrite(self.outputPath+self.imageName + '_6_PointerLine.jpg' , img)cv2.imwrite(self.outputPath+self.imageName + '_7_PointerPoint.jpg' , orgin_img)
–
读数
计算夹角
v1=[self.zeroPoint[0]-self.centerPoint[0],self.centerPoint[1]-self.zeroPoint[1]]v2=[self.farPoint[0]-self.centerPoint[0],self.centerPoint[1]-self.farPoint[1]]theta=Functions.GetClockAngle(v1,v2)readValue=self.divisionValue*theta
总结
对程序重新进行封装,提高了可读性
程序地址:指针式仪表读数源码github
记得点个star
创作不易,有需要开发的可以联系我,在校研究生
如果觉得《指针式仪表识别读数 Python(已开源数据集)》对你有帮助,请点赞、收藏,并留下你的观点哦!