首页 > 系统相关 >Linux C 使用ZBar库解析二维码和条形码

Linux C 使用ZBar库解析二维码和条形码

时间:2025-01-15 23:58:45浏览次数:3  
标签:条形码 scanner int image Linux height width zbar ZBar

1. 编译zbar库

下载 zbar 库源码,这里需要注意下,如果识别的二维码中有中文的话,会出现乱码,一般二维码里中文为UTF-8编码,zbar会默认给你把UTF-8转换为ISO8859-1。有两种解决办法,一是自己再转换一下编码格式;二是修改下zbar源码,很简单,只需要修改源码目录下的 zbar/qrcode/qrdectxt.c 文件中的两行内容,推荐用这个方法,修改内容如下图:

进入源码根目录创建一个build目录,然后进入build目录执行命令,其中"--prefix"表示设置库的安装目录,按你实际的安装目录修改下。

../configure --prefix=/home/tl/work/zbar-0.10/output --disable-video --without-python --without-gtk --without-qt --without-imagemagick CFLAGS=""

再 make,make install 。

如果只是使用zbar库的C API,可以只复制安装目录下的 include/zbar.h 头文件,其他头文件都不需要。

2. zbar库的使用

实现了两个示例,示例一实现了扫描一张图片中的二维码或条形码,示例二实现了摄像头扫码功能。部分代码参考了源码中的examples/scan_image.c文件。

示例一源码如下,其中stb_image.h在我另一篇博客中有说明,博客链接在底下有贴。

#include "zbar.h"

// 定义 STB_IMAGE_IMPLEMENTATION 以实现 stb_image.h 的函数
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        printf("用法: %s <图片文件>\n", argv[0]);
        return -1;
    }

    const char *filename = argv[1];

    // 加载图像,并转换为灰度图(单通道)
    int width, height, channels;
    unsigned char *img_gray = stbi_load(filename, &width, &height, &channels, 1);
    if (!img_gray)
    {
        printf("stbi_load failed: %s\n", stbi_failure_reason());
        return -1;
    }

    zbar_image_scanner_t *scanner = NULL;
    scanner = zbar_image_scanner_create();
    // 配置扫描器(测试了下,可以不用设置,zbar默认就是这样),
    // ZBAR_NONE 表示所有支持的条码类型,
    // ZBAR_CFG_ENABLE, 1 表示开启某种功能,
    // ZBAR_NONE, ZBAR_CFG_ENABLE, 1 表示开启识别所有支持的条码类型。
    zbar_image_scanner_set_config(scanner, ZBAR_NONE, ZBAR_CFG_ENABLE, 1);

    // 只单独开启识别某种条码功能,比如只需要识别二维码,
    // 默认情况下zbar开启了识别所有支持的条码类型,需要先设置关闭。
    // zbar_image_scanner_set_config(scanner, ZBAR_NONE, ZBAR_CFG_ENABLE, 0);
    // 再开启识别二维码功能
    // zbar_image_scanner_set_config(scanner, ZBAR_QRCODE, ZBAR_CFG_ENABLE, 1);

    zbar_image_t *image = zbar_image_create();
    // 设置图像格式为灰度图(Y800)
    zbar_image_set_format(image, *(int *)"Y800");
    zbar_image_set_size(image, width, height);
    zbar_image_set_data(image, img_gray, width * height, NULL);

    // 扫描图像中的条形码
    int n = zbar_scan_image(scanner, image);
    if (n <= 0)
    {
        printf(n < 0 ? "Scan error.\n" : "No barcode found.\n");
        return -1;
    }
    printf("n: %d\n", n);

    // 提取结果
    const zbar_symbol_t *symbol = zbar_image_first_symbol(image);
    for (; symbol; symbol = zbar_symbol_next(symbol))
    {
        zbar_symbol_type_t typ = zbar_symbol_get_type(symbol);
        const char *data = zbar_symbol_get_data(symbol);
        printf("decoded %s symbol \"%s\"\n",
               zbar_get_symbol_name(typ), data);
    }

    zbar_image_destroy(image);
    zbar_image_scanner_destroy(scanner);
    stbi_image_free(img_gray);

    return 0;
}

 示例二源码如下:

#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include "zbar.h"

int yuyv_to_gray(const uint8_t *img_yuyv, int width, int height, uint8_t *img_gray)
{
    // 初始化 SwsContext
    struct SwsContext *sws_ctx = sws_getContext(
        width, height, AV_PIX_FMT_YUYV422, // 输入格式
        width, height, AV_PIX_FMT_GRAY8,   // 输出格式
        SWS_BILINEAR, NULL, NULL, NULL);
    if (!sws_ctx)
    {
        printf("sws_getContext failed\n");
        return -1;
    }

    // 准备输入和输出数据
    const uint8_t *src_slices[1] = {img_yuyv};
    int src_stride[1] = {width * 2}; // YUYV 每行占 2 * width 字节

    uint8_t *dst_slices[1] = {img_gray};
    int dst_stride[1] = {width}; // 灰度图每行占 width 字节

    // 执行转换
    int converted_lines = sws_scale(sws_ctx, src_slices, src_stride, 0, height,
                                    dst_slices, dst_stride);
    if (converted_lines != height)
    {
        printf("sws_scale failed\n");
        sws_freeContext(sws_ctx);
        return -1;
    }

    // 释放 SwsContext
    sws_freeContext(sws_ctx);

    return 0;
}

void scan_image(zbar_image_scanner_t *scanner, zbar_image_t *image)
{
    // 扫描图像中的条形码
    int n = zbar_scan_image(scanner, image);
    if (n <= 0)
    {
        return;
    }
    printf("n: %d\n", n);

    // 提取结果
    const zbar_symbol_t *symbol = zbar_image_first_symbol(image);
    for (; symbol; symbol = zbar_symbol_next(symbol))
    {
        zbar_symbol_type_t typ = zbar_symbol_get_type(symbol);
        const char *data = zbar_symbol_get_data(symbol);
        printf("decoded %s symbol \"%s\"\n",
               zbar_get_symbol_name(typ), data);
    }
}

int main(void)
{
    const char *input_format_name = "video4linux2"; // 输入格式名称,Linux下为video4linux2或v4l2
    const char *device_name = "/dev/video0";        // 摄像头设备名称
    const char *camera_resolution = "640x480";      // 摄像头分辨率
    int ret = -1;
    int video_streamid = -1;
    AVDictionary *options = NULL;
    AVInputFormat *fmt = NULL;
    AVFormatContext *in_context = NULL;

    // 打印ffmpeg版本信息
    printf("ffmpeg version: %s\n", av_version_info());

    // 注册所有设备
    avdevice_register_all();

    // 查找输入格式
    fmt = av_find_input_format(input_format_name);
    if (!fmt)
    {
        printf("av_find_input_format error");
        return -1;
    }

    // 设置分辨率
    av_dict_set(&options, "video_size", camera_resolution, 0);

    // 打开输入流并初始化格式上下文
    ret = avformat_open_input(&in_context, device_name, fmt, &options);
    if (ret != 0)
    {
        // 错误的时候释放options,成功的话 avformat_open_input 内部会释放
        av_dict_free(&options);
        printf("avformat_open_input error");
        return -1;
    }

    // 查找流信息
    if (avformat_find_stream_info(in_context, 0) < 0)
    {
        printf("avformat_find_stream_info failed\n");
        goto end;
    }

    // 查找视频流索引
    video_streamid = av_find_best_stream(in_context, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (video_streamid < 0)
    {
        printf("cannot find video stream");
        goto end;
    }
    AVStream *video_stream = in_context->streams[video_streamid];
    printf("input stream, width: %d, height: %d, format: %s\n",
           video_stream->codecpar->width, video_stream->codecpar->height,
           av_get_pix_fmt_name((enum AVPixelFormat)video_stream->codecpar->format));

    if (video_stream->codecpar->format != AV_PIX_FMT_YUYV422)
    {
        printf("video format error\n");
        goto end;
    }

    // 分配内存
    AVFrame *input_frame = av_frame_alloc();
    if (!input_frame)
    {
        printf("av_frame_alloc error\n");
        goto end;
    }

    zbar_image_scanner_t *scanner = NULL;
    scanner = zbar_image_scanner_create();
    zbar_image_scanner_set_config(scanner, ZBAR_NONE, ZBAR_CFG_ENABLE, 1);

    zbar_image_t *image = NULL;
    image = zbar_image_create();
    zbar_image_set_format(image, *(int *)"Y800");
    uint32_t width = video_stream->codecpar->width;
    uint32_t height = video_stream->codecpar->height;
    zbar_image_set_size(image, width, height);
    uint8_t *img_gray = (uint8_t *)malloc(width * height);
    zbar_image_set_data(image, img_gray, width * height, NULL);

    // 读取帧并进行转换
    AVPacket pkt;

    while (av_read_frame(in_context, &pkt) >= 0)
    {
        if (pkt.stream_index == video_streamid)
        {
            yuyv_to_gray(pkt.data, width, height, img_gray);
            scan_image(scanner, image);
        }
        av_packet_unref(&pkt);
    }

end:
    if (in_context)
        avformat_close_input(&in_context);
    if (image)
        zbar_image_destroy(image);
    if (scanner)
        zbar_image_scanner_destroy(scanner);

    free(img_gray);

    return 0;
}

相关博客链接:Linux C 使用Quirc库解析二维码-CSDN博客

标签:条形码,scanner,int,image,Linux,height,width,zbar,ZBar
From: https://blog.csdn.net/qq_42161913/article/details/145166221

相关文章

  • 【Linux运维】如何在Linux中列出USB设备
    Linux操作系统提供了许多命令来列出系统中连接的USB设备。这些命令非常有用,无论是查看已连接设备的信息还是进行系统调试。在本文中,我们将介绍一些常用的命令以及它们的使用方法,帮助你了解如何在Linux中列出USB设备。1、lsusb命令lsusb命令是列出USB设备信息的常用命令。它会显......
  • Linux下MySQL数据库的导入与导出以及查看端口
    在Linux系统下,MySQL是一种广泛使用的关系型数据库管理系统。本文将详细介绍如何在Linux下进行MySQL数据库的导入和导出操作,以及如何查看MySQL的运行端口。一、MySQL数据库的导出导出MySQL数据库可以通过 mysqldump工具来实现,它是MySQL自带的用于备份数据库的命令行工具。1.1......
  • Linux核心指令
    1.创建目录mkdir(makedirectory)选项说明-p创建多层目录(递归创建目录)-v显示创建目录的过程,工作几乎不用案例01:创建/kylin/目录,查看目录内容,进入目录并闲适当前位置(绝对路径的位置)#1.创建目录mkdir/kylin/#2.查看目录内容ls-l/kylin/ll/kylin/......
  • Linux
    基础概念与系统架构Linux的起源与发展Linux起源于LinusTorvalds在1991年发布的Linux内核。它是一种类Unix操作系统,遵循开源软件的原则,众多开发者和社区围绕内核开发了各种发行版。其发展得益于互联网的协作模式,如Debian、RedHat等发行版的不断演进,推动了Lin......
  • Linux的/proc目录
    1.Linuxproc目录介绍Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统,它只存在内存当中,而不占用外存空间。它以文件系统的方式为访问系统内核数据的操作提供接口),存储的是当前内核运行状态的一系列特殊......
  • 【c++】【Linux】堆和栈的区别
    【c++】【Linux】堆和栈的区别区别堆栈管理方式由程序员手动分配手动释放由系统自动管理生长方式从低地址向高地址增长从高地址向低地址增长空间大小32位linux下可占2.9G左右32位Linux下占10M左右windows下占1M左右存储内容动态分配的内存,常用于存储链表、对象等动态数据......
  • Linux系统内存使用优化技巧
    目录交换空间(Swap)的优化禁用Swap降低swappiness值减少动态内存分配使用大页(Hugepage)优化数据访问,使用缓存和缓冲区使用堆栈缓存利用外部缓存组件使用cgroups限制进程内存使用创建cgroup限制内存使用调整OOMScore调整进程的OOM分数终止未使用的服务和......
  • Linux 文件乱码问题及基于 inode 的排查与解决
    Linux文件乱码问题及基于inode的排查与解决在Linux系统中,文件乱码通常是由于显示或读取时使用的字符编码与文件实际编码不一致导致的。本文将详细介绍Linux文件乱码的常见原因、解决方法,包括压缩包解压后的乱码问题,并结合inode的概念,讲解如何利用inode进行问题排查和......
  • 【Linux】在虚拟机中安装
      ......
  • 【Linux】信号的艺术:深入理解 Linux 进程信号
    ......