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)
{// 初始化 SwsContextstruct 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;}// 释放 SwsContextsws_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或v4l2const 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博客

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【北京迅为】iTOP-4412全能版使用手册-第七十九章 Qt网络编程

iTOP-4412全能版采用四核Cortex-A9&#xff0c;主频为1.4GHz-1.6GHz&#xff0c;配备S5M8767 电源管理&#xff0c;集成USB HUB,选用高品质板对板连接器稳定可靠&#xff0c;大厂生产&#xff0c;做工精良。接口一应俱全&#xff0c;开发更简单,搭载全网通4G、支持WIFI、蓝牙、…

用python实战excel和word自动化

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 python实现excel和word自动化--批量处理 前言--需求快要期末了需要&#xff0c;提交一个年级的学生成绩数据&#xff0c;也就是几百份。当前我们收集了一份excel表格&#xf…

uniapp 预加载分包,减少loading

在 uniapp 中&#xff0c;可以通过配置 pages.json 文件中的 preloadRule 属性来实现页面预加载功能。以下是具体操作步骤&#xff1a; 1. 在 pages.json 中配置 preloadRule preloadRule 用于指定哪些页面需要预加载&#xff0c;以及预加载时机。下面是一个示例配置&#xf…

C++ 智能指针、内存泄露、野指针、悬空指针

智能指针、内存泄露、野指针、悬空指针 内存泄露野指针 、悬空指针野指针悬空指针 智能指针std::unique_ptr创建 指向内存空间其它方法 std::shared_ptr 内存泄露 概念&#xff1a; 内存无法释放 举例&#xff1a; int* p new int[100]; int a{}; p &a;new了一个长度为…

介绍PyTorch张量

介绍PyTorch张量 介绍PyTorch张量 PyTorch张量是我们在PyTorch中编程神经网络时将使用的数据结构。 在编程神经网络时&#xff0c;数据预处理通常是整个过程的第一步&#xff0c;数据预处理的一个目标是将原始输入数据转换为张量形式。 torch.Tensor​类的实例 PyTorch张量…

【R安装】R语言的详细安装及环境配置(2024年11月)

目录 R及Rstudio下载R下载Rstudio下载 R及Rstudio安装R安装Rtools 安装Rstudio安装 运行 RStudio通过RStudio配置使用特定的R版本 参考 R及Rstudio下载 R下载 R官网-The R Project for Statistical Computing 点击【download R】&#xff0c;进入下载界面&#xff1a; 选择…

Docker官网安装

1.官网 官方文档 https://www.docker.com/ Docker Hub官网 镜像 https://hub.docker.com/ 2.Docker 的三要素 1、镜像 2、容器 3、仓库 小总结 3.Docker 平台架构图 &#xff08;架构版本&#xff09; 4.安装Docker CentOS | Docker Docs 1.确定你是CentOS7及以上版本 …

寒假第一次牛客周赛 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)…

GARCH指导的神经网络在金融市场波动性预测中的应用

“GARCH-Informed Neural Networks for Volatility Prediction in Financial Markets” 论文地址&#xff1a;https://arxiv.org/pdf/2410.00288v1 摘要 波动性作为衡量风险的关键指标&#xff0c;广泛应用于金融投资的定价中。GARCH模型及其变体是用于股票波动性预测的传统工…

STM32-笔记43-低功耗

一、什么是低功耗&#xff1f; 低功耗‌是指通过优化设计和采用特定的技术手段&#xff0c;降低电子设备在运行过程中消耗的能量&#xff0c;从而延长电池寿命、提高性能和减少发热。低功耗设计主要从芯片设计和系统设计两个方面进行&#xff0c;旨在减少所有器件的功率损耗&am…

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

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

【Ubuntu 24.04】虚拟机常见问题解决

1.24开启3D加速黑屏 参考文章&#xff1a;Ubuntu24开机黑屏&#xff0c;VMware卡死&#xff0c;虚拟机繁忙解决方案 没有3D加速就没有动画&#xff0c;所以我们需要开启3D加速&#xff0c;但是直接开启3D加速会黑屏 由于Ubuntu24内部的图形加速驱动异常&#xff0c;因此需要更新…

辅助云运维

为客户提供运维支持&#xff0c;保障业务连续性。 文章目录 一、服务范围二、服务内容三、服务流程四、 服务交付件五、责任分工六、 完成标志 一、服务范围 覆盖范围 云产品使用咨询、问题处理、配置指导等&#xff1b; 云产品相关操作的技术指导&#xff1b; 云相关资源日常…

灵活妙想学数学

灵活妙想学数学 题1&#xff1a;海星有几只&#xff1f; 一共有12只海洋生物&#xff0c;分别是5只脚的海星&#xff0c;8只脚的章鱼和10只脚的鱿鱼&#xff0c;这些海洋动物的脚一共有87只&#xff0c;每种生物至少有1只&#xff0c;问海星有几只&#xff1f; 解&#xff1a…

Java中的并发工具类:让多线程编程更轻松

Java中的并发工具类&#xff1a;让多线程编程更轻松 1. 引言&#xff1a;多线程编程的“痛” 多线程编程是Java开发中的一大难点&#xff0c;尤其是在高并发场景下&#xff0c;稍有不慎就会遇到线程安全问题、死锁、性能瓶颈等问题。比如&#xff1a; public class Counter …

Vue3使用vue-count-to数字滚动模块报错解决方案

小伙伴们是不是遇到了vue3项目使用vue-count-to出现报错的问题 报错如下&#xff1a; TypeError: Cannot read properties of undefined (reading _c) 这个错误信息具体是说没读取到_c的属性 具体不清楚是什么原因&#xff0c;排查还得去看源码&#xff0c;所以我们来解决&a…

idea上git log面板的使用

文章目录 各种颜色含义具体的文件的颜色标签颜色&#x1f3f7;️ 节点和路线 各种颜色含义 具体的文件的颜色 红色&#xff1a;表示还没有 git add 提交到暂存区绿色&#xff1a;表示已经 git add 过&#xff0c;但是从来没有 commit 过蓝色&#xff1a;表示文件有过改动 标…

一分钟学习数据安全——数据安全的核心概念CIA以及安当解决方案

数据安全三要素是指保密性&#xff08;Confidentiality&#xff09;、完整性&#xff08;Integrity&#xff09;和可用性&#xff08;Availability&#xff09;&#xff0c;它们是信息安全领域的核心概念&#xff0c;旨在确保信息的安全和可信度。这边文章用一分钟的时间&#…

Electron 开发者的 Tauri 2.0 实战指南:文件系统操作

作为 Electron 开发者&#xff0c;我们习惯了使用 Node.js 的 fs 模块来处理文件操作。在 Tauri 2.0 中&#xff0c;文件系统操作被重新设计&#xff0c;采用了 Rust 的安全特性和权限系统。本文将帮助你理解和重构这部分功能。 文件操作对比 Electron 的文件操作 在 Electr…

1️⃣Java中的集合体系学习汇总(List/Map/Set 详解)

目录 01. Java中的集合体系 02. 单列集合体系​ 1. Collection系列集合的遍历方式 &#xff08;1&#xff09;迭代器遍历&#xff08;2&#xff09;增强for遍历​编辑&#xff08;3&#xff09;Lambda表达式遍历 03.List集合详解 04.Set集合详解 05.总结 Collection系列…