?? jpegbianma.txt
字號:
simplejpegenc.h
/*
這是一個簡單的jpeg編碼程序,支持1:1:1采樣的baseline彩色jpeg,輸入只能是24bit的BMP文件
代碼結構只求能說明各步驟過程,并不做特別的優化,效率較為一般。
*/
#ifndef __JENC__
#define __JENC__
#include <string>
#include <windows.h>
#include <stdio.h>
#include <malloc.h>
#include <math.h>
#include "jpeg.h"
#include "jpegformat.h"
using namespace std;
class JEnc
{
public:
// bmfile:輸入文件
// jpgfile:輸出文件
// Q:質量
void Invoke(string bmFile, string jpgFile, long Q)
{
FILE* pFile; // 輸入文件句柄
if ((pFile = fopen(bmFile.c_str(),"rb")) == NULL) // 打開文件
{
throw("open bmp file error.");
}
// 獲取jpeg編碼需要的bmp數據結構,jpeg要求數據緩沖區的高和寬為8或16的倍數(視采樣方式而定)
BMBUFINFO bmBuffInfo = GetBMBuffSize(pFile);
imgWidth = bmBuffInfo.imgWidth; // 圖像寬
imgHeight = bmBuffInfo.imgHeight; // 圖像高
buffWidth = bmBuffInfo.buffWidth; // 緩沖寬
buffHeight = bmBuffInfo.buffHeight; // 緩沖高
size_t buffSize = buffHeight * buffWidth * 3; // 緩沖長度,因為是24bits,所以*3
BYTE* bmData = new BYTE[buffSize]; // 申請內存空間
GetBMData(pFile, bmData, bmBuffInfo); // 獲取數據
fclose(pFile); // 關閉文件
//=====================================
// 計算編碼需要的緩沖區,RGB信號需要別分別編碼,所以需要3個緩沖區,這里只是1:1:1所以是一樣大
size_t yuvBuffSize = buffWidth * buffHeight;
BYTE* pYBuff = new BYTE[yuvBuffSize];
BYTE* pUBuff = new BYTE[yuvBuffSize];
BYTE* pVBuff = new BYTE[yuvBuffSize];
// 將RGB信號轉換為YUV信號
BGR2YUV111(bmData,pYBuff,pUBuff,pVBuff);
// 將信號分割為8x8的塊
DivBuff(pYBuff, buffWidth, buffHeight, DCTSIZE, DCTSIZE );
DivBuff(pUBuff, buffWidth, buffHeight, DCTSIZE, DCTSIZE );
DivBuff(pVBuff, buffWidth, buffHeight, DCTSIZE, DCTSIZE );
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表
pOutFile = fopen(jpgFile.c_str(),"wb");
// 寫入各段
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(pYBuff,pUBuff,pVBuff);
WriteEOI();
fclose(pOutFile);
delete[] bmData;
}
private:
FILE* pOutFile;
int buffWidth;
int buffHeight;
int imgWidth;
int imgHeight;
// 獲取BMP文件輸出緩沖區信息
BMBUFINFO 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;
}
// 獲取圖像數據
void GetBMData(FILE* pFile, BYTE* pBuff, BMBUFINFO buffInfo)
{
BITMAPFILEHEADER bmHead; // 文件頭信息塊
BITMAPINFOHEADER bmInfo; // 圖像描述信息塊
size_t dataLen = 0; // 文件數據區長度
long alignBytes = 0; // 為對齊4字節需要補足的字節數
UINT lineSize = 0;
fseek(pFile,0,SEEK_SET); // 將讀寫指針指向文件頭部
fread(&bmHead,sizeof(bmHead),1,pFile); // 讀取文件頭信息塊
fread(&bmInfo,sizeof(bmInfo),1,pFile); // 讀取位圖信息塊
//計算對齊的字節數
alignBytes = (((bmInfo.biWidth * bmInfo.biBitCount) + 31) & ~31) / 8L
- (bmInfo.biWidth * bmInfo.biBitCount) / 8L; // 計算圖象文件數據段行補齊字節數
//計算數據緩沖區長度
lineSize = bmInfo.biWidth * 3;
// 因為bmp文件數據是倒置的所以從最后一行開始讀
for (int i = bmInfo.biHeight - 1; i >= 0; --i)
{
fread(&pBuff[buffInfo.buffWidth * i * 3],lineSize,1,pFile);
fseek(pFile,alignBytes,SEEK_CUR); // 跳過對齊字節
}
}
// 轉換色彩空間BGR-YUV,111采樣
void 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 = tmpY; //放入輸入緩沖
pUBuff = tmpU;
pVBuff = tmpV;
}
}
//********************************************************** **********
// 方法名稱:DivBuff
// 最后修訂日期:2003.5.3
//
// 參數說明:
// lpBuf:輸入緩沖,處理后的數據也存儲在這里
// width:緩沖X方向長度
// height:緩沖Y方向長度
// xLen:X方向切割長度
// yLen:Y方向切割長度
//********************************************************** **********
void 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; //刪除臨時緩沖
}
//********************************************************** **********
// 方法名稱:SetQuantTable
//
// 方法說明:根據所需質量設置量化表
//
// 參數說明:
// std_QT:標準量化表
// QT:輸出量化表
// Q:質量參數
//********************************************************** **********
// 根據所需質量設置量化表
void SetQuantTable(const BYTE* std_QT,BYTE* QT, int Q)
{
INT tmpVal = 0; //臨時變量
DWORD i = 0;
if (Q < 1) Q = 1; //限制質量系數
if (Q > 100) Q = 100;
//非線性映射 1->5000, 10->500, 25->200, 50->100, 75->50, 100->0
if (Q < 50)
{
Q = 5000 / Q;
}
else
{
Q = 200 - Q * 2;
}
for (i = 0; i < DCTBLOCKSIZE; ++i)
{
tmpVal = (std_QT * Q + 50L) / 100L;
if (tmpVal < 1) //數值范圍限定
{
tmpVal = 1L;
}
if (tmpVal > 255)
{
tmpVal = 255L;
}
QT[FZBT] = static_cast<BYTE>(tmpVal);
}
}
//為float AA&N IDCT算法初始化量化表
void InitQTForAANDCT()
{
UINT i = 0; //臨時變量
UINT j = 0;
UINT k = 0;
for (i = 0; i < DCTSIZE; i++) //初始化亮度信號量化表
{
for (j = 0; j < DCTSIZE; j++)
{
YQT_DCT[k] = (FLOAT) (1.0 / ((DOUBLE) YQT[FZBT[k]] *
aanScaleFactor * aanScaleFactor[j] * 8.0));
++k;
}
}
k = 0;
for (i = 0; i < DCTSIZE; i++) //初始化色差信號量化表
{
for (j = 0; j < DCTSIZE; j++)
{
UVQT_DCT[k] = (FLOAT) (1.0 / ((DOUBLE) UVQT[FZBT[k]] *
aanScaleFactor * aanScaleFactor[j] * 8.0));
++k;
}
}
}
//寫文件開始標記
void WriteSOI(void)
{
fwrite(&SOITAG,sizeof(SOITAG),1,this->pOutFile);
}
//寫APP0段
void WriteAPP0(void)
{
JPEGAPP0 APP0;
APP0.segmentTag = 0xE0FF;
APP0.length = 0x1000;
APP0.id[0] = 'J';
APP0.id[1] = 'F';
APP0.id[2] = 'I';
APP0.id[3] = 'F';
APP0.id[4] = 0;
APP0.ver = 0x0101;
APP0.densityUnit = 0x00;
APP0.densityX = 0x0100;
APP0.densityY = 0x0100;
APP0.thp = 0x00;
APP0.tvp = 0x00;
fwrite(&APP0,sizeof(APP0),1,this->pOutFile);
}
//寫入DQT段
void WriteDQT(void)
{
UINT i = 0;
JPEGDQT_8BITS DQT_Y;
DQT_Y.segmentTag = 0xDBFF;
DQT_Y.length = 0x4300;
DQT_Y.tableInfo = 0x00;
for (i = 0; i < DCTBLOCKSIZE; i++)
{
DQT_Y.table = YQT;
}
fwrite(&DQT_Y,sizeof(DQT_Y),1,this->pOutFile);
DQT_Y.tableInfo = 0x01;
for (i = 0; i < DCTBLOCKSIZE; i++)
{
DQT_Y.table = UVQT;
}
fwrite(&DQT_Y,sizeof(DQT_Y),1,this->pOutFile);
}
//寫入SOF段
void WriteSOF(void)
{
JPEGSOF0_24BITS SOF;
SOF.segmentTag = 0xC0FF;
SOF.length = 0x1100;
SOF.precision = 0x08;
SOF.height = Intel2Moto(USHORT(this->imgHeight));
SOF.width = Intel2Moto(USHORT(this->imgWidth));
SOF.sigNum = 0x03;
SOF.YID = 0x01;
SOF.QTY = 0x00;
SOF.UID = 0x02;
SOF.QTU = 0x01;
SOF.VID = 0x03;
SOF.QTV = 0x01;
SOF.HVU = 0x11;
SOF.HVV = 0x11;
/*switch (this->SamplingType)
{
case 1:
SOF.HVY = 0x11;
break;
case 2:
SOF.HVY = 0x12;
break;
case 3:
SOF.HVY = 0x21;
break;
case 4:
SOF.HVY = 0x22;
break;
}*/
SOF.HVY = 0x11;
fwrite(&SOF,sizeof(SOF),1,this->pOutFile);
}
//寫入DHT段
void WriteDHT(void)
{
UINT i = 0;
JPEGDHT DHT;
DHT.segmentTag = 0xC4FF;
DHT.length = Intel2Moto(19 + 12);
DHT.tableInfo = 0x00;
for (i = 0; i < 16; i++)
{
DHT.huffCode = STD_DC_Y_NRCODES[i + 1];
}
fwrite(&DHT,sizeof(DHT),1,this->pOutFile);
for (i = 0; i <= 11; i++)
{
WriteByte(STD_DC_Y_VALUES);
}
//------------------------------------------------
DHT.tableInfo = 0x01;
for (i = 0; i < 16; i++)
{
DHT.huffCode = STD_DC_UV_NRCODES[i + 1];
}
fwrite(&DHT,sizeof(DHT),1,this->pOutFile);
for (i = 0; i <= 11; i++)
{
WriteByte(STD_DC_UV_VALUES);
}
//----------------------------------------------------
DHT.length = Intel2Moto(19 + 162);
DHT.tableInfo = 0x10;
for (i = 0; i < 16; i++)
{
DHT.huffCode = STD_AC_Y_NRCODES[i + 1];
}
fwrite(&DHT,sizeof(DHT),1,this->pOutFile);
for (i = 0; i <= 161; i++)
{
WriteByte(STD_AC_Y_VALUES);
}
//-----------------------------------------------------
DHT.tableInfo = 0x11;
for (i = 0; i < 16; i++)
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -