嵌入式linux基于摄像头视频流的二维码/一维码识别和生成(zbar,qrencode)

概况

    二维码又称二维条码,常见的二维码为QR Code,QR全称Quick Response,是一个近几年来移动设备上超流行的一种编码方式,它比传统的Bar Code条形码能存更多的信息,也能表示更多的数据类型。详细的可以参考二维码介绍链接 https://cli.im/news/help/10601 如今二维码和一维码存在于我们生活的每一个角落,由于智能手机的普及几乎每个地方都会用,二维码和一维码的存在减少了大量的人工输入文字和读取文字的操作。
    本文挑选的识别和生成二维码/条形码库的为(zbar和qrencode):

zbar 的github地址: https://github.com/JetLinWork/ZBar
+这个是官网也有tar格式的包提供下载: http://zbar.sourceforge.net/download.html
qrencode 的github地址: https://github.com/JetLinWork/libqrencode

Zbar : 二维码识别

    关于zbar的 交叉编译可以参照文章 https://blog.csdn.net/Guet_Kite/article/details/78881318 。一下是我使用瑞芯微平台的rv1126的交叉编译文档参考。

# 下载源码  
wget --no-check-certificate https://nchc.dl.sourceforge.net/project/zbar/zbar/0.10/zbar-0.10.tar.bz2
# 解压
tar -xvf zbar-0.10.tar.gz
# 进入源码目录
cd zbar-0.10
# 配置编译选项 选项详情查看 ./configure --help
./configure --prefix=$(pwd)/output --host=arm-linux-gnueabihf --enable-shared --enable-static --without-imagemagick --without-jpeg --without-python --without-gtk --without-qt --disable-video
# 编译,导出
make && make install

–prefix=$(pwd)/output 这个编译选项为 指定输出的目录为源码目录下的 output/ 文件夹下生成 库和头文件。
–host=arm-linux-gnueabihf 这个编译选项为指定交叉编译器,例如我当前的交叉编译器为 arm-linux-gnueabihf-gcc 。
    其他编译选项参照 ./configure --help 应该描述的很清楚,库的soname 为 libzbar.so.0,进行软连接或者重命名放到设备里。
编译生成的结果
根据需要拷贝静态库/动态库(lib)和头文件(include/),编译时链接即可。效果如下
识别示例

下面两个使用的例子都是用的openCV余先梳理得到zbar需要的8位黑白图像数据:

  1. 从图片识别的实现(代码片段删除类相关信息截取)

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <unistd.h>
#include "opencv2/opencv.hpp"
#include "zbar/zbar.h"

typedef enum TQrCodeTypeTag  
{
    kQrCodeTypeNONE        =      0,  //<  no symbol decoded
    kQrCodeTypePARTIAL     =      1,  //<  intermediate status
    kQrCodeTypeEAN8        =      8,  //<  EAN-8
    kQrCodeTypeUPCE        =      9,  //<  UPC-E
    kQrCodeTypeISBN10      =     10,  //<  ISBN-10 (from EAN-13). @since 0.4
    kQrCodeTypeUPCA        =     12,  //<  UPC-A
    kQrCodeTypeEAN13       =     13,  //<  EAN-13
    kQrCodeTypeISBN13      =     14,  //<  ISBN-13 (from EAN-13). @since 0.4
    kQrCodeTypeI25         =     25,  //<  Interleaved 2 of 5. @since 0.4
    kQrCodeTypeCODE39      =     39,  //<  Code 39. @since 0.4
    kQrCodeTypePDF417      =     57,  //<  PDF417. @since 0.6
    kQrCodeTypeQRCODE      =     64,  //<  QR Code. @since 0.10
    kQrCodeTypeCODE128     =    128,  //<  Code 128
    kQrCodeTypeSYMBOL      = 0x00ff,  //<  mask for base symbol type
    kQrCodeTypeADDON2      = 0x0200,  //<  2-digit add-on flag
    kQrCodeTypeADDON5      = 0x0500,  //<  5-digit add-on flag
    kQrCodeTypeADDON       = 0x0700,  //<  add-on flag mask
}TQrCodeType;
/// 识别结果 result buf大小根据识别结果配置
typedef struct TQrCodeResultTag
{
    TQrCodeType QrCodeType;     ///< 识别结果类型
    uint16_t len;               ///< 识别结果内容长度
    char *result;               ///< 识别结果内容数据
}TQrCodeResult;

bool DecodeImage(const char *image_path, TQrCodeResult *result)
{
    if (!image_path || !strlen(image_path) || !result)
        return false;
    if (access(image_path, F_OK) != 0)
    {
        printf("ERR: image %s is not exist!!!\n", image_path);
        return false;
    }

    zbar::ImageScanner scanner;
    /// configure the reader
    scanner.set_config(zbar::ZBAR_NONE, zbar::ZBAR_CFG_ENABLE, 1);
    cv::String img_path(image_path);
    Mat image = imread(img_path);
    Mat imageGray;
    cvtColor(image, imageGray, CV_RGB2GRAY);
    if (!imageGray.data)
    {
        printf("read picture error!\n");
        return false;
    }
    int width = imageGray.cols;
    int height = imageGray.rows;
    uchar *raw = (uchar *)imageGray.data;
    zbar::Image imageZbar(width, height, "Y800", raw, width * height);
    // imageZbar.convert(fourcc('Y','8','0','0'));
    /// scan the image for barcodes
    scanner.scan(imageZbar);
    
    zbar::Image::SymbolIterator symbol = imageZbar.symbol_begin();
    if (imageZbar.symbol_begin() == imageZbar.symbol_end())
    {
        printf("decode failed!\n");
        result->len = 0;
        result->QrCodeType = kQrCodeTypeNONE;
    }
    for (; symbol != imageZbar.symbol_end(); ++symbol)
    {
        result->QrCodeType = (TQrCodeType)symbol->get_type();
        result->len = (uint16_t)symbol->get_data().size();
        memcpy(result->result, symbol->get_data().c_str(), symbol->get_data().size());
        printf("type: %s\n", symbol->get_type_name().c_str());
        printf("data[%d]: %s\n", symbol->get_data().size(), symbol->get_data().c_str());
    }
    // clean up
    imageZbar.set_data(NULL, 0);
    return true;
}
  1. 从图像流中获取 我这边使用的RGB的数据,只需要调整openCV相关操作如下即可。
/// vec->data() 为图像数据, gray转换结果使用图片处理相同流程即可
cv::Mat gray;
cv::Mat temp_img(height_, width_, CV_8UC3, vec->data());
cvtColor(temp_img, gray, CV_RGB2GRAY);

以上即可进行二维码识别,摄像头输入的流数据可能存在图像畸变的情况,解决可以参考 https://blog.csdn.net/qq_32864683/article/details/83019619 进行摄像头的棋盘格标定即可。

QrEncode : 二维码生成

	二维码生成就相对比较简单,当时需要结合对应嵌入式设备的GUI进行展示效果。该库生成的为每个像素点1pixel的二维码对应到图像中需要进行放大处理。
	交叉编译不再赘述,同上面到根目录。
mkdir build
cd build
../configure --without-png --host=arm-linux-gnueabihf
make && make install
# 拷贝  libqrencode.so qrencode.h  对lib进行软连接 libqrencode.so.4   libqrencode.so.4.0.2

生成16位(565)BMP和24位BMP图像的示例代码如下参考:

#include "stdio.h"
#include "string.h"
#include "qrencode/qrencode.h"
/// 位图head的结构  
#pragma pack(push, 1)
typedef struct BITMAPFILEHEADER  
{   
    uint16_t bfType;   
    uint32_t bfSize;   
    uint16_t bfReserved1;   
    uint16_t bfReserved2;   
    uint32_t bfOffBits;   
}BITMAPFILEHEADER;   

typedef struct BITMAPINFOHEADER  
{   
    uint32_t biSize;   
    uint32_t biWidth;   
    uint32_t biHeight;   
    uint16_t biPlanes;   
    uint16_t biBitCount;   
    uint32_t biCompression;   
    uint32_t biSizeImage;   
    uint32_t biXPelsPerMeter;   
    uint32_t biYPelsPerMeter;   
    uint32_t biClrUsed;   
    uint32_t biClrImportant;   
}BITMAPINFODEADER;  

typedef struct BITMAPINFOHEADER565
{   
    uint32_t biSize;   
    uint32_t biWidth;   
    uint32_t biHeight;   
    uint16_t biPlanes;   
    uint16_t biBitCount;   
    uint32_t biCompression;   
    uint32_t biSizeImage;   
    uint32_t biXPelsPerMeter;   
    uint32_t biYPelsPerMeter;   
    uint32_t biClrUsed;   
    uint32_t biClrImportant;    
    uint32_t bi4RedMask;   
    uint32_t bi4GreenMask;   
    uint32_t bi4BlueMask;  
}BITMAPINFOHEADER565;  
#pragma pack(pop)
typedef enum TQrCodeBmpTag  
{
    kQrCodeBmp565          =      0,  //<  16位 565 BMP
    kQrCodeBmp888          =      1,  //<  24位 888 BMP
}TQrCodeBmp;
bool EncodeImage(const char *image_path, const char *content, uint32_t color/* 二维码生成颜色 B G R 各一个字节 范围0~0xff 例如 0xff0000为 蓝色 */, TQrCodeBmp type)
{
    if(!content || !image_path || !strlen(content) || color == 0xffffff)
    {
        printf("EncodeImage Invalid Param!\n");
        return false;
    }

    QRcode* p_qrc;
    if ((bool)(p_qrc = QRcode_encodeData(strlen(content), (const unsigned char*)content, 0, QR_ECLEVEL_L)))
    {
        unsigned int pic_width, width_adjusted, data_bytes, x, y, l, n;
        unsigned char*  bmp_24_data = NULL;
        unsigned char*  bmp_565_data = NULL;
        BITMAPFILEHEADER file_head_888;
        BITMAPINFOHEADER info_head_888;
        BITMAPFILEHEADER file_head_565;
        BITMAPINFOHEADER565 info_head_565;
        
        pic_width = p_qrc->width;
        width_adjusted = pic_width * 8 * 3;
        if (width_adjusted % 4)
            width_adjusted = (width_adjusted / 4 + 1) * 4;
        data_bytes = width_adjusted * pic_width * 8;                

        if(kQrCodeBmp888 == type)
        {
            /// Prepare bmp headers 24 bit
            if (!(bmp_24_data = (unsigned char*)malloc(data_bytes)))
            {
                printf("EncodeImage malloc fail\n");
                return false;
            }
            /// wihte back ground
            memset(bmp_24_data, 0xff, data_bytes);
            /// fill head
            file_head_888.bfType = 0x4d42;  ///< "BM"
            file_head_888.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + data_bytes;
            printf("24 bit bmp size =0x%x\n", file_head_888.bfSize);
            file_head_888.bfReserved1 = 0;
            file_head_888.bfReserved2 = 0;
            file_head_888.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
            info_head_888.biSize = sizeof(BITMAPINFOHEADER);
            info_head_888.biWidth = pic_width*8;
            info_head_888.biHeight = -((int)pic_width*8);
            info_head_888.biPlanes = 1;
            info_head_888.biBitCount = 24;
            info_head_888.biCompression = 0; ///< BI_RGB
            info_head_888.biSizeImage = 0;
            info_head_888.biXPelsPerMeter = 0;
            info_head_888.biYPelsPerMeter = 0;
            info_head_888.biClrUsed = 0;
            info_head_888.biClrImportant = 0;
            
            unsigned char* qr_source_data = p_qrc->data;
            for (y = 0; y < pic_width; y++)
            {
                unsigned char* pixel_24_data = bmp_24_data + width_adjusted * y * 8;
                for (x = 0; x < pic_width; x++)
                {
                    if (*qr_source_data & 1)
                    {
                        for (l = 0; l < 8; l++)
                        {
                            for (n = 0; n < 8; n++)
                            {
                                /// fill color
                                *(pixel_24_data + n * 3 + width_adjusted * l) = color & 0xff0000;            
                                *(pixel_24_data + 1 + n * 3 + width_adjusted * l) = color & 0xff00;  
                                *(pixel_24_data + 2 + n * 3 + width_adjusted * l) = color & 0xff;
                            }
                        }
                    }
                    pixel_24_data += 3 * 8;
                    qr_source_data++;
                }
            }
            
            FILE* f;
            if ((bool)(f = fopen(image_path, "wb")))
            {
                fwrite(&file_head_888, sizeof(BITMAPFILEHEADER), 1, f);
                fwrite(&info_head_888, sizeof(BITMAPINFOHEADER), 1, f);
                fwrite(bmp_24_data, sizeof(unsigned char), data_bytes, f);
                printf("qrcode has generated 24 bit bmp in %s\n", image_path);
                fclose(f);
            }
            else
            {
                free(bmp_24_data);
                QRcode_free(p_qrc);
                printf("Unable to open file\n");
                return false;
            }
        }
        else
        {
            /// Prepare bmp headers 16 bit 565
            if (!(bmp_565_data = (unsigned char*)malloc(data_bytes*2/3+100)))
            {
                printf("EncodeImage malloc fail\n");
                return false;
            }
            memset(bmp_565_data, 0xff, data_bytes*2/3+100);
            file_head_565.bfType = 0x4d42;  ///< "BM"
            file_head_565.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER565) + data_bytes*2/3;
            printf("16 bit bmp size =0x%x\n", file_head_565.bfSize);
            file_head_565.bfReserved1 = 0;
            file_head_565.bfReserved2 = 0;
            file_head_565.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER565);
            info_head_565.biSize = 0x28;
            info_head_565.biWidth = pic_width*8;
            info_head_565.biHeight = -((int)pic_width*8);
            info_head_565.biPlanes = 1;
            info_head_565.biBitCount = 16;
            info_head_565.biCompression = 3; ///< BI_BITFIELDS
            info_head_565.biSizeImage = 0;
            info_head_565.biXPelsPerMeter = 0;
            info_head_565.biYPelsPerMeter = 0;
            info_head_565.biClrUsed = 0;
            info_head_565.biClrImportant = 0;
            info_head_565.bi4RedMask = 0xF800;
            info_head_565.bi4GreenMask = 0x7E0;
            info_head_565.bi4BlueMask = 0x1F;
            
            unsigned char* qr_source_data = p_qrc->data;
            for (y = 0; y < pic_width; y++)
            {
                unsigned char* pixel_565_data = bmp_565_data + pic_width * 8 * 2 * y * 8;
                for (x = 0; x < pic_width; x++)
                {
                    if (*qr_source_data & 1)
                    {
                        for (l = 0; l < 8; l++)
                        {
                            for (n = 0; n < 8; n++)
                            {
                                /// fill color
                                *(pixel_565_data + n * 2 + pic_width * 8 * 2 * l) = ((((color & 0xff0000)>>16) * 32 / 256) << 3) + (((color & 0xff) * 64 / 256)>>3);           
                                *(pixel_565_data + 1 + n * 2 + pic_width * 8 * 2 * l) = (((color & 0xff) * 64 / 256)<<5) + (((color & 0xff00)>>8) * 32 / 256);
                            }
                        }
                    }
                    pixel_565_data += 2 * 8;
                    qr_source_data++;
                }
            }

            FILE* f;
            if ((bool)(f = fopen(image_path, "wb")))
            {
                fwrite(&file_head_565, sizeof(BITMAPFILEHEADER), 1, f);
                fwrite(&info_head_565, sizeof(BITMAPINFOHEADER565), 1, f);
                fwrite(bmp_565_data, sizeof(unsigned char), data_bytes*2/3, f);
                printf("qrcode has generated 16 bit bmp in %s\n", image_path);
                fclose(f);
            }
            else
            {
                free(bmp_24_data);
                free(bmp_565_data);
                QRcode_free(p_qrc);
                printf("Unable to open file\n");
                return false;
            }
        }

        /// Free data
        free(bmp_24_data);
        free(bmp_565_data);
        QRcode_free(p_qrc);
    }
    else
    {
        printf("NULL returned\n");
        return false;
    }
    printf("EncodeImage Finish!\n");
    return true;
}

也可以直接调用GUI进行绘制图像数据效果如下(内容为百度的网址),一般加个白色背景即可。
二维码

识别框显示

zbar识别结果是带识别位置信息的,只要读取一下点的序列即可

for(int cnt = 0; cnt < symbol->get_location_size(); cnt++)
{
	printf("[%d](%d,%d) ", cnt, symbol->get_location_x(cnt), symbol->get_location_y(cnt));
}

二维码的点的顺序是逆时针显示的,映射到140*140的画布上,如果有需要按照比例转换到原始图像分辨率,调用gui画框即可。

  • 0
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
OpenCV+zbar开源库实现摄像头识别二维码,测试验证识别率非常高,已实现简单的应用。 打包源在VS2013下可以完全编译成功,附加包含OpenCV库及zbar-0.10-setup.exe,zbar-0.10.tar.bz2 下载Demo后需要安装 zbar-0.10-setup.exe 以下代可以可以完成整个流程的开发,也可以贡献积分下载资源包。 1、 环境准备 (1) OpenCV库2.49 (2) ZBar开源库 (3) VS2013 2、 VS2013环境配置 (1) 配置附加包含目录 C/C++ -- 附加包含目录 include\opencv\include\ include\opencv\include\opencv include\opencv\include\opencv2 include (2) 配置链接器 链接器 -- 附加库目录 lib32\opencv\lib lib32 (3) 配置链接器 链接器--输入--附加依赖项 opencv_core249d.lib opencv_highgui249d.lib opencv_imgproc249d.lib libzbar-0.lib 3、 代开发 (1)包含头文件 include include include include include include using namespace std; using namespace zbar; using namespace cv; (2)实现函数 void MatToCImage(cv::Mat &mat, CImage &cImage) { //create new CImage int width = mat.cols; int height = mat.rows; int channels = mat.channels(); cImage.Destroy(); //clear cImage.Create(width, height, 8 * channels); //默认图像像素单通道占用1个字节 //copy values uchar* ps; uchar* pimg = (uchar*)cImage.GetBits(); //A pointer to the bitmap buffer int step = cImage.GetPitch(); for (int i = 0; i (i)); for (int j = 0; j GetDlgItem(IDC_STATIC_IMG)->GetClientRect(▭); cv::VideoCapture capture(0);//从摄像头读入图像 while (!m_bCloseCamera) { cv::Mat frame; capture >> frame; cv::Mat newframe; cv::Size ResImgSiz = cv::Size(rect.Width(), rect.Height()); cv::resize(frame, newframe, ResImgSiz, CV_INTER_CUBIC); MatToCImage(newframe, imgDst); imgDst.Draw(pThis->GetDlgItem(IDC_STATIC_IMG)->GetDC()->GetSafeHdc(), rect); ImageScanner scanner; scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1); Mat imageGray; cvtColor(frame, imageGray, CV_RGB2GRAY); int width = imageGray.cols; int height = imageGray.rows; uchar *raw = (uchar *)imageGray.data; Image imageZbar(width, height, "Y800", raw, width * height); scanner.scan(imageZbar); //扫描条 Image::SymbolIterator symbol = imageZbar.symbol_begin(); if (imageZbar.symbol_begin() == imageZbar.symbol_end()) { } else { iIndex++; if (iIndex > 999999) { iIndex = 0; } for (; symbol != imageZbar.symbol_end(); ++symbol) { char szInfo[1024]; memset(szInfo, 0, sizeof(szInfo)); sprintf(szInfo, "[d]类型:%s\r\n条:%s\r\n", iIndex , symbol->get_type_name().c_str(), symbol->get_data().c_str()); pThis->GetDlgItem(IDC_EDIT1)->SetWindowText(szInfo); } } imageZbar.set_data(NULL, 0); } imgDst.Destroy(); capture.release(); return 0; }
### 回答1: OpenCV和ZBar是两个常用的开源库,可以用于实现摄像头识别二维码的功能。 首先,OpenCV是一款用于计算机视觉的开源库,它提供了丰富的图像处理和计算机视觉算法,可以用来处理图像和视频。OpenCV支持各种程序设计语言,包括C++、Python等,因此非常方便开发者进行二维码的图像处理和识别。 而ZBar是一款专门用于二维码和条形扫描的开源库。它提供了各种功能强大的接口,可以对摄像头捕捉到的图像进行二维码识别ZBar支持多种编程语言,如C、C++、Python等,因此可以与OpenCV结合使用。 要实现摄像头识别二维码的功能,可以按照以下步骤进行: 1. 首先,使用OpenCV打开摄像头,获取实时图像流。 2. 将摄像头捕捉到的图像传递给ZBar库进行二维码识别ZBar库提供了相关的函数和接口,可以将摄像头捕获的图像进行处理,并识别其中的二维码。 3. 当ZBar识别二维码时,可以通过回调函数或其他方式将识别到的二维码信息传递给应用程序,可以对识别到的二维码进行相关的操作,如解析二维码内容,打开对应的链接或进行其他业务处理。 总的来说,通过将OpenCV和ZBar结合使用,我们可以实现摄像头实时识别二维码的功能。这样,我们可以利用摄像头实时获取图像流,并通过ZBar库对图像进行识别,从而实现对二维码的实时扫描和处理。 ### 回答2: OpenCV是一个开源的计算机视觉库,提供了丰富的图像处理和计算机视觉算法。而ZBar是一个开源的条二维码识别库,能够通过摄像头进行实时的二维码识别。 使用OpenCV和ZBar库实现摄像头识别二维码的步骤如下: 1. 首先,我们需要使用OpenCV库中的VideoCapture类来打开摄像头,并获取实时的视频帧。 2. 在每一帧中,我们将使用ZBar库提供的二维码识别功能。通过将每一帧转换为灰度图像,可以提高识别效果。 3. 在灰度图像上,我们可以使用ZBar库的Scanner类进行二维码的扫描。Scanner类可以检测到图像中的所有二维码,并返回相关信息。 4. 当扫描到二维码时,我们可以从Scanner类中获取二维码的内容。 5. 最后,我们可以使用OpenCV库提供的绘图功能,在视频帧上标记出识别到的二维码的位置和内容。 通过以上步骤,我们可以实现摄像头实时识别二维码的功能。这种方法可以应用于一些需要实时扫描二维码的场景,例如门禁系统、物流追踪等。同时,OpenCV和ZBar库的开源特性也使得开发者能够根据自己的需求进行定制和修改,提高二维码识别的准确率和效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值