OpenCV之七段数码管识别(含代码)

1 背景

利用opencv识别数码管,采用传统的穿线法,前提是利用深度学习目标检测的方法,在机柜中将数码管区域切割出来,然后再对切割出来的区域进行识别,判断数字是多少。切割出来的形状如图所示:

数码管各段表示为

2 识别思路

先对数码管进行灰度化和二值化,将数字变为255,背景变为0,然后利用穿线法,对abcdefg7个区域依次穿线,判断是否有255的值,有则表示该区域高亮,最后结合7个区域的高亮信息,综合判断数字是多少

  1. a b c d e f g result
  2. 0 √ √ √ √ √ √ 63
  3. 1 √ √ 6
  4. 2 √ √ √ √ √ 91
  5. 3 √ √ √ √ √ 79
  6. 4 √ √ √ √ 102
  7. 5 √ √ √ √ √ 109
  8. 6 √ √ √ √ √ √ 125
  9. 7 √ √ √ 7
  10. 8 √ √ √ √ √ √ √ 127
  11. 9 √ √ √ √ √ 103

3 灰度化

opencv有自带的灰度化函数cv2.cvtColor(),但是在使用过程中发现,对于一些整体亮度低的数码管灰度化后,会丢失数字信息,看不出来哪段亮,考虑到数码管都是红色的,红色通道的数据最重要,因此设计了一个自己灰度化的函数

  1. def tomygray(image):
  2. height = image.shape[0]
  3. width = image.shape[1]
  4. gray = np.zeros((height, width, 1), np.uint8)
  5. for i in range(height):
  6. for j in range(width):
  7. # pixel = max(image[i,j][0], image[i,j][1], image[i,j][2])
  8. pixel = 0.0 * image[i, j][0] + 0.0 * image[i, j][1] + 1 * image[i, j][2]
  9. gray[i, j] = pixel
  10. return gray

4 二值化

opencv有多种二值化的方法,主要包括固定阈值和自适应阈值的方法,具体介绍可看《OpenCV之阈值化操作总结

自适应阈值主要适用于一张图片中亮度不一样的情况,而对于我们的数码管来说,由于大小很小,基本上没有亮度变化,因此使用固定阈值的方法即可。函数原型如下

ret, dst = cv2.threshold(src, thresh, maxval, type)

函数最重要的部分是thresh值的设置,由于不同图片的数码管亮度不同,不可以选择同一个阈值,需要分别计算每张图片的固定阈值,计算阈值有很多方法,我用到的方法有以下两种

4.1 统计直方图

统计直方图中像素的分布情况,根据数量最多的像素值来设置一个阈值(下边的参数都是调试效果比较好的值,自己可根据具体情况来设置)

  1. hist = cv2.calcHist([image_gray], [0], None, [256], [0,256])
  2. #plt.hist(hist.ravel(), 256, [0,256])
  3. #plt.savefig(filename + '_hist.png')
  4. #plt.show()
  5. min_val, max_val, min_index, max_index = cv2.minMaxLoc(hist)
  6. ret, image_bin = cv2.threshold(image_gray, int(max_index[1])-7, 255,
  7. cv2.THRESH_BINARY)

4.2 计算平均值

计算灰度图的平均像素值,根据平均值设定阈值

  1. mean,stddev = cv2.meanStdDev(image_gray)
  2. ret, image_bin = cv2.threshold(image_gray, meanvalue + 65, 255,
  3. cv2.THRESH_BINARY)

5 穿线法

得到二值化的图像后,将图像进行分割,切成一个一个的数字,然后每个都用穿线法来判断值是多少

  1. def TubeIdentification(filename, num, image):
  2. tube = 0
  3. tubo_roi = [
  4. [image.shape[0] * 0/3, image.shape[0] * 1/3, image.shape[1] * 1/2,
  5. image.shape[1] * 1/2],
  6. [image.shape[0] * 1/3, image.shape[0] * 1/3, image.shape[1] * 2/3,
  7. image.shape[1] - 1 ],
  8. [image.shape[0] * 2/3, image.shape[0] * 2/3, image.shape[1] * 2/3,
  9. image.shape[1] - 1 ],
  10. [image.shape[0] * 2/3, image.shape[0] -1 , image.shape[1] * 1/2,
  11. image.shape[1] * 1/2],
  12. [image.shape[0] * 2/3, image.shape[0] * 2/3, image.shape[1] * 0/3,
  13. image.shape[1] * 1/3],
  14. [image.shape[0] * 1/3, image.shape[0] * 1/3, image.shape[1] * 0/3,
  15. image.shape[1] * 1/3],
  16. [image.shape[0] * 1/3, image.shape[0] * 2/3, image.shape[1] * 1/2,
  17. image.shape[1] * 1/2]]
  18. i = 0
  19. while(i < 7):
  20. if(Iswhite(image, int(tubo_roi[i][0]), int(tubo_roi[i][1]),
  21. int(tubo_roi[i][2]),int(tubo_roi[i][3]))):
  22. tube = tube + pow(2,i)
  23. cv2.line(image, ( int(tubo_roi[i][3]),int(tubo_roi[i][1])),
  24. (int(tubo_roi[i][2]), int(tubo_roi[i][0])),
  25. (255,0,0), 1)
  26. i += 1
  27. if(tube==63):
  28. onenumber = 0
  29. elif(tube==6):
  30. onenumber = 1
  31. elif(tube==91):
  32. onenumber = 2
  33. elif(tube==79):
  34. onenumber = 3
  35. elif(tube==102 or tube==110):
  36. #110是因为有干扰情况
  37. onenumber = 4
  38. elif(tube==109):
  39. onenumber = 5
  40. elif(tube==125):
  41. onenumber = 6
  42. elif(tube==7):
  43. onenumber = 7
  44. elif(tube==127):
  45. onenumber = 8
  46. elif(tube==103):
  47. onenumber = 9
  48. else:
  49. onenumber = -1
  50. cv2.imwrite(filename + '_' + str(num) + '_' + str(onenumber) + '.png', image)
  51. return onenumber
  52. def Iswhite(image, row_start, row_end, col_start, col_end):
  53. white_num = 0
  54. j=row_start
  55. i=col_start
  56. while(j <= row_end):
  57. while(i <= col_end):
  58. if(image[j][i] == 255):
  59. white_num+=1
  60. i+=1
  61. j+=1
  62. i=col_start
  63. #print('white num is',white_num)
  64. if(white_num >= 5):
  65. return True
  66. else:
  67. return False

6 识别主程序

  1. def digitalrec(image):
  2. filename = str(image).split('.jpg', 1)[0]
  3. image_org = cv2.imread(image)
  4. height = image_org.shape[0]
  5. width = image_org.shape[1]
  6. #transe image to gray
  7. #image_gray = cv2.cvtColor(image_org, cv2.COLOR_RGB2GRAY)
  8. image_gray = tomygray(image_org)
  9. cv2.imwrite(filename + '_gray.png',image_gray)
  10. meanvalue = image_gray.mean()
  11. if meanvalue >= 200:
  12. hist = cv2.calcHist([image_gray], [0], None, [256], [0,256])
  13. #plt.hist(hist.ravel(), 256, [0,256])
  14. #plt.savefig(filename + '_hist.png')
  15. #plt.show()
  16. min_val, max_val, min_index, max_index = cv2.minMaxLoc(hist)
  17. ret, image_bin = cv2.threshold(image_gray, int(max_index[1])-7, 255,
  18. cv2.THRESH_BINARY)
  19. else:
  20. mean,stddev = cv2.meanStdDev(image_gray)
  21. ret, image_bin = cv2.threshold(image_gray, meanvalue + 65, 255,
  22. cv2.THRESH_BINARY)
  23. #image_bin = cv2.adaptiveThreshold(image_gray, 255,
  24. # cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
  25. # cv2.THRESH_BINARY, 11,
  26. # 0)
  27. x, y, w, h = cv2.boundingRect(image_bin)
  28. image_bin = image_bin[max(y-5,0) : h+10, max(x-5,0) : w+10]
  29. cv2.imwrite(filename + '_bin.png',image_bin)
  30. #split number and identify it
  31. num = 0
  32. result = ''
  33. while True:
  34. if(num < 3):
  35. roi = image_bin[0: height, int(width / 3 * num):
  36. int(width / 3 * (num + 1))]
  37. onenumber = TubeIdentification(filename, num, roi)
  38. if(onenumber == -1):
  39. result += '0'
  40. else:
  41. result += str(onenumber)
  42. num += 1
  43. else:
  44. break
  45. print('picture of %s detect result is %s'%(filename,result))
  46. return result

7 识别效果

8 展望

上边介绍的方法可以实现数码管的识别,但是由于有很多对像素的操作,比较耗时,平均识别一张图片需要2s左右,而且使用传统的方法来识别数码管,涉及到很多参数的设置与调试,鲁棒性不强,尤其是在灰度化和二值化的时候,参数设置很关键。

最好的解决方法是可以利用深度学习的方法来识别,提高识别率,有时间的话会尝试新的方法来解决。

(0)

相关推荐