視頻 | OpenCV Webinar 8:OpenCV條形碼識別模塊開發(fā)分享
點擊左上方藍(lán)字關(guān)注我們

直播視頻回放:
作者:梁峻豪,王天麒,孫中夏 (南方科技大學(xué)計算機(jī)科學(xué)與工程系)
最近,我們?yōu)镺penCV貢獻(xiàn)了一維條形碼識別模塊,代碼收錄在:
https://github.com/opencv/opencv_contrib/tree/master/modules/barcode。
我們收集的數(shù)據(jù)集(數(shù)據(jù)集地址:https://github.com/SUSTech-OpenCV/BarcodeTestDataset,共250張條碼圖片)上進(jìn)行了測試,我們的識別算法正確率達(dá)到了96%,速度為20ms每張圖像。作為對比,我們也測試了ZXing在該數(shù)據(jù)集上的表現(xiàn),其正確率為64.4%,速度為90ms每張圖像。
注:測試速度不包含初始化以及讀圖時間。同時,我們的算法是C++實現(xiàn),ZXing是Java實現(xiàn)。另外,對于用圖片數(shù)據(jù)集進(jìn)行的測試,ZXing99%的時間是在做彩色圖到灰度圖的轉(zhuǎn)換。
本文將對此模塊的原理和使用方式進(jìn)行介紹。
條形碼介紹
條形碼是將寬度不等的多個黑條和空白,按照一定的編碼規(guī)則排列,用以表達(dá)一組信息的圖形標(biāo)識符,如下圖所示:

條碼區(qū)域與其他圖像相比有如下兩個重要特點:第一,條碼區(qū)域內(nèi)的條空是平行排列的,方向趨于一致;第二,為 了條碼的可識讀性,條碼在制作時條和空之間有著較大的反射率差,從而條碼區(qū)域內(nèi)的灰度對比度較大,而且邊緣信息豐富。
基于方向一致性的條碼定位算法
根據(jù)條形碼方向趨于一致的特點,我們可以將圖像分塊,通過計算每個塊內(nèi)梯度方向的一致性,來濾除那些低一致性的塊。下圖是篩選過后剩下的塊:

由于包含條碼區(qū)域的塊一定連續(xù)存在的特性,我們可以通過對這些圖像塊再進(jìn)行一個改進(jìn)的腐蝕操作過濾掉部分背景圖像塊。下圖是濾除部分背景圖像塊后剩余的塊:

得到這些塊之后,我們再根據(jù)每個圖像塊內(nèi)的平均梯度方向進(jìn)行連通。因為如果是相鄰的圖像塊都屬于同一個條碼的話,那么他們的平均梯度方向也一定相同。
得到連通區(qū)域之后我們再根據(jù)條碼圖像的特性進(jìn)行篩選,比如連通區(qū)域內(nèi)的梯度大于閾值的點的比例,組成連通區(qū)域的圖像塊數(shù)量等。
最后,用最小外接矩形去擬合每個連通區(qū)域,并計算外界矩形的方向是否和連通區(qū)域內(nèi)的平均梯度方向一致,過濾掉差距較大的連通區(qū)域。將平均梯度方向作為矩形的方向,并將矩形作為最終的定位框。

條形碼解碼
目前我們支持了三種類型的條碼解碼,它們分別是EAN13、 EAN8 和UPC-A。(下圖為EAN13 條碼示例)
條碼的識別主要流程如下圖:

其中:
優(yōu)化的超分辨率策略指的是對較小的條碼進(jìn)行超分辨率放大,不同大小條碼做不同處理。
解碼算法的核心是基于條碼編碼方式的向量距離計算。因為條碼的編碼格式為固定的數(shù)個"條空",所以可以在約定好"條空"間隔之后。將固定的條空讀取為一個向量,接下來與約定好的編碼格式向匹配,取匹配程度最高的編碼為結(jié)果。
在解碼步驟中,解碼的單位為一條線,由于噪點,條空的粘連等原因,單獨條碼的解碼結(jié)果存在較大的不確定性,因此我們加入了對多條線的掃碼,通過對均勻分布的掃描與解碼,能夠?qū)⒍祷^程中的一些不完美之處加以抹除。
具體實現(xiàn)為:首先在檢測線上尋找起始符,尋找到起始符之后,對前半部分進(jìn)行讀取與解碼,接著尋找中間分割符,接著對后半部分進(jìn)行讀取與解碼,最后尋找終結(jié)符,并對整個條碼進(jìn)行首位生成與校驗(此處以EAN13格式舉例,不同格式不盡相同)。最后,每條線都會存在一個解碼結(jié)果,所以對其進(jìn)行投票,只將最高且總比例在有效結(jié)果50%以上的結(jié)果返回。這一部分我們基于ZXing的算法實現(xiàn)做了一些改進(jìn)(投票等)。
更換二值化和解碼器指的是在為解碼成功遍歷使用每種解碼器和二值化嘗試解碼。
使用方式
C++
#include "opencv2/barcode.hpp"
#include "opencv2/imgproc.hpp"
using namespace cv;
Ptr<barcode::BarcodeDetector> bardet = makePtr<barcode::BarcodeDetector>("sr.prototxt", "sr.caffemodel"); //如果不使用超分辨率則可以不指定模型路徑
Mat input = imread("your file path");
Mat corners; //返回的檢測框的四個角點坐標(biāo),如果檢測到N個條碼,那么維度應(yīng)該是[N][4][2]
std::vector<std::string> decoded_info; //返回的解碼結(jié)果,如果解碼失敗,則為空string
std::vector<barcode::BarcodeType> decoded_format; //返回的條碼類型,如果解碼失敗,則為BarcodeType::NONE
bool ok = bardet->detectAndDecode(input, decoded_info, decoded_format, corners);
Python
import cv2
bardet = cv2.barcode_BarcodeDetector()
img = cv2.imread("your file path")
ok, decoded_info, decoded_type, corners = bardet.detectAndDecode(img)
更多使用方式請參考文檔:
https://docs.opencv.org/master/dc/df7/classcv_1_1barcode_1_1BarcodeDetector.html
參考文獻(xiàn)
王祥敏,汪國有. 一種基于方向一致性的條碼定位算法[EB/OL]. 北京:中國科技論文在線 [2015-04-22]. http://www.paper.edu.cn/releasepaper/content/201504-338.
END
點贊三連,支持一下吧↓
