精华内容
下载资源
问答
  • android二维码识别原理与测试方法

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

    首先看看二维码识别原理:

    一.我们都是使用二维码生成工具制码,原理对于我们意义并不是很大,这里就不浪费地方复制黏贴了。二维码编码原理请google。

    二.下面是与本次问题相关的一些经验。

    1.      同一尺寸同一纠错等级下:二维码的点阵密度和包含的字串长度成正比。即信息含量越大,点阵越密。第2张码点阵就密集的多。

    2.   同一尺寸同一URL:二维码的点阵密度,和纠错等级成正比。(即纠错等级越高,点阵越密。)采用目前的低端机C8650测试,(后置摄像头300W像素,支持自动对焦)

    测试发现,相同尺寸下,点阵密度到达一定程度将无法识别。纠错等级是用来当二维码不完整时,也可以正确识别而产生的。

    简单的可以理解为,只要纠错等级高,即使只有半张二维码,也可以扫描识别正确。由于我们的二维码是放在店内的,二维码不易损坏。

    因此我们完全可以尝试用最低等级,以降低点阵密度,提高识别率。

    3.更悲剧的是有的门店的二维码只有这么点小:

    4.周围环境,如拍摄距离,角度,光线等因素都会造成识别以后图像信息缺失,导致失败。比如一茶一坐的二维码是放在会反光的桌牌里,相比较来看,它的二维码识别率就低很多。

    各种证明+推测得出以下结论,主要问题点:

    1.   由于我们的URL长度较长,导致生成的二维码,在小尺寸下,点阵过密,导致无法识别。不考虑环境因素。

    2.二维码物料大部分是放在桌牌里的,餐厅光线条件差,很多机型识别以后图像损失严重。亲们想一想,用个低端机在昏暗的光线下自拍,能看清楚自己的脸不???就是iphone这种机型也会经常识别失真。

     各种机型的识别程度估计:

     

    机型

    摄像头

    识别成功率毛估

     

    高端机

    支持自动对焦,像素高,芯片好

     80%

     

    中端机 

    支持自动对焦,像素低,芯片一般

     60%

     

     低端机

     不支持自动对焦

     。。。。

     


    建议改进方法如下,供参考:

      

    方法编号

    方法

    优点

    缺点

    1

    1.将URL改短

    2.重新印刷二维码

    可以继续张贴小尺寸的二维码

    1.  需要修改我们的系统

    2.  重新印刷二维码

    2

    将二维码纠错等级降低,重新印刷二维码。

    可以继续张贴小尺寸的二维码

    重新印刷二维码

    3

    只改动APP,

    对低端机提示不支持二维码扫描。

    用户体验会好

    低端机无法使用。

    是否要对低端机区别对待,要看产品的定位,和用户机型的了解。

    后续的监测与测试方法:

    建议使用UserTrack + 低端机检测包配合使用,

    统计出无法扫描成功的低端机占总扫描设备的百分比,再来做方案2 和3 的选择。

     

    目前:

    2.6.3扫码更换成淘宝主客户端扫码控件,这样改进点为:

    1扫码时自动打开闪光灯,补偿光线,并且自动对焦。降低环境影响。

    2将URL改为短连接,重新制码,降低点阵密度。

    3.首先识别机型,如果机型不支持自动对焦则提示用户淘宝点点暂时不支持这种机型(未实现)

    4.用UserTrack + 低端机检测包,监测失败机型(未实现)


    展开全文
  • 由一块一块黑白方块组成的二维码在...1)二维码存储信息原理与计算机识别0和1是一样的。比如一个黑色块代表1,白色块代表0,我们假设“1000101”代表信息“A”,用二维码表示:黑白白白黑白黑,当我们手机扫描二维码

    由一块一块黑白方块组成的二维码在我们生活中随处可见,收付款、加好友等等。那么二维码是如何通过黑白方块来存储信息的呢?

    一、二维码的40种版本与8种款式
    1)二维码版本:21x21 ~177x177,共有40个版本。除了面积不同之外,版本越高,二维码承载和储存的信息越多。
    2)二维码款式:有8个款式在,这些款式中黑白块分布均匀。

    二、二维码存储信息的基本原理
    1)二维码存储信息原理与计算机识别0和1是一样的。比如一个黑色块代表1,白色块代表0,我们假设“1000101”代表信息“A”,用二维码表示:黑白白白黑白黑,当我们手机扫描二维码时,将这些黑白块转换成0和1,如此就知道了二维码所存储的信息了。
    2)二维码的组成:定位信息+版本信息+数据编码信息+存储数据信息等。
    在我们用手机扫描二维码时,首先是定位。二维码上排列成三角形的三个大方块、黑色虚线都是用来定位的。我们手机扫描定位后,开始了解二维码的大致情况,这里面包含二维码的版本信息、数据编码之类,剩下区域就是存储数据信息。手机识别二维码信息是从右下角开始的(从下到上,从右到左)。

    三、二维码遮挡或损坏一部分仍然可以识别的原因
    这是因为在二维码中的存储数据区域可以分成两个部分:纠错区域、存储信息区域。纠错部分是备份数据的区域,所以即使二维码被遮挡了一部分,仍然可以用手机识别出来。
    此外,二维码是用容错等级的,一共4个等级。
    (1)L——7%
    (2)M——15%
    (3)Q——25%
    (4)H——30%
    所以二维码的容错等级越高,即使二维码被遮挡的部分大一点依旧不影响扫描,但是提高容错率意味着纠错区域越大,那么二维码存储的数据就自然变少了。

    四、二维码中黑白块分布均匀,没有出先部分全黑或部分全白的现象
    这是因为在生成二维码的过程中,为了避免生成大面积的黑块和白块,进行了“掩码”的操作,二维码经过掩码计算后就会在8个款式中选择一个最合适的一款进行处理,处理完之后二维码中的黑白块就变得均匀了。

    五、二维码会被用光吗?
    二维码是上世纪一家日本公司发明的,早在2016年,全国日平均扫描次数达到15亿次,那么不禁产生疑问:二维码会被用光吗?因为二维码实质上是由0和1组成的,我们知道0和1的组成接近无数种,所以我们不必杞人忧天。

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

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

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

    展开全文
  • 二维码识别

    2013-05-02 14:51:00
    在德问上读到一个问题说是识别二维码,于是尝试了一下,二维码识别 原理方面太难,看来看去确实不懂,我是通过php的curl函数,通过get方式得到返回的数据,然后显示到自己网站上。 这只是一次小尝试,不过前段时间...
  • 二维码识别技术

    2013-04-10 15:25:36
    二维码识别的初步研究 二维码的初步介绍 组成原理
  • 安卓和IOS两个平台微信识别二维码的机制分别是什么?这两个平台解码的步骤是?
  • 二维码识别zbar.zip

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

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

    万次阅读 多人点赞 2017-12-18 22:35:06
    二维码生成原理及解析代码 自从大街小巷的小商小贩都开始布满了腾讯爸爸和阿里爸爸的二维码之后,我才感觉到我大天朝共享支付的优越性。最近毕业论文写的差不多了,在入职之前多学一些东西也是好的。这里秉着好奇心...
  • Atitit java 二维码识别 图片识别   1.1. 解码11.2. 首先,我们先说一下二维码一共有40个尺寸。官方叫版本Version。11.3. 二维码的样例:21.4. 定位图案21.5. 数据编码31.6. 错误修正容量L水平7%的字码可被修正M...
  • 51单片机二维码识别

    千次阅读 2019-07-23 10:05:39
    现在在做一个硬件项目,主要是用“高端的”51单片机连一个摄像头完成二维码识别的问题。 目录: 一、解决方案 二、详细分析 一、解决方案: 现在的需求是:给出二维码(可能存在图像偏移、旋转等问题),输出...
  • halcon二维码识别

    2019-10-18 14:57:55
    5、二维码识别halcon中实战 素材: read_image(Image,‘222.jpg’) rgb1_to_gray (Image, GrayImage) 第一步:创建模板 *************************************************************************** *初学...
  • 二维码生成原理

    2019-07-22 21:59:55
    二维码生成原理及解析代码 最近在学ps,随便了解一波二...
  • 提高二维码识别

    万次阅读 2013-12-09 12:00:52
    一般情况下的二维码都是很容易识别的,这里指的是直接在电脑上或手机上生成的二维码图片,这类图片由于直接生成的,所以都很清晰,很容易识别出来。 我这里所说的二维码识别率指的下类的二维码: 1.二维码打印...
  • 树莓派python实现二维码识别——ZBar

    千次阅读 多人点赞 2019-05-15 21:56:26
    在本文中,采用树莓派进行二维码识别,支持中英文的二维码或者一维条码的识别。硬件由树莓派3Bplus和usb摄像头组成,软件由python、二维码识别开源工具zbar和opencv组成。通过本文,可以学习如何正确快速安装zbar及...
  •  二维码高速识别系统采集和处理粘贴在高速转动的风扇叶片上的Data Matrix两维码图像,并对二维码数据进行识别。此系统可应用于需要对二维码进行识别的各种领域,尤其适合高速流水线的二维码检测。  2 性能指标...
  • 一、二维码简介_二维码基本概念_二维码基本原理 1.二维码又称二维条码,常见的二维码为QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,...
  • 该设计基于STM32F429+OV5642实现二维码识别功能。解码库被打包成lib了。 先来段视频: 附件内容截图:
  • 原理2.1图像预处理2.2 定位标轮廓提取2.3 多二维码鲁棒识别3 运行结果3.1对单张图片进行多二维码识别3.2 获取一系列图片的轨迹 1.背景介绍 使用视觉slam(simultaneous localization and mapping)技术时,经常需要...
  • 二维码识别技术 原理:一维码是将线条与空白按一定的编码规则组合起来,用以表定一定的信息 原理:二维码技术利用在二维平面上黑白相间的图形来记录数据,这些几何图形通过一定规律分布来表示特定信息 一维码...
  • WP二维码识别

    千次阅读 2013-05-16 16:34:04
    这里我们使用开源的图像识别库Zxing lib的silverlight/WP7版本来做二维码图像的识别。基本原理为:每隔一定时间从摄像头的视频流截取一张图片,获取其亮度信息,由于二维码只有黑白两色,所以只从亮度信息就可以获取...
  • 基于Android的二维码识别系统的研究与实现,里面有论文和源码

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 14,480
精华内容 5,792
关键字:

二维码识别原理