前些日子做 二维码识别。二维码识别有一个技巧,就是 二维码定位,
然后使用多次尝试全局二值化,局部二值化,可以提高 低质量的 二维码的识别率。
全局二值化 可以将 OTSU的阈值,进行 上下浮动阶梯二值化。
局部二值化 可以结合多个方法, 比如 wolf,niblack等。
识别库主要是集成 Zbar或者 Zxing库。
我写了个测试工具专门测试 Zbar或者 Zxing.
随便从网上下载了截图了几个 二维码测试:
zbar识别如下:
zxing测试如下:
很多人说,zbar比 zxing快,其实主要是参数配置问题。
zxing 需要配置对应的识别器, zbar默认开启它支持的识别器。
zxing主要代码如下:
COMMON_LIB_DECL vector<zxing_out> zxing_decode(const Mat& img, const string& fmt = "QR_CODE")
{
zxing::Ref<zxing::LuminanceSource> source = MatSource::create(img);
zxing::Ref<zxing::Binarizer> binarizer(new zxing::GlobalHistogramBinarizer(source));
zxing::Ref<zxing::BinaryBitmap> bitmap(new zxing::BinaryBitmap(binarizer));
zxing::Ref<zxing::Reader> reader;
vector<zxing_out> res;
if (fmt == "AZTEC"){
reader.reset(new AztecReader);
}else if (fmt == "CODABAR"){
reader.reset(new CodaBarReader);
}else if (fmt == "CODE_39"){
reader.reset(new Code39Reader);
}else if (fmt == "CODE_93"){
reader.reset(new Code93Reader);
}else if (fmt == "CODE_128"){
reader.reset(new Code128Reader);
}else if (fmt == "DATA_MATRIX"){
reader.reset(new DataMatrixReader);
}else if (fmt == "EAN_8"){
reader.reset(new EAN8Reader);
}else if (fmt == "EAN_13"){
reader.reset(new EAN13Reader);
}else if (fmt == "ITF"){
reader.reset(new ITFReader);
}else if (fmt == "PDF_417"){
reader.reset(new PDF417Reader);
}else if (fmt == "QR_CODE"){
reader.reset(new QRCodeReader);
}else if (fmt == "UPC_A"){
reader.reset(new UPCAReader);
}else if (fmt == "UPC_E"){
reader.reset(new UPCEReader);
}else if (fmt == "MULTI_QR_CODE"){
QRCodeMultiReader mreader;
try {
auto results = mreader.decodeMultiple(bitmap, zxing::DecodeHints::QR_CODE_HINT);
for (auto result : results)
{
zxing_out o;
o.text = result->getText()->getText();
o.name = zxing::BarcodeFormat::barcodeFormatNames[result->getBarcodeFormat()];
auto arrPt = result->getResultPoints();
for (int i = 0; i < arrPt->size(); ++i)
o.position.push_back(cv::Point(arrPt[i]->getX(), arrPt[i]->getY()));
res.push_back(o);
}
}catch (zxing::ReaderException const& re) {
(void)re;
}
return res;
}else {
reader.reset(new QRCodeReader);
}
try {
zxing_out o;
zxing::Ref<zxing::Result> result(reader->decode(bitmap));
o.text = result->getText()->getText();
o.name = zxing::BarcodeFormat::barcodeFormatNames[result->getBarcodeFormat()];
auto arrPt = result->getResultPoints();
for (int i = 0; i < arrPt->size(); ++i)
o.position.push_back(cv::Point(arrPt[i]->getX(), arrPt[i]->getY()));
res.push_back(o);
}catch (zxing::ReaderException const& re) {
(void)re;
// continue
}
return res;
}
zbar代码
COMMON_LIB_DECL vector<zbar_out> zbar_decode(const Mat& img, int zbarSymbolType = 64)
{
vector<zbar_out> results;
Mat grayImg;
if (img.channels() == 1)
grayImg = img;
else if (img.channels() == 3)
cv::cvtColor(img, grayImg, CV_RGB2GRAY);
ImageScanner scanner;
scanner.set_config((zbar_symbol_type_t)zbarSymbolType, ZBAR_CFG_ENABLE, 1);
int w = grayImg.step;
int h = grayImg.rows;
Mat qrImg;
Image image(w, h, "Y800");
image.set_data(grayImg.data, w*h);
//扫描
int n = scanner.scan(image);
if (n <= 0)
{
image.set_data(NULL, 0);
return results;
}
// 提取 results
auto syms = image.get_symbols();
for (auto symI = syms.symbol_begin(); symI != syms.symbol_end(); ++symI) {
zbar_out param;
param.name = symI->get_type_name();
param.text = symI->get_data();
for (int i = 0; i < symI->get_location_size(); ++i)
{
int x = symI->get_location_x(i);
int y = symI->get_location_y(i);
param.position.push_back(Point(x, y));
}
results.push_back(param);
}
// 清理
image.set_data(NULL, 0);
return results;
}
二维码识别的重点在 二维码检测,我实现了一个效果不错的二维码检测器,基于多特征融合,可以在诸多干扰中提取二维码。
不过很多二维码是无法识别的,质量太低