ZBar源码分析——image.c | 2021SC@SDUSC
2021SC@SDUSC
一、Image 模块
ZBar的工作流程在以往的博客中做过一些介绍,在前几次的博客分析中也对video input部分和image scanner部分的一些代码进行了分析。在分析的过程中,我们发现,ZBar的Image模块起着不可或缺的作用,所有的分析和解码工作都是建立在图像的基础上完成的。
从工作流程图中我们也能看出,视频输入后,需要转换成图像的形式进行后续分析,并不是之间对视频进行处理。 在之前的博客中,也对Image模块的部分代码进行过简单介绍和分析,这篇博客将从image.c展开,对Image模块进行探索。
二、代码分析
首先对zbar_image_t的结构(ZBar图像的数据结构)进行说明。这在前面的博客中提及过。
struct zbar_image_s {uint32_t format; /* fourcc image format code */unsigned width, height; /* image size */const void *data; /* image sample data */unsigned long datalen; /* allocated/mapped size of data */unsigned crop_x, crop_y; /* crop rectangle */unsigned crop_w, crop_h;void *userdata; /* user specified data associated w/image *//* cleanup handler */zbar_image_cleanup_handler_t *cleanup;refcnt_t refcnt; /* reference count */zbar_video_t *src; /* originator */int srcidx; /* index used by originator */zbar_image_t *next; /* internal image lists */unsigned seq; /* page/frame sequence number */zbar_symbol_set_t *syms; /* decoded result set */
};
uint32_t格式;/*fourcc图像格式代码*/
注:FourCC全称Four-Character Codes,代表四字符代码 (four character code), 它是一个32位的标示符,其实就是typedef unsigned int FOURCC;是一种独立标示视频数据流格式的四字符代码。
视频播放软件通过查询 FourCC 代码并且寻找与 FourCC 代码相关联的视频解码器来播放特定的视频流。比如: DIV3 = DivX Low-Motion, DIV4 = DivX Fast-Motion, DIVX = DivX4, FFDS = FFDShow 等。比如wav、avi等RIFF文件的标签头标示,Quake 3的模型文件.md3中也大量存在等于“IDP3”的FOURCC。
unsigned width, height; /*图像大小*/
const void *data; /*图像样本数据*/
unsigned long datalen; /*已分配/映射的数据大小*/
unsigned crop_x, crop_y; /*裁剪矩形*/
unsigned crop_w, crop_h;
void*userdata; /*与图像关联的用户指定数据*/
refcnt_t refcnt; /*引用计数*/
zbar_video_t*src; /*生成器*/
int srcidx; /*生成器使用的索引*/
zbar_image_t *next; /*内部图像列表*/
unsigned seq; /*页/帧序列号*/
zbar_symbol_set_t*syms; /*解码结果集*/
检索、扫描并裁剪矩形
void zbar_image_set_crop (zbar_image_t *img,unsigned x,unsigned y,unsigned w,unsigned h)
{unsigned img_w = img->width;if(x > img_w) x = img_w;if(x + w > img_w) w = img_w - x;img->crop_x = x;img->crop_w = w;unsigned img_h = img->height;if(y > img_h) y = img_h;if(y + h > img_h) h = img_h - y;img->crop_y = y;img->crop_h = h;
}
在前面的代码分析中提到,BZar在对图像进行扫描、解码等操作时,都是基于矩形进行的。这个函数将识别得到的图像中条码附近的矩形区域进行了处理,将不包含条码的区域裁剪掉,得到需要处理的区域。
图像内存释放
inline void zbar_image_free_data (zbar_image_t *img)
{if(!img)return;if(img->src) {zbar_image_t *newimg;/* replace video image w/new copy */assert(img->refcnt); /* FIXME needs lock */newimg = zbar_image_create();memcpy(newimg, img, sizeof(zbar_image_t));/* recycle video image */newimg->cleanup(newimg);/* detach old image from src */img->cleanup = NULL;img->src = NULL;img->srcidx = -1;}else if(img->cleanup && img->data) {if(img->cleanup != zbar_image_free_data) {/* using function address to detect this case is a bad idea;* windows link libraries add an extra layer of indirection...* this works around that problem (bug #2796277)*/zbar_image_cleanup_handler_t *cleanup = img->cleanup;img->cleanup = zbar_image_free_data;cleanup(img);}elsefree((void*)img->data);}img->data = NULL;
}
接下来分析的函数负责释放图像内存。首先注意到,这个函数是内联函数。
内联函数
在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗。
为了解决这个问题,特别的引入了inline修饰符,表示为内联函数。
栈空间就是指放置程式的局部数据也就是函数内数据的内存空间,在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足所造成的程式出错的问题,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。
ZBar项目中特地将释放图像内存的函数声明为内联函数,说明该函数在ZBar项目的运行过程中将被频繁调用,换言之,ZBar的运行过程中将频繁对图像进行创建和释放。这也在某种程度上体现了Image模块对ZBar项目的重要性。
回到函数本身,在释放图像内存时,首先判断当前图像是否处在一个视频文件的逐帧迭代中(由src属性确定),如果是,则更换带有新副本的视频图像,将下一帧图像需要的资源进行内存拷贝,并对当前图像的索引进行重置。如果当前图像并不处在一个视频文件的逐帧迭代中,或是以及完成迭代,则检查其中关联的数据是否被释放,如果未被释放,则需要先释放这些数据,否则会产生空指针异常。这些检查工作完成后,方可释放图像内存。
值得一提的是,上述函数中提及了memcpy()函数:
函数原型为:void *memcpy(void *destin, void *source, unsigned n);
参数:
destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
source-- 指向要复制的数据源,类型强制转换为 void* 指针。
n-- 要被复制的字节数。
它实现了从源source所指的内存地址的起始位置开始拷贝n个字节到目标destin所指的内存地址的起始位置中。
图像写入文件
typedef struct zimg_hdr_s {uint32_t magic, format;uint16_t width, height;uint32_t size;
} zimg_hdr_t;int zbar_image_write (const zbar_image_t *img,const char *filebase)
{int len = strlen(filebase) + 16;char *filename = malloc(len);int n = 0, rc = 0;FILE *f;zimg_hdr_t hdr;strcpy(filename, filebase);if((img->format & 0xff) >= ' ')n = snprintf(filename, len, "%s.%.4s.zimg",filebase, (char*)&img->format);elsen = snprintf(filename, len, "%s.%08" PRIx32 ".zimg",filebase, img->format);assert(n < len - 1);filename[len - 1] = '\0';zprintf(1, "dumping %.4s(%08" PRIx32 ") image to %s\n",(char*)&img->format, img->format, filename);f = fopen(filename, "w");if(!f) {
#ifdef HAVE_ERRNO_Hrc = errno;zprintf(1, "ERROR opening %s: %s\n", filename, strerror(rc));
#elserc = 1;
#endifgoto error;}hdr.magic = 0x676d697a;hdr.format = img->format;hdr.width = img->width;hdr.height = img->height;hdr.size = img->datalen;if(fwrite(&hdr, sizeof(hdr), 1, f) != 1 ||fwrite(img->data, 1, img->datalen, f) != img->datalen) {
#ifdef HAVE_ERRNO_Hrc = errno;zprintf(1, "ERROR writing %s: %s\n", filename, strerror(rc));
#elserc = 1;
#endiffclose(f);goto error;}rc = fclose(f);error:free(filename);return(rc);
}
对ZBar工作流程进行简单分析可以得知,ZBar对图像进行获取后,会在内存中以临时文件的形式进行保存,便于后续扫描器和解码器的使用。
在实现该函数时,首先声明了图像的渲染格式,即 zimg_hdr_s,所有的图像都会以该数据结构规整后的格式存入。代码中将渲染格式中的magic属性规定为0x676d697a,这也许是图像的统一色调。
该函数开始时,对图像文件创建一份副本,副本的文件大小=原文件大小+16。这是为了防止在后续的处理过程中,对图像文件进行了增量而导致申请的内存不够出现数据丢失的问题。
该函数的后续处理较为简单,通过几个分支对不同格式的图像进行分类保存。
三、总结
本次博客对ZBar扫描器的Image模块的几个关键函数进行了分析,也是对Video模块的一些补充说明。如有不足,敬请指正。
ZBar源码分析——image.c | 2021SC@SDUSC相关推荐
- ZBar源码分析——video.c | 2021SC@SDUSC
2021SC@SDUSC 目录 一.Video模块 二.代码分析 三.总结 一.Video模块 我们知道,扫描时提供给ZBar的不都是静态的图片,也有可能是动态的视频.例如我们日常生活中调用的微信扫码 ...
- ZBar源码分析(五)
2021SC@SDUSC 目录 一.image头文件分析 二.image源文件分析 1.zbar_image_create函数分析 2._zbar_image_free函数分析 3.一系列get.se ...
- ZBar源码分析——多线程部分代码分析 | 2021SC@SDUSC
2021SC@SDUSC 目录 一.ZBar中的多线程 线程:cpu调度的最小单位 何为线程安全? 锁机制 二.ZBar中使用多线程的代码示例 Window线程的上锁与解锁 Vedio视频流的上锁与解 ...
- ZBar源码分析(一)
2021SC@SDUSC 目录 一.zbar.h头文件分析 二.图像处理器代码分析 一.zbar.h头文件分析 在安装路径下,include中有个zbar.h文件,首先从这个头文件入手. zbar.h ...
- ZBar源码分析(七)
2021SC@SDUSC 目录 一.zbar_image_write函数分析 二.Processor C++ wrapper分析 一.zbar_image_write函数分析 该函数将原始图像数据转储 ...
- ZBar源码分析(三)
2021SC@SDUSC 目录 一.zbar_processor_create函数分析 二.zbar_processor_destroy函数分析 三.zbar_processor_init函数分析 四 ...
- ZBar源码分析(二)
2021SC@SDUSC 目录 一.processor头文件分析 二.processor源文件_zbar_processor_handle_input函数分析 一.processor头文件分析 #de ...
- ZBar源码分析(十一)
2021SC@SDUSC 目录 一.convert.c 分析 1.以NULL结尾的已知格式列表,按优先顺序排列 2.函数分析 一.convert.c 分析 1.以NULL结尾的已知格式列表,按优先顺序 ...
- Nett源码剖析注册通道2021SC@SDUSC
2021SC@SDUSC 在绑定端口过程中,类initAndRegister里有注册通道方法ChannelFuture regFuture = config().group().register(ch ...
最新文章
- 怎么样给ajax的ulr加密,研究Ajax请求受登录保护的URL的优雅解决
- Excutor线程池
- Zigbee如何在智能家居中成为领先的连接技术?
- C#基础系列 - 抽象类及其方法的学习
- 框架 go_GoFrame v1.13.1 发布,Go 应用开发框架
- leetcode--Longest Substring Without Repeating Characters
- Server-U文件名中文乱码问题解决方法
- JSP指令、动作和对象
- c语言编程GetTickCount,c语言计算时间方法---clock-GetTickCount-QueryPerformanceCounter
- IDEA Jrebel 激活方法
- embed预览pdf_09.html使用iframe、embed查看pdf不显示(未解决),使用pdf.js预览pdf
- 既然 AAC 要比 MP3 好,且体积差不多,为什么网上不流行 AAC 格式的音频呢?
- VGA与DVI接口以及HDMI
- 软件随想录:程序员部落酋长Joel谈软件(阮一峰译)-3
- np.power与np.linalg.inv
- 爬虫B站任意视频 弹幕文字+时间
- zabbix监控客户端时前端配置不成功
- Stm32 Max6675 K型热电偶 采集温度值(代码+相关方法)
- Win勒索病毒害惨中国学生!微软:最新Win10很安全
- 科学与信仰水火不容吗?