http://blog.csdn.net/dcrmg/article/details/52132313
成像比较清晰,二维码占据整个图像的比例达到4成以上的标准二维码,用opencv和zbar识别还是很容易的,而且zbar的鲁棒性很好。
// Zbar_code.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include<iostream>
#include<zbar.h>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\opencv.hpp>
using namespace std;
using namespace cv;
using namespace zbar;
Rect DrawXY(const Mat image, Mat &imageOut, const int threshodValue, const int binaryzationValue);
int main()
{
// 定义Zbar扫描的类
ImageScanner scanner;
// 初始化
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
// 加载二维码图像数据
Mat srcImage = imread("baidu.jpg");
if (!srcImage.data) {
cout << "Input image error!" << endl;
system("pause");
return 0;
}
Mat src_copy = srcImage.clone();
Mat src_gray, dstImage;
cvtColor(srcImage, src_gray, CV_BGR2GRAY);
int width = src_gray.cols;
int height = src_gray.rows;
// wrap the image
uchar*raw = (uchar*)src_gray.data;
Image imageZbar(width, height, "Y800", raw, width*height);
// 开始扫描
scanner.scan(imageZbar);
// 扩展结果
Image::SymbolIterator symbol = imageZbar.symbol_begin();
if (imageZbar.symbol_begin() == imageZbar.symbol_end()) {
cout << "扫描失败,检查图片数据!" << endl;
}
for (; symbol != imageZbar.symbol_end(); ++symbol) {
cout << "类型:" << endl << symbol->get_type_name() << endl;
cout << "条码:" << endl << symbol->get_data() << endl;
}
imshow("原始二维码图片", srcImage);
Rect rect(0, 0, 0, 0);
// 调用函数将二维码区域截取
rect = DrawXY(srcImage, dstImage, srcImage.rows / 10, 150);
Mat roi = srcImage(rect);
// 根据获取的二维码矩形区域四个角点坐标在原图像上画出二维码
rectangle(src_copy, Point(rect.x, rect.y), Point(rect.x + rect.width, rect.y + rect.height), Scalar(0, 0, 255), 4);
imshow("水平垂直投影", dstImage);
imshow("截取的二维码区域", roi);
imshow("圈画原始图像二维码区域", src_copy);
waitKey(0);
imageZbar.set_data(NULL, 0);
return 0;
}
//***********************************************
// 函数通过水平和垂直方向投影,找到两个方向上投影的交叉矩形,定位到条形码/二维码
// int threshodValue 投影的最少像素单位
// int binaryzationValue 原图像阈值分割值
//***********************************************
Rect DrawXY(const Mat image, Mat &imageOut, const int threshodValue, const int binaryzationValue)
{
// 将原始图像备份
Mat img = image.clone();
// 非单通道图像先预处理--灰度化
if (img.channels()>1)
{
cvtColor(img, img, CV_RGB2GRAY);
}
// 根据原始图像的尺寸构建新的图像用于接收投影信息
Mat out(img.size(), img.type(), Scalar(255));
imageOut = out;
// 对每一个传入的图片做灰度归一化,以便使用同一套阈值参数
normalize(img, img, 0, 255, NORM_MINMAX);
// 垂直方向投影,构建数组向量,包含img.cols维度
vector<int> vectorVertical(img.cols, 0);
// 根据像素坐标所在像素值大小与阈值比较,若有黑店覆盖则计数+1
for (int i = 0; i<img.cols; i++)
{
for (int j = 0; j<img.rows; j++)
{
if (img.at<uchar>(j, i)<binaryzationValue)
{
vectorVertical[i]++;
}
}
}
// 列值归一化
int high = img.rows / 6;
normalize(vectorVertical, vectorVertical, 0, high, NORM_MINMAX);
for (int i = 0; i<img.cols; i++)
{
for (int j = 0; j<img.rows; j++)
{
// 少于设置的像素统计值则不显示柱状图,这里srcImage.rows / 10根据二维码的定位区域得到
// 还需要研究,后期会更新这参数计算原理
if (vectorVertical[i]>threshodValue)
{
line(imageOut, Point(i, img.rows), Point(i, img.rows - vectorVertical[i]), Scalar(0, 0, 0));
}
}
}
//水平投影
vector<int> vectorHorizontal(img.rows, 0);
for (int i = 0; i<img.rows; i++)
{
for (int j = 0; j<img.cols; j++)
{
if (img.at<uchar>(i, j)<binaryzationValue)
{
vectorHorizontal[i]++;
}
}
}
normalize(vectorHorizontal, vectorHorizontal, 0, high, NORM_MINMAX);
for (int i = 0; i<img.rows; i++)
{
for (int j = 0; j<img.cols; j++)
{
if (vectorHorizontal[i]>threshodValue)
{
line(imageOut, Point(img.cols - vectorHorizontal[i], i), Point(img.cols, i), Scalar(0));
}
}
}
//找到投影四个角点坐标
vector<int>::iterator beginV = vectorVertical.begin();
vector<int>::iterator beginH = vectorHorizontal.begin();
vector<int>::iterator endV = vectorVertical.end() - 1;
vector<int>::iterator endH = vectorHorizontal.end() - 1;
int widthV = 0;
int widthH = 0;
int highV = 0;
int highH = 0;
while (*beginV<threshodValue)
{
beginV++;
widthV++;
}
while (*endV<threshodValue)
{
endV--;
widthH++;
}
while (*beginH<threshodValue)
{
beginH++;
highV++;
}
while (*endH<threshodValue)
{
endH--;
highH++;
}
//投影矩形
Rect rect(widthV, highV, img.cols - widthH - widthV, img.rows - highH - highV);
return rect;
}
二维码:
条形码: