车辆仪表指针读数

本章记录实习期间做的车辆仪表检测程序中有关指针读数有关的问题和解决方案。
有关指针读数中最重要的部分为圆盘检测和指针检测,可以利用opencv库自带的霍夫曼圆检测来解决圆盘识别的问题,但要针对圆的半径大小去设置检测半径参数,如果设置不合理,检测效果就不理想,所以事先需要人为经验去估计圆盘的半径大小。识别出圆盘位置以后,要解决的问题就是指针识别,这里可以运用霍夫曼直线检测,但在直线检测之前要先对图像进行预处理,这里我使用的是CV2中的Canny()函数提取出图像的边缘信息,然后再将图像进行直线检测。如果用阈值法预处理图像然后再进行直线检测的效果可能不理想,会检测不到指针的正确位置,所以我把阈值处理法该为了边缘提取。下面是我的改进别人的代码:
import cv2
import numpy as np

def avg_circles(circles, b):
avg_x = 0
avg_y = 0
avg_r = 0
for i in range(b):
# optional - average for multiple circles (can happen when a gauge is at a slight angle)
avg_x = avg_x + circles[0][i][0]
avg_y = avg_y + circles[0][i][1]
avg_r = avg_r + circles[0][i][2]
avg_x = int(avg_x / (b))
avg_y = int(avg_y / (b))
avg_r = int(avg_r / (b))
return avg_x, avg_y, avg_r

def dist_2_pts(x1, y1, x2, y2):
# print np.sqrt((x2-x1)2+(y2-y1)2)
return np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)

def calibrate_gauge(gauge_number, file_type):
‘’’
This function should be run using a test image in order to calibrate the range available to the dial as well as the
units. It works by first finding the center point and radius of the gauge. Then it draws lines at hard coded intervals
(separation) in degrees. It then prompts the user to enter position in degrees of the lowest possible value of the gauge,
as well as the starting value (which is probably zero in most cases but it won’t assume that). It will then ask for the
position in degrees of the largest possible value of the gauge. Finally, it will ask for the units. This assumes that
the gauge is linear (as most probably are).
It will return the min value with angle in degrees (as a tuple), the max value with angle in degree45s (as a tuple),
and the units (as a string).
这个函数用测试图片来校准刻度盘和刻度盘可用的范围单位。需要之前所得的中心点以及半径。然后绘制出刻度。
需要输入表盘读数最小角度,最大角度,最小值,最大值,以及单位(min_angle,max_angle,min_value,max_value,units)
‘’’

img = cv2.imread('C:/Users/Desktop/python-cv-samples-master/examples/analog-gauge-reader/images/gauge-%s.%s' % (gauge_number, file_type))
print(img)
height, width = img.shape[:2]
# 将图片转为灰度图片
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # convert to gray
# gray = cv2.GaussianBlur(gray, (5, 5), 0)
# gray = cv2.medianBlur(gray, 5)

# for testing, output gray image
# cv2.imwrite('gauge-%s-bw.%s' %(gauge_number, file_type),gray)

# detect circles
# restricting the search from 35-48% of the possible radii gives fairly good results across different samples.  Remember that
# these are pixel values which correspond to the possible radii search range.
# 霍夫圆环检测
# image:8位,单通道图像
# method:定义检测图像中圆的方法。目前唯一实现的方法cv2.HOUGH_GRADIENT。

# dp:累加器分辨率与图像分辨率的反比。dp获取越大,累加器数组越小。
# minDist:检测到的圆的中心,(x,y)坐标之间的最小距离。如果minDist太小,则可能导致检测到多个相邻的圆。如果minDist太大,则可能导致很多圆检测不到。
# param1:用于处理边缘检测的梯度值方法。
# param2:cv2.HOUGH_GRADIENT方法的累加器阈值。阈值越小,检测到的圈子越多。
# minRadius:半径的最小大小(以像素为单位)。
# maxRadius:半径的最大大小(以像素为单位)。

circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, np.array([]), 100, 50, int(height * 0.4),
                           int(height * 0.5))
# average found circles, found it to be more accurate than trying to tune HoughCircles parameters to get just the right one
a, b, c = circles.shape
# 获取圆的坐标x,y和半径r
x, y, r = avg_circles(circles, b)

# draw center and circle
cv2.circle(img, (x, y), r, (0, 0, 255), 3, cv2.LINE_AA)  # draw circle
cv2.circle(img, (x, y), 2, (0, 255, 0), 3, cv2.LINE_AA)  # draw center of circle

# for testing, output circles on image
# cv2.imwrite('gauge-%s-circles.%s' % (gauge_number, file_type), img)

# for calibration, plot lines from center going out at every 10 degrees and add marker
# for i from 0 to 36 (every 10 deg)

'''
goes through the motion of a circle and sets x and y values based on the set separation spacing.  Also adds text to each
line.  These lines and text labels serve as the reference point for the user to enter
NOTE: by default this approach sets 0/360 to be the +x axis (if the image has a cartesian grid in the middle), the addition
(i+9) in the text offset rotates the labels by 90 degrees so 0/360 is at the bottom (-y in cartesian).  So this assumes the
gauge is aligned in the image, but it can be adjusted by changing the value of 9 to something else.
根据画出的刻度值,给定x,y的值,并在此位置添加文本信息。
这些刻度和文本标签用作用户输入的参考点

'''
separation = 5.0  # in degrees
interval = int(360 / separation)
p1 = np.zeros((interval, 2))  # set empty arrays
p2 = np.zeros((interval, 2))
p_text = np.zeros((interval, 2))
for i in range(0, interval):
    for j in range(0, 2):
        if (j % 2 == 0):
            p1[i][j] = x + 0.9 * r * np.cos(separation * i * 3.14 / 180)  # point for lines
        else:
            p1[i][j] = y + 0.9 * r * np.sin(separation * i * 3.14 / 180)
text_offset_x = 10
text_offset_y = 5
for i in range(0, interval):
    for j in range(0, 2):
        if (j % 2 == 0):
            p2[i][j] = x + r * np.cos(separation * i * 3.14 / 180)
            p_text[i][j] = x - text_offset_x + 1.2 * r * np.cos(
                (separation) * (i + 9) * 3.14 / 180)  # point for text labels, i+9 rotates the labels by 90 degrees
        else:
            p2[i][j] = y + r * np.sin(separation * i * 3.14 / 180)
            p_text[i][j] = y + text_offset_y + 1.2 * r * np.sin(
                (separation) * (i + 9) * 3.14 / 180)  # point for text labels, i+9 rotates the labels by 90 degrees

# add the lines and labels to the image
for i in range(0, interval):
    cv2.line(img, (int(p1[i][0]), int(p1[i][1])), (int(p2[i][0]), int(p2[i][1])), (0, 255, 0), 2)
    cv2.putText(img, '%s' % (int(i * separation)), (int(p_text[i][0]), int(p_text[i][1])), cv2.FONT_HERSHEY_SIMPLEX,
                0.3, (0, 0, 0), 1, cv2.LINE_AA)

cv2.imwrite('C:/Users/simao.wang.HIRAIN/Desktop/python-cv-samples-master/examples/analog-gauge-reader/images/gauge-%s-calibration.%s' % (gauge_number, file_type), img)

# get user input on min, max, values, and units
print('gauge number: %s' % gauge_number)
min_angle = input('Min angle (lowest possible angle of dial) - in degrees: ')  # the lowest possible angle
max_angle = input('Max angle (highest possible angle) - in degrees: ')  # highest possible angle
min_value = input('Min value: ')  # usually zero
max_value = input('Max value: ')  # maximum reading of the gauge
units = input('Enter units: ')

# for testing purposes: hardcode and comment out raw_inputs above
# min_angle = 45
# max_angle = 320
# min_value = 0
# max_value = 200
# units = "PSI"

return min_angle, max_angle, min_value, max_value, units, x, y, r

def get_current_value(img, min_angle, max_angle, min_value, max_value, x, y, r, gauge_number, file_type):
# for testing purposes
# img = cv2.imread(‘gauge-%s.%s’ % (gauge_number, file_type))

gray2 = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

edges = cv2.Canny(gray2, 50,150,apertureSize = 3)

# Set threshold and maxValue
# thresh = 70
# maxValue = 255
#
# # for testing purposes, found cv2.THRESH_BINARY_INV to perform the best
# # th, dst1 = cv2.threshold(gray2, thresh, maxValue, cv2.THRESH_BINARY);
# # th, dst2 = cv2.threshold(gray2, thresh, maxValue, cv2.THRESH_BINARY_INV);
# # th, dst3 = cv2.threshold(gray2, thresh, maxValue, cv2.THRESH_TRUNC);
# # th, dst4 = cv2.threshold(gray2, thresh, maxValue, cv2.THRESH_TOZERO);
# # th, dst5 = cv2.threshold(gray2, thresh, maxValue, cv2.THRESH_TOZERO_INV);
# # cv2.imwrite('gauge-%s-dst1.%s' % (gauge_number, file_type), dst1)
# # cv2.imwrite('gauge-%s-dst2.%s' % (gauge_number, file_type), dst2)
# # cv2.imwrite('gauge-%s-dst3.%s' % (gauge_number, file_type), dst3)
# # cv2.imwrite('gauge-%s-dst4.%s' % (gauge_number, file_type), dst4)
# # cv2.imwrite('gauge-%s-dst5.%s' % (gauge_number, file_type), dst5)
#
# # apply thresholding which helps for finding lines
# th, dst2 = cv2.threshold(gray2, thresh, maxValue, cv2.THRESH_BINARY_INV);

# found Hough Lines generally performs better without Canny / blurring, though there were a couple exceptions where it would only work with Canny / blurring
# dst2 = cv2.medianBlur(dst2, 5)
# dst2 = cv2.Canny(dst2, 50, 150)
# dst2 = cv2.GaussianBlur(dst2, (5, 5), 0)

# for testing, show image after thresholding
# cv2.imwrite('C:/Users/Desktop/python-cv-samples-master/examples/analog-gauge-reader/images/gauge-%s-tempdst1.%s' % (gauge_number, file_type), dst2)

# find lines
minLineLength = 300
maxLineGap = 5
lines = cv2.HoughLinesP(image=edges, rho=1, theta=np.pi / 180, threshold=100, minLineLength=minLineLength,
                        maxLineGap=maxLineGap)  # rho is set to 3 to detect more lines, easier to get more then filter them out later

# for testing purposes, show all found lines
# for i in range(0, len(lines)):
#   for x1, y1, x2, y2 in lines[i]:
#      cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)
#      cv2.imwrite('gauge-%s-lines-test.%s' %(gauge_number, file_type), img)

# remove all lines outside a given radius

print('lines', lines)
final_line_list = []
# print "radius: %s" %r

diff1LowerBound = 0.15  # diff1LowerBound and diff1UpperBound determine how close the line should be from the center
diff1UpperBound = 0.25
diff2LowerBound = 0.5  # diff2LowerBound and diff2UpperBound determine how close the other point of the line should be to the outside of the gauge
diff2UpperBound = 1.0
for i in range(0, len(lines)):
    for x1, y1, x2, y2 in lines[i]:
        # print('x1',x1)
        # print('y1',y1)
        # print('x2',x2)
        # print('y2',y2)
        diff1 = dist_2_pts(x, y, x1, y1)  # x, y is center of circle
        # print('diff1',diff1)
        diff2 = dist_2_pts(x, y, x2, y2)  # x, y is center of circle
        # print('diff2', diff2)
        # set diff1 to be the smaller (closest to the center) of the two), makes the math easier
        if (diff1 > diff2):
            temp = diff1
            diff1 = diff2
            diff2 = temp
        # check if line is within an acceptable range
        # print('diff1UpperBound*r',diff1UpperBound*r)
        # print('diff1LowerBound*r', diff1LowerBound*r)
        # print('diff2UpperBound*r', diff2UpperBound*r)
        # print('diff2LowerBound*r', diff2LowerBound*r)
        if (((diff1 < diff1UpperBound * r) and (diff1 > diff1LowerBound * r) and (
                diff2 < diff2UpperBound * r)) and (diff2 > diff2LowerBound * r)):
            print('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')
            print('diff1UpperBound*r', diff1UpperBound * r)
            print('diff1LowerBound*r', diff1LowerBound * r)
            print('diff2UpperBound*r', diff2UpperBound * r)
            print('diff2LowerBound*r', diff2LowerBound * r)
            print('diff1', diff1)
            print('diff2', diff2)
            line_length = dist_2_pts(x1, y1, x2, y2)
            # add to final list
            print([x1, y1, x2, y2])
            final_line_list.append([x1, y1, x2, y2])

# testing only, show all lines after filtering
# for i in range(0,len(final_line_list)):
#     x1 = final_line_list[i][0]
#     y1 = final_line_list[i][1]
#     x2 = final_line_list[i][2]
#     y2 = final_line_list[i][3]
#     cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)

# assumes the first line is the best one

print(final_line_list)
x1 = final_line_list[0][0]
y1 = final_line_list[0][1]
x2 = final_line_list[0][2]
y2 = final_line_list[0][3]
cv2.line(img, (x1, y1), (x2, y2), (0, 255, 0), 2)

# for testing purposes, show the line overlayed on the original image
# cv2.imwrite('gauge-1-test.jpg', img)
cv2.imwrite('C:/Users/Desktop/python-cv-samples-master/examples/analog-gauge-reader/images/gauge-%s-lines-1.%s' % (gauge_number, file_type), img)

# find the farthest point from the center to be what is used to determine the angle
dist_pt_0 = dist_2_pts(x, y, x1, y1)
dist_pt_1 = dist_2_pts(x, y, x2, y2)
if (dist_pt_0 > dist_pt_1):
    x_angle = x1 - x
    y_angle = y - y1
else:
    x_angle = x2 - x
    y_angle = y - y2
# take the arc tan of y/x to find the angle
res = np.arctan(np.divide(float(y_angle), float(x_angle)))
# np.rad2deg(res) #coverts to degrees

# print x_angle
# print y_angle
# print res
# print np.rad2deg(res)

# these were determined by trial and error
res = np.rad2deg(res)
if x_angle > 0 and y_angle > 0:  # in quadrant I
    final_angle = 270 - res
if x_angle < 0 and y_angle > 0:  # in quadrant II
    final_angle = 90 - res
if x_angle < 0 and y_angle < 0:  # in quadrant III
    final_angle = 90 - res
if x_angle > 0 and y_angle < 0:  # in quadrant IV
    final_angle = 270 - res

# print final_angle

old_min = float(min_angle)
old_max = float(max_angle)

new_min = float(min_value)
new_max = float(max_value)

old_value = final_angle

old_range = (old_max - old_min)
new_range = (new_max - new_min)
new_value = (((old_value - old_min) * new_range) / old_range) + new_min

return new_value

def main():
gauge_number = 1
file_type = ‘jpg’
# name the calibration image of your gauge ‘gauge-#.jpg’, for example ‘gauge-5.jpg’. It’s written this way so you can easily try multiple images
min_angle, max_angle, min_value, max_value, units, x, y, r = calibrate_gauge(gauge_number, file_type)

print('min_angle=', min_angle)
print('max_angle=', max_angle)
print('min_value=', min_value)
print('max_value=', max_value)
print('units=', units)
print('x=', x)
print('y=', y)
print('r=', r)

# feed an image (or frame) to get the current value, based on the calibration, by default uses same image as calibration
img = cv2.imread('C:/Users/Desktop/python-cv-samples-master/examples/analog-gauge-reader/images/gauge-%s.%s' % (gauge_number, file_type))

print()
val = get_current_value(img, min_angle, max_angle, min_value, max_value, x, y, r, gauge_number, file_type)
print("Current reading: %s %s" % (val, units))

if name == ‘main’:
main()

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值