目錄
一、什么是數(shù)字識別?
二、如何實現(xiàn)數(shù)字識別?
三、識別LCD屏幕上面的數(shù)字的原理詳解
四、算法實現(xiàn)步驟
五、算法代碼實現(xiàn)
六、效果展示和分析
七、問題擴展與延伸
一、什么是數(shù)字識別?
??所謂的數(shù)字識別,就是使用算法自動識別出圖片中的數(shù)字。具體的效果如下圖所示:
上圖展示了算法的處理效果,算法能夠自動的識別到LCD屏幕上面的數(shù)字,這在現(xiàn)實場景中具有很大的實際應用價值。下面我們將對它的實現(xiàn)細節(jié)進行詳細解析。
二、如何實現(xiàn)數(shù)字識別?
??對于數(shù)字識別這個任務而言,它并不是一個新的研究方向,很久之前就有很多的學者們在關注這個問題,并提出了一些可行的解決方案,本小節(jié)我們將對這些方案進行簡單的總結。
方案一:使用現(xiàn)成的OCR技術。 OCR,即文字識別,它是一個比較成熟的技術,當前已經(jīng)具有很多性能優(yōu)異的開源工具包可以供大家使用,即我們僅僅需要關注的是如何調用這些接口即可,如何你想要了解它們的實現(xiàn)細節(jié),那么你就需要去詳細的理解源碼啦。除此之外,當前的OCR算法已經(jīng)可以很好的識別圖像中的簡單文字和數(shù)字等,當前研究的一個熱點是如何準確快速的識別出圖片中的傾斜文本和不同語言的文字等,下圖展示了一個簡單的識別樣例,即識別筆筒上面的文字。
方案二:使用深度神經(jīng)網(wǎng)絡。 隨著深度學習技術的快速發(fā)展,神經(jīng)網(wǎng)絡被引入到計算機視覺當中的多個領域中,對于數(shù)字識別這個簡單的任務而言,神經(jīng)網(wǎng)絡可以獲得準確的識別結果,比較有名的是mnist數(shù)據(jù)集。但是這種方法首先需要用戶收集大量的數(shù)據(jù)集;然后需要進行模型訓練和參數(shù)調節(jié);通常需要花費較大的人力和物力。
方案三:使用本文的思路,即根據(jù)七段數(shù)碼管的知識識別LCD上的數(shù)字。 這種方法適用于識別LCD屏幕上的數(shù)字,當然通過合理的擴展,也可以用來識別LCD上面的文字;該算法不僅具有較快的識別速度,而且可以取得較高的識別精度。
三、識別LCD屏幕上面的數(shù)字的原理詳解
??在現(xiàn)實生活中,我們經(jīng)常會看到各種各樣的LCD屏幕,小到我們的MP3,大到廣場中的電視等,隨著各種應用的不斷出現(xiàn),LCD屏幕頻繁的出現(xiàn)在我們現(xiàn)實生活中的多個場景中,而快速、準確的識別出LCD上面的數(shù)字就成為了一個新的剛需,這樣可以極大的節(jié)約人力和物力成本,下面將對LCD屏幕數(shù)字識別的原理進行說明, 知其然不許知其所以然。
上圖展示的是一個 七段數(shù)碼管, 我們常見的LCD屏幕其實就是通過七段數(shù)碼管組合而成的,因而了解它的構成至關重要。通過上圖我們可以觀察到這個數(shù)碼管中含有7段,并用0-6分別標出,其實還有一個小數(shù)點表示8,這些段下面都有相應的led燈,當我們需要顯示不同的數(shù)字時只需要點亮不同段的LED燈即可,這個原理是不是很簡單!通過組合不同段,我們可以獲得128種組合(即2的7次方),具體的組合結果如下圖所示,對于這128種組合而言,我們只對其中的0-9這10個數(shù)字比較感興趣,其它的組合并不是我們關注的目標。
即,我們只對0-9這10個數(shù)字的組合比較感興趣,這其實就是我們常用的數(shù)字,我們可以通過它們組合成任意一個數(shù)字!!! 通過上圖我們可以觀察到當我們點亮特定段的LED等之后,LCD就能顯示出特定的數(shù)字,那么我們可不可以通過判斷不同段的特征來判斷當前的數(shù)字呢,這其實就是本文的實現(xiàn)思路!!!
四、算法實現(xiàn)步驟
??為了完成LCD上面的數(shù)字識別,我們需要按照如下的步驟進行實現(xiàn)該算法。
步驟1-定位面板上面的LCD的位置 。由于LCD屏幕和面板具有較大的亮度差異,我們可以使用邊緣檢測算法獲得LCD的位置。
步驟2-獲取LCD屏幕 。通過上一步我們可以獲得一個邊緣map;接著我們需要在這個邊緣map中尋找輪廓;最后最大的矩形區(qū)域即是我們需要的LCD。
步驟3-定位LCD上面的數(shù)字 。在獲取到LCD之后,我們感興趣的是LCD上面的數(shù)字,那么我們就需要對這些數(shù)字進行定位,即輸出它們的位置信息。由于LCD上面的數(shù)字和周圍的區(qū)域之間具有較大的差異,因而使用閾值和形態(tài)學操作就可以很好的完成這個任務。
步驟4-進行數(shù)字識別 。在獲取到數(shù)字的位置信息后,我們將使用opencv中內置的算法對這些數(shù)字進行識別,從而獲得我們需要的結果。
五、算法代碼實現(xiàn)
# coding=utf-8 # 導入一些python包 from imutils.perspective import four_point_transform from imutils import contours import imutils import cv2 # 定義每一個數(shù)字對應的字段 DIGITS_LOOKUP = { (1, 1, 1, 0, 1, 1, 1): 0, (0, 0, 1, 0, 0, 1, 0): 1, (1, 0, 1, 1, 1, 1, 0): 2, (1, 0, 1, 1, 0, 1, 1): 3, (0, 1, 1, 1, 0, 1, 0): 4, (1, 1, 0, 1, 0, 1, 1): 5, (1, 1, 0, 1, 1, 1, 1): 6, (1, 0, 1, 0, 0, 1, 0): 7, (1, 1, 1, 1, 1, 1, 1): 8, (1, 1, 1, 1, 0, 1, 1): 9 } # 讀取輸入圖片 image = cv2.imread("example.jpg") # 將輸入圖片裁剪到固定大小 image = imutils.resize(image, height=500) # 將輸入轉換為灰度圖片 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 進行高斯模糊操作 blurred = cv2.GaussianBlur(gray, (5, 5), 0) # 執(zhí)行邊緣檢測 edged = cv2.Canny(blurred, 50, 200, 255) cv2.imwrite('edge.png', edged) # 在邊緣檢測map中發(fā)現(xiàn)輪廓 cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) # 根據(jù)大小對這些輪廓進行排序 cnts = sorted(cnts, key=cv2.contourArea, reverse=True) displayCnt = None # 循環(huán)遍歷所有的輪廓 for c in cnts: # 對輪廓進行近似 peri = cv2.arcLength(c, True) approx = cv2.approxPolyDP(c, 0.02 * peri, True) # 如果當前的輪廓有4個頂點,我們返回這個結果,即LCD所在的位置 if len(approx) == 4: displayCnt = approx break # 應用視角變換到LCD屏幕上 warped = four_point_transform(gray, displayCnt.reshape(4, 2)) cv2.imwrite('warped.png', warped) output = four_point_transform(image, displayCnt.reshape(4, 2)) # 使用閾值進行二值化 thresh = cv2.threshold(warped, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1] cv2.imwrite('thresh1.png', thresh) kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (1, 5)) # 使用形態(tài)學操作進行處理 thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) cv2.imwrite('thresh2.png', thresh) # 在閾值圖像中查找輪廓,然后初始化數(shù)字輪廓列表 cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) cnts = imutils.grab_contours(cnts) digitCnts = [] # 循環(huán)遍歷所有的候選區(qū)域 for c in cnts: # 計算輪廓的邊界框 (x, y, w, h) = cv2.boundingRect(c) # 如果當前的這個輪廓區(qū)域足夠大,它一定是一個數(shù)字區(qū)域 if w >= 15 and (h >= 30 and h <= 40): digitCnts.append(c) # 從左到右對這些輪廓進行排序 digitCnts = contours.sort_contours(digitCnts, method="left-to-right")[0] digits = [] # 循環(huán)處理每一個數(shù)字 i = 0 for c in digitCnts: # 獲取ROI區(qū)域 (x, y, w, h) = cv2.boundingRect(c) roi = thresh[y:y + h, x:x + w] # 分別計算每一段的寬度和高度 (roiH, roiW) = roi.shape (dW, dH) = (int(roiW * 0.25), int(roiH * 0.15)) dHC = int(roiH * 0.05) # 定義一個7段數(shù)碼管的集合 segments = [ ((0, 0), (w, dH)), # 上 ((0, 0), (dW, h // 2)), # 左上 ((w - dW, 0), (w, h // 2)), # 右上 ((0, (h // 2) - dHC) , (w, (h // 2) + dHC)), # 中間 ((0, h // 2), (dW, h)), # 左下 ((w - dW, h // 2), (w, h)), # 右下 ((0, h - dH), (w, h)) # 下 ] on = [0] * len(segments) # 循環(huán)遍歷數(shù)碼管中的每一段 for (i, ((xA, yA), (xB, yB))) in enumerate(segments): # 檢測分割后的ROI區(qū)域,并統(tǒng)計分割圖中的閾值像素點 segROI = roi[yA:yB, xA:xB] total = cv2.countNonZero(segROI) area = (xB - xA) * (yB - yA) # 如果非零區(qū)域的個數(shù)大于整個區(qū)域的一半,則認為該段是亮的 if total / float(area) > 0.5: on[i]= 1 # 進行數(shù)字查詢并顯示結果 digit = DIGITS_LOOKUP[tuple(on)] digits.append(digit) cv2.rectangle(output, (x, y), (x + w, y + h), (0, 255, 0), 1) cv2.putText(output, str(digit), (x - 10, y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 255, 0), 2) # 顯示最終的輸出結果 print(u"{}{}.{} \u00b0C".format(*digits)) cv2.imshow("Input", image) cv2.imshow("Output", output) cv2.waitKey(0)
六、效果展示和分析
??上圖展示了該算法的運行結果和一些中間結果。第1行第1列表示的是原始的輸入圖片,它和代碼中的image對應,我們需要識別的是LCD面板上面的34.5;第1行第2列表示的是Canny邊緣檢測算法的檢測結果,它對應于代碼中的edged,通過該圖我們可以發(fā)現(xiàn)Canny邊緣檢測的結果中含有我們感興趣的目標,即中間的LCD;第1行第3列表示的是對輸入的灰度圖片應用視角變換后的結果,即獲得了LCD屏幕所在的位置,它和代碼中的warped相互對應;第2行第1列表示的是對獲取到的LCD屏幕進行二值化后的結果,它和代碼中的thresh 相互對應,由于LCD上面的數(shù)字和背景之間具有較大的差異,因而通過簡單的二值化我們就可以獲得我們感興趣的目標-數(shù)字;第2行第2列表示的是對二值化結果進行形態(tài)學操作之后的結果,它和代碼中的thresh 相互對應,我們可以發(fā)現(xiàn)執(zhí)行了形態(tài)學操作之后的結果更多平滑,同時過濾掉很多的噪聲,有利于后續(xù)的識別。
??上圖展示了本算法獲取到的LCD屏幕中的數(shù)字,通過上圖我們可以發(fā)現(xiàn)該算法準確的獲得了這些數(shù)字的位置信息,有利于后續(xù)的識別操作。
??上圖展示了算法進行數(shù)字識別的實現(xiàn)細節(jié)。即通過遍歷每一個數(shù)字中的7個段,并統(tǒng)計該段中非零像素的個數(shù),當其統(tǒng)計值大于整個區(qū)域的一半時,認為該段是亮的,當統(tǒng)計完所有的這7個段之后,在預定義的數(shù)字詞典中進行查找,并輸出最終的結果即可。
??上圖展示了算法最終的輸出結果,我們可以觀察到算法快速、準確的獲得了我們需要的結果,滿足了我們的預期。
七、問題擴展與延伸
??通過上面的解析你可能已經(jīng)知道了如何來很好的解決上面這個問題。細心的你也許會發(fā)現(xiàn)上述結果中輸出的 點號是人為添加上去的,并不是算法自動獲取的,而在現(xiàn)實場景中我們經(jīng)常會遇到小數(shù)點,比如溫度、濕度等,那么我的問題來啦,如何利用算法自動獲取圖中的小數(shù)點,使得算法最終自動輸出34.5的結果呢,這個問題留給聰明的你進行思考吧!!!(其實數(shù)碼管是有八段的,第八段就是小數(shù)點的!!!
本文為張軍原創(chuàng)文章,轉載無需和我聯(lián)系,但請注明來自張軍的軍軍小站,個人博客http://m.eyofj.com
更多文章、技術交流、商務合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
