IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> ZBar源码分析——image.c | 2021SC@SDUSC -> 正文阅读

[C++知识库]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);
        }
        else
            free((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-- 要被复制的字节数。

该函数返回一个指向目标存储区destin的指针。

它实现了从源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);
    else
        n = 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_H
        rc = errno;
        zprintf(1, "ERROR opening %s: %s\n", filename, strerror(rc));
#else
        rc = 1;
#endif
        goto 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_H
        rc = errno;
        zprintf(1, "ERROR writing %s: %s\n", filename, strerror(rc));
#else
        rc = 1;
#endif
        fclose(f);
        goto error;
    }

    rc = fclose(f);

error:
    free(filename);
    return(rc);
}

对ZBar工作流程进行简单分析可以得知,ZBar对图像进行获取后,会在内存中以临时文件的形式进行保存,便于后续扫描器和解码器的使用。

在实现该函数时,首先声明了图像的渲染格式,即?zimg_hdr_s,所有的图像都会以该数据结构规整后的格式存入。代码中将渲染格式中的magic属性规定为0x676d697a,这也许是图像的统一色调。

该函数开始时,对图像文件创建一份副本,副本的文件大小=原文件大小+16。这是为了防止在后续的处理过程中,对图像文件进行了增量而导致申请的内存不够出现数据丢失的问题。

该函数的后续处理较为简单,通过几个分支对不同格式的图像进行分类保存。

三、总结

本次博客对ZBar扫描器的Image模块的几个关键函数进行了分析,也是对Video模块的一些补充说明。如有不足,敬请指正。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:09:27  更:2021-11-22 12:11:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 7:30:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码