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

来源:互联网 发布:红警for mac 编辑:程序博客网 时间:2024/06/07 09:34

转载请标明是引用于 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;    }   }    }  }


 

原创粉丝点击