基本思路:

      利用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;
}