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

📄 simple_jpeg_enc.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[i] = tmpY;           //放入输入缓冲
   pUBuff[i] = tmpU;
   pVBuff[i] = 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[i] * Q + 50L) / 100L;

   if (tmpVal < 1)                 //数值范围限定
   {
    tmpVal = 1L;
   }
   if (tmpVal > 255)
   {
    tmpVal = 255L;
   }
   QT[FZBT[i]] = 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[i] * 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[i] * 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[i] = YQT[i];
  }    
  fwrite(&DQT_Y,sizeof(DQT_Y),1,this->pOutFile);

  DQT_Y.tableInfo  = 0x01;
  for (i = 0; i < DCTBLOCKSIZE; i++)
  {
   DQT_Y.table[i] = UVQT[i];
  }
  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[i] = STD_DC_Y_NRCODES[i + 1];
  } 
  fwrite(&DHT,sizeof(DHT),1,this->pOutFile);
  for (i = 0; i <= 11; i++)
  {
   WriteByte(STD_DC_Y_VALUES[i]);  
  }  
  //------------------------------------------------
  DHT.tableInfo  = 0x01;
  for (i = 0; i < 16; i++)
  {
   DHT.huffCode[i] = STD_DC_UV_NRCODES[i + 1];
  }
  fwrite(&DHT,sizeof(DHT),1,this->pOutFile);
  for (i = 0; i <= 11; i++)
  {
   WriteByte(STD_DC_UV_VALUES[i]);  
  } 
  //----------------------------------------------------
  DHT.length   = Intel2Moto(19 + 162);
  DHT.tableInfo  = 0x10;
  for (i = 0; i < 16; i++)
  {
   DHT.huffCode[i] = STD_AC_Y_NRCODES[i + 1];
  }
  fwrite(&DHT,sizeof(DHT),1,this->pOutFile);
  for (i = 0; i <= 161; i++)
  {
   WriteByte(STD_AC_Y_VALUES[i]);  
  }  
  //-----------------------------------------------------
  DHT.tableInfo  = 0x11;
  for (i = 0; i < 16; i++)
  {
   DHT.huffCode[i] = STD_AC_UV_NRCODES[i + 1];
  }
  fwrite(&DHT,sizeof(DHT),1,this->pOutFile); 

⌨️ 快捷键说明

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