LINUX下jpeglib的dome(二)

参照jpeg-6b/example.c这个demo可以很容易的在jpeg与bmp之间进行转换

 

1.将bmp转为jpg

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "jpeglib.h"
#include <string.h>

#pragma pack(1)
typedef struct __BITMAPFILEHEADER__
{
    u_int16_t bfType;
    u_int32_t bfSize;
    u_int16_t bfReserved1;
    u_int16_t bfReserved2;
    u_int32_t bfOffBits;
}BITMAPFILEHEADER; 

typedef struct __BITMAPINFOHEADER 
{ 
    u_int32_t biSize; 
    u_int32_t biWidth; 
    u_int32_t biHeight; 
    u_int16_t biPlanes; 
    u_int16_t biBitCount; 
    u_int32_t biCompression; 
    u_int32_t biSizeImage; 
    u_int32_t biXPelsPerMeter; 
    u_int32_t biYPelsPerMeter; 
    u_int32_t biClrUsed; 
    u_int32_t biClrImportant; 
}BITMAPINFOHEADER; 


/******************** JPEG COMPRESSION SAMPLE INTERFACE *******************/

/* 

 本例的这一半展示了如何将数据输入JPEG压缩器。
 我们提供了一个最小的版本,不需要担心这样的改进
 作为错误恢复(JPEG代码将退出(),如果它得到一个错误)。
 
*/


/*
*图像数据格式:
*
*标准输入图像格式是一个像素矩形数组,用
*每个像素具有相同数量的“组件”值(颜色通道)。
*每个像素行是一个JSAMPLEs数组(通常是无符号字符)。
*如果您正在处理颜色数据,那么每个像素的颜色值
*必须与该行相邻;例如,R, G, B, R, G, B, R, G, B,…对24位
* RGB颜色。
*
*对于本例,我们将假设该数据结构与方法匹配
*我们的应用程序将图像存储在内存中,所以我们只需传递a
*指向我们的图像缓冲区。特别地,假设图像是
* RGB颜色,由:
 
*/

#if 0
extern JSAMPLE * image_buffer;    /* 指向数组的R,G, b阶数据 */
extern int image_height;    /* 图像中的行数 */
extern int image_width;        /* 图像中的列数 */
#endif


/*
 * JPEG压缩的示例例程。我们假设目标文件名
 *并传入压缩质量因子。
 */

GLOBAL(void)
write_JPEG_file (char * jpgfile, char* bmpfile, int quality)
{
    int w, h, bpp; //bpp-->byte_per_pix
    int len;
    char* image_buffer = NULL;
    BITMAPFILEHEADER bmphead;
    BITMAPINFOHEADER infohead;
    /*
    *这个结构包含JPEG压缩参数和指向的指针
    *工作空间(根据JPEG库的需要分配)。
    *有可能有几个这样的结构,表示多个
    *压缩/解压过程,同时存在。我们参考
    *将任何一个结构(及其关联的工作数据)作为“JPEG对象”。
     */
    struct jpeg_compress_struct cinfo;
    /* 
  *这个结构表示一个JPEG错误处理程序。它是单独声明的因为应用程序通常想要提供一个专门的错误处理程序
*(有关示例,请参见该文件的后半部分)。但这里我们只是
*使用简单的方法,使用标准的错误处理程序
*在stderr上打印一条消息,如果压缩失败,调用exit()。
*注意,这个结构必须与主JPEG参数一样长
* struct,以避免悬浮指针问题。
     */
    struct jpeg_error_mgr jerr;
    /* More stuff */
    FILE * out_jpgfile;        /* target file */
    FILE * in_bmpfile;        /* input file */

    JSAMPROW row_pointer[1];    /* pointer to JSAMPLE row[s] */
    int row_stride;        /* 图像缓冲区中的物理行宽度 */

    /* Step 1:分配和初始化JPEG压缩对象 */

    /*我们必须首先设置错误处理程序,以防初始化
    *步骤失败。(不太可能,但如果你的内存不足,就会发生这种情况。)
    *这个例程填充结构jerr的内容,并返回jerr的内容
    *地址,我们放置到链接字段在cinfo。
     */
    cinfo.err = jpeg_std_error(&jerr);
    /* Now we can initialize the JPEG compression object. */
    jpeg_create_compress(&cinfo);

    /* Step 2: specify data destination (eg, a file) */
    /* Note: steps 2 and 3 can be done in either order. */

    /* Here we use the library-supplied code to send compressed data to a
     * stdio stream. You can also write your own code to do something else.
     * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
     * requires it in order to write binary files.需要它来编写二进制文件。
     */
    if ((out_jpgfile = fopen(jpgfile, "wb")) == NULL) {
        fprintf(stderr, "can't open jpg %s\n", jpgfile);
        exit(1);
    }
    if ((in_bmpfile = fopen(bmpfile, "rb")) == NULL) {
        fprintf(stderr, "can't open bmp=%s\n", bmpfile);
        exit(1);
    }
    len = sizeof(bmphead)+sizeof(infohead);
    printf("len=%d\n",len);
    image_buffer = (char*)malloc(len*sizeof(char));
    if(image_buffer == NULL)
    {
        printf("malloc error\n");
        return ;
    }
    fread(image_buffer, len, 1, in_bmpfile);
    memcpy(&bmphead, image_buffer, sizeof(bmphead));
    memcpy(&infohead, image_buffer+sizeof(bmphead), sizeof(infohead));
    if(bmphead.bfType != 0x4D42) //must be 0x4D42='BM'
    {
        printf("not a bmp file\n");
        return ;
    }
    if(infohead.biBitCount != 24)  //这儿只支持bit_pre_pix=24的bmp格式
    {
        printf("error: now bitCount=%d not 24bit\n", infohead.biBitCount);
        return ;
    }
    free(image_buffer);
    w = infohead.biWidth;
    h = infohead.biHeight;
    bpp = infohead.biBitCount/8;
    printf("w=%d,h=%d,bitcount=%d\n", w, h, bpp);
    image_buffer = (char*)malloc(w*h*bpp);
    if(image_buffer == NULL)
    {
        printf("malloc w*h*bpp error\n");
        return ;
    }
    fread(image_buffer, w*h*bpp, 1, in_bmpfile);
    jpeg_stdio_dest(&cinfo, out_jpgfile);

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

    /* 首先,我们提供输入图像的描述。
* cinfo结构必须填写四个字段:
     */
    cinfo.image_width = w;     /* 图像的宽度和高度,以像素为单位 */
    cinfo.image_height = h;
    cinfo.input_components = 3;        /* #每像素的颜色分量 */
    cinfo.in_color_space = JCS_RGB;     /* 输入图像的颜色空间 */
    /* 现在使用库的例程设置默认压缩参数。
        你必须设置至少cinfo。在调用这个之前,
    *因为默认值取决于源颜色空间。)
     */
    jpeg_set_defaults(&cinfo);
    /* 现在您可以设置任何您希望设置的非默认参数。
    *这里我们只是演示了使用质量(量化表)缩放:
     */
    jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

    /* Step 4: 启动压缩 */

    /* TRUE确保我们将编写一个完整的交换jpeg文件。
        除非你非常确定自己在做什么,否则不要使用假。
     */
    jpeg_start_compress(&cinfo, TRUE);

    /* Step 5: 而(扫描线还有待书写) */
    /* jpeg_write_scanlines(...); */

    /* 这里我们使用库的状态变量cinfo。next_scanline作为
    *循环计数器,这样我们就不用跟踪自己了。
    *为简单起见,我们每次调用传递一条扫描线;你可以通过
    不过,如果你愿意的话,可以多设置一点。
     */
    row_stride = w* 3;    /* 图像缓冲区中的每一行JSAMPLEs*/

    while (cinfo.next_scanline < cinfo.image_height) {
        /* jpeg_write_scanlines需要指向scanlines的指针数组。
            这里的数组只有一个元素长,但是可以传递
            *如果方便的话,一次使用多个扫描线。
         */
        //因为bmp图像的数据存储格式是从左下到右上,若直接处理生成的图像是倒的,所以这儿需要从下往上处理图像 
        //row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
        row_pointer[0] = & image_buffer[(cinfo.image_height-cinfo.next_scanline-1) * row_stride];
        (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    /* Step 6:完成压缩*/

    jpeg_finish_compress(&cinfo);
    /* 在finish_compress之后,我们可以关闭输出文件。 */
    fclose(out_jpgfile);
    fclose(in_bmpfile);
    free(image_buffer);
    /* Step 7:释放JPEG压缩对象 */

    /* 这是一个重要的步骤,因为它将释放大量内存。 */
    jpeg_destroy_compress(&cinfo);

    /* And we're */
}



int main ( int argc, char *argv[] )
{
    char bmpfile[] = "./640_480.bmp";
    char jpgfile[] = "./640_480.jpg";
    write_JPEG_file(jpgfile, bmpfile, 100);
    printf("read over\n");
    return EXIT_SUCCESS;
}

2.将jpg转为bmp

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include "jpeglib.h"

#pragma pack(1)
typedef struct __BITMAPFILEHEADER__
{
    u_int16_t bfType;
    u_int32_t bfSize;
    u_int16_t bfReserved1;
    u_int16_t bfReserved2;
    u_int32_t bfOffBits;
}BITMAPFILEHEADER; 

typedef struct __BITMAPINFOHEADER 
{ 
    u_int32_t biSize; 
    u_int32_t biWidth; 
    u_int32_t biHeight; 
    u_int16_t biPlanes; 
    u_int16_t biBitCount; 
    u_int32_t biCompression; 
    u_int32_t biSizeImage; 
    u_int32_t biXPelsPerMeter; 
    u_int32_t biYPelsPerMeter; 
    u_int32_t biClrUsed; 
    u_int32_t biClrImportant; 
}BITMAPINFOHEADER; 

#define BYTE_PER_PIX 3
int create_bmp_header(BITMAPFILEHEADER* bmphead, BITMAPINFOHEADER* infohead, int w, int h)
{
    bmphead->bfType = 0x4D42; //must be 0x4D42='BM'
    bmphead->bfSize= w*h*BYTE_PER_PIX+14+40;
    bmphead->bfReserved1= 0x00;
    bmphead->bfReserved2= 0x00;
    bmphead->bfOffBits = 14+40;

    infohead->biSize = sizeof(BITMAPINFOHEADER);
    infohead->biWidth = w; 
    infohead->biHeight = -h;
    infohead->biPlanes = 1;
    infohead->biBitCount = BYTE_PER_PIX*8;
    infohead->biCompression= 0;
    infohead->biSizeImage= w*h*BYTE_PER_PIX;//640 * 480 * 3;
    infohead->biXPelsPerMeter= 0x0;
    infohead->biYPelsPerMeter= 0x0;
    infohead->biClrUsed = 0;
    infohead->biClrImportant = 0;
    return 0;
}



struct my_error_mgr {
  struct jpeg_error_mgr pub;    /* "public" fields */

  jmp_buf setjmp_buffer;    /* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
 *下面是替换标准error_exit方法的例程:
 */

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  /* >err实际上指向my_error_mgr结构体,因此强制指针*/
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* 如果我们愿意的话,我们可以推迟到回来之后. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}


/*
 * Sample routine for JPEG decompression. We assume that the source file name
 * is passed in. We want to return 1 on success, 0 on error.
 */

int read_JPEG_file (char* bmpfile, char * jpgfile)
{
    BITMAPFILEHEADER bmphead;
    BITMAPINFOHEADER infohead;
    FILE * bmp_fp = NULL;
  /* 这个结构包含JPEG解压参数和指向的指针
    *工作空间(根据JPEG库的需要分配).
   */
  struct jpeg_decompress_struct cinfo;
  /* 我们使用私有扩展JPEG错误处理程序。
    *注意,这个结构必须与主JPEG参数一样长
    * struct,以避免悬浮指针问题。
   */
  struct my_error_mgr jerr;
  /* More stuff */
  FILE * jpg_file;        /* source file */
  JSAMPARRAY buffer;        /* Output row buffer */
  int row_stride;        /* physical row width in output buffer */

  /*在这个例子中,我们想在做任何事情之前打开输入文件,
    因此,下面的setjmp()错误恢复可以假设文件是打开的。
    *非常重要:使用“b”选项fopen(),如果你在一台机器上
    *需要它来读取二进制文件。
   */

  if ((jpg_file= fopen(jpgfile, "rb")) == NULL) {
    fprintf(stderr, "can't open jpgfile=%s\n", jpgfile);
    return 0;
  }

  //prepare for bmp write 
  if (NULL == (bmp_fp = fopen(bmpfile,"wb")))
  {
     fprintf(stderr, "can't open bmpfile=%s\n", bmpfile);
      return -1;
  }

  /* Step 1: 分配和初始化JPEG解压对象 */

  /* 我们设置了正常的JPEG错误例程,然后覆盖error_exit。 */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* 为my_error_exit创建要使用的setjmp返回上下文*/
  if (setjmp(jerr.setjmp_buffer)) {
    /* 如果我们到达这里,JPEG代码已经发出错误信号。
    *我们需要清理JPEG对象,关闭输入文件,然后返回。
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(jpg_file);
    return 0;
  }
  /*现在我们可以初始化JPEG解压对象。 */
  jpeg_create_decompress(&cinfo);

  /* Step 2:指定数据源(例如,文件) */

  jpeg_stdio_src(&cinfo, jpg_file);

  /* Step 3: 使用jpeg_read_header()读取文件参数 */

  (void) jpeg_read_header(&cinfo, TRUE);
  printf("w=%d,h=%d\n", cinfo.image_width, cinfo.image_height);

  create_bmp_header(&bmphead, &infohead, cinfo.image_width, cinfo.image_height);
  fwrite(&bmphead, 14, 1, bmp_fp);
  fwrite(&infohead, 40, 1, bmp_fp);
  /* 因为我们可以忽略jpeg_read_header的返回值
    * (a) stdio数据源不能暂停,且
    * (b)我们通过TRUE来拒绝一个只包含表的JPEG文件作为错误。
    *更多信息请参见libjpeg.doc。
   */

  /* Step 4:设置减压参数 */

  /* 在本例中,我们不需要更改所设置的任何缺省值
    * jpeg_read_header(),因此我们在这里什么也不做。
   */

  /* Step 5: 开始解压 */

  (void) jpeg_start_decompress(&cinfo);
  /*我们可以忽略返回值,因为暂停是不可能的
    *使用stdio数据源。
   */

  /* 在阅读之前,我们可能需要做一些自己的设置
    *数据。在jpeg_start_decompress()之后,我们得到了正确的缩放
    *输出图像尺寸可用,以及输出颜色映射
    如果我们要求颜色量化。
    *在本例中,我们需要使输出工作缓冲区的大小正确。
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* 创建一个一行高的示例数组,当处理完图像时,该数组将消失 */
  buffer = (*cinfo.mem->alloc_sarray)
        ((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Step 6: while (scan lines remain to be read) */
  /* jpeg_read_scanlines(...); */

  /* 这里我们使用库的状态变量cinfo。output_scanline作为
*循环计数器,这样我们就不用跟踪自己了。
   */
  while (cinfo.output_scanline < cinfo.output_height) {
    /*jpeg_read_scanlines需要指向scanlines的指针数组。
这里的数组只有一个元素长,但是你可以要求
*如果方便的话,一次使用多个扫描线。
     */
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    /* 假设put_scanline_somewhere需要一个指针和样本计数。 */
    //put_scanline_someplace(buffer[0], row_stride);
    fwrite(buffer[0],row_stride,1, bmp_fp);
  }
  fclose(bmp_fp);

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /*我们可以忽略返回值,因为暂停是不可能的
*使用stdio数据源。
   */

  /* Step 8: 释放JPEG解压对象*/

  /*  这是一个重要的步骤,因为它将释放大量内存。*/
  jpeg_destroy_decompress(&cinfo);

  /* 在finish_decompress之后,我们可以关闭输入文件。
    *在这里,我们推迟它,直到没有更多的JPEG错误是可能的,
    *以便简化上面的setjmp错误逻辑。(实际上,我不喜欢
    *认为jpeg_destroy可以执行错误退出,但是为什么要假设……)
   */
  fclose(jpg_file);

  /* 此时,您可能想要检查是否有任何损坏数据
    *警告发生(测试是否jerr.pub。num_warnings是零)。
   */

  /* And we're */
  return 1;
}


int main ( int argc, char *argv[] )
{
    //char filename[] = "./testimg.jpg";
    char jpgfile[] = "./640_480.jpg";
    char bmpfile[] = "./640_480.bmp";
    read_JPEG_file(bmpfile, jpgfile);
    printf("read over\n");
    return EXIT_SUCCESS;
}

测试以后效果不太好,有色调不一致的问题 ,再次先记录一下

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值