竹印

有梦即有路,自强可得之!
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

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

Posted on 2010-12-31 10:32  竹印  阅读(5519)  评论(0编辑  收藏  举报

大多的开源或不开源的软件处理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质量不满意,嘿嘿

 

// 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;
}

// 数据转换

// 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等常见的开源库均只提供了设定质量的函数,即

jpeg_set_quality()

 

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