如何使用Open CV读取模拟量表

时间:2017-06-10 15:22:26

标签: opencv raspberry-pi detect

我有兴趣尝试使用Raspberry PI和Open CV读取模拟量表。我在opencv中只是真的搞乱了脸部检测,所以我甚至都不知道从哪里开始。任何想法,起点?

1 个答案:

答案 0 :(得分:1)

您可以使用HoughCircles方法检测圆圈,并使用Python中的opencv lib使用HoughLinesP方法检测线条。检测到这些后,您可以通过三角法从线的位置找出量规的值。 你可以在python中看到示例代码。它基本上是这样做的:

  • 使用imread方法读取图像。
  • 使用cvtColor将其变为灰色。
  • 用HoughCircles找出圆圈的中心x,y坐标和半径,这些方法有一些可以调整的参数。
  • 再次使用HoughLinesP方法检测线条,应调整参数。
  • 计算值,考虑最大值,仪表的最小值和仪表的角度间隔。

参考:https://github.com/intel-iot-devkit/python-cv-samples/tree/master/examples/analog-gauge-reader 希望这可以帮助。 代码:

import os
import cv2
import numpy

def getScriptDir():
    currentFile = __file__  # May be 'my_script', or './my_script' or
    realPath = os.path.realpath(currentFile)  # /home/user/test/my_script.py
    dirPath = os.path.dirname(realPath)
    return dirPath
def getUserRealGaugeDetails():
    min_angle = input('Min derece: ') #the lowest possible angle
    max_angle = input('Max derece ') #highest possible angle
    min_value = input('Min deger: ') #usually zero
    max_value = input('Max deger: ') #maximum reading of the gauge
    units = input('Birim girin: ')
    return min_angle,max_angle,min_value,max_value,units

def setStaticUserRealGaugeDetails():
    min_angle = 5 # input('Min angle (lowest possible angle of dial) - in degrees: ') #the lowest possible angle
    max_angle = 355  # input('Max angle (highest possible angle) - in degrees: ') #highest possible angle
    min_value = -20 #input('Min value: ') #usually zero
    max_value = 120 #input('Max value: ') #maximum reading of the gauge
    units = 'b' #input('Enter units: ')
    return min_angle,max_angle,min_value,max_value,units

def getImage():
    dirPath = getScriptDir()
    dirPath += "/images/1.jpg"
    return cv2.imread(dirPath)
def distance2Points(x1, y1, x2, y2):
    #print np.sqrt((x2-x1)^2+(y2-y1)^2)
    return numpy.sqrt((x2 - x1)**2 + (y2 - y1)**2)

def averageCircle(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

#return the center and radius of the circle
def getCircleAndCustomize(image):
    height, width = image.shape[:2]
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)  #convert to gray

    # gray = cv2.GaussianBlur(gray, (5, 5), 0)
    # gray = cv2.medianBlur(gray, 5)
    # cv2.imwrite('C:/Users/okarademirci/Desktop/analog-gauge-reader/images/gauge-gray-2.jpg', 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.
    circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, numpy.array([]), 100, 50, int(height*0.35), int(height*0.48))
    #coordinates and radius
    a, b, c = circles.shape
    x,y,r = averageCircle(circles, b)
    return x ,y ,r

def get_current_value(img, min_angle, max_angle, min_value, max_value, x, y, r):

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

    # Set threshold and maxValue
    thresh = 175
    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
    dirPath = getScriptDir() + '/images/afterTreshold.jpg'
    cv2.imwrite(dirPath, dst2)

    # find lines
    minLineLength = 10
    maxLineGap = 0
    lines = cv2.HoughLinesP(image=dst2, rho=3, theta=numpy.pi / 180, threshold=100,minLineLength=minLineLength, maxLineGap=0)  # 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
    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]:
            diff1 = distance2Points(x, y, x1, y1)  # x, y is center of circle
            diff2 = distance2Points(x, y, x2, y2)  # x, y is center of circle
            #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
            if (((diff1<diff1UpperBound*r) and (diff1>diff1LowerBound*r) and (diff2<diff2UpperBound*r)) and (diff2>diff2LowerBound*r)):
                line_length = distance2Points(x1, y1, x2, y2)
                # add to final list
                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
    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/okarademirci/Desktop/analog-gauge-reader/images/gauge-%s-lines-2.%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 = distance2Points(x, y, x1, y1)
    dist_pt_1 = distance2Points(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 = numpy.arctan(numpy.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 = numpy.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():

    # 1) get the image from directory.
    image = getImage()

    min_angle,max_angle,min_value,max_value,units = setStaticUserRealGaugeDetails()

    # 2) covnert the image to gray .    
    # 3) find the circle in the image with customization
    x,y,r = getCircleAndCustomize(image)
    # 4) find the line in the circle.
    # 5) find the value in the range of guage
    newValue = get_current_value(image,min_angle,max_angle,min_value,max_value,x,y,r)
    print(newValue)

if __name__=='__main__':
    main()