基本思路:
利用libjpeg库实现对jpg文件的解压缩,并将数据按bmp(从下到上,从左到右,BGR)存储保存)。
注意事项:
1:bmp数据存储时是按照BGR顺序
2:biHeight为正数时表示倒向的位图,读取的顺序为(从左->右,从下->上)
3:Windows在进行行扫描的时候最小的单位为4个字节,所以当每行字节数不为4的正数倍时要对字节数进行调整。缺省是每行补0
4:图像位图头信息中的高宽是实际图像的像素点的个数,与存储时需要调整对其数据使其满足4字节的倍数无关
实验内容:
步骤一:利用libjpeg库实现对jpg文件的解压缩并提取数据
FILE *fJpeg;//目标jpeg文件的句柄
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);//声明并初始化解压缩对象
row_stride = cinfo.output_width * cinfo.output_components; //计算每行所需的空间,字节为单位
while (cinfo.output_scanline < cinfo.output_height)
{
int line=cinfo.output_scanline;//当前行数
(void) jpeg_read_scanlines(&cinfo, &jpgbuf, 1);//执行该操作读取第line行数据,cinfo.output_scanline将加一,指向下一个要扫描的行
for(int i=0;i< cinfo.output_width;i++)//循环将存储在jpgbuf缓存区的数据放入data中
{
data[line*row_stride+i*cinfo.output_components+0]=jpgbuf[i*3];
data[line*row_stride+i*cinfo.output_components+1]=jpgbuf[i*3+1];
data[line*row_stride+i*cinfo.output_components+2]=jpgbuf[i*3+2];
}
}
fJpeg=fopen(JpegName,"rb");//二进制模式读取jpeg文件
jpeg_stdio_src(&cinfo, fJpeg);//指定解压对象的源文件
jpeg_read_header(&cinfo, TRUE);//读取文件信息,将图像的缺省的信息填充到cinfo结构中比便程序使用
jpeg_start_decompress(&cinfo);//开始接压缩
data=(unsigned char *)malloc(cinfo.output_width* cinfo.output_components*cinfo.output_width);//动态分配数据存储内存
memset(data,0,cinfo.output_width*cinfo.output_width*cinfo.output_components);//设置图像数据初值为0
jpgbuf = (unsigned char *) malloc(cinfo.output_width *cinfo.output_components);//动态分配缓存内存
memset(jpgbuf,0,cinfo.output_width*cinfo.output_components);//为缓存内存设置初值
步骤二:对将数据写入bmp文件中(必须按bmp文件存储数据的顺序进行存储)
//填充位图信息头
infoheader.biSize=sizeof(infoheader);//位图信息头结构的长度,为40
infoheader.biBitCount=24;//设置为真彩图
infoheader.biHeight=cinfo.image_height;
infoheader.biWidth=cinfo.image_width;
infoheader.biCompression=BI_RGB;//一般默认为BI_RGB格式表示不压缩
infoheader.biPlanes=1;//表示不用考虑
infoheader.biXPelsPerMeter=0;
infoheader.biYPelsPerMeter=0;
infoheader.biClrUsed=0;//在MSDN中队RGB位图定义可知默认值为0
//将文件 头和位图信息头写入文件中
fNewBmp=fopen(NewBmpName,"wb");//二进制写的形式打开文件
if(!fNewBmp)
{
cout<<"cannot open the NewBmpFile"<<endl;
return;
}
fwrite(&header,sizeof(header),1,fNewBmp);//写入文件头
fwrite(&infoheader,sizeof(infoheader),1,fNewBmp);//写入位图信息头
//由于BMP扫描方式为从上到下,从左到右,window扫描要满足4字节的倍数。读data数据进行修改,存储为BGR顺序
int tmpRow=((infoheader.biWidth*3+3)>>2)<<2;//调整没行的字节数,使其是4的倍数
#ifdef DEBUG__
cout<<"((infoheader.biWidth*3+3)>>2)<<2 IS "<<(((infoheader.biWidth*3+3)>>2)<<2)<<endl;
cout<<"infoheader.biWidth*3 IS "<<infoheader.biWidth*3<<endl;
#endif
jpgbuf = (unsigned char *) malloc(tmpRow);//动态分配缓存内存
memset(jpgbuf,0,tmpRow);//为缓存内存设置初值
for(int i=0;i<infoheader.biHeight;i++)
{
for(int j=0;j<infoheader.biWidth;j++)//将从下到上从左到右读取数据,并将RGB顺寻装换为BGR顺序
{
jpgbuf[3*j+0]=data[(infoheader.biHeight-1-i)*row_stride+3*j+2];
jpgbuf[3*j+1]=data[(infoheader.biHeight-1-i)*row_stride+3*j+1];
jpgbuf[3*j+2]=data[(infoheader.biHeight-1-i)*row_stride+3*j+0];
#ifdef DEBUG__
// printf("data(%d,%d,%d),buf(%d,%d,%d)",data[(infoheader.biHeight-1-i)*row_stride+3*j+0],data[(infoheader.biHeight-1-i)*row_stride+3*j+1],data[(infoheader.biHeight-1-i)*row_stride+3*j+2], jpgbuf[3*j+0], jpgbuf[3*j+1], jpgbuf[3*j+2]);
#endif
}
fwrite(jpgbuf,sizeof(unsigned char),tmpRow,fNewBmp);//每行的数据写入文件中
}
源文件:
void JpegToBmp( char *JpegName,char * NewBmpName)//将读取jpeg中的数据并保存为BMP格式的文件,生成的都是24位真彩图,不需要调色板
{
FILE *fNewBmp;//存储生成的Bmp文件的句柄
FILE *fJpeg;//目标jpeg文件的句柄
unsigned char *data; //存放解压后的数据
unsigned char *jpgbuf; //存放解压后一行图像数据
BITMAPFILEHEADER header;//存储文件头
memset(&header, 0, sizeof(header));//文件头赋初始值
BITMAPINFOHEADER infoheader;//位图信息头
memset(&infoheader, 0, sizeof(infoheader));//赋初始值
int row_stride; //定义每行的字节数
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_decompress(&cinfo);//声明并初始化解压缩对象
fJpeg=fopen(JpegName,"rb");//二进制模式读取jpeg文件
if(fJpeg==NULL) //二进制模式读取
{
printf("error: cannot open the file\n");
return ;
}//打开jpeg图片
jpeg_stdio_src(&cinfo, fJpeg);//指定解压对象的源文件
jpeg_read_header(&cinfo, TRUE);//读取文件信息,将图像的缺省的信息填充到cinfo结构中比便程序使用
jpeg_start_decompress(&cinfo);//开始接压缩
data=(unsigned char *)malloc(cinfo.output_width* cinfo.output_components*cinfo.output_width);//动态分配数据存储内存
memset(data,0,cinfo.output_width*cinfo.output_width*cinfo.output_components);//设置图像数据初值为0
jpgbuf = (unsigned char *) malloc(cinfo.output_width *cinfo.output_components);//动态分配缓存内存
memset(jpgbuf,0,cinfo.output_width*cinfo.output_components);//为缓存内存设置初值
row_stride = cinfo.output_width * cinfo.output_components; //计算每行所需的空间,字节为单位
while (cinfo.output_scanline < cinfo.output_height)
{
int line=cinfo.output_scanline;//当前行数
(void) jpeg_read_scanlines(&cinfo, &jpgbuf, 1);//执行该操作读取第line行数据,cinfo.output_scanline将加一,指向下一个要扫描的行
for(int i=0;i< cinfo.output_width;i++)//循环将存储在jpgbuf缓存区的数据放入data中
{
data[line*row_stride+i*cinfo.output_components+0]=jpgbuf[i*3];
data[line*row_stride+i*cinfo.output_components+1]=jpgbuf[i*3+1];
data[line*row_stride+i*cinfo.output_components+2]=jpgbuf[i*3+2];
#ifdef SHOWDATA__
printf("(%d,%d,%d),(%d,%d)",jpgbuf[i*3],jpgbuf[i*3+1],jpgbuf[i*3+2],line,i);//打印图像数据
#endif
}
}
free(jpgbuf);
//填充文件头信息
header.bfType= 0x4D42;//设置为“BM”
header.bfSize=sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)+cinfo.output_width* cinfo.output_components*cinfo.output_width ;
header.bfOffBits=sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
#ifdef DEBUG__
cout<<"header.bfType IS "<<(char)header.bfType<<endl;
cout<<"header.bfSize IS "<<header.bfSize<<endl;
cout<<"header.bfOffBits IS "<<header.bfOffBits<<endl;
//printf("sizeof(data) IS %d.\n",cinfo.output_width* cinfo.output_components*cinfo.output_width );
#endif
//填充位图信息头
infoheader.biSize=sizeof(infoheader);//位图信息头结构的长度,为40
infoheader.biBitCount=24;//设置为真彩图
infoheader.biHeight=cinfo.image_height;
infoheader.biWidth=cinfo.image_width;
infoheader.biCompression=BI_RGB;//一般默认为BI_RGB格式表示不压缩
infoheader.biPlanes=1;//表示不用考虑
infoheader.biXPelsPerMeter=0;
infoheader.biYPelsPerMeter=0;
infoheader.biClrUsed=0;//在MSDN中队RGB位图定义可知默认值为0
//将文件 头和位图信息头写入文件中
fNewBmp=fopen(NewBmpName,"wb");//二进制写的形式打开文件
if(!fNewBmp)
{
cout<<"cannot open the NewBmpFile"<<endl;
return;
}
fwrite(&header,sizeof(header),1,fNewBmp);//写入文件头
fwrite(&infoheader,sizeof(infoheader),1,fNewBmp);//写入位图信息头
//由于BMP扫描方式为从上到下,从左到右,window扫描要满足4字节的倍数。读data数据进行修改,存储为BGR顺序
int tmpRow=((infoheader.biWidth*3+3)>>2)<<2;//调整没行的字节数,使其是4的倍数
#ifdef DEBUG__
cout<<"((infoheader.biWidth*3+3)>>2)<<2 IS "<<(((infoheader.biWidth*3+3)>>2)<<2)<<endl;
cout<<"infoheader.biWidth*3 IS "<<infoheader.biWidth*3<<endl;
#endif
jpgbuf = (unsigned char *) malloc(tmpRow);//动态分配缓存内存
memset(jpgbuf,0,tmpRow);//为缓存内存设置初值
for(int i=0;i<infoheader.biHeight;i++)
{
for(int j=0;j<infoheader.biWidth;j++)//将从下到上从左到右读取数据,并将RGB顺寻装换为BGR顺序
{
jpgbuf[3*j+0]=data[(infoheader.biHeight-1-i)*row_stride+3*j+2];
jpgbuf[3*j+1]=data[(infoheader.biHeight-1-i)*row_stride+3*j+1];
jpgbuf[3*j+2]=data[(infoheader.biHeight-1-i)*row_stride+3*j+0];
#ifdef DEBUG__
// printf("data(%d,%d,%d),buf(%d,%d,%d)",data[(infoheader.biHeight-1-i)*row_stride+3*j+0],data[(infoheader.biHeight-1-i)*row_stride+3*j+1],data[(infoheader.biHeight-1-i)*row_stride+3*j+2], jpgbuf[3*j+0], jpgbuf[3*j+1], jpgbuf[3*j+2]);
#endif
}
fwrite(jpgbuf,sizeof(unsigned char),tmpRow,fNewBmp);//每行的数据写入文件中
}
jpeg_finish_decompress(&cinfo);//完成解压过程
jpeg_destroy_decompress(&cinfo);//释放cinfo
fclose(fJpeg);
free(data);
free(jpgbuf);
fclose(fNewBmp);
return;
}