⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 jpegbianma.txt

📁 这是一个简单的jpeg编码程序
💻 TXT
📖 第 1 页 / 共 3 页
字号:


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 + -