Python+OpenCV实现对指针式仪表的识别

前言

本文是使用Python语言基于OpenCV对图像进行二次处理,使其进行识别辨认的一个 不成熟 的案例代码相关博文。

至于为什么说不成熟呢,因为Python并不是博主 ‘母语言’ 这个代码案例来源于一次和物联网相关的一次工作经历中产生的,代码参考了网上的一些例子,并以自己的实际业务也进行了二次开发和优化,在此向那些分享代码的好心人表示感谢 😜。

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import cv2
import numpy as np
from sklearn.cluster import KMeans
from sklearn.utils import shuffle
from math import cos, pi, sin
import os

method = cv2.TM_CCOEFF

# 模路径
TEMPLATE_FILE = os.path.dirname(os.path.dirname(__file__)) + "/static/likeImg/pointer/"

'''
可以设置矩形框
'''

def get_match_rect(template, img, method):
'''获取模板匹配的矩形的左上角和右下角的坐标,'''
w, h = template.shape[1], template.shape[0]
res = cv2.matchTemplate(img, template, method)
mn_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 使用不同的方法,对结果的解释不同
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + w, top_left[1] + h)
return top_left, bottom_right


def get_center_point(top_left, bottom_right):
'''传入左上角和右下角坐标,获取中心点'''
c_x, c_y = ((np.array(top_left) + np.array(bottom_right)) / 2).astype(np.int)
return c_x, c_y


def get_circle_field_color(img, center, r, thickness):
'''获取中心圆形区域的色值集'''
temp = img.copy().astype(np.int)
cv2.circle(temp, center, r, -100, thickness=thickness)
return img[temp == -100]


def v2_by_center_circle(img, colors):
'''二值化通过中心圆的颜色集合'''
for i in range(img.shape[0]):
for j in range(img.shape[1]):
a = img[i, j]
if a in colors:
img[i, j] = 0
else:
img[i, j] = 255


def v2_by_k_means(img):
'''使用k-means二值化'''
original_img = np.array(img, dtype=np.float64)
src = original_img.copy()
delta_y = int(original_img.shape[0] * (0.4))
delta_x = int(original_img.shape[1] * (0.4))
original_img = original_img[delta_y:-delta_y, delta_x:-delta_x]
h, w, d = src.shape
dts = min([w, h])
r2 = (dts / 2) ** 2
c_x, c_y = w / 2, h / 2
a: np.ndarray = original_img[:, :, 0:3].astype(np.uint8)
# 获取尺寸(宽度、长度、深度)
height, width = original_img.shape[0], original_img.shape[1]
depth = 3
image_flattened = np.reshape(original_img, (width * height, depth))
'''
用K-Means算法在颜色样本中建立2个类。
'''
image_array_sample = shuffle(image_flattened, random_state=0)
estimator = KMeans(n_clusters=2, random_state=0)
estimator.fit(image_array_sample)
'''
我们为原始图片的每个像素进行类的分配。
'''
src_shape = src.shape
new_img_flattened = np.reshape(src, (src_shape[0] * src_shape[1], depth))
cluster_assignments = estimator.predict(new_img_flattened)
compressed_palette = estimator.cluster_centers_
a = np.apply_along_axis(func1d=lambda x: np.uint8(compressed_palette[x]), arr=cluster_assignments, axis=0)
img = a.reshape(src_shape[0], src_shape[1], depth)
threshold = (compressed_palette[0, 0] + compressed_palette[1, 0]) / 2
img[img[:, :, 0] > threshold] = 255
img[img[:, :, 0] < threshold] = 0
for x in range(w):
for y in range(h):
distance = ((x - c_x) ** 2 + (y - c_y) ** 2)
if distance > r2:
pass
img[y, x] = (255, 255, 255)
return img


'''
设置指针位置
'''


def get_pointer_rad(img):
'''获取角度'''
shape = img.shape
# c_y, c_x, depth = int(shape[0] / 2), int(shape[1] / 2), shape[2]
c_y, c_x, depth = 498, 490, shape[2]
x1 = c_x + c_x * 0.8
src = img.copy()
freq_list = []
for i in range(361):
x = (x1 - c_x) * cos(i * pi / 180) + c_x
y = (x1 - c_x) * sin(i * pi / 180) + c_y
temp = src.copy()
cv2.line(temp, (c_x, c_y), (int(x), int(y)), (0, 0, 255), thickness=3)
t1 = img.copy()
t1[temp[:, :, 2] == 255] = 255
c = img[temp[:, :, 2] == 255]
points = c[c == 0]
freq_list.append((len(points), i))
return max(freq_list, key=lambda x: x[0])


class POINTER(object):
def __init__(self, path): # 构造函数不带参数
self.path = path

def start_get_val(self):
# 获取测试图像
img_s = cv2.imread(self.path)
img = cv2.cvtColor(img_s, cv2.COLOR_BGR2GRAY)
template = cv2.imread(TEMPLATE_FILE + 'template.jpg') # 模板
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
# 匹配并返回矩形坐标
top_left, bottom_right = get_match_rect(template, img, method)
cv2.rectangle(img_s, top_left, bottom_right, 255, 2)
#################################################################
new = img_s[top_left[1]:bottom_right[1] + 1, top_left[0]:bottom_right[0] + 1]
template = cv2.imread(TEMPLATE_FILE + 'template.jpg') # 模板
top_left, bottom_right = get_match_rect(template, new, method=method)
new_ = new[top_left[1]:bottom_right[1] + 1, top_left[0]:bottom_right[0] + 1]
# 二值化图像
img = v2_by_k_means(new_)
rad = get_pointer_rad(img)
print(rad)
#################################################################
if rad[1] <= 180:
val = 0
else:
val = ((rad[1] - 180) * 1.72) + 1 # 计算刻度值
if val < 100:
val = val + 3
print(val)
return round(val)


if __name__ == '__main__':
val = POINTER("图片路径").start_get_val()
print("刻度值1:" + val)
print("刻度值2:" + round(val))

案例中使用的表盘图片:

结果:

经过多次不用的仪表盘识别,得出的正确结果已经达到85%的准确率,当然这个结果在真正生产环境还是不行的。这个不过只是一个简单的Python+OpenCV进行图像识别的一个案例,代码还是存在要再进行二次优化的空间的。

结言:

分享是一种精神,分享也是使其技术进步的一种方式,以上就是Python+OpenCV进行图像识别的一个案例,如果有心的同学,也可以在我这个基础上进行再深一层的优化,参与开源,热衷技术,提升自己 😄 。