关于libjpeg修改图片质量的讨论

大多的开源或不开源的软件处理jpg图像的时候均使用libjpeg开源库,目前最新版本为libjpeg-8b,下载链接为

http://freshmeat.net/projects/libjpeg


里面很多makefile文件,linux下不多说,在win32下,构建vc6工程只要将.vc6后缀名搜索出,将make*dsp.vc6修改为make*dsp.dsp,所有make*dsw.vc6修改为make*dsw.dsw,这里得到makeadsw.dsw和makejdsw.dsw,前者为所有编解码及测试程序工程,后者为简单的libjpeg工程。对于libjpeg的makejdsw生成了一个win32的的.lib库,这里可将库名称修改为libjpeg.lib。这样我们可参考编码程序将图片编码为jpeg图片了。

比如,现在我们利用GDI+打开任意格式图片,而后得到图片的解码数据,便可编译为jpeg图片了。

ps:为何不用GDI+直接保存jpg图片,因GDI+中生成的jpeg质量不满意,嘿嘿

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// jpeg
void  write_my_jpeg()
{
     // 初始化数据
     Bitmap bitmap(_T( "a.bmp" ));
     int  iWidth = bitmap.GetWidth();
     int  iHeight = bitmap.GetHeight();
 
     unsigned char  *pDataConv = new  unsigned char [iWidth * iHeight * 3];
 
     init_data(bitmap, pDataConv);
 
     struct  jpeg_compress_struct jcs;
 
     // 声明错误处理器,并赋值给jcs.err域
     struct  jpeg_error_mgr jem;
     jcs.err = jpeg_std_error(&jem);
 
     jpeg_create_compress(&jcs);
 
     FILE  * f = _wfopen(_T( "my.jpg" ), _T( "wb" ));
     if  (f==NULL)
     {
         delete  [] pDataConv;
         return ;
     }
     jpeg_stdio_dest(&jcs, f);
 
 
     jcs.image_width = iWidth;    // 为图的宽和高,单位为像素
     jcs.image_height = iHeight;
     jcs.input_components = 3;   // 在此为1,表示灰度图, 如果是彩色位图,则为3
     jcs.in_color_space = JCS_RGB; //JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像
     
 
     jpeg_set_defaults(&jcs);
 
    // 指定亮度及色度质量
     jcs.q_scale_factor[0] = jpeg_quality_scaling(100);
     jcs.q_scale_factor[1] = jpeg_quality_scaling(100);
 
         // 图像采样率,默认为2 * 2
    jcs.comp_info[0].v_samp_factor = 1;
     jcs.comp_info[0].h_samp_factor = 1;
 
         // 设定编码jpeg质量
     jpeg_set_quality (&jcs, 100, true );
 
     jpeg_start_compress(&jcs, TRUE);
 
     JSAMPROW row_pointer[1];   // 一行位图
     int  row_stride;      // 每一行的字节数
 
     row_stride = jcs.image_width * 3; // 如果不是索引图,此处需要乘以3
 
     // 对每一行进行压缩
     while  (jcs.next_scanline < jcs.image_height)
        {
         row_pointer[0] = (JSAMPROW)(pDataConv + jcs.next_scanline * row_stride);   
         jpeg_write_scanlines(&jcs, row_pointer, 1);
     }
 
     jpeg_finish_compress(&jcs);
 
     jpeg_destroy_compress(&jcs);
 
     //
     delete  [] pDataConv;
}

// 数据转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// data
void  init_data(Bitmap &bitmap, unsigned char  *pDataConv)
{  
     // Btimap
     
     int  iWidth = bitmap.GetWidth();
     int  iHeight = bitmap.GetHeight();
     Gdiplus::BitmapData bitmapData;
     Gdiplus::Rect rectPiece(0, 0, iWidth, iHeight);
     // lock,获取Bitmap数据
     bitmap.LockBits(&rectPiece, Gdiplus::ImageLockModeWrite,
         PixelFormat32bppARGB, &bitmapData);
     int  iBitmapPieceStride = bitmapData.Stride;
     unsigned long  *pBitmapPiecePixels = (unsigned long *)bitmapData.Scan0;
     // 获取像素点数据
     unsigned long  ulColorValueTemp = 0;
     unsigned char  usR, usG, usB;
 
     for  ( int  y = 0; y != iHeight; ++y)
     {
         for  ( int  x = 0; x != iWidth; ++x)
         {
             ulColorValueTemp = pBitmapPiecePixels[y * iBitmapPieceStride / 4 + x];
 
             usR = (ulColorValueTemp >> 16);
             usG = (ulColorValueTemp >> 8);
             usB = ulColorValueTemp;
 
             *pDataConv = usR;
             ++pDataConv;
             *pDataConv = usG;
             ++pDataConv;
             *pDataConv = usB;
             ++pDataConv;
         }
     }
     //
//  memcpy(pDataConv, pBitmapPiecePixels, iWidth * iHeight * 3);
 
     bitmap.UnlockBits(&bitmapData);
}

默认情况下,不管是GDI+,还是CxImage,OpenCV等常见的开源库均只提供了设定质量的函数,即

1
jpeg_set_quality()

 

正常情况下,这样也可以满足我们的基本需求,但对于有些图片,比如黑色背景,写白色字体的图片,在压缩时,基本设定质量最高为100,但压缩质量仍不满意,原因便在压缩时的图像采样率上,ASDSee保存jpeg图片格式时,有选择采样率的2 * 1和1* 2,而libjpeg默认为2 * 2,因此会发现ACDSee质量要好些的缘故,如果不介意,可以直接设定为1 * 1,质量会更好些,不过对于这样的图片压缩后的图片大小可能大于原bmp图片大小,具体情况请读者自行参考。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要裁剪一张图片,可以使用libjpeg库提供的函数进行操作。以下是一个简单的例子: ```c #include <stdio.h> #include <stdlib.h> #include <jpeglib.h> int main(int argc, char **argv) { if (argc != 6) { fprintf(stderr, "Usage: %s <input file> <output file> <x> <y> <width> <height>\n", argv[0]); exit(1); } const char *input_file = argv[1]; const char *output_file = argv[2]; int x = atoi(argv[3]); int y = atoi(argv[4]); int width = atoi(argv[5]); int height = atoi(argv[6]); FILE *infile = fopen(input_file, "rb"); if (!infile) { fprintf(stderr, "Failed to open input file %s\n", input_file); exit(1); } struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); jpeg_create_decompress(&cinfo); jpeg_stdio_src(&cinfo, infile); jpeg_read_header(&cinfo, TRUE); jpeg_start_decompress(&cinfo); int row_stride = cinfo.output_width * cinfo.output_components; JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1); FILE *outfile = fopen(output_file, "wb"); if (!outfile) { fprintf(stderr, "Failed to open output file %s\n", output_file); exit(1); } struct jpeg_compress_struct cinfo_out; struct jpeg_error_mgr jerr_out; cinfo_out.err = jpeg_std_error(&jerr_out); jpeg_create_compress(&cinfo_out); jpeg_stdio_dest(&cinfo_out, outfile); cinfo_out.image_width = width; cinfo_out.image_height = height; cinfo_out.input_components = cinfo.output_components; cinfo_out.in_color_space = cinfo.out_color_space; jpeg_set_defaults(&cinfo_out); jpeg_start_compress(&cinfo_out, TRUE); while (cinfo.output_scanline < cinfo.output_height) { int row = cinfo.output_scanline; if (row >= y && row < y + height) { jpeg_read_scanlines(&cinfo, buffer, 1); int offset = (row - y) * row_stride + x * cinfo.output_components; jpeg_write_scanlines(&cinfo_out, buffer + offset, 1); } else { jpeg_skip_scanlines(&cinfo, 1); } } jpeg_finish_decompress(&cinfo); jpeg_destroy_decompress(&cinfo); fclose(infile); jpeg_finish_compress(&cinfo_out); jpeg_destroy_compress(&cinfo_out); fclose(outfile); return 0; } ``` 这个程序从命令行接收输入文件名、输出文件名、裁剪的x、y坐标和宽度、高度参数。它使用libjpeg库从输入文件中读取图像,裁剪它,并将结果写入输出文件中。 要编译这个程序,需要安装libjpeg库,并使用以下命令: ``` gcc -o jpeg_crop jpeg_crop.c -ljpeg ``` 然后可以使用以下命令来裁剪一张图片: ``` ./jpeg_crop input.jpg output.jpg 100 100 200 200 ``` 这将从输入文件中裁剪一个200x200像素的矩形,从左上角的坐标(100, 100)开始,并将结果写入输出文件中。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值