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;
- }
// 获取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;
- }
- }
// 转换色彩空间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; //删除临时缓冲
- }
//********************************************************************
// 方法名称: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表
// 第四步:寝化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();
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);
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;
- }
- }
- }
- }