主要通过opencv库内霍夫曼直线检测和圆检测实现指针识别,文章主要介绍思路。
源码下载地址https://download.csdn.net/download/qq_44781688/20009503.
(内涵报告和解释 分析)
python版本为3.7 但运行的库都很普通也没有什么特别复杂的计算,所以其他版本理论上应该也不会出现什么错误,其他库自行安装即可。
欢迎交流讨论
首先创建了一个light.py文件,利用两个表盘一样指针不同的图片对应位置比较留下最亮的像素,得到没有指针的表盘。
需要表盘位置比较准确,两张图表盘位置相近,否则太大差异没有意义
不相同也可以通过模板匹配或者截取部分等方式对齐,自行学习研究
用Image库求最亮去除指针,保存方便下一步去除表盘
from PIL import Image,ImageChops
#两图像求最亮去除指针
im=ImageChops.lighter(Image.open('6.jpg'), Image.open('7.jpg'))
im.save("none1.jpg")
创建了一个pointer.py文件 保留指针
与没有指针的none.jpg作比较 尽量去除表盘留下指针
实际发现2.5量程的表盘没有完全对齐 效果不是特别好 还需要进一步处理
1.6量程对齐了 效果很好
def pointer(filepath,judge):
im=Image.open(filepath)
if judge:
im_none= Image.open("none1.jpg")
else:
im_none = Image.open("none.jpg") #选取不同的量程图片做处理
im1= ImageChops.difference(im_none,im)
im1.save("pointer.jpg")
im = cv2.imread("pointer.jpg")
return im
利用 cv2.HoughCircles函数检测圆,一般来说不需要太多预处理,直接彩色图读进去处理即可,简单调整参数后即可检测到想要大小范围的圆。也可以用导入形态学
from scipy.ndimage import measurements
其中measurements.center_of_mass()可以找到各个物体中心,结果类似。但是不会一个物体检测到多次,但对同一圆参数不同可能cv2.HoughCircles会检测到多个,以下代码借此求平均加强精度
中心一般都不是特别准,需要简单调整结果如加减等
图像预处理 求中心
import cv2
import numpy as np
def centre(filepath,judge):
org = cv2.imread(filepath,1)
img = org
img_gray = cv2.cvtColor(org, cv2.COLOR_BGR2GRAY)
# 低同滤波进行平滑图像
img_gray = cv2.medianBlur(img_gray, 5)
cimg = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)
# 提取圆形
circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,10,param1=150,param2=90,minRadius=465,maxRadius=485)
circles = np.uint16(np.around(circles))
x = 0
y = 0
j=0
for i in circles[0,:]:
# draw the outer circle
cv2.circle(img,(i[0],i[1]),i[2],(0,0,255),10)
x = (x*j+i[0])/(j+1)
y = (y*j+i[1])/(j+1)
j=j+1
# draw the center of the circle
if judge:
y=y+8
x=x+1
cv2.circle(img, (int(x), int(y)), 2, (0, 0, 255), 5)#刻度不同图像不同偏差不同
else:
x=x-8
y=y-4
cv2.circle(img,(int(x), int(y)),2,(0,0,255),5)
# 显示原图和处理后的图像
# cv2.imshow("org",org)
cv2.imshow("processed",img)
print("中心为:",i[0]-8,i[1]-4)
cv2.waitKey(0)
img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
return img,int(x),int(y)
if __name__ == "__main__":
img,x,y=centre("5.jpg",0)
print(x,y)
pointer.py文件完成调取上述函数,完成对应部分后输出结果和图像
主要为了边缘检测后方便检测直线
frame = pointer.pointer(filepath,judge)#进行指针处理的图像 大部分运算用这个
frame = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR)
gray = frame.copy()
# cv2.imshow('origin', gray)
subplot(332)
imshow(gray)
title("去除表盘")
# 高斯除噪
kernel = np.ones((6, 6), np.float32) / 36
gray_cut_filter2D = cv2.filter2D(org[0:org.shape[0], 0:org.shape[1]], -1, kernel)
# 灰度图 二值化
gray_img = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(gray_img, 80, 100, cv2.THRESH_BINARY) # 小于70 黑 大于200白
#边缘检测
test_main = thresh1.copy()
edges = cv2.Canny(test_main, 50, 200, apertureSize=3)
主要用了cv2.HoughLinesP直接输出确定直线的两个点坐标,选取最长的两条直线,输出的前两个line,即为表针两边直线。(由于表针较粗所以能检测到两条,但是由于不是从中心所以角度不能直接计算读数,要求交点再通过中心计算)通过sympy库内的Line函数表示直线用intersection函数求两条直线交集,交点即为直线段点
也可以用cv2.HoughLines函数,输出的是距离和直线角度
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=15, maxLineGap=60) # 函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
for line in lines:
# print(lines) # 多维数组
x1, y1, x2, y2 = line[0]
cv2.line(result, (x1, y1), (x2, y2), (0, 0, 255),3)
cv2.drawMarker(result,(x2,y2),(255, 0, 0),thickness=2,markerType=cv2.MARKER_STAR,line_type=cv2.LINE_8,markerSize=20)
x1, y1, x2, y2 = lines[0][0]
l = Line(Point(x1, y1),Point( x2, y2))
x1, y1, x2, y2 = lines[1][0]
l1 = Line(Point(x1, y1),Point( x2, y2))
x = np.float64(l1.intersection(l)) #sympy库求直线交点
print("指针端点",x)
1.表针细,只检测到一条直线且过圆心,就通过输出的直线角度直接计算读数,但是通常误差可能大
2.其他影响检测到一条直线不过圆心:这就要圆心检测之后以圆心和半径创建用sympy库内Circle,直线与圆求交点,交点即与圆心和指针端点在同一条直线上。可视为指针端点
C1 = Circle(Point(x0, y0), 400)
l1 = Line(Point(x1, y1),Point( x2, y2))
print(l1.intersection(C1))
但是这样会输出两个点,需要区分指针方向来区分交点选哪一个
3.利用两条直线检测,可以不用区分交点位置,因为两条直线交点只有一个。但是还有问题就是个别图片检测不到两条直线或者检测到其他非表针的直线,就要多修改参数或者改变预处理方式尽量去除干扰
本文就是在发现指针旁文字多时出错进而多加了去除表盘的操作
直接使用指针端点和圆心直线斜率计算角度容易出现返回值小于180度的情况,无法显示180-360度的角度。
下面代码自己编写了计算函数通过两直线向量积求角度沿顺时针0-360输出
def angle(v1,v2):
x1,y1 = [v1[2]-v1[0],v1[3]-v1[1]]
x2,y2 = [v2[2]-v2[0],v2[3]-v2[1]]
dot = x1*x2+y1*y2
det = x1*y2-y1*x2
theta = np.arctan2(det, dot)
theta = theta if theta>0 else 2*np.pi+theta
distinguish = 2.5/(2.54/2.5)/265 #比例关系
output = theta * 180 / np.pi * distinguish
return output
outpointer=org
if judge:
org_x0, org_y0 = 340,719 #图像零点坐标 第一个刻度不是很均匀 实际选为零刻度下标点位置
else:
org_x0, org_y0 = 418,738
cv2.line(outpointer, (int(x[0][0]), int(x[0][1])), (x0,y0), 255, 3)
cv2.line(outpointer, (org_x0,org_y0), (x0,y0), 255, 3)
out = angle.angle((x0, y0,org_x0,org_y0),(x0, y0,int(x[0][0]), int(x[0][1])),judge)
subplot(337)
imshow(outpointer)
title("指针")
number = Image.new('RGB', (1280, 1024), 'black')
drawer = ImageDraw.Draw(number) #标注
font = ImageFont.truetype("simhei.ttf",200, encoding="utf-8")
drawer.text((0, 500), text="读数为", font=font, fill='red')
drawer.text((600, 500), text="%0.4f"%out, font=font, fill='red')
number = array(number)
subplot(338)
imshow(number)
title("读数")
show()
图示可见运行结果精度非常高
由于许多段落对于思路展示没有意思,所有代码有一定删减
有意者下载测试使用
源码下载地址https://download.csdn.net/download/qq_44781688/20009503.(内含报告和使用说明)其中每个函数成文件写了main函数可以单独运行调试
欢迎大家学习交流,有问题可以私信或者评论,我见到会尽快回复。