一、前言
第一个版本的指针式仪表识别读数程序写得有点乱,有空重新整理下代码,写成了类MeterDetection进行封装。
原始指针仪表识别地址
基于深度学习方法的指针识别
程序地址:指针式仪表读数源码github
数据集为coco.zip,在github中
记得点个star
二、使用方法
1.安装相关的库
pip install -r requirments.txt
numpy==1.19.5
opencv_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=None
self.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*theta
print(theta,readValue)
return readValue
except Exception as e:# 写一个except
print("程序错误:",e)
self.ImgCutCircle() 截取表盘区域,滤除背景
def ImgCutCircle(self):
#截取表盘区域,滤除背景
img=self.image
dst = 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 * 255
cv2.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=bitwiseOr
return 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.cirleData
img = 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)
#(中心点坐标,(宽度,高度),旋转的角度)= = rect
a, (w, h), c = rect
w = int(w)
h = int(h)
''' 满足条件:“长宽比例”,“面积”'''
if h == 0 or w == 0:
pass
else:
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_cntset
return 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) # pic
output = 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 = 1
x2 = 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斜率 b
lineSet.append([k, b]) # 求中心点的点集[k,b]
cv2.imwrite(self.outputPath+self.imageName + '_4_fitNumLine.jpg' , img)
self.lineSet=lineSet
return lineSet
self.getIntersectionPoints()获取刻度线交点
def getIntersectionPoints(self):
#获取刻度线交点
img = self.image
lineSet=self.lineSet
w, h, c = img.shape
point_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 = wx
k2, b2 = wy
# print('kkkbbbb',k1[0],b1[0],k2[0],b2[0])
# k1-->[123]
try:
if (b2 - b1) == 0:
b2 = b2 - 0.1
if (k1 - k2) == 0:
k1 = k1 - 0.1
x = (b2 - b1) / (k1 - k2)
y = k1 * x + b1
x = int(round(x))
y = int(round(y))
except:
x = (b2 - b1 - 0.01) / (k1 - k2 + 0.01)
y = k1 * x + b1
x = 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.poniterMask
orgin_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=0
pointerLine=[]
#最长的线段为指针
for line in lines:
x1, y1, x2, y2 = line[0]
d1=Functions.Disttances((x1, y1),(x2, y2))
if(d1>dmax):
dmax=d1
pointerLine=line[0]
x1, y1, x2, y2 = pointerLine
d1=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)
–
读数
计算夹角文章来源:https://www.toymoban.com/news/detail-417883.html
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
创作不易,有需要开发的可以联系我,在校研究生文章来源地址https://www.toymoban.com/news/detail-417883.html
到了这里,关于指针式仪表识别读数 Python(已开源数据集)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!