首先說明我的測試場景是“識別打印在紙上的二維碼”,在掃描結果中尋找二維碼並進行識別,而不是直接讓攝像頭對着二維碼掃描。
zbar和zxing用的都是自己從github上clone的c++源碼/接口編譯出來的dll,都是默認參數
再說結論:測了大概幾千張圖片,兩個庫的准確率差不多(由於圖片場景的多樣性,確切的准確率數字也沒有什么意義),但是zbar的速度要快很多,大概是zxing的4-5倍。其實兩個庫的准確率都不太如人意,稍微模糊一點就無法識別,甚至有一些不模糊的圖像也識別不出。相比之下,微信的識別效果就逆天了,怎么折騰都能識別出來,讓我很好奇。
后來自己嘗試改進識別效果,先看了一下二維碼的識別原理,太復雜了,無從下手。於是嘗試對圖像進行預處理改進,結果只是用了一個二值化加開運算就讓識別效果得到了大幅提升,讓我很奇怪這么簡單的預處理為什么開發人員沒有去做呢?然后又繼續優化了一下,發現二值化的閾值對二維碼的識別非常關鍵,badcase通常是因為閾值不合適導致的,於是犧牲了一下性能,在識別程序中多次嘗試不同閾值,最終識別效果達到了比較令人滿意的結果,准確率從90%左右上升到99.8%左右,絕大部分打印不清晰導致的badcase都得到了解決,代碼如下:
#include <iostream> #include <include\zbar.h> #include "opencv/cv.h" #include "opencv/highgui.h" using namespace std; using namespace cv; using namespace zbar; //zbar接口 string ZbarDecoder(Mat img) { string result; ImageScanner scanner; const void *raw = (&img)->data; // configure the reader scanner.set_config(ZBAR_QRCODE, ZBAR_CFG_ENABLE, 1); // wrap image data Image image(img.cols, img.rows, "Y800", raw, img.cols * img.rows); // scan the image for barcodes int n = scanner.scan(image); // extract results result = image.symbol_begin()->get_data(); image.set_data(NULL, 0); return result; } //對二值圖像進行識別,如果失敗則開運算進行二次識別 string GetQRInBinImg(Mat binImg) { string result = ZbarDecoder(binImg); if(result.empty()) { Mat openImg; Mat element = getStructuringElement(MORPH_RECT, Size(3, 3)); morphologyEx(binImg, openImg, MORPH_OPEN, element); result = ZbarDecoder(openImg); } return result; } //main function string GetQR(Mat img) { Mat binImg; //在otsu二值結果的基礎上,不斷增加閾值,用於識別模糊圖像 int thre = threshold(img, binImg, 0, 255, cv::THRESH_OTSU); string result; while(result.empty() && thre<255) { threshold(img, binImg, thre, 255, cv::THRESH_BINARY); result = GetQRInBinImg(binImg); thre += 20;//閾值步長設為20,步長越大,識別率越低,速度越快 } return result; }