利用libjpeg库实现jpg与bmp图片互转指南

jpg与bmp图片互转指南

一、原理:

​ jpg与bmp图片格式都是以rgb像素为基础的,但是jpg在bmp的rgb的基础上进行了压缩。而且存储的方式是bgr,因此,在二者转换过程中需要转换对应的格式,而且只需要的到rgb再使用libjpeg库即可进行互相转换。

二、转换工具:libjpeg库

移植jpeg库

1.下载JPEG库的源代码 http://www.ijg.org/files/ JPEG官方网站

2.jpegsrc.v9a.tar.gz 为源码文件,需要往自己的linux中安装该库

在家目录解压源码文件

cp ./jpegsrc.v9a.tar.gz /home

cd /home

tar -xvf jpegsrc.v9a.tar.gz

mkdir jpeg_lib

配置文件:
cd jpeg-9a

./configure --prefix=/home/jpeg_lib

make

make install

cd …/jpeg_lib/lib

cp ./* /lib

3.编译命令:

gcc xxx.c -o xxx -I/home/gec/jpeg_lib/include -L/home/gec/jpeg_lib/lib -ljpeg

4.当需要用到库的时候在头文件中加入:这些头文件

#include <stdio.h>
#include "jpeglib.h"
#include <setjmp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <linux/input.h> 
#include <sys/mman.h>
#include <stdlib.h>
#include <string.h>

三、jpeg转为bmp

利用jpeg库从jpg图片获得rgb:

//读取并转换jpeg像素点为rgb像素点
GLOBAL(int)read_JPEG_file (const char * filename)
{
    //定义一个JPEG解码信息结构体
    struct jpeg_decompress_struct cinfo;

    //定义一个错误信息结构体
    struct my_error_mgr jerr;
    /* More stuff */
    FILE * infile;		/*定义源文件指针 标准IO的*/
    JSAMPARRAY buffer;		/*输出行缓冲*/
    int row_stride;		/*输出行缓冲的宽度 */

    //打开源文件 
    if ((infile = fopen(filename, "rb")) == NULL)
    {
        fprintf(stderr, "can't open %s\n", filename);
        return 0;
    }

    /* 第一步:初始化JPEG解码对象*/

    /* 我们开始初始化错误对象*/
    cinfo.err = jpeg_std_error(&jerr.pub);
    jerr.pub.error_exit = my_error_exit;
    if (setjmp(jerr.setjmp_buffer)) 
    {
        jpeg_destroy_decompress(&cinfo);
        fclose(infile);
        return 0;
    }

    /*现在我们可以初始化解码对象了*/
    jpeg_create_decompress(&cinfo);

    /*把源文件与解码对象关联起来 */
    jpeg_stdio_src(&cinfo, infile);

    /*读取JPEG图片的头数据*/
    (void) jpeg_read_header(&cinfo, TRUE);

    

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

    //得到一行数据所占用的字节数 
    row_stride = cinfo.output_width * cinfo.output_components;

    /*根据行字节数去分配堆空间*/
    buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

    char *p=NULL;	

    bmp_width = cinfo.output_width;
    bmp_height = cinfo.output_height;

    int x=0,y=0;
    unsigned char r=0;
    unsigned char g=0;
    unsigned char b=0; 
    rgb_buf = malloc(cinfo.output_width * cinfo.output_height * 3);

    //重点 重点 重重点!!!! 解码就在这里进行了 上面的都是准备工作!!
    while (cinfo.output_scanline < cinfo.output_height)
    {
        
        //解码一行JPEG图形的数据变成bmp数据并存放到buffer 中 
        (void) jpeg_read_scanlines(&cinfo, buffer, 1);

        p = buffer[0]; 

        y  = bmp_height - cinfo.output_scanline; 
            
        //取出RGB数据 
        for(x = 0 ; x < cinfo.output_width ; x++)
        {
            r = *p++;
            g = *p++; 
            b = *p++; 

            //赋值到rgb_buf上  
            rgb_buf[bmp_width * y * 3 + x * 3] = b;
            rgb_buf[bmp_width * y * 3 + x * 3 + 1] = g;
            rgb_buf[bmp_width * y * 3 + x * 3 + 2] = r;
        }
    }

    //结束解码对象
    (void) jpeg_finish_decompress(&cinfo);

    //销毁解码对象
    jpeg_destroy_decompress(&cinfo);

    //关闭文件
    fclose(infile);

    return 1;
}

把rgb写入到bmp文件中,并处理bmp文件头:

int fn_Make_Bmp()
{
    int bmp_fd = open("./test.bmp" , O_RDWR | O_TRUNC | O_CREAT , 0777);
    if(bmp_fd <= 0)
    {
        perror("");
        return -1;
    }
    //处理bmp数据头
    struct bitmap_header head_info;
    memset(&head_info , 0 , 14);
    head_info.type = BM;//BM = 19778
    head_info.size = bmp_height * bmp_width * 3 + 54;
    head_info.offbits = 54;

    struct bitmap_info bmp_info;
    memset(&bmp_info , 0 , 40);
    bmp_info.size = 40;
    bmp_info.width = bmp_width;
    bmp_info.height = bmp_height;
    bmp_info.planes = 1;
    bmp_info.bit_count = 24;
    bmp_info.size_img = bmp_height * bmp_width * 3;
    bmp_info.X_pel = bmp_width;
    bmp_info.Y_pel = bmp_height;

    //写入
    write(bmp_fd , &head_info , 14);
    write(bmp_fd , &bmp_info , 40);
    write(bmp_fd , rgb_buf , bmp_height * bmp_width * 3);

    free(rgb_buf);

    return 0;
}

四、bmp转jpeg

获取所有的bmp像素,并且转换为jpg对应的bgr存储形式

int fn_Open_Bmp(const char * filename)
{
    int bmp_fd = open(filename , O_RDWR);
    if(bmp_fd <= 0)
    {
        perror("");
        return -1;
    }
	
    //为头信息申请空间
    struct bitmap_header * head_info = malloc(14);
    struct bitmap_info * bmp_info = malloc(40);
    if(head_info == NULL || bmp_info == NULL)
    {
        printf("申请bmp头信息空间失败\n");
        return -1;
    }
    
    //读取bmp的头信息
    read(bmp_fd , head_info , 14);
    read(bmp_fd , bmp_info , 40);
    bmp_w = bmp_info->width;
    bmp_h = bmp_info->height;
    printf("bmp_x = %d bmp_y = %d\n" , bmp_w , bmp_h);

    //读取RGB信息
    rgb_buf = malloc(bmp_w * bmp_h * 3);
    if(rgb_buf == NULL)
    {
        printf("申请bmp_rgb空间失败\n");
        return -1;
    }
    unsigned int ret =  read(bmp_fd , rgb_buf , bmp_w * bmp_h * 3);
    if(ret != bmp_w * bmp_h * 3)
    {
        printf("读取bmp_rgb失败\n");
        return -1;
    }

    unsigned char tmp;
    //把RGB转换成BGR
    for(int i = 0 ; i < bmp_h ; i++)
    {
        for(int j = 0 ; j < bmp_w ; j++)
        {
            tmp = rgb_buf[bmp_w * i * 3 + j * 3];
            rgb_buf[bmp_w * i * 3 + j * 3] = rgb_buf[bmp_w * i * 3 + j * 3 + 2];
            rgb_buf[bmp_w * i * 3 + j * 3 + 2] = tmp;
        }
    }

    free(bmp_info);
    free(head_info);
    close(bmp_fd);
    return 0;
}

把bgr使用jpeg库进行压缩,并且存储到jpg文件中

GLOBAL(void)write_JPEG_file (const char * filename)
{
    struct jpeg_compress_struct com_cinfo;
    struct jpeg_error_mgr com_jerr;
    FILE * outfile;/* target file */
    JSAMPROW row_pointer[1];/* pointer to JSAMPLE row[s] 一行位图 */
    int row_stride;/* physical row width in image buffer */

    /* Step 1: 申请并初始化jpeg压缩对象,同时要指定错误处理器
     */
    com_cinfo.err = jpeg_std_error(&com_jerr);

    /* Now we can initialize the JPEG compression object. */

    jpeg_create_compress(&com_cinfo);

    if ((outfile = fopen(filename, "w+")) == NULL) 
    {
        fprintf(stderr, "can't open %s\n", filename);
        exit(1);
    }
    jpeg_stdio_dest(&com_cinfo, outfile);

    /* Step 3: 设置压缩参数 */

    /* image width and height, in pixels */

    com_cinfo.image_width = bmp_w;

    com_cinfo.image_height = bmp_h;

    com_cinfo.input_components = 3;/* 3表示彩色位图,如果是灰度图则为1 */

    com_cinfo.in_color_space = JCS_RGB; /* JCS_RGB表示彩色图像 */

    /* jpeg_set_defaults函数一定要等设置好图像宽、高、色彩通道数计色彩空间四个参数后才能调用,因为这个函数要用到这四个值,调用jpeg_set_defaults函数后,libjpeg库采用默认的设置对图像进行压缩,如果需要改变设置,如压缩质量,调用这个函数后,可以调用其它设置函数,如jpeg_set_quality函数。其实图像压缩时有好多参数可以设置,但大部分我们都用不着设置,只需调用jpeg_set_defaults函数值为默认值即可。   */

    jpeg_set_defaults(&com_cinfo);
	//经过测试jpeg_set_quality的质量参数写为60比较合适
    jpeg_set_quality(&com_cinfo , 60 , TRUE /* limit to baseline-JPEG values */);

    /* jpeg_start_compress,然后可以对每一行进行压缩,也可以对若干行进行压缩,甚至可以对整个的图像进行一次压缩,压缩完成后,要调用jpeg_finish_compress函数*/
    /* Step 4: Start compressor */
    jpeg_start_compress(&com_cinfo, TRUE);

    /* Step 5: while (scan lines remain to be written) */

    row_stride = bmp_w * 3;/* JSAMPLEs per row in image_buffer  每一行的字节数*/

    //对每一行进行压缩

    while (com_cinfo.next_scanline < com_cinfo.image_height) 
    {
        row_pointer[0] = & rgb_buf[(bmp_h - com_cinfo.next_scanline - 1) * row_stride];// image_buffer指向要压缩的数据  
        jpeg_write_scanlines(&com_cinfo, row_pointer, 1);
    }

    /*最后就是释放压缩工作过程中所申请的资源了*/
    /* Step 6: Finish compression */

    jpeg_finish_compress(&com_cinfo);

    fclose(outfile);

    /* Step 7: release JPEG compression object */

    jpeg_destroy_compress(&com_cinfo);

    /* And we're done! */

}
  • 1
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
这里给出一个简单的利用libjpeg实现对图像的双边滤波处理的C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <jpeglib.h> #include <jerror.h> #include <math.h> // 定义双边滤波函数 void bilateral_filter(unsigned char *src_data, unsigned char *dst_data, int width, int height, int channels, int radius, double sigma_d, double sigma_r) { int window_size = 2 * radius + 1; double *w_d = (double *)malloc(sizeof(double) * window_size * window_size); double *w_r = (double *)malloc(sizeof(double) * window_size * window_size); double weight_d, weight_r, weight_sum; double pixel_value; int offset, i, j, k, l, m, n; int x, y, x1, y1, x2, y2; int neighbor_x, neighbor_y; int src_index, dst_index; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { offset = (y * width + x) * channels; weight_sum = 0.0; pixel_value = 0.0; for (i = -radius; i <= radius; i++) { for (j = -radius; j <= radius; j++) { x1 = x + i; y1 = y + j; if (x1 < 0 || x1 >= width || y1 < 0 || y1 >= height) { continue; } x2 = x1 - x; y2 = y1 - y; weight_d = exp(-(x2 * x2 + y2 * y2) / (2.0 * sigma_d * sigma_d)); neighbor_x = src_data[(y1 * width + x1) * channels]; neighbor_y = src_data[(y1 * width + x1) * channels + 1]; neighbor_z = src_data[(y1 * width + x1) * channels + 2]; weight_r = exp(-(neighbor_x - src_data[offset]) * (neighbor_x - src_data[offset]) / (2.0 * sigma_r * sigma_r)); weight_r *= exp(-(neighbor_y - src_data[offset + 1]) * (neighbor_y - src_data[offset + 1]) / (2.0 * sigma_r * sigma_r)); weight_r *= exp(-(neighbor_z - src_data[offset + 2]) * (neighbor_z - src_data[offset + 2]) / (2.0 * sigma_r * sigma_r)); w_d[(j + radius) * window_size + (i + radius)] = weight_d; w_r[(j + radius) * window_size + (i + radius)] = weight_r; weight_sum += weight_d * weight_r; pixel_value += weight_d * weight_r * src_data[offset + j * width * channels + i * channels]; } } dst_data[offset] = pixel_value / weight_sum; dst_data[offset + 1] = pixel_value / weight_sum; dst_data[offset + 2] = pixel_value / weight_sum; } } free(w_d); free(w_r); } // 主函数 int main() { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; FILE *infile; JSAMPARRAY buffer; int row_stride; // 打开JPEG文件 if ((infile = fopen("input.jpg", "rb")) == NULL) { fprintf(stderr, "can't open input file\n"); return 0; } // 初始化JPEG解压对象 cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); // 读取JPEG图像数据 row_stride = cinfo.output_width * cinfo.output_components; buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1); unsigned char *data = (unsigned char *)malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); unsigned char *output_data = (unsigned char *)malloc(cinfo.output_width * cinfo.output_height * cinfo.output_components); while (cinfo.output_scanline < cinfo.output_height) { jpeg_read_scanlines(&cinfo, buffer, 1); memcpy(data + row_stride * (cinfo.output_scanline - 1), buffer[0], row_stride); } // 关闭JPEG解压对象 jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); // 对图像进行双边滤波 bilateral_filter(data, output_data, cinfo.output_width, cinfo.output_height, cinfo.output_components, 5, 10.0, 100.0); // 将结果保存为JPEG文件 struct jpeg_compress_struct dstinfo; struct jpeg_error_mgr dsterr; FILE *outfile; JSAMPROW row_pointer[1]; int dst_row_stride; if ((outfile = fopen("output.jpg", "wb")) == NULL) { fprintf(stderr, "can't open output file\n"); return 0; } dstinfo.err = jpeg_std_error(&dsterr); jpeg_create_compress(&dstinfo); jpeg_stdio_dest(&dstinfo, outfile); dstinfo.image_width = cinfo.output_width; dstinfo.image_height = cinfo.output_height; dstinfo.input_components = cinfo.output_components; dstinfo.in_color_space = cinfo.out_color_space; jpeg_set_defaults(&dstinfo); jpeg_start_compress(&dstinfo, TRUE); dst_row_stride = dstinfo.image_width * dstinfo.input_components; row_pointer[0] = (JSAMPROW)malloc(dst_row_stride); while (dstinfo.next_scanline < dstinfo.image_height) { for (int i = 0; i < dst_row_stride; i++) { row_pointer[0][i] = output_data[dstinfo.next_scanline * dst_row_stride + i]; } jpeg_write_scanlines(&dstinfo, row_pointer, 1); } jpeg_finish_compress(&dstinfo); jpeg_destroy_compress(&dstinfo); fclose(outfile); // 释放内存 free(data); free(output_data); free(row_pointer[0]); return 0; } ``` 需要注意的是,这里的双边滤波函数只支持处理RGB三通道图像,如果需要处理其他类型的图像,需要进行相应的修改。在使用时,需要将`input.jpg`和`output.jpg`替换为实际的JPEG图像文件名。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值