现在的位置: 首页 > 综合 > 正文

BMP转JPG(法二)RGB数据经过YUV交织

2013年05月29日 ⁄ 综合 ⁄ 共 5063字 ⁄ 字号 评论关闭

转载请标明是引用于 http://blog.csdn.net/chenyujing1234 

欢迎大家拍砖!

 

源码下载地址:http://download.csdn.net/detail/chenyujing1234/4441643

编译平台:VS2005

 

在上一篇文章<<BMP转JPG(法一)VS2005环境下采用makefile编译、使用libjpeg.lib函数库>>

我们介绍了BMP转JPG的第一种方法,现在讲第二种方法。

 

 BMP原图:

JPG结果图:

第一步、获得JPEG编码需要的bmp数据结构并获得数据。

(1)获取BMP文件输出缓冲区信息这部分相对简单,就是从文件流读取BITMAPFILEHEADER信息与BITMAPINFOHEADER信息,获得8或16整数倍的宽与高;

它是通过GetBMBuffSize函数实现的。

// 获取BMP文件输出缓冲区信息
BMBUFINFO JEnc::GetBMBuffSize(FILE* pFile)
 {
  BITMAPFILEHEADER bmHead;					//文件头信息块 
  BITMAPINFOHEADER bmInfo;					//图像描述信息块
  BMBUFINFO   bmBuffInfo;
  UINT colSize = 0;
  UINT rowSize = 0;

  fseek(pFile,0,SEEK_SET);					//将读写指针指向文件头部
  fread(&bmHead,sizeof(bmHead),1,pFile);    //读取文件头信息块
  fread(&bmInfo,sizeof(bmInfo),1,pFile);    //读取位图信息块

  // 计算填充后列数,jpeg编码要求缓冲区的高和宽为8或16的倍数
  if (bmInfo.biWidth % 8 == 0)
  {
   colSize = bmInfo.biWidth;
  }
  else
  {
   colSize = bmInfo.biWidth + 8 - (bmInfo.biWidth % 8);
  }

  // 计算填充后行数
  if (bmInfo.biHeight % 8 == 0)
  {
   rowSize = bmInfo.biHeight;
  }
  else
  {
   rowSize = bmInfo.biHeight + 8 - (bmInfo.biHeight % 8);
  }

  bmBuffInfo.BitCount = 24;
  bmBuffInfo.buffHeight = rowSize;			// 缓冲区高
  bmBuffInfo.buffWidth = colSize;			// 缓冲区宽
  bmBuffInfo.imgHeight = bmInfo.biHeight;	// 图像高
  bmBuffInfo.imgWidth = bmInfo.biWidth;		// 图像宽

  return bmBuffInfo;
 }

(2)获得图像数据。如下图所示


 

第二步、将RGB信号转换为YUV信号

从上图读出的有效数据中取出R、G、B Byte,然后根据三个分量交织得到Y、U、V分量。

以下函数中pBuf为输入的RGB有效数据,输出的结果分别存在pYBuff、pUBuff、pVBuff中。

 // 转换色彩空间BGR-YUV,111采样
 void JEnc::BGR2YUV111(BYTE* pBuf, BYTE* pYBuff, BYTE* pUBuff, BYTE* pVBuff)
 {
  DOUBLE tmpY   = 0;         //临时变量
  DOUBLE tmpU   = 0;
  DOUBLE tmpV   = 0;
  BYTE tmpB   = 0;       
  BYTE tmpG   = 0;
  BYTE tmpR   = 0;
  UINT i    = 0;
  size_t elemNum = _msize(pBuf) / 3;  //缓冲长度

  for (i = 0; i < elemNum; i++)
  {
   tmpB = pBuf[i * 3];
   tmpG = pBuf[i * 3 + 1];
   tmpR = pBuf[i * 3 + 2];
   tmpY = 0.299 * tmpR + 0.587 * tmpG + 0.114 * tmpB;
   tmpU = -0.1687 * tmpR - 0.3313 * tmpG + 0.5 * tmpB + 128;
   tmpV = 0.5 * tmpR - 0.4187 * tmpG - 0.0813 * tmpB + 128;
   //if(tmpY > 255){tmpY = 255;}     //输出限制
   //if(tmpU > 255){tmpU = 255;}
   //if(tmpV > 255){tmpV = 255;}
   //if(tmpY < 0){tmpY = 0;}  
   //if(tmpU < 0){tmpU = 0;}  
   //if(tmpV < 0){tmpV = 0;}
   pYBuff[i] = tmpY;           //放入输入缓冲
   pUBuff[i] = tmpU;
   pVBuff[i] = tmpV;
  }
 }
第三步、将YUV信号分别分割为8x8的块
 //********************************************************************
 // 方法名称:DivBuff 
 // 最后修订日期:2003.5.3 
 //
 // 参数说明:
 // lpBuf:输入缓冲,处理后的数据也存储在这里
 // width:缓冲X方向长度
 // height:缓冲Y方向长度
 // xLen:X方向切割长度
 // yLen:Y方向切割长度
 //********************************************************************
void JEnc::DivBuff(BYTE* pBuf,UINT width,UINT height,UINT xLen,UINT yLen)
{
  UINT xBufs   = width / xLen;						//X轴方向上切割数量
  UINT yBufs   = height / yLen;						//Y轴方向上切割数量
  UINT tmpBufLen  = xBufs * xLen * yLen;			//计算临时缓冲区长度
  BYTE* tmpBuf  = new BYTE[tmpBufLen];				//创建临时缓冲
  UINT i    = 0;									//临时变量
  UINT j    = 0;
  UINT k    = 0; 
  UINT n    = 0;
  UINT bufOffset  = 0;								//切割开始的偏移量

  for (i = 0; i < yBufs; ++i)						//循环Y方向切割数量
  {
	n = 0;											//复位临时缓冲区偏移量
	for (j = 0; j < xBufs; ++j)						//循环X方向切割数量  
	{   
		bufOffset = yLen * xLen * i * xBufs + j * xLen;	//计算单元信号块的首行偏移量  
		for (k = 0; k < yLen; ++k)					//循环块的行数
		{
			memcpy(&tmpBuf[n],&pBuf[bufOffset],xLen);      //复制一行到临时缓冲
			n += xLen;								//计算临时缓冲区偏移量
			bufOffset += width;						//计算输入缓冲区偏移量
		}
	}
	memcpy(&pBuf[i * tmpBufLen],tmpBuf,tmpBufLen);  //复制临时缓冲数据到输入缓冲
  } 
  delete[] tmpBuf;									//删除临时缓冲
} 
第四步:寝化YUV量化表
 // 第四步:寝化YUV量化表
  SetQuantTable(std_Y_QT, YQT, Q);			// 设置Y量化表
  SetQuantTable(std_UV_QT,UVQT, Q);			// 设置UV量化表  
  InitQTForAANDCT();						// 初始化AA&N需要的量化表
  pVLITAB=VLI_TAB + 2047;                   // 设置VLI_TAB的别名
  BuildVLITable();							// 计算VLI表   
第五步:写入各段
  WriteSOI();              
  WriteAPP0();
  WriteDQT();
  WriteSOF();
  WriteDHT();
  WriteSOS();
第六步:计算Y/UV信号的交直分量的huffman表

这里使用标准的huffman表,并不是计算得出,缺点是文件略长,但是速度快

 BuildSTDHuffTab(STD_DC_Y_NRCODES,STD_DC_Y_VALUES,STD_DC_Y_HT);
  BuildSTDHuffTab(STD_AC_Y_NRCODES,STD_AC_Y_VALUES,STD_AC_Y_HT);
  BuildSTDHuffTab(STD_DC_UV_NRCODES,STD_DC_UV_VALUES,STD_DC_UV_HT);
  BuildSTDHuffTab(STD_AC_UV_NRCODES,STD_AC_UV_VALUES,STD_AC_UV_HT);
 第七步:处理单元数据
 //********************************************************************
 // 方法名称:ProcessData 
 //
 // 方法说明:处理图像数据FDCT-QUANT-HUFFMAN
 //
 // 参数说明:
 // lpYBuf:亮度Y信号输入缓冲
 // lpUBuf:色差U信号输入缓冲
 // lpVBuf:色差V信号输入缓冲
 //********************************************************************
 void JEnc::ProcessData(BYTE* lpYBuf,BYTE* lpUBuf,BYTE* lpVBuf)
 { 
  size_t yBufLen = _msize(lpYBuf);           //亮度Y缓冲长度
  size_t uBufLen = _msize(lpUBuf);           //色差U缓冲长度          
  size_t vBufLen = _msize(lpVBuf);           //色差V缓冲长度
  FLOAT dctYBuf[DCTBLOCKSIZE];            //Y信号FDCT编码临时缓冲
  FLOAT dctUBuf[DCTBLOCKSIZE];            //U信号FDCT编码临时缓冲 
  FLOAT dctVBuf[DCTBLOCKSIZE];            //V信号FDCT编码临时缓冲 
  UINT mcuNum   = 0;             //存放MCU的数量 
  SHORT yDC   = 0;             //Y信号的当前块的DC
  SHORT uDC   = 0;             //U信号的当前块的DC
  SHORT vDC   = 0;             //V信号的当前块的DC 
  BYTE yCounter  = 0;             //YUV信号各自的写入计数器
  BYTE uCounter  = 0;
  BYTE vCounter  = 0;
  UINT i    = 0;             //临时变量              
  UINT j    = 0;                 
  UINT k    = 0;
  UINT p    = 0;
  UINT m    = 0;
  UINT n    = 0;
  UINT s    = 0; 

  mcuNum = (this->buffHeight * this->buffWidth * 3)
   / (DCTBLOCKSIZE * 3);         //计算MCU的数量

  for (p = 0;p < mcuNum; p++)        //依次生成MCU并写入
  {
   yCounter = 1;//MCUIndex[SamplingType][0];   //按采样方式初始化各信号计数器
   uCounter = 1;//MCUIndex[SamplingType][1];
   vCounter = 1;//MCUIndex[SamplingType][2];

   for (; i < yBufLen; i += DCTBLOCKSIZE)
   {
    for (j = 0; j < DCTBLOCKSIZE; j++)
    {
     dctYBuf[j] = FLOAT(lpYBuf[i + j] - 128);
    }   
    if (yCounter > 0)
    {    
     --yCounter;
     ProcessDU(dctYBuf,YQT_DCT,STD_DC_Y_HT,STD_AC_Y_HT,&yDC);     
    }
    else
    {
     break;
    }
   }  
   //------------------------------------------------------------------  
   for (; m < uBufLen; m += DCTBLOCKSIZE)
   {
    for (n = 0; n < DCTBLOCKSIZE; n++)
    {
     dctUBuf[n] = FLOAT(lpUBuf[m + n] - 128);
    }    
    if (uCounter > 0)
    {    
     --uCounter;
     ProcessDU(dctUBuf,UVQT_DCT,STD_DC_UV_HT,STD_AC_UV_HT,&uDC);         
    }
    else
    {
     break;
    }
   }  
   //-------------------------------------------------------------------  
   for (; s < vBufLen; s += DCTBLOCKSIZE)
   {
    for (k = 0; k < DCTBLOCKSIZE; k++)
    {
     dctVBuf[k] = FLOAT(lpVBuf[s + k] - 128);
    }
    if (vCounter > 0)
    {
     --vCounter;
     ProcessDU(dctVBuf,UVQT_DCT,STD_DC_UV_HT,STD_AC_UV_HT,&vDC);        
    }
    else
    {
     break;
    }
   }  
  } 
 }

 

抱歉!评论已关闭.