天天看點

千呼萬喚始出來!OpenCV終于支援一維碼解碼!

公衆号:OpenCV團隊

作者:梁峻豪,王天麒,孫中夏 (南方科技大學計算機科學與工程系)

最近,我們為OpenCV貢獻了一維條形碼識别子產品,代碼收錄在:

​​https://github.com/opencv/opencv_contrib/tree/master/modules/barcode。​​

我們收集的資料集(資料集位址:https://github.com/SUSTech-OpenCV/BarcodeTestDataset,共250張條碼圖檔)上進行了測試,我們的識别算法正确率達到了96%,速度為20ms每張圖像。作為對比,我們也測試了Zxing在該資料集上的表現,其正确率為64.4%,速度為90ms每張圖像。

注:測試速度不包含初始化以及讀圖時間。同時,我們的算法是C++實作,ZXing是Java實作。另外,對于用圖檔資料集進行的測試,ZXing99%的時間是在做彩色圖到灰階圖的轉換。

本文将對此子產品的原理和使用方式進行介紹。

條形碼介紹

條形碼是将寬度不等的多個黑條和空白,按照一定的編碼規則排列,用以表達一組資訊的圖形辨別符,如下圖所示:

千呼萬喚始出來!OpenCV終于支援一維碼解碼!

條碼區域與其他圖像相比有如下兩個重要特點:第一,條碼區域内的條空是平行排列的,方向趨于一緻;第二,為 了條碼的可識讀性,條碼在制作時條和空之間有着較大的反射率差,進而條碼區域内的灰階對比度較大,而且邊緣資訊豐富。

基于方向一緻性的條碼定位算法

  1. 根據條形碼方向趨于一緻的特點,我們可以将圖像分塊,通過計算每個塊内梯度方向的一緻性,來濾除那些低一緻性的塊。下圖是篩選過後剩下的塊:
千呼萬喚始出來!OpenCV終于支援一維碼解碼!
  1. 由于包含條碼區域的塊一定連續存在的特性,我們可以通過對這些圖像塊再進行一個改進的腐蝕操作過濾掉部分背景圖像塊。下圖是濾除部分背景圖像塊後剩餘的塊:
千呼萬喚始出來!OpenCV終于支援一維碼解碼!
  1. 得到這些塊之後,我們再根據每個圖像塊内的平均梯度方向進行連通。因為如果是相鄰的圖像塊都屬于同一個條碼的話,那麼他們的平均梯度方向也一定相同。
  2. 得到連通區域之後我們再根據條碼圖像的特性進行篩選,比如連通區域内的梯度大于門檻值的點的比例,組成連通區域的圖像塊數量等。
  3. 最後,用最小外接矩形去拟合每個連通區域,并計算外界矩形的方向是否和連通區域内的平均梯度方向一緻,過濾掉差距較大的連通區域。将平均梯度方向作為矩形的方向,并将矩形作為最終的定位框。
  4. 千呼萬喚始出來!OpenCV終于支援一維碼解碼!

條形碼解碼

目前我們支援了三種類型的條碼解碼,它們分别是EAN13、 EAN8 和UPC-A。(下圖為EAN13 條碼示例)

千呼萬喚始出來!OpenCV終于支援一維碼解碼!

條碼的識别主要流程如下圖:

千呼萬喚始出來!OpenCV終于支援一維碼解碼!

其中:

  1. 優化的超分辨率政策指的是對較小的條碼進行超分辨率放大,不同大小條碼做不同處理。
  2. 解碼算法的核心是基于條碼編碼方式的向量距離計算。因為條碼的編碼格式為固定的數個"條空",是以可以在約定好"條空"間隔之後。将固定的條空讀取為一個向量,接下來與約定好的編碼格式向比對,取比對程度最高的編碼為結果。
  3. 在解碼步驟中,解碼的機關為一條線,由于噪點,條空的粘連等原因,單獨條碼的解碼結果存在較大的不确定性,是以我們加入了對多條線的掃碼,通過對均勻分布的掃描與解碼,能夠将二值化過程中的一些不完美之處加以抹除。

    具體實作為:首先在檢測線上尋找起始符,尋找到起始符之後,對前半部分進行讀取與解碼,接着尋找中間分割符,接着對後半部分進行讀取與解碼,最後尋找終結符,并對整個條碼進行首位生成與校驗(此處以EAN13格式舉例,不同格式不盡相同)。最後,每條線都會存在一個解碼結果,是以對其進行投票,隻将最高且總比例在有效結果50%以上的結果傳回。這一部分我們基于ZXing的算法實作做了一些改進(投票等)。 

  4. 更換二值化和解碼器指的是在為解碼成功周遊使用每種解碼器和二值化嘗試解碼。

使用方式

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; //傳回的檢測框的四個角點坐标,如果檢測到N個條碼,那麼次元應該是[N][4][2]
std::vector<std::string> decoded_info; //傳回的解碼結果,如果解碼失敗,則為空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​​

參考文獻

王祥敏,汪國有. 一種基于方向一緻性的條碼定位算法[EB/OL]. 北京:中國科技論文線上 [2015-04-22]. http://www.paper.edu.cn/releasepaper/content/201504-338.