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

📄 jpeg_code.cpp

📁 实现JPEG编码功能
💻 CPP
📖 第 1 页 / 共 3 页
字号:
//////////////////////////////////////////////////////////////////////////
//JPEG_CODE类的实现
#include "StdAfx.h"
#include "jpeg_code.h" 
#include "DIBDC.h"
//////////////////////////////////////////////////////////////////////////
//JPEG用到的常变量以及基本码表
// DCT转换尺寸
static const BYTE DCTSIZE = 8;
// DCT转换块长度
static const BYTE DCTBLOCKSIZE = 64;  

// 存放VLI表
BYTE VLI_TAB[4096];
BYTE* pVLITAB;                        //VLI_TAB的别名,使下标在-2048-2048

// 存放2个量化表
BYTE YQT[DCTBLOCKSIZE]; 
BYTE UVQT[DCTBLOCKSIZE]; 
// 存放2个FDCT变换要求格式的量化表
FLOAT YQT_DCT[DCTBLOCKSIZE];
FLOAT UVQT_DCT[DCTBLOCKSIZE];
//存放4个Huffman表
HUFFCODE STD_DC_Y_HT[12];
HUFFCODE STD_DC_UV_HT[12];
HUFFCODE STD_AC_Y_HT[256];
HUFFCODE STD_AC_UV_HT[256];

static BYTE bytenew=0; // The byte that will be written in the JPG file
static CHAR bytepos=7; //bit position in the byte we write (bytenew)
//should be<=7 and >=0
static USHORT mask[16]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768};

static const DOUBLE aanScaleFactor[8] = {1.0, 1.387039845, 1.306562965, 1.175875602,
1.0, 0.785694958, 0.541196100, 0.275899379};

//量化后DC范围在-2^11 - 2^11 - 1之间,量化后AC范围在-2^10 - 2^10 - 1之间
static const INT AC_MAX_QUANTED = 1023;   //量化后AC的最大值
static const INT AC_MIN_QUANTED = -1024;   //量化后AC的最小值
static const INT DC_MAX_QUANTED = 2047;   //量化后DC的最大值
static const INT DC_MIN_QUANTED = -2048;   //量化后DC的最小值


//标准亮度信号量化模板
const static BYTE std_Y_QT[64] = 
{
 16, 11, 10, 16, 24, 40, 51, 61,
  12, 12, 14, 19, 26, 58, 60, 55,
  14, 13, 16, 24, 40, 57, 69, 56,
  14, 17, 22, 29, 51, 87, 80, 62,
  18, 22, 37, 56, 68, 109,103,77,
  24, 35, 55, 64, 81, 104,113,92,
  49, 64, 78, 87, 103,121,120,101,
  72, 92, 95, 98, 112,100,103,99
};

//标准色差信号量化模板
const static BYTE std_UV_QT[64] = 
{
 17, 18, 24, 47, 99, 99, 99, 99,
  18, 21, 26, 66, 99, 99, 99, 99,
  24, 26, 56, 99, 99, 99, 99, 99,
  47, 66, 99 ,99, 99, 99, 99, 99,
  99, 99, 99, 99, 99, 99, 99, 99,
  99, 99, 99, 99, 99, 99, 99, 99,
  99, 99, 99, 99, 99, 99, 99, 99,
  99, 99, 99, 99, 99, 99, 99, 99
};

//正向 8x8 Z变换表
const static BYTE FZBT[64] =
{
 0, 1, 5, 6, 14,15,27,28,
  2, 4, 7, 13,16,26,29,42,
  3, 8, 12,17,25,30,41,43,
  9, 11,18,24,31,40,44,53,
  10,19,23,32,39,45,52,54,
  20,22,33,38,46,51,55,60,
  21,34,37,47,50,56,59,61,
  35,36,48,49,57,58,62,63 
};

//色彩空间系数常量,依次是411,111,211采样的系数,211采样的2种方式的系数相同
static const FLOAT COLORSPACECOEF[4][3] = {{1,0.25,0.25},{1,1,1},{1,0.5,0.5},{1,0.5,0.5}};
//MCU中各型号分量出现的比率
static const BYTE MCUIndex[4][3] = {{4,1,1},{1,1,1},{2,1,1},{2,1,1}};

// 标准Huffman表 (cf. JPEG standard section K.3) 
static BYTE STD_DC_Y_NRCODES[17]={0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0};
static BYTE STD_DC_Y_VALUES[12]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

static BYTE STD_DC_UV_NRCODES[17]={0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0};
static BYTE STD_DC_UV_VALUES[12]={0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};

static BYTE STD_AC_Y_NRCODES[17]={0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0X7D };
static BYTE STD_AC_Y_VALUES[162]= {
 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
  0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
  0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
  0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
  0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
  0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
  0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
  0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
  0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
  0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
  0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
  0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
  0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
  0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
  0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
  0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
  0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
  0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
  0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
  0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
  0xf9, 0xfa };

 static BYTE STD_AC_UV_NRCODES[17]={0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0X77};
 static BYTE STD_AC_UV_VALUES[162]={
  0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21,
   0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
   0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91,
   0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0,
   0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34,
   0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26,
   0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38,
   0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
   0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
   0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
   0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
   0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
   0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96,
   0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5,
   0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4,
   0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3,
   0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2,
   0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda,
   0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
   0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
   0xf9, 0xfa };  
//////////////////////////////////////////////////////////////////////////
//具体方法的实现
//////////////////////////////////////////////////////////////////////////
//JPEG编码的函数
// bmFile:输入文件
// jpgFile:输出文件
// Q:质量
void JPEG_CODE::Invoke(string bmFile, string jpgFile, long Q)
 {
   //////////////////////////////////////////////////////////////////////////
     //先把图片存储到Dib中,一会访问文件,这个就无法读取了
  CDIBDC *Dib;
  Dib=NULL;
  CString FN;
  FN.Format("%s",bmFile.c_str());
 
  Dib=new CDIBDC;
  Dib->Load(FN);
  
  bytenew=0;
  bytepos=7;	
  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(Dib->GetIndex(),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 + 2048;                             // 设置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;
 }

//////////////////////////////////////////////////////////////////////////
 // 获取BMP文件输出缓冲区信息
BMBUFINFO JPEG_CODE::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 JPEG_CODE::GetBMData(BYTE**GetIndex,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文件数据是倒置的所以从最后一行开始读
 if (bmInfo.biBitCount==24) {
  for (int i = bmInfo.biHeight - 1; i >= 0; --i)
  {   
   fread(&pBuff[buffInfo.buffWidth * i * 3],lineSize,1,pFile);   
   fseek(pFile,alignBytes,SEEK_CUR);             // 跳过对齐字节            
  }  
 }
 else
 {

//////////////////////////////////////////////////////////////////////////
//处理不是24位的位图,只要把图像的数据付给pBuff就可以了,仍然把图片按照24位
//位图的JPEG进行压缩
 int s,t;
  for(int i=0;i<buffHeight;i++)
	 for(int j=0;j<buffWidth;j++)
	 {
		 if(i>=bmInfo.biHeight-1)
              s=bmInfo.biHeight-1;
		 else
			 s=i;
		 if(j>=bmInfo.biWidth-1)
			  t=bmInfo.biWidth-1;
		 else
			 t=j;
		 pBuff[buffWidth*3*i+3*j]=GetIndex[s][t*4];
		 pBuff[buffWidth*3*i+3*j+1]=GetIndex[s][t*4+1];
		 pBuff[buffWidth*3*i+3*j+2]=GetIndex[s][t*4+2];
	 }
 }

 }

//////////////////////////////////////////////////////////////////////////
 // 转换色彩空间BGR-YUV,111采样
 void JPEG_CODE::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;
  }
 }
//////////////////////////////////////////////////////////////////////////
 // lpBuf:输入缓冲,处理后的数据也存储在这里
 void JPEG_CODE::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方向切割数量

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -