实现任务:把一张图片解析出来,并插入到目标图片

借助libjpeg库,libjpeg这个库主要用于处理jpeg数据,比如将RGB压缩成JPEG,或者将JPEG解压为RGB。

libjpeg处理的图片一般是矩形,矩形里是一行行数据,一行数据为”scanline”。每行数据是一个个像素,像素的值由component单元组成,RGB有三个component,灰度值有一个component。图片信息相当于一个二维数组,压缩是把二维数组压缩为一个jpeg图片,解压是把jpeg图片恢复为二维数组。

jpg图片的提取步骤:

1.分配和初始化一个decompression(解压结构体)对象 cInfo和错误处理结构体对象jerr

 struct jpeg_decompress_struct cinfo;
 struct jpeg_error_mgr jerr;

jpeg_decompress_struct是libjpeg库中用于管理JPEG解压缩过程的主要结构体。它包含了所有解码JPEG图像所需的信息,包括解码参数、图像信息、输入输出数据指针、解码状态等。这个结构体可以看作是libjpeg解码器的主控制结构。

jpeg_error_mgr结构体用于管理libjpeg库的错误处理。它定义了如何报告和处理JPEG解码过程中可能出现的各种错误。libjpeg库通过这个结构体来配置错误处理行为,比如设置错误输出、错误恢复选项等。

2.设置出错处理函数并初始化编解码结构对象

 cinfo.err = jpeg_std_error(&jerr);
 jpeg_create_decompress(&cinfo);

在libjpeg库中,实现了默认错误处理函数,当错误发生时,比如如果内存不足(非常可能发生,后面会介绍)等,则默认错误处理函数将会调用exit函数结束整个进程

3.指定源文件

infile = fopen(filename, "rb");
    if (!infile) {
        fprintf(stderr, "Can't open %s\n", filename);
        exit(1);
    }
  jpeg_stdio_src(&cinfo, infile);
在libjpeg库中仅仅提供了文件作为输入数据的接口

4.用jpeg_read_header获得jpg图片信息

调用jpeg_read_header(&cinfo, TRUE)读取jpeg文件头信息,这个和初始化解码对象一样,是必须要调用的,是约定。

5.设置解压参数,比如放大、缩小

插入图片的不缩放时可以不必设置,不设置的话通常就是JCS_RGB,RGB888的格式,除此之外,还可以设置缩放大小等,scale_num,scale_denom:因为实际的显示设备千变万化,我们可能需要根据实际情况对输出数据进行一些缩放才能够显示。libjpeg支持对输出数据进行缩放(scale),这个变量就是用来设置缩放的参数。

6.启动解压:jpeg_start_decompress(…)

经过前面的参数设置,接着调用jpeg_start_decompress(&cinfo);开始解码。

7.循环调用jpeg_read_scanlines(…)

width = cinfo.output_width;
    *height = cinfo.output_height;
    fprintf(stderr, "QR size=[%d*%d] \n", *width,*height);
    JSAMPROW row_pointer[1];   // 指向一行像素数据的指针
    *buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo,
                                         JPOOL_IMAGE,
                                         (*width)*cinfo.output_components,
                                         *height); // 分配内存

计算输出行缓冲区大小,分配足够的内存来存储解码后的图像数据,即用buffer来存储。

对解码出来的数据做处理,逐行取出并放入buffer中。

while (cinfo.output_scanline < cinfo.output_height) {
        fprintf(stderr, "Info : jpeg_read_scanlines %d/%d +++\n",cinfo.output_scanline,cinfo.output_height);
        row_pointer[0] = (*buffer)[cinfo.output_scanline];
        (void) jpeg_read_scanlines(&cinfo, row_pointer, 1);
        fprintf(stderr, "Info : jpeg_read_scanlines %d/%d ---\n",cinfo.output_scanline,cinfo.output_height);
    }

cinfo.output_scanline的值通常在调用jpeg_read_scanlines函数后由libjpeg库内部更新。每当jpeg_read_scanlines成功读取一行数据后,libjpeg库会自动增加cinfo.output_scanline的值,以准备读取下一行数据。


8.释放decompress结构体并关闭打开的文件

jpeg_finish_decompress(&cinfo);
jpeg_destroy_decompress(&cinfo);
fclose(infile);

总体代码如下:

// 解码JPEG图像的函数
void decode_jpeg(const char *filename, JSAMPARRAY *buffer, int *width, int *height) {
    FILE *infile;
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    // 设置错误处理
    cinfo.err = jpeg_std_error(&jerr);
    jpeg_create_decompress(&cinfo);

    // 打开输入文件
    infile = fopen(filename, "rb");
    if (!infile) {
        fprintf(stderr, "Can't open %s\n", filename);
        exit(1);
    }
    jpeg_stdio_src(&cinfo, infile);

    // 读取文件头
    (void) jpeg_read_header(&cinfo, TRUE);

    // 开始解码
    jpeg_start_decompress(&cinfo);

    // 计算输出行缓冲区大小
    *width = cinfo.output_width;
    *height = cinfo.output_height;
    fprintf(stderr, "QR size=[%d*%d] \n", *width,*height);
    JSAMPROW row_pointer[1];   // 指向一行像素数据的指针
    *buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo,
                                         JPOOL_IMAGE,
                                         (*width)*cinfo.output_components,
                                         *height); // 分配内存

    // 解码每一行
    while (cinfo.output_scanline < cinfo.output_height) {
        fprintf(stderr, "Info : jpeg_read_scanlines %d/%d +++\n",cinfo.output_scanline,cinfo.output_height);
        row_pointer[0] = (*buffer)[cinfo.output_scanline];
        (void) jpeg_read_scanlines(&cinfo, row_pointer, 1);
        fprintf(stderr, "Info : jpeg_read_scanlines %d/%d ---\n",cinfo.output_scanline,cinfo.output_height);
    }
    fprintf(stderr, "QR Decode finished\n");
    // 完成解码
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
}

参考:http://t.csdnimg.cn/oUf5X

把解码后的图片插入到目标图片上

1获取源图像和目标图像的像素位置

 const JSAMPLE *src_pixel = &source[y][x * 3];
 uint32_t *dst_pixel = &target[(y + y_offset) * dst_w + x + x_offset];

src_pixel 是一个指向JSAMPLE类型的常量指针,它现在指向了source数组中某个像素的红色分量的地址。通过递增这个指针,可以访问到同一像素的绿色和蓝色分量。此处的source数组就是上面存储解压信息的buffer。

target数组被假设为是以像素为单位的一维数组,而每个像素占用了4个字节(一个32位整数),用于存储ARGB或RGBA格式的颜色值

source数组是以颜色分量为单位的(即R、G、B、A分别存储,每个分量占用1个字节),那么在计算像素位置时确实需要将x乘以4.

2假设源图像是RGB格式,目标图片也是RGB

则这里进行简单的粘贴复制即可。

整体代码如下:

void insert_image(uint32_t *target, const JSAMPARRAY source, int src_w, int src_h, int dst_w, int dst_h, int x_offset, int y_offset) {
    for (int y = 0; y < src_h; ++y) {
        for (int x = 0; x < src_w; ++x) {
            // 获取源图像和目标图像的像素位置
            const JSAMPLE *src_pixel = &source[y][x * 3];
            uint32_t *dst_pixel = &target[(y + y_offset) * dst_w + x + x_offset];

            // 假设源图像是RGB格式,这里进行简单的颜色复制
            //*dst_pixel = (0xFF << 24) | (*src_pixel << 16) | (*(src_pixel + 1) << 8) | *(src_pixel + 2);
            *dst_pixel = (*src_pixel << 16) | (*(src_pixel + 1) << 8) | *(src_pixel + 2);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值