1 背景
利用opencv识别数码管,采用传统的穿线法,前提是利用深度学习目标检测的方法,在机柜中将数码管区域切割出来,然后再对切割出来的区域进行识别,判断数字是多少。切割出来的形状如图所示:
数码管各段表示为
2 识别思路
先对数码管进行灰度化和二值化,将数字变为255,背景变为0,然后利用穿线法,对abcdefg7个区域依次穿线,判断是否有255的值,有则表示该区域高亮,最后结合7个区域的高亮信息,综合判断数字是多少
-
a b
c d e f g result
-
0 √ √ √ √ √ √
63
-
1 √ √
6
-
2 √ √ √ √ √
91
-
3 √ √ √ √ √
79
-
4 √ √ √ √
102
-
5 √ √ √ √ √
109
-
6 √ √ √ √ √ √
125
-
7 √ √ √
7
-
8 √ √ √ √ √ √ √
127
-
9 √ √ √ √ √
103
3 灰度化
opencv有自带的灰度化函数cv2.cvtColor(),但是在使用过程中发现,对于一些整体亮度低的数码管灰度化后,会丢失数字信息,看不出来哪段亮,考虑到数码管都是红色的,红色通道的数据最重要,因此设计了一个自己灰度化的函数
-
def tomygray(image):
-
height = image.shape[
0]
-
width = image.shape[
1]
-
gray = np.zeros((height, width,
1), np.uint8)
-
for i
in range(height):
-
for j
in range(width):
-
# pixel = max(image[i,j][0], image[i,j][1], image[i,j][2])
-
pixel =
0.0 * image[i, j][
0] +
0.0 * image[i, j][
1] +
1 * image[i, j][
2]
-
gray[i, j] = pixel
-
return gray
4 二值化
opencv有多种二值化的方法,主要包括固定阈值和自适应阈值的方法,具体介绍可看《OpenCV之阈值化操作总结》
自适应阈值主要适用于一张图片中亮度不一样的情况,而对于我们的数码管来说,由于大小很小,基本上没有亮度变化,因此使用固定阈值的方法即可。函数原型如下
ret, dst = cv2.threshold(src, thresh, maxval, type)
函数最重要的部分是thresh值的设置,由于不同图片的数码管亮度不同,不可以选择同一个阈值,需要分别计算每张图片的固定阈值,计算阈值有很多方法,我用到的方法有以下两种
4.1 统计直方图
统计直方图中像素的分布情况,根据数量最多的像素值来设置一个阈值(下边的参数都是调试效果比较好的值,自己可根据具体情况来设置)
-
hist = cv2.calcHist([image_gray], [
0],
None, [
256], [
0,
256])
-
#plt.hist(hist.ravel(), 256, [0,256])
-
#plt.savefig(filename + "_hist.png")
-
#plt.show()
-
min_val, max_val, min_index, max_index = cv2.minMaxLoc(hist)
-
ret, image_bin = cv2.threshold(image_gray, int(max_index[
1])
-7,
255,
-
cv2.THRESH_BINARY)
4.2 计算平均值
计算灰度图的平均像素值,根据平均值设定阈值
-
mean,stddev = cv2.meanStdDev(image_gray)
-
ret, image_bin = cv2.threshold(image_gray, meanvalue +
65,
255,
-
cv2.THRESH_BINARY)
5 穿线法
得到二值化的图像后,将图像进行分割,切成一个一个的数字,然后每个都用穿线法来判断值是多少
-
def TubeIdentification(filename, num, image):
-
tube =
0
-
tubo_roi = [
-
[image.shape[
0] *
0/
3, image.shape[
0] *
1/
3, image.shape[
1] *
1/
2,
-
image.shape[
1] *
1/
2],
-
[image.shape[
0] *
1/
3, image.shape[
0] *
1/
3, image.shape[
1] *
2/
3,
-
image.shape[
1] -
1 ],
-
[image.shape[
0] *
2/
3, image.shape[
0] *
2/
3, image.shape[
1] *
2/
3,
-
image.shape[
1] -
1 ],
-
[image.shape[
0] *
2/
3, image.shape[
0]
-1 , image.shape[
1] *
1/
2,
-
image.shape[
1] *
1/
2],
-
[image.shape[
0] *
2/
3, image.shape[
0] *
2/
3, image.shape[
1] *
0/
3,
-
image.shape[
1] *
1/
3],
-
[image.shape[
0] *
1/
3, image.shape[
0] *
1/
3, image.shape[
1] *
0/
3,
-
image.shape[
1] *
1/
3],
-
[image.shape[
0] *
1/
3, image.shape[
0] *
2/
3, image.shape[
1] *
1/
2,
-
image.shape[
1] *
1/
2]]
-
i =
0
-
while(i <
7):
-
if(Iswhite(image, int(tubo_roi[i][
0]), int(tubo_roi[i][
1]),
-
int(tubo_roi[i][
2]),int(tubo_roi[i][
3]))):
-
tube = tube + pow(
2,i)
-
-
cv2.line(image, ( int(tubo_roi[i][
3]),int(tubo_roi[i][
1])),
-
(int(tubo_roi[i][
2]), int(tubo_roi[i][
0])),
-
(
255,
0,
0),
1)
-
i +=
1
-
-
if(tube==
63):
-
onenumber =
0
-
elif(tube==
6):
-
onenumber =
1
-
elif(tube==
91):
-
onenumber =
2
-
elif(tube==
79):
-
onenumber =
3
-
elif(tube==
102
or tube==
110):
-
#110是因为有干扰情况
-
onenumber =
4
-
elif(tube==
109):
-
onenumber =
5
-
elif(tube==
125):
-
onenumber =
6
-
elif(tube==
7):
-
onenumber =
7
-
elif(tube==
127):
-
onenumber =
8
-
elif(tube==
103):
-
onenumber =
9
-
else:
-
onenumber =
-1
-
-
cv2.imwrite(filename +
'_' + str(num) +
'_' + str(onenumber) +
'.png', image)
-
return onenumber
-
-
def Iswhite(image, row_start, row_end, col_start, col_end):
-
white_num =
0
-
j=row_start
-
i=col_start
-
-
while(j <= row_end):
-
while(i <= col_end):
-
if(image[j][i] ==
255):
-
white_num+=
1
-
i+=
1
-
j+=
1
-
i=col_start
-
#print('white num is',white_num)
-
if(white_num >=
5):
-
return
True
-
else:
-
return
False
-
6 识别主程序
-
def digitalrec(image):
-
filename = str(image).split(
".jpg",
1)[
0]
-
image_org = cv2.imread(image)
-
-
height = image_org.shape[
0]
-
width = image_org.shape[
1]
-
-
#transe image to gray
-
#image_gray = cv2.cvtColor(image_org, cv2.COLOR_RGB2GRAY)
-
image_gray = tomygray(image_org)
-
cv2.imwrite(filename +
'_gray.png',image_gray)
-
-
meanvalue = image_gray.mean()
-
if meanvalue >=
200:
-
hist = cv2.calcHist([image_gray], [
0],
None, [
256], [
0,
256])
-
#plt.hist(hist.ravel(), 256, [0,256])
-
#plt.savefig(filename + "_hist.png")
-
#plt.show()
-
min_val, max_val, min_index, max_index = cv2.minMaxLoc(hist)
-
ret, image_bin = cv2.threshold(image_gray, int(max_index[
1])
-7,
255,
-
cv2.THRESH_BINARY)
-
else:
-
mean,stddev = cv2.meanStdDev(image_gray)
-
ret, image_bin = cv2.threshold(image_gray, meanvalue +
65,
255,
-
cv2.THRESH_BINARY)
-
-
#image_bin = cv2.adaptiveThreshold(image_gray, 255,
-
# cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
-
# cv2.THRESH_BINARY, 11,
-
# 0)
-
-
x, y, w, h = cv2.boundingRect(image_bin)
-
image_bin = image_bin[max(y
-5,
0) : h+
10, max(x
-5,
0) : w+
10]
-
cv2.imwrite(filename +
'_bin.png',image_bin)
-
-
#split number and identify it
-
num =
0
-
result =
''
-
while
True:
-
if(num <
3):
-
roi = image_bin[
0: height, int(width /
3 * num):
-
int(width /
3 * (num +
1))]
-
onenumber = TubeIdentification(filename, num, roi)
-
if(onenumber ==
-1):
-
result +=
"0"
-
else:
-
result += str(onenumber)
-
num +=
1
-
else:
-
break
-
print(
"picture of %s detect result is %s"%(filename,result))
-
return result
7 识别效果
8 展望
上边介绍的方法可以实现数码管的识别,但是由于有很多对像素的操作,比较耗时,平均识别一张图片需要2s左右,而且使用传统的方法来识别数码管,涉及到很多参数的设置与调试,鲁棒性不强,尤其是在灰度化和二值化的时候,参数设置很关键。
最好的解决方法是可以利用深度学习的方法来识别,提高识别率,有时间的话会尝试新的方法来解决。
<li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true"> <use xlink:href="#csdnc-thumbsup"></use> </svg><span class="name">点赞</span> <span class="count">5</span> </a></li> <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{"mod":"popu_824"}"><svg class="icon" aria-hidden="true"> <use xlink:href="#icon-csdnc-Collection-G"></use> </svg><span class="name">收藏</span></a></li> <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true"> <use xlink:href="#icon-csdnc-fenxiang"></use> </svg>分享</a></li> <!--打赏开始--> <!--打赏结束--> <li class="tool-item tool-more"> <a> <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg> </a> <ul class="more-box"> <li class="item"><a class="article-report">文章举报</a></li> </ul> </li> </ul> </div> </div> <div class="person-messagebox"> <div class="left-message"><a href="https://blog.csdn.net/zong596568821xp"> <img src="https://profile.csdnimg.cn/F/C/8/3_zong596568821xp" class="avatar_pic" username="zong596568821xp"> <img src="https://g.csdnimg.cn/static/user-reg-year/1x/7.png" class="user-years"> </a></div> <div class="middle-message"> <div class="title"><span class="tit"><a href="https://blog.csdn.net/zong596568821xp" data-report-click="{"mod":"popu_379"}" target="_blank">ZONG_XP</a></span> <span class="flag expert"> <a href="https://blog.csdn.net/home/help.html#classicfication" target="_blank"> <svg class="icon" aria-hidden="true"> <use xlink:href="#csdnc-blogexpert"></use> </svg> 博客专家 </a> </span> </div> <div class="text"><span>发布了162 篇原创文章</span> · <span>获赞 381</span> · <span>访问量 83万+</span></div> </div> <div class="right-message"> <a href="https://bbs.csdn.net/forums/p-zong596568821xp" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-messageboard">他的留言板 </a> <a class="btn btn-sm bt-button personal-watch" data-report-click="{"mod":"popu_379"}">关注</a> </div> </div> </div>