精华内容
下载资源
问答
  • 二维码图像识别原理
    2017-10-20 15:11:17
    二维码结构
    1.版本信息:version1(21*21),version2,...,version40,一共40个版本。版本代表每行有多少模块,每一个版本比前一个版本增加4个码元,计算公式为(n-1)*4+21,每个码元存储一个二进制0或者 1。1代表黑色,0表示白色。比如,version1表示每一行有21个码元。
    2:格式信息:存储容错级别L(7%),M(15%),Q(25%),R(35%)。容错:允许存储的二维码信息出现重复部分,级别越高,重复信息所占比例越高。目的:即使二维码被图标遮住一部分,一样可以获取全部二维码内容。有图片的二维码, 图片不算二维码的一部分,它遮住一部分码元,但还是可以扫描到所有内容。
    3.数据和纠错码字:实际保存的二维码信息,和纠错码字(用于修正二维码损坏带来的错误,就是说当码元被图片遮住,可以通过纠错码字来找回)。
    4.位置探测图形、位置探测图形分隔符、定位图形,校正图形:用于对二维码的定位。位置探测图形用于标记矩形大小,3个图形确定一个矩形。定位符是因为二维码有40个版本尺寸,当尺寸过大后需要有根标准线,不然扫描的时候可能会扫歪。


    二维码的生成
    信息按照一定的编码规则后变成二进制,通过黑白色形成矩形。
    1、根据version和纠错级别(纠错码的数量)编码生成一个二进制序列,序列包含
    编码类型的二进制(数字类型,字符类型有特定的编码)     编码内容的长度的二进制      编码内容的二进制    结束符(4个0)
    2、将序列按8bits为一组重排,如果所有的编码加起来不是8个倍数,还要在后面加上足够的0。
    3、补码。如果序列还没有达到最大的bits数的限制,还要加一些补齐码(Padding Bytes),Padding Bytes就是重复下面的两个bytes:11101100 00010001。每种版本的bits的位数是不同的。
    4、生成纠错码。可以查看文档的第30页到44页的Table-13到Table-22的定义表,可以知道生成纠错码的过程。
    5、穿插放置。把数据码和纠错码的各个8位一组的十进制数(codewords)交替放在一起。如何交替呢,规则如下:
    对于数据码:把每个块的第一个codewords先拿出来按顺度排列好,然后再取第一块的第二个,如此类推。
    对于纠错码:规则也是一样。
    然后按数据码在前纠错码在后合并起来。
    6、加上Reminder Bits,对于某些Version的QR,上面的还不够长度,还要加上Remainder Bits,比如:5Q版的二维码,还要加上7个bits,Remainder Bits加零就好了。关于哪些Version需要多少个Remainder bit,可以参看文档的第15页的Table-1的定义表。
    7、按照一定规则进行掩码,就是进行异或,分散数据,进行画图。


    二维码的解析


    1、定位图形:首先寻找探测图形,就是二维码上的三个方块。这三个方块的作用就是不管在哪个方向扫描图形,都可以扫到,不信可以将手机翻转测试一下。在通过二维码上的定位图形和分隔符确定二维码信息的图像。定位图形确定二维码符号中模块的坐标,二维码中的模块都是固定的,包括校正图形,版本信息,数据和纠错码。分隔符呢,就是将探测图形与二维码信息图像分开。


    2、灰度化二维码信息像素:手机拍到的图像都是彩色的,所以拍摄到二维码也不列外,它也是彩色的,只不过除了黑白,其他颜色非常浅而已。灰度化是指通过颜色的深浅来识别二维码,就是说颜色深的按深灰处理,浅色的按浅灰处理,去掉其他颜色。


    3、去掉二维码信息像素的噪点:相机的传感器在把光线作为接收信号和输出过程产生的粗糙像素,这些粗糙的像素是照片中不应该出现的干扰因素。噪点就是指这些粗糙的像素。


    4、二值化二维码信息像素:二值化是说将图像上像素灰度值设置为0或者255,也就是变成只有黑白两种颜色。第一步已经灰度化变成只有深灰和浅灰两种颜色,现在二值化是将深灰变成黑色,浅灰变成白色。为什么变成黑白色呢。因为二维码图像其实是由二进制的0或者1组成,0代表白色,1代表黑色。二维码在二值化时会将二维码图像变成只有黑白色的条码,然后根据解析公式什么的(因为像素是0-255之间,要全部转变成0或者255,估计得经过一些计算,然后0就是0,255变为1)转化成二进制信息。


    5、二维码译码和纠错:将得到的二进制信息进行译码和纠错。得到的二进制信息是版本格式信息、数据和纠错码经过一定的编码方式生成的,所以译码是对版本格式信息,数据和纠错码进行解码和对比。纠错是和译码同时进行的,将数据进行纠错。


    得到数据

    更多相关内容
  • 二维码识别原理

    万次阅读 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天内无条件退款

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

    展开全文
  • 基于LabVIEW Vision的图像处理可识别二维码图像匹配 基于LabVIEW Vision的图像处理可识别二维码图像匹配 基于LabVIEW Vision的图像处理可识别二维码图像匹配 基于LabVIEW ...
  • 二维码视觉识别系统

    2022-08-17 16:14:53
    在现代的印刷、五金、食品等行业,质量的要求越来越高,为了对产品进行标识,或者处于防伪方面的需要,普遍使用了字符、条码以及二维码等。使用康耐德智能机器视觉系统可以开始搭建二维码读取系统,并将读码信息进行...

    视觉系统:30w机器视觉系统

    图像精度:15um/pixel

    检测效率:8pcs/s

    项目评语:

    在现代的印刷、五金、食品等行业,质量的要求越来越高,为了对产品进行标识,或者处于防伪方面的需要,普遍使用了字符、条码以及二维码等。使用机器视觉系统可以开始搭建二维码读取系统,并将读码信息进行当地管理和分类,上传到服务器实现对产品的跟踪,还可以对二维码特征进行产品定位等。

    二维码识别视觉系统方案详述

    硬件配置:
    硬件是二维码识别视觉检测系统实现的基础,通用的机器视觉系统主要由:工业相机+图像采集卡+工业镜头+机器视觉光源及控制器+工业计算机等组成。本方案在满足系统要求的前提下,本着物美价廉的原则,精选优质稳定的机器视觉光源及配套的电源控制器,快速分辨率工业相机,低畸变工业镜头,从而搭建一个高性价比的机器视觉测量系统。

    工业相机的确定
    根据检查内容,电容外观检查系统没有牵涉到尺寸检查、定位导航等高精度检查项目,所以对于相机的分辨率要求不高,对成像质量也没有苛刻的要求,因此我们优先考虑成本优势,选择500w万像素的CMOS相机。

    工业镜头的确定
    根据所选相机的传感器尺寸大小(1/3’)、拍摄的视野大小(30*15mm)及镜头的工作距离,选择35mm定焦镜头,可以满足拍摄要求。 

    机器视觉光源与控制器的确定
    结合现有二维码,我们已经进行了多次拍摄及测量实验,并考虑系统需求,突现特征,从而获得更高的测量准度,选用四面可调光源,特别选择机器视觉专用光源控制器(电源),以得到好的照明并且延长光源寿命。

    软件系统
    本方案选用康耐德二维码识别视觉系统系统,作为二维码识别视觉系统的软件模块,其准确的定位、优秀的算法可以适用于二维码识别,且测量时所见即所得。

    展开全文
  • 二维码识别zbar.zip

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

    千次阅读 2022-04-10 10:40:02
    支持各类二维码、条形码等的识别; 多种码混合识别,且红色框线定位; 支持png, jpg, bmp等各种图片格式; 绿色工具免安装,识别效率快准狠。
  • android二维码识别原理与测试方法

    千次阅读 2017-10-20 11:38:39
    首先看看二维码识别原理: 一.我们都是使用二维码生成工具制码,原理对于我们意义并不是很大,这里就不浪费地方复制黏贴了。二维码编码原理请google。 二.下面是与本次问题相关的一些经验。 1. 同一尺寸...
  • 1 简介 QR二维码识别技术是数字... 本文首先对国内外QR二维码识别技术进行了深入研究和对比,主要包括QR码的原理,结构特点,编码规则以及重点研究数字图像处理算法在QR二维码上的应用.结合QR二维码自身结构特征,通过
  • 点击上方“小白学视觉”,选择加"星标"或“置顶”重磅干货,第一时间送达 本文转自|新机器视觉生活在数字时代的我们,很多场合都用到了二维码。看网页要扫二维码,加好友要扫二维...
  • 目前,国内QR码的识别大多是基于PC的,而且对于光照不均、背景较复杂的二维码图像,其识别速度和准确度也有待提高。另外,Android平台上当前流行的二维码识别软件普遍存在着扫描速度慢,需要精确对焦等缺点,而且...
  • 二维码研究综述--传统图像处理方法

    千次阅读 2022-01-26 09:37:37
    题目:《Data Matrix二维条码图像识别的算法研究与实现》 学生:李雅静 2009年6月 1.2 摘要 问题:实际采集到的二维条码图像 , 不可避免的会遇到光照不均 、 拍摄角度多样 、 背景图案复杂 、 条码污损等问题 。研究...
  • Android二维码原理与优化方向

    万次阅读 多人点赞 2020-03-03 16:37:31
    做过Android的二维码扫描的童鞋可能会遇到过,很多二维码识别不了,或者识别速度慢。一般造成这个识别不出来的原因,大概以下几点: Android手机配置不一样,手机像素高低也有不同,有的手机还不支持自动对焦 环境...
  • 【OpenCV】二维码识别

    千次阅读 2022-04-06 21:43:37
    文章目录前言一、OpenCV自带二维码识别功能二、使用pyzbar识别三、串口发送数据四、播放音乐总结 前言 最终实现视频动态识别二维码。 一、OpenCV自带二维码识别功能 import cv2 as cv import numpy as np src_...
  • 浅析二维码基本原理

    万次阅读 2020-10-21 16:08:01
    识别技术的发展可以分为三个阶段:自然识别阶段、模式识别阶段、自动识别阶段。...信息封装是指通过对物体的直观信息进行抽象,将信息转化为可编码的文字、图像等形式。然后通过数据编码对原始数据进
  • 二维码识别

    千次阅读 2018-12-06 09:58:10
    版权声明:本文为博主原创文章,未经博主允许不得转载。...二维码 (2-dimensional bar code),是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的。 在许多种类...
  • Java二维码编码识别

    2021-07-28 14:10:37
    二维码图像存储的信息属于字符信息,如果信息少,黑白块数量也少,反之就同理。 我们用二进制01 来表示成它的信息码 字符型char 在java占2byte,16bit 0000 0000 0000 0000 -字符 public class StringTest { ...
  • 二维码基本原理

    千次阅读 2019-09-25 14:24:10
    二维码扫描器是根据条码的颜色反差 (Color Contrast) 而识别,因此以黑白颜色配搭效果最好。二维码的反差色不明显会出现扫描不出来的情况。白色的二维码,可以换成黑色的底色。二维码扫描器是根据条码的颜色反差 ...
  • 矩阵式二维条码是建立在计算机图像处理技术、组合编码原理等基础上的一种新型图形符号自动识读处理码制。具有代表性的矩阵式二维条码有:Code One、MaxiCode、QR Code、 Data Matrix、Han Xin Code、Grid Matrix 等...
  • opencv二维码识别解码

    千次阅读 2019-08-22 16:18:14
    使用opencv库识别QR二维码,框出图片中的二维码,并使用开源库Zxing解码,在这过程中学习理解opencv库相应的函数。 环境: 1. window7系统       2. QT create 1.准备 首先安装QT和QT create...
  • 51单片机二维码识别

    千次阅读 2019-07-23 10:05:39
    现在在做一个硬件项目,主要是用“高端的”51单片机连一个摄像头完成二维码识别的问题。 目录: 一、解决方案 二、详细分析 一、解决方案: 现在的需求是:给出二维码(可能存在图像偏移、旋转等问题),输出...
  • 1 简介 QR二维码识别技术是数字... 本文首先对国内外QR二维码识别技术进行了深入研究和对比,主要包括QR码的原理,结构特点,编码规则以及重点研究数字图像处理算法在QR二维码上的应用.结合QR二维码自身结构特征,通过
  • 二维码原理和制作

    千次阅读 2020-07-25 20:58:22
    什么是二维码 1、二维码又称QR Code,QR全称Quick Response,...在代码编制上巧妙地利用构成计算机内部逻辑基础的“0”、“1”比特流的概念,使用若干个与二进制相对应的几何形体来表示文字数值信息,通过图像输入设备
  • Data Matrix二维码编码原理及其识别技术

    万次阅读 多人点赞 2020-03-26 23:29:47
    Data Matrix二维码由美国国际资料公司(International Data Matrix)于1989年发明,是一种由黑色、白色的色块以正方形或长方形组成的二维码,其发展构想是希望在较小的标签上存储更多的信息量。DM码适合于小零件的的...
  • OpenCV 二维码定位与识别

    千次阅读 2019-12-17 10:48:22
    但常常会因为二维码图像污点、光照不均匀以及二维码图像倾斜等原因,使得二维码的识别正确率低,针对这些问题,通过学习贾老师OpenCV课程以及其他博主的经验[作者仟人斩],实现了基于OpenCV的二维码定位与识别,但仍...
  • 二维码介绍 Android中用于二维码相关的库比较少,并且大多数已经不再维护(具体可见https://android-arsenal.com/tag/81)。其中最常用的是zxing和zbar。 zxing项目是谷歌推出的用来识别多种格式条形码的开源项目...
  • 前言 因工作需要,需要定位图片中的二维码;我遂查阅了相关资料,也学习了opencv开源库。...定位二维码不仅仅是为了识别二维码;还可以通过二维码图像进行水平纠正以及相邻区域定位。定位二维码,不仅需...
  • 二维码生成原理及解析代码

    万次阅读 多人点赞 2017-12-18 22:35:06
    二维码生成原理及解析代码 自从大街小巷的小商小贩都开始布满了腾讯爸爸和阿里爸爸的二维码之后,我才感觉到我大天朝共享支付的优越性。最近毕业论文写的差不多了,在入职之前多学一些东西也是好的。这里秉着好奇心...
  • 二维条码从类别上分为两大类,一类是...等,其原理都是建立在一维条码的基础上。另一类是 以 QR 码为代表的矩阵式二维条码,包括 Data Matrix 、 Maxi Code 、 Code One 等, 是一些比较

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 5,268
精华内容 2,107
热门标签
关键字:

二维码图像识别原理