Linux C 使用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博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/954318.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

金融项目实战 06|Python实现接口自动化——日志、实名认证和开户接口

目录 一、日志封装及应用&#xff08;理解&#xff09; 二、认证开户接口脚本编写 1、代码编写 1️⃣api目录 2️⃣script目录 2、BeautifulSoup库 1️⃣简介及例子 2️⃣提取html数据工具封装 3、认证开户参数化 一、日志封装及应用&#xff08;理解&#xff09; &…

基于springboot+vue+微信小程序的宠物领养系统

基于springbootvue微信小程序的宠物领养系统 一、介绍 本项目利用SpringBoot、Vue和微信小程序技术&#xff0c;构建了一个宠物领养系统。 本系统的设计分为两个层面&#xff0c;分别为管理层面与用户层面&#xff0c;也就是管理者与用户&#xff0c;管理权限与用户权限是不…

【微服务】面试题 5、分布式系统理论:CAP 与 BASE 详解

分布式系统理论&#xff1a;CAP 与 BASE 详解 一、CAP 定理 背景与定义&#xff1a;1998 年由加州大学科学家埃里克布鲁尔提出&#xff0c;分布式系统存在一致性&#xff08;Consistency&#xff09;、可用性&#xff08;Availability&#xff09;、分区容错性&#xff08;Part…

【Vue】Vue组件--上

目录 一、组件基础 二、组件的嵌套关系 1. 基础架构 2. 嵌套 三、组件注册方式 1. 局部注册&#xff1a; 2. 全局注册&#xff1a; 四、组件传递数据 1. 基础架构 2. 传递多值 3. 动态传递数据 五、组件传递多种数据类型 1. Number 2. Array 3. Object 六、组…

鸿蒙UI开发——键盘弹出避让模式设置

1、概 述 我们在鸿蒙开发时&#xff0c;不免会遇到用户输入场景&#xff0c;当用户准备输入时&#xff0c;会涉及到输入法的弹出&#xff0c;我们的界面针对输入法的弹出有两种避让模式&#xff1a;上抬模式、压缩模式。 下面针对输入法的两种避让模式的设置做简单介绍。 2、…

python Streamlit和AKShare 实现的股票数据查询系统

1. 系统概述 这是一个基于Streamlit和AKShare的股票数据查询系统&#xff0c;提供了便捷的股票数据查询和可视化功能。系统支持按板块筛选股票、多股票代码查询、数据导出等功能。 1.1 主要功能 股票代码直接输入查询按板块筛选和选择股票历史数据和实时行情查询财务报表数据…

蓝桥杯备赛:顺序表和单链表相关算法题详解(上)

目录 一.询问学号&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; 2.解析与代码实现&#xff1a; &#xff08;1&#xff09;解析&#xff1a; &#xff08;2&#xff09;代码实现&#xff1a; 二.寄包柜&#xff08;顺序表&#xff09; 1.题目来源&#xff1a; …

数据结构-ArrayLIst-一起探索顺序表的底层实现

各位看官早安午安晚安呀 如果您觉得这篇文章对您有帮助的话 欢迎您一键三连&#xff0c;小编尽全力做到更好 欢迎您分享给更多人哦 大家好&#xff0c;我们今天来学习java数据结构的第一章ArrayList&#xff08;顺序表&#xff09; 1.ArrayList的概念 那小伙伴就要问了线性表到…

RabbitMQ(四)

SpringBoot整合RabbitMQ SpringBoot整合1、生产者工程①创建module②配置POM③YAML④主启动类⑤测试程序 2、消费者工程①创建module②配置POM③YAML文件内配置&#xff1a; ④主启动类⑤监听器 3、RabbitListener注解属性对比①bindings属性②queues属性 SpringBoot整合 1、生…

初始Java4

目录 一.继承 1.定义&#xff1a; 2.继承的语法&#xff1a; 3.子类访问父类 4.子类构造方法 5.super与this 6.继承方法 7.final关键字 &#xff08;1&#xff09;.变量不变 &#xff08;2&#xff09;.方法不变 &#xff08;3&#xff09;.类不可继承 8.继承与组合…

极限竞速 地平线5“d3dx12_43.dll”文件丢失或错误导致游戏运行异常如何解决?windows系统DLL文件修复方法

d3dx12_43.dll是存放在windows系统中的一个重要dll文件&#xff0c;缺少它可能会造成部分软件不能正常运行。当你的电脑弹出提示“无法找到d3dx12_43.dll”或“计算机缺少d3dx12_43.dll”等错误问题&#xff0c;请不用担心&#xff0c;我们将深入解析DLL文件错误的成因&#xf…

Leecode刷题C语言之超过阈值的最小操作数②

执行结果:通过 执行用时和内存消耗如下&#xff1a; // 最小堆的节点结构体 typedef struct {long long* heap;int size;int capacity; } MinHeap;// 初始化最小堆 MinHeap* createMinHeap(int capacity) {MinHeap* minHeap (MinHeap*)malloc(sizeof(MinHeap));minHeap->s…

[Qt]常用控件介绍-按钮类控件-QPushButton、QRedioButton、QCheckBox、QToolButton控件

目录 1.QPushButton按钮 介绍 属性 Demo&#xff1a;键盘方向键控制人物移动 2.Redio Button按钮 属性 clicked、pressed、released、toggled区别 单选按钮的分组 Demo&#xff1a;点餐小程序 3.CheckBox按钮 属性 Demo&#xff1a;获取今天的形成计划 4.ToolBu…

寒假第一次牛客周赛 Round 76回顾

AC数&#xff1a;2&#xff08;A、C&#xff09; B 思路&#xff1a; 等价于求&#xff1a; 数量最多的字符 #include<stdio.h> int main() {int n,num;int a[26]{0};//用于存储字母 a 到 z 的出现次数。scanf("%d",&n);char s[n];scanf("%s",s)…

StyleGaussian: Instant 3D Style Transferwith Gaussian Splatting 论文解读

目录 一、概述 二、相关工作 1、辐射场 2、3D编辑 3、风格迁移 三、StyleGaussian 1、特征嵌入 2、风格迁移 3、解码 四、实验 1、不同backbone下的量化和定性指标 2、解码器设计上的测试 3、内容损失平衡 4、风格平滑插值 一、概述 提出了StyleGaussian&#x…

基于django实现类似ebay的电子商务系统全英文

完整源码项目包获取→点击文章末尾名片&#xff01;

win32汇编环境,窗口程序中组合框的应用举例

;运行效果 ;win32汇编环境,窗口程序中组合框的应用举例 ;比如在窗口程序中生成组合框&#xff0c;增加子项&#xff0c;删除某项&#xff0c;取得指定项内容等 ;直接抄进RadAsm可编译运行。重点部分加备注。 ;以下是ASM文件 ;>>>>>>>>>>>>…

Docker 镜像制作原理 做一个自己的docker镜像

一.手动制作镜像 启动容器进入容器定制基于容器生成镜像 1.启动容器 启动容器之前我们首先要有一个镜像&#xff0c;这个镜像可以是从docker拉取&#xff0c;例如&#xff1a;现在pull一个ubuntu镜像到本机。 docker pull ubuntu:22.04 我们接下来可以基于这个容器进行容器…

微信小程序获取openid

2025年1月15日&#xff1a; 1、现在云服务器上安装nodejs&#xff0c;然后写个get接口&#xff1a; const express require(express); const app express();app.get(/getOpenid,(req,res)>{res.send("success"); })app.listen(3000,()>{console.log(server…

ASP.NET Core - 配置系统之配置添加

ASP.NET Core - 配置系统之配置添加 2. 配置添加 2. 配置添加 配置系统可以读取到配置文件中的信息&#xff0c;那必然有某个地方可以将配置文件添加到配置系统中。之前的文章中讲到 ASP.NET Core 入口文件中&#xff0c;builder(WebApplicationBuilder 对象) 中有一个 Config…