一、前言
第一個版本的指針式儀表識別讀數(shù)程序?qū)懙糜悬c亂,有空重新整理下代碼,寫成了類MeterDetection進行封裝。
原始指針儀表識別地址
基于深度學習方法的指針識別
程序地址:指針式儀表讀數(shù)源碼github
數(shù)據(jù)集為coco.zip,在github中
記得點個star
二、使用方法
1.安裝相關(guān)的庫
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) #創(chuàng)建類對象
readValue=A.Readvalue() #調(diào)用類方法
三、方法說明
MeterDetection類說明
<MeterClass.py>
類參數(shù)
定義了類中的相關(guān)參數(shù)
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()
主函數(shù)
調(diào)用類中其他的方法進行儀表讀數(shù)
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() 截取表盤區(qū)域,濾除背景
def ImgCutCircle(self):
#截取表盤區(qū)域,濾除背景
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)) # 把類型換成整數(shù)
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)
#輪廓查找,根據(jù)版本不同,返回參數(shù)不同
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)
#(中心點坐標,(寬度,高度),旋轉(zhuǎn)的角度)= = 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) # 中位數(shù),上限區(qū)
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:刻度線擬合直線數(shù)組,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)
–
讀數(shù)
計算夾角文章來源:http://www.zghlxwxcb.cn/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
總結(jié)
對程序重新進行封裝,提高了可讀性
程序地址:指針式儀表讀數(shù)源碼github
記得點個star
創(chuàng)作不易,有需要開發(fā)的可以聯(lián)系我,在校研究生文章來源地址http://www.zghlxwxcb.cn/news/detail-417883.html
到了這里,關(guān)于指針式儀表識別讀數(shù) Python(已開源數(shù)據(jù)集)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!