精华内容
下载资源
问答
  • 信息编码基本原理 我们日常甚或中

    编码原理

    我们日常生活中通过扫描二维码可以获得相应信息,例如:扫描百度首页的二维码就可以进入到百度首页,百度首页二维码图像中存储的其实是字符信息——百度首页的地址:https://www.baidu.com
    又因为计算机中数据都是通过二进制数编码存储的,所以字符信息可以看做是一串具有规则的1、0排列字符串。
    假如对应字符的编码为(4bit位随便编排一种): w:0000 ,b:0001,a:0010,i:0011,.:0100,d:0101,u:0110 … …

     www.baidu.com
    相应存储编码:0000 0000 0000 0100 0001 0010 0011 0101 0110... ...        
                 w    w    w   .     b     a    i    d   u... ...
    

    那么通过这些0、1字符串,将其按0代表白色方块,1代表黑色方块,按规律可视化成我们现在所看到的的二维码,通过工具扫描之后即可访问相应信息。

    以上就是二维码的内涵所在,但是要真正按要求画出一个现在计算机可以识别成功的二维码图像还得遵循统一的字符编码要求和二维码图像的绘制要求,并不是想象中那么简单,现在所见的二维码是分了板块的,有固定的算法和功能模块。对于能识别出的二维码具体的绘制要求和算法,就不展开介绍了,感兴趣的读者可以自行了解。

    字符编码

    为什么要统一字符编码?

    具体用哪些二进制数字表示哪个符号,当然每个人都可以约定自己的一套(这就叫编码),而大家如果要想互相通信而不造成混乱,那么大家就必须使用相同的编码规则。
    计算机全世界的人都在用,每个国家的字符都是不一样的,但是不存在外国人写的代码中国人运行出来结果不同,或者是中国人在代码中写的汉字,在国外人运行出来出现乱码的情况。前提是使用同一个编码标准。
    目前的文字编码标准主要有 ASCII、GB2312、GBK、Unicode等。ASCII 编码是最简单的西文编码方案。GB2312、GBK、GB18030 是汉字字符编码方案的国家标准。ISO/IEC 10646 和 Unicode 都是全球字符编码的国际标准

    常见字符编码

    ASCII编码:

    由于计算机是美国人发明的,因此,最早只有128个字母(0~127)被编码到计算机里,也就是大小写英文字母、数字和一些符号,这个编码表被称为ASCII编码,比如大写字母 A 的编码是65,小写字母 z 的编码是122。
      但是要处理中文显然一个字节是不够的,至少需要两个字节,而且还不能和ASCII编码冲突,所以,中国制定了GB2312编码,用来把中文编进去。
      全世界有上百种语言,日本把日文编到Shift_JIS里,韩国把韩文编到Euc-kr里,各国有各国的标准,就会不可避免地出现冲突,结果就是,在多语言混合的文本中,显示出来会有乱码。为此引入Unicode编码。
    部分ASCII码展示:  在这里插入图片描述
      … …
    在这里插入图片描述

    统一码(Unicode)

    Unicode 是为了解决传统的字符编码方案的局限而产生的,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求。就是将世界上所有的文字用2个字节统一进行编码。那样,像这样统一编码,2个字节就已经足够容纳世界上所有的语言的大部分文字了。
    现在用的是UCS-2,即2个字节编码,而UCS-4是为了防止将来2个字节不够用才开发的(非常偏僻的字符)。

    ASCII编码是1个字节,而Unicode编码通常是2个字节,举例如下
    字母 A 用ASCII编码是十进制的65,二进制的01000001;

    字符 0 用ASCII编码是十进制的48,二进制的00110000,注意字符 ‘0’ 和整数 0 是不同的;

    汉字 已经超出了ASCII编码的范围,用Unicode编码是十进制的20013,二进制的01001110 00101101。

    如果把ASCII编码的 A 用Unicode编码,只需要在前面补0就可以,因此, A 的Unicode编码是00000000 01000001。
    但是,可以发现 写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。为此可以改变一下Unicode的编码方案(下文介绍)。

    部分Unicode编码:在这里插入图片描述
    汉字在Unicode中的编码范围:
    链接: 汉字编码详细查询.
    在这里插入图片描述

    UTF-8

    UTF-8是针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部分修改后,便可继续使用。

    Unicode只是确定了所有字符的编码数值,但是它并没有指定代码点如何在计算机上存储。 UCS4、UTF-8、UTF-16(UTF后的数字代表编码的最小单位,如UTF-8表示最小单位1字节(=8 bits),所以它可以使用1、2、3字节等进行编码存储,UTF-16表示最小单位2字节,所以它可以使用2、4字节进行编码)都是Unicode的编码方案。其中UTF-8因可以兼容ASCII而被广泛使用。
    eg:UTF-8
    当存储到英文字母 A 的时候,只分配一个字节8bit大小就足够了,于是存储01000001
    当存储到中文 的时候,分配三个字节24bit大小空间存储,于是存储11100100 10111000 10101101
    概括来讲就是按需分配编码位

    UTF-8基本编码规则:1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
    2)对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的unicode码。
    如下表所示:字母x表示可用编码的位。

    Unicode符号范围 | UTF-8编码方式
    (十六进制) | (二进制)
    0000 0000-0000 007F | 0xxxxxxx
    0000 0080-0000 07FF | 110xxxxx 10xxxxxx (可填11位unicode码)
    0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx (可填16位unicode码)
    0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx (可填21位unicode码)

    以“中”(01001110 00101101)为例,依上表可知“二”在UTF-8编码中为三个字节
    使用格式1110xxxx 10xxxxxx 10xxxxxx,从最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了
    11100100 10111000 10101101 加黑字体顺序连接就为Unicode编码

    Java实现简易二维码

    由于UTF-8编码规则较为复杂,将其简化:对于英文,分配8bit,采用Unicode编码,不够8bit,补0;对于中文,分配3字节24bit位,第一个字节全为1,表示向后获取2个字节的真实数据,后两个字节是按照Unicode编码。

    重要数据结构:

    //生成二维码
    //将String转为char,再转为二进制字符串
    char c=text.charAt(i);
    cbinary=Integer.toBinaryString(c);	//将c变为二进制字符串
    toBinaryString()形参本来为int型,char传入发生自动转型,获得char的十进制数
    
    
    //解析二维码图片获得文字信息
    int rgb=buff.getRGB(m, j);
    if(rgb==Color.white.getRGB())
       rebinarytext=rebinarytext+"0";
    else
       rebinarytext=rebinarytext+"1";   
       //将字符串rebinarytext按照二进制转为十进制整型数
       Integer.parseInt(rebinarytext, 2)
       //强制转型整型为char,获得字符串
       retext=retext+(char)Integer.parseInt(rebinarytext, 2);
    
    

    实现效果

    二维码生成器 2021-07-26 12-39-18

    展开全文
  • 二维码识别原理

    千次阅读 2020-07-16 11:26:22
    二维码的特征定位和信息识别 背景介绍 视觉的方法可以用来估计位置和姿态。最容易想到的是在目标上布置多个容易识别的特征,这样使用opencv相机标定和、相机畸变矫正、轮廓提取、solvepnp来获取目标相对于相机的位姿...

    二维码的特征定位和信息识别

    背景介绍

    视觉的方法可以用来估计位置和姿态。最容易想到的是在目标上布置多个容易识别的特征,这样使用opencv相机标定和、相机畸变矫正、轮廓提取、solvepnp来获取目标相对于相机的位姿。在实际使用中只要相机和目标一方是估计的,那就可以得到全局坐标。如果相机和靶标都在移动,那只能获取到相对坐标。但是受限于相机视角和景深,这样多个特征的识别虽然精度可以很高,但是范围却很小。

    对于如何扩大范围,使用二维码是一个很好的思路。首先,二维码本身具有多个特征,单个二维码可以用来实现上述方法的功能。其次,二维码本身带有信息,如果二维码的布置事先已知,那么位置和姿态估计的范围将只受限于二维码的数量。

    本文主要是二维码的特征识别和信息识别。

    二维码的的生成

    二维码是在一个网站上生成的,经过手机的测试,生成的二维码没有问题。http://www.liantu.com/,该网站是免费的,生成的图片及二维码各种参数可以自定义。
    本文的测试图片有20张,是数字1到5,每个数字隔90度旋转各一张。

    在这里插入图片描述

    二维码的特征识别

    二维码特征识别的思路是:第一步,寻找二维码的三个角的定位角点,需要对图片进行平滑滤波,二值化,寻找轮廓,筛选轮廓中有两个子轮廓的特征,从筛选后的轮廓中找到面积最接近的3个即是二维码的定位角点。第二步:判断3个角点处于什么位置,主要用来对图片进行透视校正(相机拍到的图片)或者仿射校正(对网站上生成的图片进行缩放拉伸旋转等操作后得到的图片)。需要判断三个角点围成的三角形的最大的角就是二维码左上角的点。然后根据这个角的两个边的角度差确定另外两个角点的左下和右上位置。第三步,根据这些特征识别二维码的范围。

    具体的代码:

        Mat src = imread( "pic\\456.jpg", 1 );
        if(src.empty())  
        {  
            fprintf(stderr, "Can not load image!\n");  
            return 0;  
        }  
    
        Mat src_all=src.clone();  
    
        //彩色图转灰度图  
        Mat src_gray;
        cvtColor( src, src_gray, CV_BGR2GRAY );  
    
        //对图像进行平滑处理  
        blur( src_gray, src_gray, Size(3,3) );  
    
        //使灰度图象直方图均衡化  
        equalizeHist( src_gray, src_gray );  
    
        namedWindow("src_gray");  
        imshow("src_gray",src_gray);         //灰度图
    
        //指定112阀值进行二值化
        Mat threshold_output;
        threshold( src_gray, threshold_output, 112, 255, THRESH_BINARY ); 
    

    #ifdef DEBUG
    namedWindow(“二值化后输出”);
    imshow(“二值化后输出”,threshold_output); //二值化后输出
    #endif

        //需要的变量定义
        Scalar color = Scalar(1,1,255 );  
        vector<vector<Point>> contours,contours2;  
        vector<Vec4i> hierarchy;  
        Mat drawing = Mat::zeros( src.size(), CV_8UC3 );  
        //Mat drawing2 = Mat::zeros( src.size(), CV_8UC3 );  
        Mat drawingAllContours = Mat::zeros( src.size(), CV_8UC3 ); 
    
        //利用二值化输出寻找轮廓
        findContours(threshold_output, contours, hierarchy,  CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0) );  
    
        //寻找轮廓的方法
        int tempindex1 = 0;
        int tempindex2 = 0;
    
        for(int i = 0;i<contours.size();i++)
        {
            if(hierarchy[i][2] == -1)
                continue;
            else
                tempindex1 = hierarchy[i][2];                //第一个子轮廓的索引
    
            if(hierarchy[tempindex1][2] == -1)
                continue;
            else
            {
                tempindex2 = hierarchy[tempindex1][2];        //第二个子轮廓的索引
                //记录搜索到的有两个子轮廓的轮廓并把他们的编号存储
                in.a1 = i;
                in.a2 = tempindex1;
                in.a3 = tempindex2;
                vin.push_back(in);
            }
        }
    
        //按面积比例搜索
        vector<index>::iterator it;
        for(it = vin.begin();it != vin.end();)
        {
            vector<Point> out1Contours = contours[it->a1];
            vector<Point> out2Contours = contours[it->a2];
            double lenth1 = arcLength(out1Contours,1);
            double lenth2 = arcLength(out2Contours,1);
            if(abs(lenth1/lenth2-2)>1)
            {
                it = vin.erase(it);
            }
            else
            {
                drawContours( drawing, contours, it->a1,  CV_RGB(255,255,255) , CV_FILLED, 8); 
                it++;
            }
        }
    
        //获取三个定位角的中心坐标  
        Point point[3];
        int i = 0;
        vector<Point> pointthree;
        for(it = vin.begin(),i = 0;it != vin.end();i++,it++)
        {
            point[i] = Center_cal( contours, it->a1 );
            pointthree.push_back(point[i]);
        }
    
        if(pointthree.size() <3)
        {
            cout << "找到的定位角点不足3个"<<endl;
            return 0;  
        }
    
        //计算轮廓的面积,计算定位角的面积,从而计算出边长
        double area = contourArea(contours[vin[0].a1]);
        int area_side = cvRound (sqrt (double(area)));  
        for(int i=0; i<3; i++)  
        {  
            //画出三个定位角的中心连线  
            line(drawing,point[i%3],point[(i+1)%3],color,area_side/10,8);  
        }  
    
        //清除找到的3个点,以便处理下一幅图片使用
        vin.clear();
    
        //由3个定位角校正图片
        //=========================================
        //找到角度最大的点
        double ca[2];
        double cb[2];
    
        ca[0] =  pointthree[1].x - pointthree[0].x;
        ca[1] =  pointthree[1].y - pointthree[0].y;
        cb[0] =  pointthree[2].x - pointthree[0].x;
        cb[1] =  pointthree[2].y - pointthree[0].y;
        double angle1 = 180/3.1415*acos((ca[0]*cb[0]+ca[1]*cb[1])/(sqrt(ca[0]*ca[0]+ca[1]*ca[1])*sqrt(cb[0]*cb[0]+cb[1]*cb[1])));
        double ccw1;
        if(ca[0]*cb[1] - ca[1]*cb[0] > 0) ccw1 = 0;
        else ccw1 = 1;
    
        ca[0] =  pointthree[0].x - pointthree[1].x;
        ca[1] =  pointthree[0].y - pointthree[1].y;
        cb[0] =  pointthree[2].x - pointthree[1].x;
        cb[1] =  pointthree[2].y - pointthree[1].y;
        double angle2 = 180/3.1415*acos((ca[0]*cb[0]+ca[1]*cb[1])/(sqrt(ca[0]*ca[0]+ca[1]*ca[1])*sqrt(cb[0]*cb[0]+cb[1]*cb[1])));
        double ccw2;
        if(ca[0]*cb[1] - ca[1]*cb[0] > 0) ccw2 = 0;
        else ccw2 = 1;
    
        ca[0] =  pointthree[1].x - pointthree[2].x;
        ca[1] =  pointthree[1].y - pointthree[2].y;
        cb[0] =  pointthree[0].x - pointthree[2].x;
        cb[1] =  pointthree[0].y - pointthree[2].y;
        double angle3 = 180/3.1415*acos((ca[0]*cb[0]+ca[1]*cb[1])/(sqrt(ca[0]*ca[0]+ca[1]*ca[1])*sqrt(cb[0]*cb[0]+cb[1]*cb[1])));
        double ccw3;
        if(ca[0]*cb[1] - ca[1]*cb[0] > 0) ccw3 = 0;
        else ccw3 = 1;
    
        CvPoint2D32f poly[4];
        if(angle3>angle2 && angle3>angle1)
        {
            if(ccw3)
            {
                poly[1] = pointthree[1];
                poly[3] = pointthree[0];
            }
            else
            {
                poly[1] = pointthree[0];
                poly[3] = pointthree[1];
            }
            poly[0] = pointthree[2];
            Point temp(pointthree[0].x + pointthree[1].x - pointthree[2].x , pointthree[0].y + pointthree[1].y - pointthree[2].y );
            poly[2] = temp;
        }
        else if(angle2>angle1 && angle2>angle3)
        {
            if(ccw2)
            {
                poly[1] = pointthree[0];
                poly[3] = pointthree[2];
            }
            else
            {
                poly[1] = pointthree[2];
                poly[3] = pointthree[0];
            }
            poly[0] = pointthree[1];
            Point temp(pointthree[0].x + pointthree[2].x - pointthree[1].x , pointthree[0].y + pointthree[2].y - pointthree[1].y );
            poly[2] = temp;
        }
        else if(angle1>angle2 && angle1 > angle3)
        {
            if(ccw1)
            {
                poly[1] = pointthree[1];
                poly[3] = pointthree[2];
            }
            else
            {
                poly[1] = pointthree[2];
                poly[3] = pointthree[1];
            }
            poly[0] = pointthree[0];
            Point temp(pointthree[1].x + pointthree[2].x - pointthree[0].x , pointthree[1].y + pointthree[2].y - pointthree[0].y );
            poly[2] = temp;
        }
    
        CvPoint2D32f trans[4];
        int temp = 50;
        trans[0] = Point2f(0+temp,0+temp);  
        trans[1] = Point2f(0+temp,100+temp);  
        trans[2] = Point2f(100+temp,100+temp);  
        trans[3] = Point2f(100+temp,0+temp);
    
        //获取透视投影变换矩阵
        CvMat *warp_mat = cvCreateMat(3, 3, CV_32FC1);
        cvGetPerspectiveTransform(poly, trans, warp_mat);
    
        //计算变换结果
        IplImage ipl_img(src_all);
        IplImage *dst = cvCreateImage(cvSize(1000, 1000), 8, 3);
        cvWarpPerspective(&ipl_img,dst,warp_mat);
        //=========================================
    

    #ifdef DEBUG
    namedWindow(“透视变换后的图”);
    cvShowImage(“透视变换后的图”,dst); //透视变换后的图

        drawContours( drawingAllContours, contours, -1,  CV_RGB(255,255,255) , 1, 8);
        namedWindow("DrawingAllContours");  
        imshow( "DrawingAllContours", drawingAllContours );  
    
        namedWindow(pathtemp);  
        imshow(pathtemp , drawing );    //3个角点填充
    

    #endif

        //接下来要框出这整个二维码  
        Mat gray_all,threshold_output_all;  
        vector<vector<Point> > contours_all;  
        vector<Vec4i> hierarchy_all;  
        cvtColor( drawing, gray_all, CV_BGR2GRAY );  
    
        threshold( gray_all, threshold_output_all, 45, 255, THRESH_BINARY ); 
    
        findContours( threshold_output_all, contours_all, hierarchy_all,  RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0) );//RETR_EXTERNAL表示只寻找最外层轮廓  
    
        Point2f fourPoint2f[4];  
        //求最小包围矩形  
        RotatedRect rectPoint = minAreaRect(contours_all[0]);  
    
        //将rectPoint变量中存储的坐标值放到 fourPoint的数组中  
        rectPoint.points(fourPoint2f);  
        for (int i = 0; i < 4; i++)  
        {  
            line(src_all, fourPoint2f[i%4], fourPoint2f[(i + 1)%4],
                Scalar(20,21,237), 3);  
        }  
    
        namedWindow(pathtemp);  
        imshow(pathtemp , src_all ); 
    
        //截取二维码区域
        CvSize size= cvSize(200,200);//区域大小
        cvSetImageROI(dst,cvRect(0,0,size.width, size.height));//设置源图像ROI
        IplImage* pDest = cvCreateImage(size,dst->depth,dst->nChannels);//创建目标图像
        cvCopy(dst,pDest); //复制图像
        cvSaveImage("Roi.jpg",pDest);//保存目标图像
    

    二维码的信息识别

    二维码的信息识别使用的是zbar,一个开源的二维码识别库,经过测试,对图像进行平滑,灰度等处理后识别效率还是很高的。zbar的算法流程简介:http://blog.csdn.net/u013738531/article/details/54574262

        //对截取后的区域进行解码
        Mat imageSource = cv::Mat(pDest); 
        cvResetImageROI(pDest);//源图像用完后,清空ROI
        cvtColor( imageSource, imageSource, CV_BGR2GRAY );  //zbar需要输入灰度图像才能很好的识别
    
        //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;      
            cout<<"条码:"<<endl<<symbol->get_data()<<endl;         
        }
    
        imageZbar.set_data(NULL,0);    
    

    程序运行结果

    运行结果1:网站上生成的二维码,依次是原图,二值化,角点定位,旋转矫正,识别结果。
    在这里插入图片描述
    运行结果2:相机拍摄的名片上二维码,依次是灰度图,二值化,角点定位,透视矫正,识别结果。
    在这里插入图片描述

    部分二维码工具:

    最全面的二维码生成网站:https://cli.im/
    App生成渠道二维码统计:https://www.openinstall.io/promotion.html
    电脑手机文件互传秒完成:https://github.com/claudiodangelis/qrcp

    本文程序中一些代码参考以下博客:

    http://blog.csdn.net/nick123chao/article/details/77573675
    http://blog.csdn.net/u010925447/article/details/77996455

    展开全文
  • 二维码识别原理

    千次阅读 2020-07-07 08:40:25
    点击上方“3D视觉工坊”,选择“星标”干货第一时间送达整理:公众号@图像处理与计算机视觉本文仅做学术分享,如有侵权,请联系删除。二维码的特征定位和信息识别背景介绍视觉的方法可以用来估计位...

    点击上方“3D视觉工坊”,选择“星标”

    干货第一时间送达

    整理:公众号@图像处理与计算机视觉

    本文仅做学术分享,如有侵权,请联系删除。

    二维码的特征定位和信息识别

    背景介绍

    视觉的方法可以用来估计位置和姿态。最容易想到的是在目标上布置多个容易识别的特征,这样使用opencv相机标定和、相机畸变矫正、轮廓提取、solvepnp来获取目标相对于相机的位姿。在实际使用中只要相机和目标一方是估计的,那就可以得到全局坐标。如果相机和靶标都在移动,那只能获取到相对坐标。但是受限于相机视角和景深,这样多个特征的识别虽然精度可以很高,但是范围却很小。

    对于如何扩大范围,使用二维码是一个很好的思路。首先,二维码本身具有多个特征,单个二维码可以用来实现上述方法的功能。其次,二维码本身带有信息,如果二维码的布置事先已知,那么位置和姿态估计的范围将只受限于二维码的数量。

    本文主要是二维码的特征识别和信息识别。

    二维码的的生成

    二维码是在一个网站上生成的,经过手机的测试,生成的二维码没有问题。http://www.liantu.com/,该网站是免费的,生成的图片及二维码各种参数可以自定义。 
    本文的测试图片有20张,是数字1到5,每个数字隔90度旋转各一张。

    二维码的特征识别

    二维码特征识别的思路是:第一步,寻找二维码的三个角的定位角点,需要对图片进行平滑滤波,二值化,寻找轮廓,筛选轮廓中有两个子轮廓的特征,从筛选后的轮廓中找到面积最接近的3个即是二维码的定位角点。第二步:判断3个角点处于什么位置,主要用来对图片进行透视校正(相机拍到的图片)或者仿射校正(对网站上生成的图片进行缩放拉伸旋转等操作后得到的图片)。需要判断三个角点围成的三角形的最大的角就是二维码左上角的点。然后根据这个角的两个边的角度差确定另外两个角点的左下和右上位置。第三步,根据这些特征识别二维码的范围。 
    具体的代码:

            Mat src = imread( "pic\\456.jpg", 1 );        if(src.empty())  
            {  
                fprintf(stderr, "Can not load image!\n");  
                return 0;  
            }  
    
            Mat src_all=src.clone();  
    
            //彩色图转灰度图  
            Mat src_gray;
            cvtColor( src, src_gray, CV_BGR2GRAY );  
    
            //对图像进行平滑处理  
            blur( src_gray, src_gray, Size(3,3) );  
    
            //使灰度图象直方图均衡化  
            equalizeHist( src_gray, src_gray );  
    
            namedWindow("src_gray");  
            imshow("src_gray",src_gray);         //灰度图
    
            //指定112阀值进行二值化
            Mat threshold_output;
            threshold( src_gray, threshold_output, 112, 255, THRESH_BINARY );
    
    #ifdef DEBUG
            namedWindow("二值化后输出");  
            imshow("二值化后输出",threshold_output);   //二值化后输出#endif
    
            //需要的变量定义
            Scalar color = Scalar(1,1,255 );  
            vector<vector<Point>> contours,contours2;  
            vector<Vec4i> hierarchy;  
            Mat drawing = Mat::zeros( src.size(), CV_8UC3 );  
            //Mat drawing2 = Mat::zeros( src.size(), CV_8UC3 );  
            Mat drawingAllContours = Mat::zeros( src.size(), CV_8UC3 );
    
            //利用二值化输出寻找轮廓
            findContours(threshold_output, contours, hierarchy,  CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0) );  
    
            //寻找轮廓的方法
            int tempindex1 = 0;        int tempindex2 = 0;        for(int i = 0;i<contours.size();i++)
            {            if(hierarchy[i][2] == -1)                continue;            else
                    tempindex1 = hierarchy[i][2];                //第一个子轮廓的索引
    
                if(hierarchy[tempindex1][2] == -1)                continue;            else
                {
                    tempindex2 = hierarchy[tempindex1][2];        //第二个子轮廓的索引
                    //记录搜索到的有两个子轮廓的轮廓并把他们的编号存储
                    in.a1 = i;
                    in.a2 = tempindex1;
                    in.a3 = tempindex2;
                    vin.push_back(in);
                }
            }        //按面积比例搜索
            vector<index>::iterator it;        for(it = vin.begin();it != vin.end();)
            {            vector<Point> out1Contours = contours[it->a1];            vector<Point> out2Contours = contours[it->a2];            double lenth1 = arcLength(out1Contours,1);            double lenth2 = arcLength(out2Contours,1);            if(abs(lenth1/lenth2-2)>1)
                {
                    it = vin.erase(it);
                }            else
                {
                    drawContours( drawing, contours, it->a1,  CV_RGB(255,255,255) , CV_FILLED, 8);
                    it++;
                }
            }        //获取三个定位角的中心坐标  
            Point point[3];        int i = 0;        vector<Point> pointthree;        for(it = vin.begin(),i = 0;it != vin.end();i++,it++)
            {
                point[i] = Center_cal( contours, it->a1 );
                pointthree.push_back(point[i]);
            }        if(pointthree.size() <3)
            {            cout << "找到的定位角点不足3个"<<endl;            return 0;  
            }        //计算轮廓的面积,计算定位角的面积,从而计算出边长
            double area = contourArea(contours[vin[0].a1]);        int area_side = cvRound (sqrt (double(area)));  
            for(int i=0; i<3; i++)  
            {  
                //画出三个定位角的中心连线  
                line(drawing,point[i%3],point[(i+1)%3],color,area_side/10,8);  
            }  
    
            //清除找到的3个点,以便处理下一幅图片使用
            vin.clear();        //由3个定位角校正图片
            //=========================================
            //找到角度最大的点
            double ca[2];        double cb[2];
    
            ca[0] =  pointthree[1].x - pointthree[0].x;
            ca[1] =  pointthree[1].y - pointthree[0].y;
            cb[0] =  pointthree[2].x - pointthree[0].x;
            cb[1] =  pointthree[2].y - pointthree[0].y;        double angle1 = 180/3.1415*acos((ca[0]*cb[0]+ca[1]*cb[1])/(sqrt(ca[0]*ca[0]+ca[1]*ca[1])*sqrt(cb[0]*cb[0]+cb[1]*cb[1])));        double ccw1;        if(ca[0]*cb[1] - ca[1]*cb[0] > 0) ccw1 = 0;        else ccw1 = 1;
    
            ca[0] =  pointthree[0].x - pointthree[1].x;
            ca[1] =  pointthree[0].y - pointthree[1].y;
            cb[0] =  pointthree[2].x - pointthree[1].x;
            cb[1] =  pointthree[2].y - pointthree[1].y;        double angle2 = 180/3.1415*acos((ca[0]*cb[0]+ca[1]*cb[1])/(sqrt(ca[0]*ca[0]+ca[1]*ca[1])*sqrt(cb[0]*cb[0]+cb[1]*cb[1])));        double ccw2;        if(ca[0]*cb[1] - ca[1]*cb[0] > 0) ccw2 = 0;        else ccw2 = 1;
    
            ca[0] =  pointthree[1].x - pointthree[2].x;
            ca[1] =  pointthree[1].y - pointthree[2].y;
            cb[0] =  pointthree[0].x - pointthree[2].x;
            cb[1] =  pointthree[0].y - pointthree[2].y;        double angle3 = 180/3.1415*acos((ca[0]*cb[0]+ca[1]*cb[1])/(sqrt(ca[0]*ca[0]+ca[1]*ca[1])*sqrt(cb[0]*cb[0]+cb[1]*cb[1])));        double ccw3;        if(ca[0]*cb[1] - ca[1]*cb[0] > 0) ccw3 = 0;        else ccw3 = 1;
    
            CvPoint2D32f poly[4];        if(angle3>angle2 && angle3>angle1)
            {            if(ccw3)
                {
                    poly[1] = pointthree[1];
                    poly[3] = pointthree[0];
                }            else
                {
                    poly[1] = pointthree[0];
                    poly[3] = pointthree[1];
                }
                poly[0] = pointthree[2];
                Point temp(pointthree[0].x + pointthree[1].x - pointthree[2].x , pointthree[0].y + pointthree[1].y - pointthree[2].y );
                poly[2] = temp;
            }        else if(angle2>angle1 && angle2>angle3)
            {            if(ccw2)
                {
                    poly[1] = pointthree[0];
                    poly[3] = pointthree[2];
                }            else
                {
                    poly[1] = pointthree[2];
                    poly[3] = pointthree[0];
                }
                poly[0] = pointthree[1];
                Point temp(pointthree[0].x + pointthree[2].x - pointthree[1].x , pointthree[0].y + pointthree[2].y - pointthree[1].y );
                poly[2] = temp;
            }        else if(angle1>angle2 && angle1 > angle3)
            {            if(ccw1)
                {
                    poly[1] = pointthree[1];
                    poly[3] = pointthree[2];
                }            else
                {
                    poly[1] = pointthree[2];
                    poly[3] = pointthree[1];
                }
                poly[0] = pointthree[0];
                Point temp(pointthree[1].x + pointthree[2].x - pointthree[0].x , pointthree[1].y + pointthree[2].y - pointthree[0].y );
                poly[2] = temp;
            }
    
            CvPoint2D32f trans[4];        int temp = 50;
            trans[0] = Point2f(0+temp,0+temp);  
            trans[1] = Point2f(0+temp,100+temp);  
            trans[2] = Point2f(100+temp,100+temp);  
            trans[3] = Point2f(100+temp,0+temp);        //获取透视投影变换矩阵
            CvMat *warp_mat = cvCreateMat(3, 3, CV_32FC1);
            cvGetPerspectiveTransform(poly, trans, warp_mat);        //计算变换结果
            IplImage ipl_img(src_all);
            IplImage *dst = cvCreateImage(cvSize(1000, 1000), 8, 3);
            cvWarpPerspective(&ipl_img,dst,warp_mat);        //=========================================#ifdef DEBUG
            namedWindow("透视变换后的图");  
            cvShowImage("透视变换后的图",dst);         //透视变换后的图
    
            drawContours( drawingAllContours, contours, -1,  CV_RGB(255,255,255) , 1, 8);
            namedWindow("DrawingAllContours");  
            imshow( "DrawingAllContours", drawingAllContours );  
    
            namedWindow(pathtemp);  
            imshow(pathtemp , drawing );    //3个角点填充#endif
    
            //接下来要框出这整个二维码  
            Mat gray_all,threshold_output_all;  
            vector<vector<Point> > contours_all;  
            vector<Vec4i> hierarchy_all;  
            cvtColor( drawing, gray_all, CV_BGR2GRAY );  
    
            threshold( gray_all, threshold_output_all, 45, 255, THRESH_BINARY );
    
            findContours( threshold_output_all, contours_all, hierarchy_all,  RETR_EXTERNAL, CHAIN_APPROX_NONE, Point(0, 0) );//RETR_EXTERNAL表示只寻找最外层轮廓  
    
            Point2f fourPoint2f[4];  
            //求最小包围矩形  
            RotatedRect rectPoint = minAreaRect(contours_all[0]);  
    
            //将rectPoint变量中存储的坐标值放到 fourPoint的数组中  
            rectPoint.points(fourPoint2f);  
            for (int i = 0; i < 4; i++)  
            {  
                line(src_all, fourPoint2f[i%4], fourPoint2f[(i + 1)%4],
                    Scalar(20,21,237), 3);  
            }  
    
            namedWindow(pathtemp);  
            imshow(pathtemp , src_all );
    
            //截取二维码区域
            CvSize size= cvSize(200,200);//区域大小
            cvSetImageROI(dst,cvRect(0,0,size.width, size.height));//设置源图像ROI
            IplImage* pDest = cvCreateImage(size,dst->depth,dst->nChannels);//创建目标图像
            cvCopy(dst,pDest); //复制图像
            cvSaveImage("Roi.jpg",pDest);//保存目标图像
    

    二维码的信息识别

    二维码的信息识别使用的是zbar,一个开源的二维码识别库,经过测试,对图像进行平滑,灰度等处理后识别效率还是很高的。zbar的算法流程简介:http://blog.csdn.net/u013738531/article/details/54574262。

            //对截取后的区域进行解码
            Mat imageSource = cv::Mat(pDest);
            cvResetImageROI(pDest);//源图像用完后,清空ROI
            cvtColor( imageSource, imageSource, CV_BGR2GRAY );  //zbar需要输入灰度图像才能很好的识别
    
            //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;      
                cout<<"条码:"<<endl<<symbol->get_data()<<endl;        
            }
    
            imageZbar.set_data(NULL,0);    
    

    程序运行结果

    运行结果1:网站上生成的二维码,依次是原图,二值化,角点定位,旋转矫正,识别结果。

    运行结果2:相机拍摄的名片上二维码,依次是灰度图,二值化,角点定位,透视矫正,识别结果。

    推荐阅读

    重磅!3DCVer-学术论文写作投稿 交流群已成立

    扫码添加小助手微信,可申请加入3D视觉工坊-学术论文写作与投稿 微信交流群,旨在交流顶会、顶刊、SCI、EI等写作与投稿事宜。

    同时也可申请加入我们的细分方向交流群,目前主要有3D视觉CV&深度学习SLAM三维重建点云后处理自动驾驶、CV入门、三维测量、VR/AR、3D人脸识别、医疗影像、缺陷检测、行人重识别、目标跟踪、视觉产品落地、视觉竞赛、车牌识别、硬件选型、学术交流、求职交流等微信群,请扫描下面微信号加群,备注:”研究方向+学校/公司+昵称“,例如:”3D视觉 + 上海交大 + 静静“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进去相关微信群。原创投稿也请联系。

    ▲长按加微信群或投稿

    ▲长按关注公众号

    3D视觉从入门到精通知识星球:针对3D视觉领域的知识点汇总、入门进阶学习路线、最新paper分享、疑问解答四个方面进行深耕,更有各类大厂的算法工程人员进行技术指导。与此同时,星球将联合知名企业发布3D视觉相关算法开发岗位以及项目对接信息,打造成集技术与就业为一体的铁杆粉丝聚集区,近1000+星球成员为创造更好的AI世界共同进步,知识星球入口:

    学习3D视觉核心技术,扫描查看介绍,3天内无条件退款

     圈里有高质量教程资料、可答疑解惑、助你高效解决问题

    展开全文
  • 一维码和二维码图像识别

    千次阅读 2016-08-23 17:37:02
    步骤 1、提取图像中条码区 2、识别条码 示例 一维码的识别 图1.1原图 图1.2提取条码区 图1.3识别结果   二维码识别 图2.1原图 图2.2提取条码区 图2.3识别结果

    1、步骤

    (1) 提取图像中条码区

    (2) 识别条码

    2、示例

    2.1、一维码的识别


    图1.1原图


    图1.2提取条码区


    图1.3识别结果

     

    2.2、二维码的识别


    图2.1原图


    图2.2提取条码区


    图2.3识别结果


    作者:Jacky_Ponder,转载或分享请注明出处。QQ:2814152689

    展开全文
  • android二维码识别原理与测试方法

    千次阅读 2017-10-20 11:38:39
    首先看看二维码识别原理: 一.我们都是使用二维码生成工具制码,原理对于我们意义并不是很大,这里就不浪费地方复制黏贴了。二维码编码原理请google。 二.下面是与本次问题相关的一些经验。 1. 同一尺寸...
  • Java二维码编码识别

    2021-07-28 14:10:37
    二维码图像存储的信息属于字符信息,如果信息少,黑白块数量也少,反之就同理。 我们用二进制01 来表示成它的信息码 字符型char 在java占2byte,16bit 0000 0000 0000 0000 -字符 public class StringTest { ...
  • 本课将继续介绍图像识别中的条形码识别、二维码识别。我们将分别介绍二者的基本概念、操作流程、参数解析及应用案例。 条形码识别 条形码识别的概念 条形码识别,即在一幅灰度图像中指定 ROI 区域检测出所有符合参数...
  • 小知识:浅谈二维码的生成和识别原理

    万次阅读 多人点赞 2019-12-26 15:14:15
    静态二维码 二进制生成图形码 二维码的定位 前言 不知不觉中,我们的生活到处充满了二维码。登录账户需要二维码;加好友需要二维码;共享单车需要二维码;商品包装上也有二维码;甚至连楼下卖水果的阿姨手里都...
  • 数字图像处理之二维码图像提取算法(二)

    万次阅读 多人点赞 2014-11-06 13:27:59
    图像输入-> 图像预处理-> 二维码图像切割(定位)-> 二维码图像校正-> 译码 译码:二维码码字提取-> 纠错译码-> 信息译码 纠错译码:求解伴随因子(判断)  正确码字=Yji ^ 错误码字。; 信息译码:...
  • 二维码及其识别技术

    千次阅读 2015-04-02 11:28:18
    二维码及其识别技术 什么是二维码  二维条码是指在一维条码的基础上扩展出另一维具有可读性的条码,使用黑白矩形图案表示二进制数据,被设备扫描后可获取其中所包含的信息。一维条码的宽度记载着数据,而其...
  • zxing扫描二维码识别图片二维码及其优化策略 Posted on 2016-01-08 | In Android | | Visitors | 20213二维码介绍Android中用于二维码相关的库比较少,并且大多数已经不再维护(具体可见...
  • 二维码识别

    千次阅读 2018-12-06 09:58:10
    版权声明:本文为博主原创文章,未经博主允许不得转载。...二维码 (2-dimensional bar code),是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的。 在许多种类...
  • 这些黑白间隔的矩形块很容易进行图像处理的检测。 (2)校正图形:根据尺寸的不同,矫正图形的个数也不同。矫正图形主要用于QR码形状的矫正,尤其是当QR码印刷在不平坦的面上,或者拍照时候发生畸变等。 (3)定位...
  • 二维码识别zbar.zip

    2020-04-12 13:21:57
    后来自己尝试改进识别效果,先看了一下二维码识别原理,太复杂了,无从下手。于是尝试对图像进行预处理改进,结果只是用了一个二值化加开运算就让识别效果得到了大幅提升,让我很奇怪这么简单的预处理为什么开发...
  • 二维码生成及其原理

    2013-11-12 10:47:00
    二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型 AD: 二维码又称QR Code,QR全称Quick ...
  • Data Matrix二维码由美国国际资料公司(International Data Matrix)于1989年发明,是一种由黑色、白色的色块以正方形或长方形组成的二维码,其发展构想是希望在较小的标签上存储更多的信息量。DM码适合于小零件的的...
  • WP二维码识别

    千次阅读 2013-05-16 16:34:04
    这里我们使用开源的图像识别库Zxing lib的silverlight/WP7版本来做二维码图像的识别。基本原理为:每隔一定时间从摄像头的视频流截取一张图片,获取其亮度信息,由于二维码只有黑白两色,所以只从亮度信息就可以获取...
  • java开发,公司最近需要在对凭证识别的过程中加入二维码识别,在网上搜索后,选定了两款开源工具,分别是谷歌的ZXING,以及QRcode,但本地测试后发现,QRcode相对于ZXING效果还是要差一点。但ZXING主要还是针对移动端...
  • 原理二维码可认为是信息编码+信息解码+信息分发处理三种技术的综合。利用摄像头和二维码识别技术,可快速获得二维码信息内容,结合后端分发处理系统,可以实现移动支付、网页浏览、资源下载、防伪验证...
  • 二维条码从类别上分为两大类,一类是...等,其原理都是建立在一维条码的基础上。另一类是 以 QR 码为代表的矩阵式二维条码,包括 Data Matrix 、 Maxi Code 、 Code One 等, 是一些比较
  • 使用OpenCV+Zbar组合可以很容易的识别图片中的二维码,特别是标准的二维码,这里标准指的是二维码成像清晰,图片中二维码的空间占比在40%~100%之间,这样标准的图片,Zbar识别起来很容易,不需要opencv额外的处理。...
  • 二维码生成原理及解析代码

    万次阅读 多人点赞 2017-12-18 22:35:06
    二维码生成原理及解析代码 自从大街小巷的小商小贩都开始布满了腾讯爸爸和阿里爸爸的二维码之后,我才感觉到我大天朝共享支付的优越性。最近毕业论文写的差不多了,在入职之前多学一些东西也是好的。这里秉着好奇心...
  • 提高二维码识别

    万次阅读 2013-12-09 12:00:52
    一般情况下的二维码都是很容易识别的,这里指的是直接在电脑上或手机上生成的二维码图片,这类图片由于直接生成的,所以都很清晰,很容易识别出来。 我这里所说的二维码识别率指的下类的二维码: 1.二维码打印...
  • 二维码又称QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型:比如:字符,数字,日文,中文等等。这两天学习了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,547
精华内容 1,818
关键字:

二维码图像识别原理