一维码由一组规则排列的黑色线条、白色线条以及对应的字符组成。对倾斜的(没有严重形变)一维码的角度校正,可以根据其黑白相间、排列规则的特点,计算傅里叶频谱,通过傅里叶频谱中直线的倾斜角度计算空间域图像一维码需校正的角度。

先贴出来待校正的一维码和其傅里叶频谱图:

 
       

傅里叶频谱中亮度值代表了频率变化的强弱,直线的方向代表了频率变化的方向。上图傅里叶频谱中最亮的那条线就是与一维码黑白相间条纹相垂直的方向,找到这条线的角度,就可以计算出一维码的校正角度。

校正步骤:

1. 计算图像X,Y方向上梯度图像,并求和,突出图像边缘信息

2. 离散傅里叶变换,画出一维码的频谱图

3. 霍夫变换定位到傅里叶频谱图中直线,获得直线角度

4. 计算一维码需校正角度,通过仿射变换,校正图像

X、Y方向梯度和:

傅里叶频谱:

阈值化,保留频率变化最明显的分量:

Hough直线定位:

一维码角度校正:

Zbar识别:

Code 实现:

#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"
#include "iostream"
#include "zbar.h" using namespace std;
using namespace zbar;
using namespace cv; int main(int argc,char *argv[])
{
Mat image,imageGray,imageGuussian;
Mat imageSobelX,imageSobelY,imageSobelOut;
imageGray=imread(argv[1],0);
imageGray.copyTo(image);
imshow("Source Image",image);
GaussianBlur(imageGray,imageGuussian,Size(3,3),0);
//水平和垂直方向灰度图像的梯度和,使用Sobel算子
Mat imageX16S,imageY16S;
Sobel(imageGuussian,imageX16S,CV_16S,1,0,3,1,0,4);
Sobel(imageGuussian,imageY16S,CV_16S,0,1,3,1,0,4);
convertScaleAbs(imageX16S,imageSobelX,1,0);
convertScaleAbs(imageY16S,imageSobelY,1,0);
imageSobelOut=imageSobelX+imageSobelY;
imshow("XY方向梯度和",imageSobelOut);
Mat srcImg =imageSobelOut;
//宽高扩充,非必须,特定的宽高可以提高傅里叶运算效率
Mat padded;
int opWidth = getOptimalDFTSize(srcImg.rows);
int opHeight = getOptimalDFTSize(srcImg.cols);
copyMakeBorder(srcImg, padded, 0, opWidth-srcImg.rows, 0, opHeight-srcImg.cols, BORDER_CONSTANT, Scalar::all(0));
Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
Mat comImg;
//通道融合,融合成一个2通道的图像
merge(planes,2,comImg);
dft(comImg, comImg);
split(comImg, planes);
magnitude(planes[0], planes[1], planes[0]);
Mat magMat = planes[0];
magMat += Scalar::all(1);
log(magMat, magMat); //对数变换,方便显示
magMat = magMat(Rect(0, 0, magMat.cols & -2, magMat.rows & -2));
//以下把傅里叶频谱图的四个角落移动到图像中心
int cx = magMat.cols/2;
int cy = magMat.rows/2;
Mat q0(magMat, Rect(0, 0, cx, cy));
Mat q1(magMat, Rect(0, cy, cx, cy));
Mat q2(magMat, Rect(cx, cy, cx, cy));
Mat q3(magMat, Rect(cx, 0, cx, cy));
Mat tmp;
q0.copyTo(tmp);
q2.copyTo(q0);
tmp.copyTo(q2);
q1.copyTo(tmp);
q3.copyTo(q1);
tmp.copyTo(q3);
normalize(magMat, magMat, 0, 1, CV_MINMAX);
Mat magImg(magMat.size(), CV_8UC1);
magMat.convertTo(magImg,CV_8UC1,255,0);
imshow("傅里叶频谱", magImg);
//HoughLines查找傅里叶频谱的直线,该直线跟原图的一维码方向相互垂直
threshold(magImg,magImg,180,255,CV_THRESH_BINARY);
imshow("二值化", magImg);
vector<Vec2f> lines;
float pi180 = (float)CV_PI/180;
Mat linImg(magImg.size(),CV_8UC3);
HoughLines(magImg,lines,1,pi180,100,0,0);
int numLines = lines.size();
float theta;
for(int l=0; l<numLines; l++)
{
float rho = lines[l][0];
theta = lines[l][1];
float aa=(theta/CV_PI)*180;
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
line(linImg,pt1,pt2,Scalar(255,0,0),3,8,0);
}
imshow("Hough直线",linImg);
//校正角度计算
float angelD=180*theta/CV_PI-90;
Point center(image.cols/2, image.rows/2);
Mat rotMat = getRotationMatrix2D(center,angelD,1.0);
Mat imageSource = Mat::ones(image.size(),CV_8UC3);
warpAffine(image,imageSource,rotMat,image.size(),1,0,Scalar(255,255,255));//仿射变换校正图像
imshow("角度校正",imageSource);
//Zbar一维码识别
ImageScanner scanner;
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
int width1 = imageSource.cols;
int height1 = imageSource.rows;
uchar *raw = (uchar *)imageSource.data;
Image imageZbar(width1, height1, "Y800", raw, width1 * height1);
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<<endl;
cout<<"条码:"<<endl<<symbol->get_data()<<endl<<endl;
}
namedWindow("Source Window",0);
imshow("Source Window",imageSource);
waitKey();
imageZbar.set_data(NULL,0);
return 0;
}

Opencv+Zbar二维码识别(一维码校正)的更多相关文章

  1. Opencv+Zbar二维码识别(二维码校正)

    二维码和车牌识别基本都会涉及到图像的校正,主要是形变和倾斜角度的校正,一种二维码的畸变如下图: 这个码用微信扫了一下,识别不出来,但是用Zbar还是可以准确识别的~~. 这里介绍一种二维码校正方法,通 ...

  2. Opencv+Zbar二维码识别(标准条形码/二维码识别)

    使用Opencv+Zbar组合可以很容易的识别图片中的二维码,特别是标准的二维码,这里标准指的是二维码成像清晰,图片中二维码的空间占比在40%~100%之间,这样标准的图片,Zbar识别起来很容易,不 ...

  3. tornado zbar 二维码识别 ,配合nginx 反向代理,supervisord 监控

    tornado zbar 二维码识别 ,配合nginx 反向代理,supervisord 监控 1.zbar识别二维码程序,python2.6.6 #!/usr/bin/env python # co ...

  4. android利用zbar二维码扫描-(解决中文乱码及扫描区域定义)

    写在最前(这是对上一篇博文的问题做的更新[android利用zbar二维码扫描]) project下载   zbarLib编译project  project下载0积分 bug 在2.3的系统中Hol ...

  5. [PHP]快速实现:将二维数组转为一维数组

    如何将下面的二维数组转为一维数组. $msg = array( array( 'id'=>'45', 'name'=>'jack' ), array( 'id'=>'34', 'na ...

  6. 妙用Excel数据透视表和透视图向导,将二维数据转换为一维数据

    项目中,每年都会有各种经销商的各种产品目标数据导入,经销商和产品过多,手工操作过于单调和复杂.那有没有一种方式可以将复杂的二维数据转换为一维数据呢? 有,强大的Excel就支持此功能. 常用Excel ...

  7. MVC5中使用jQuery Post 二维数组和一维数组到Action

    很久没有写了,最近在做一个MVC项目,这是我做的第一个MVC项目.之前可以说多MVC一点都不了解,今天把昨天遇到的一个问题记录下来.MVC大神就请飘过吧,跟我遇到同样问题的可以进来看看.遇到的第一个问 ...

  8. php - 二维数组转一维数组总结

    二维数组转一维数组总结 例如将如下二位数组转以为以为一维数组 $records = [ [ 'id' => 2135, 'first_name' => 'John', 'last_name ...

  9. 数据可视化之PowerQuery篇(四)二维表转一维表,看这篇文章就够了

    https://zhuanlan.zhihu.com/p/69187094 数据分析的源数据应该是规范的,而规范的其中一个标准就是数据源应该是一维表,它会让之后的数据分析工作变得简单高效. 在之前的文 ...

随机推荐

  1. Linux基础学习一

    swap:虚拟内存ctrl+a:跳到命令首部 ctrl+e:跳到命令尾部alias:指令别名cp -r:递归复制粘贴mv 源路径 目标路径:移动操作 (如果提示是否覆盖,在mv前加\即可不提示:\mv ...

  2. Linux学习笔记(一) 文件系统

    对于每一个 Linux 学习者来说,了解 Linux 文件系统的结构是十分有必要的 因为在 Linux 中一切皆文件,可以说只有深入了解 Linux 的文件系统,才会对 Linux 有更深刻的理解 L ...

  3. buf.toString()

    buf.toString([encoding[, start[, end]]]) encoding {String} 默认:'utf8' start {Number} 默认:0 end {Number ...

  4. 版本控制git之五-标签管理 tags 标签 代码版本 如: v1.0

      版本控制git之五-标签管理 打标签 像其他版本控制系统(VCS)一样,Git 可以给历史中的某一个提交打上标签,以示重要. 比较有代表性的是人们会使用这个功能来标记发布结点(v1.0 等等). ...

  5. Python爬虫例子(笔记,不适合参考,愿意看的可以看看)

    话不多说,直接上代码: import re import csv #爬虫的一个小例子,爬的是百度贴吧(网页版)某个帖子的各个楼层的用户名,发言内容和发言时间(使用到了正则表达式) source3.tx ...

  6. 集训第四周(高效算法设计)L题 (背包贪心)

    Description   John Doe is a famous DJ and, therefore, has the problem of optimizing the placement of ...

  7. 集训第四周(高效算法设计)A题 Ultra-QuickSort

    原题poj 2299:http://poj.org/problem?id=2299 题意,给你一个数组,去统计它们的逆序数,由于题目中说道数组最长可达五十万,那么O(n^2)的排序算法就不要再想了,归 ...

  8. 【Codeforces 444A】DZY Loves Physics

    [链接] 我是链接,点我呀:) [题意] 题意 [题解] 两个点的子图他们的"密度"是比所有联通生成子图都要大的 "只要胆子大,遇到什么问题都不怕!" [代码] ...

  9. CodeForcesGym 100517H Hentium Scheduling

    Hentium Scheduling Time Limit: 2000ms Memory Limit: 262144KB This problem will be judged on CodeForc ...

  10. COLLECTL LINUX 监控

    http://blog.csdn.net/leichelle/article/details/23590289