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博客