基本思路:利用开源库libjpeg实现将bmp图像转换为jpeg图像,并可以设置jpeg压缩的质量因子。
实验注意事项:
1:bmp数据存储时是按照BGR顺序。
2:biHeight为正数时表示倒向的位图,读取的顺序为(从左->右,从下->上)
3:Windows在进行行扫描的时候最小的单位为4个字节,所以当每行字节数不为4的正数倍时要对字节数进行调整。缺省是每行补0
4:图像位图头信息中的高宽是实际图像的像素点的个数,与存储时需要调整对其数据使其满足4字节的倍数无关。
预备知识:
1:bmp格式
参照网址:http://blog.youkuaiyun.com/lesky/article/details/22188502
2:利用libjpeg实现jpeg压缩。
参照:http://hi.baidu.com/d_south/blog/item/621285d494e41c09a08bb706.html
3:libjpeg库参照:
库源文件获取:http://libjpeg.sourceforge.net/
编译:http://blog.youkuaiyun.com/subfate/article/details/7037159
实验内容
步骤一:读取bmp文件,并将数据调整为从从上到下、从左到右、RGB顺序。
1:读取bmp文件头和位图头信息
FILE *fBmp;//bmp文件的句柄
BITMAPFILEHEADER header;//存储文件头
memset(&header, 0, sizeof(header));//文件头赋初始值
BITMAPINFOHEADER infoheader;//位图信息头
memset(&infoheader, 0, sizeof(infoheader));//赋初始值
fBmp = fopen(BmpName ,"rb");// 打开要压缩的源文件
fread(&header,sizeof(unsigned char),sizeof(header),fBmp);//读取头文件
fread(&infoheader,sizeof(unsigned char),sizeof(infoheader),fBmp);//读取位图信息头
m_iImageWidth = infoheader.biWidth;
m_iImageHeight = infoheader.biHeight;
m_iBitsPerPixel = infoheader.biBitCount;
m_iBytesPerPixel=m_iBitsPerPixel/8;//每个像素点所占的字节数
//m_iLineByteCnt=m_iImageWidth*m_iBytesPerPixel;
m_iLineByteCnt=((m_iImageWidth*m_iBytesPerPixel+3)>>2)<<2;//调整每行的字节数为4的整数倍
重点:m_iLineByteCnt=((m_iImageWidth*m_iBytesPerPixel+3)>>2)<<2;//调整每行的字节数为4的整数倍
2:读取并调整数据的存储顺序
m_pLineData=new unsigned char [m_iLineByteCnt];//动态分配空间存储每行图像数据
m_iImageDataSize=m_iLineByteCnt* m_iImageHeight;
m_pImageData= new unsigned char[m_iImageDataSize];//动态分配空间存储图像数据
if((m_iBytesPerPixel==3)&&( m_iImageHeight >0))//暂时只处理3通道bmp图像且倒向存储
{
for(int scanLine=0;scanLine<m_iImageHeight;scanLine++)
{
fread(m_pLineData,sizeof(unsigned char),m_iLineByteCnt ,fBmp);//读取第一行数据
for(int tmpCol=0;tmpCol<m_iImageWidth;tmpCol++ )
{
m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+0]=m_pLineData[tmpCol*3+2];
m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+1]=m_pLineData[tmpCol*3+1];
m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+2]=m_pLineData[tmpCol*3+0];
}//调整数据存储顺序(BGR->RGB,从下到上->从上到下)
}
}
步骤二:对步骤一得到的数据进行jpeg压缩,将调用libjpeg库
1:参数的设置等准备
/使用IJP提供的libjpeg库对数据进行压缩///
jpeg_create_compress (&cinfo);// 初始化jpeg压缩对象
fJpeg=fopen(JpegName,"wb");
if(!fJpeg)
{
printf("ERROR1: Can not open the jpeg image .\n");
return NULL;
}
jpeg_stdio_dest(&cinfo, fJpeg); //指定压缩后的图像所存放的目标文件 ,二进制打开
// 设置压缩参数,主要参数有图像宽、高、色彩通道数(1:索引图像,3:其他),
// 色彩空间(JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像),压缩质量
cinfo.image_width = m_iImageWidth; // 为图的宽和高,单位为像素
cinfo.image_height = m_iImageHeight;
cinfo.input_components = 3; // 在此为1,表示灰度图, 如果是彩色位图,则为3
cinfo.in_color_space = JCS_RGB; //JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像
jpeg_set_defaults(&cinfo); // 调用jpeg_set_defaults函数后,jpeglib库采用默认的设置对图像进行压缩
jpeg_set_quality(&cinfo, QUALITY_FACTOR , TRUE ); // jpeglib库采用默认的设置对图像进行压缩
jpeg_start_compress(&cinfo, TRUE);
2:逐行写入数据(核心)
while (cinfo.next_scanline < cinfo.image_height)
{
for(int j=0;j<m_iLineByteCnt;j++)
{
m_pLineData[j]=m_pImageData[cinfo.next_scanline*m_iLineByteCnt+j];
}
row_pointer[0]=m_pLineData;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
}
jpeg_finish_compress(&cinfo);
源代码:
unsigned char * BmpToJpeg(char * BmpName,char * JpegName,int QUALITY_FACTOR)
{
FILE *fBmp;//bmp文件的句柄
FILE *fJpeg;//目标jpeg文件的句柄
BITMAPFILEHEADER header;//存储文件头
memset(&header, 0, sizeof(header));//文件头赋初始值
BITMAPINFOHEADER infoheader;//位图信息头
memset(&infoheader, 0, sizeof(infoheader));//赋初始值
long m_iImageWidth=0;
long m_iImageHeight=0;
WORD m_iBitsPerPixel=0;
WORD m_iBytesPerPixel=0;
int m_iLineByteCnt=0;//每行的字节数
int m_iImageDataSize=0;//图像数据的总字节数
unsigned char * m_pImageData;//存放RGB数据
unsigned char * m_pLineData;//存储每行的数据
struct jpeg_compress_struct cinfo;//申请并初始化jpeg压缩对象
struct jpeg_error_mgr jerr;
JSAMPROW row_pointer[1]; // 一行位图, 指向JSAMPLE row[s]
cinfo.err=jpeg_std_error (&jerr);
fBmp = fopen(BmpName ,"rb");// 打开要压缩的源文件
if(!fBmp)
{
printf("ERROR1: Can not open the bmp image.\n");
return NULL;
}
fread(&header,sizeof(unsigned char),sizeof(header),fBmp);//读取头文件
fread(&infoheader,sizeof(unsigned char),sizeof(infoheader),fBmp);//读取位图信息头
if(header.bfType != 0x4D42)
{
cout<<"the type of img is not BMP"<<endl;
return NULL;
}//通过头文件判断是否是bmp格式
m_iImageWidth = infoheader.biWidth;
m_iImageHeight = infoheader.biHeight;
m_iBitsPerPixel = infoheader.biBitCount;
m_iBytesPerPixel=m_iBitsPerPixel/8;//每个像素点所占的字节数
//m_iLineByteCnt=m_iImageWidth*m_iBytesPerPixel;
m_iLineByteCnt=((m_iImageWidth*m_iBytesPerPixel+3)>>2)<<2;//调整每行的字节数为4的整数倍
#ifdef DEBUG__
cout<<"m_iLineByteCnt IS "<<m_iLineByteCnt<<endl;
#endif
m_pLineData=new unsigned char [m_iLineByteCnt];//动态分配空间存储每行图像数据
m_iImageDataSize=m_iLineByteCnt* m_iImageHeight;
m_pImageData= new unsigned char[m_iImageDataSize];//动态分配空间存储图像数据
#ifdef DEBUG__
cout<<"header.bfType IS "<<header.bfType<<endl;
cout<<"infoheader.biSize IS "<<infoheader.biSize<<endl;
cout<<"infoheader.biWidth IS "<<infoheader.biWidth<<endl;
cout<<"infoheader.biHeight IS "<<infoheader.biHeight<<endl;
cout<<"infoheader.biBitCount IS "<<infoheader.biBitCount<<endl;
cout<<"m_iLineByteCnt IS "<<m_iLineByteCnt<<endl;
#endif
if((m_iBytesPerPixel==3)&&( m_iImageHeight >0))//暂时只处理3通道bmp图像且倒向存储
{
for(int scanLine=0;scanLine<m_iImageHeight;scanLine++)
{
fread(m_pLineData,sizeof(unsigned char),m_iLineByteCnt ,fBmp);//读取第一行数据
for(int tmpCol=0;tmpCol<m_iImageWidth;tmpCol++ )
{
m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+0]=m_pLineData[tmpCol*3+2];
m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+1]=m_pLineData[tmpCol*3+1];
m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+2]=m_pLineData[tmpCol*3+0];
#ifdef DEBUG__
// printf("(%d,%d,%d)(%d,%d,%d)",m_pLineData[tmpCol*3+0],m_pLineData[tmpCol*3*3+1],m_pLineData[tmpCol*3+2],m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+0],m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+1],m_pImageData[(m_iImageHeight-1-scanLine)*m_iLineByteCnt+tmpCol*3+2]);
#endif
}//调整数据存储顺序(BGR->RGB,从下到上->从上到下)
}
}
/使用IJP提供的libjpeg库对数据进行压缩///
jpeg_create_compress (&cinfo);// 初始化jpeg压缩对象
fJpeg=fopen(JpegName,"wb");
if(!fJpeg)
{
printf("ERROR1: Can not open the jpeg image .\n");
return NULL;
}
jpeg_stdio_dest(&cinfo, fJpeg); //指定压缩后的图像所存放的目标文件 ,二进制打开
// 设置压缩参数,主要参数有图像宽、高、色彩通道数(1:索引图像,3:其他),
// 色彩空间(JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像),压缩质量
cinfo.image_width = m_iImageWidth; // 为图的宽和高,单位为像素
cinfo.image_height = m_iImageHeight;
cinfo.input_components = 3; // 在此为1,表示灰度图, 如果是彩色位图,则为3
cinfo.in_color_space = JCS_RGB; //JCS_GRAYSCALE表示灰度图,JCS_RGB表示彩色图像
jpeg_set_defaults(&cinfo); // 调用jpeg_set_defaults函数后,jpeglib库采用默认的设置对图像进行压缩
jpeg_set_quality(&cinfo, QUALITY_FACTOR , TRUE ); // jpeglib库采用默认的设置对图像进行压缩
jpeg_start_compress(&cinfo, TRUE);
while (cinfo.next_scanline < cinfo.image_height)
{
#ifdef DEBUG__
//cout<<"cinfo.next_scanline before jpeg_write_scanlines IS "<<cinfo.next_scanline<<endl;
#endif
for(int j=0;j<m_iLineByteCnt;j++)
{
m_pLineData[j]=m_pImageData[cinfo.next_scanline*m_iLineByteCnt+j];
}
row_pointer[0]=m_pLineData;
jpeg_write_scanlines(&cinfo, row_pointer, 1);
#ifdef DEBUG__
//cout<<"cinfo.next_scanline after jpeg_write_scanlines IS "<<cinfo.next_scanline<<endl;
#endif
}
jpeg_finish_compress(&cinfo);
free(m_pLineData);//释放内存
fclose(fJpeg);
fclose(fBmp);
return m_pImageData;//返回存储RGB的数据的指针
}
相关文章: