|
目前能在网上找到的二维码资料,都是编码居多,也就是从一串字符串生成二维码(常用算法如 QRCode),而解码识别方面的参考教程比较少。二维码解码库有 Quirc、Zbar 等,更多的开发者是用在安卓、OpenCV 和 Python 等高级环境上,对于 MCU 级别的使用,并且基本是经过封装后的实现,灵活性较差。于是便想着自己从头使用 Zbar 库的接口,实现图片中的二维码检测及识别。 测试环境- 开发板:STM32F429I Disco
- CPU:Arm® 32-bit Cortex®-M4 180 MHz
- 内存:2Mbytes Flash, 256KMbytes RAM, 64-Mbit 外部 SDRAM
- 软件:RT-Thread 操作系统
. L+ z4 @- D. c2 `. L9 d 实现过程 这里说明一下,Zbar 库要求输入的是灰度图像数据(也就是黑白照),每个像素用一个字节表示灰度,从 0x00 全白到 0xff 全黑,有了图像数据,再传入图像的宽高便能解码了。 通过 Zbar 库识别二维码,大致可以分为下面 3 个过程: - 创建配置 image_scanner、zbar_image 对象,并设置图像信息
- 扫描图像并识别
- 提取解码结果+ x: x8 K1 |! C, U' j6 E! @+ ~
我写了一个函数,能够直接读取 bmp 8 位灰度图片(因为 bmp 文件包含了图像数据便于处理),并打印输出其中的二维码识别结果: _. h2 D' n0 ~2 f$ r, E# W7 M
- #include <rtthread.h>
" t% x" }! C3 _; g% P - #include <rtdevice.h>- A: d1 o5 C, m1 v/ k7 `. A
" j+ ]: w- Y# G0 t- #include "zbar.h"
& G1 P0 q9 y& j- ]
1 C' U3 e0 e; j2 K6 M8 ^! O& P. T- #define BITMAP_IMG_OFFSET 0x0A* }- ]! U. E# w
- #define BITMAP_IMG_WIDTH_OFFSET 0x126 l, p5 |3 X; o3 t; G- C
- #define BITMAP_IMG_HEIGHT_OFFSET 0x16
8 ?0 j; k' m# t) N - #define BITMAP_IMG_FILE_SIZE_OFFSET 0x02
. [( P2 _, j- u$ }8 \/ b& E
& B8 s3 h! p( Q( b/ X- zbar_image_scanner_t *rt_scanner = NULL;5 r* t3 B( X: l+ }
- 7 M) V9 Y& \; \$ w, {. ]- f
- void qr_bitmap_decoder(const rt_uint8_t *img_buffer)
1 h& t- b7 c9 U' u - {0 i/ A8 O2 ^* S6 o$ M9 ^
- /* create a reader */
8 T, |7 b4 t4 ?% S, t9 B3 j, T - rt_scanner = zbar_image_scanner_create();
* O" [3 t( X6 s+ [/ G
$ x, ]& ^: R& B* b/ ?4 d- g6 S- /* configure the reader */9 h0 H- }2 X( [' y9 H- ^( r2 a# p8 z) q
- zbar_image_scanner_set_config(rt_scanner, 0, ZBAR_CFG_ENABLE, 1);3 H3 w' ~, \1 k5 k9 f0 U
- ( l' |' b1 Q2 p, G- l3 M: A1 V
- /* obtain image data */7 O' |: ^. x$ r& F- z
- void *raw = (void *)img_buffer;
o( D$ ^' j: o/ b. W7 _% U# G
" P1 H' }+ v& x- int img_width = *(int *)(&img_buffer[BITMAP_IMG_WIDTH_OFFSET]);- L# F! I' `2 M+ v
- int img_height = *(int *)(&img_buffer[BITMAP_IMG_HEIGHT_OFFSET]);* ]9 _7 i& W/ |
- int img_offset = *(int *)(&img_buffer[BITMAP_IMG_OFFSET]);4 s) V* a( L. u- `) W; X
- int file_size = *(int *)(&img_buffer[BITMAP_IMG_FILE_SIZE_OFFSET]);
+ U% |7 Q# {9 B
; k- X2 G' B/ v- rt_kprintf("img_info: width=%d, height=%d, file_size=%d\n", img_width, img_height, file_size);
: v) w; ~- @; x) p! `
R/ d5 C+ |8 T i6 e/ D- /* wrap image data *// \& O/ [0 B# l! _ \+ C
- zbar_image_t *image = zbar_image_create();+ s2 N9 R* r& h4 X$ D4 l+ S
- zbar_image_set_format(image, *(int*)"Y800");
0 O8 Q0 i) l" w: M1 u9 e2 y' a0 _ - zbar_image_set_size(image, img_width, img_height);% `0 v: }$ M% n8 [3 {: `0 I
- zbar_image_set_data(image, (void *)(&img_buffer[img_offset]), img_width * img_height, zbar_image_free_data);. {/ [) {+ m& X5 a: _
- 3 G) A D: z" N; Z
- /* scan the image for barcodes */% w: `: A/ L+ } \# b/ [" W3 H0 s
- int n = zbar_scan_image(rt_scanner, image);
. t+ G2 p& T0 O4 \2 a# n6 B
5 _1 m) y! w4 X! ^. N. L- /* extract results */
4 P, a& P1 i3 \4 u6 }7 v - const zbar_symbol_t *symbol = zbar_image_first_symbol(image);
( x% G7 t) a9 w) H0 V - for(; symbol; symbol = zbar_symbol_next(symbol)) { x7 d3 \0 [! R" F7 w
- /* do something useful with results */7 |* `8 e: { ^" T: b( p
- zbar_symbol_type_t typ = zbar_symbol_get_type(symbol);
6 n4 o% u& ]" o. ]1 T; b* i- W) l. M - const char *data = zbar_symbol_get_data(symbol);
/ ?: b3 } t6 m+ [+ B - rt_kprintf("decoded %s symbol "%s"\n",2 [1 z8 l7 U( V# J0 s2 V
- zbar_get_symbol_name(typ), data);
# T: q# }. O4 w& z' _/ z - }
$ Q0 i' C9 I3 j$ k. j - $ F3 b( b8 g: p `. M( G1 T& Y& D
- /* clean up */
9 ?( b! Q3 N6 J( v - zbar_image_destroy(image);
1 ?% K, j7 |: h- P+ c3 O) r/ D - zbar_image_scanner_destroy(rt_scanner);$ e4 B. V! o3 ], L
- }
复制代码
5 ~' P: {7 K9 T; u6 E+ B* c& p# u2 N, h( M" ?+ O
7 G+ n2 d# A8 @2 O
运行结果 来看下实测的识别效果,RT-Thread 提供了 finsh Shell,可以很方便地使用命令行的方式运行写好的测试例程: 样本1(图片尺寸 256*256,纯二维码): 样本2(图片尺寸 480*360,摄像头拍摄): |
我移植zbar到arm上,找不到啥问题但就是扫不到码zbar_scan_image始终返回08 O' n6 S/ q' J$ ]4 n% [& p
谢谢