jpeg解码相对h264/avc那些还是很简单的,下面通过几百行的代码演示Jpeg的解码过程。
#include "JpegDecoder.h"
#include "Config.h"
#include "Stl.h"
#include <stdio.h>
#include <cmath>
using namespace JpegCodec;
using namespace tinyStl;
//------------------ Public Field -----------------------------------------------------//
JpegDecoder::JpegDecoder(const char *fileName) : yDC(0), cbDC(0), crDC(0), endOfDecoder(false), readCount(0x80)
{
FILE *fp = fopen(fileName, "rb+"); //打开文件
/* 计算文件大小 */
fseek(fp, 0, SEEK_END);
int length = ftell(fp);
/* 重定位到文件头 */
fseek(fp, 0, SEEK_SET);
stream = new uint8_t[length];
fread(stream, 1, length, fp); // 读取整个文件数据
fclose(fp); // 关闭文件
}
JpegDecoder::~JpegDecoder()
{
delete []stream; //free 文件缓冲区
}
void JpegDecoder::Decoder(Matrix &mat)
{
/* Preparation */
ReadQuantTable();
ReadImageSize();
ComputeDHT();
/* 创建Matrix */
mat.Create(m_height, m_width, 3);
int rowBlkNr = (m_height + 15) >> 4; // 垂直方向分块
int colBlkNr = (m_width + 15) >> 4; // 水平方向分块
/* 解码 */
ToStartOfData();
for (int i = 0; i < rowBlkNr; i++)
{
for (int j = 0; j < colBlkNr; j++)
{
if (endOfDecoder) return;
DecoderMCU(); // 解码一个 MacroBlock
FillYCbCr(); // 填充 YCbCr 缓冲区
/* 写入到 mat */
ConvertClrSpace(mat.data,i * 16, j * 16);
}
}
}
//------------------ Proteced Field ----------------------------------------------------//
//------------------ 0.helper function ----------------------------------
/* @brief 获取压缩数据的下一个有效位
*/
int JpegDecoder::NextBit()
{
if (readCount == 0x0)
{
// reset
readCount = 0x80;
curIndex ++;
// check
if (stream[ curIndex ] == 0xFF) //标记值
{
curIndex ++;
if (stream[ curIndex ] & 0xD7) ResetDC();
else if (stream[ curIndex ] == 0xD9) endOfDecoder = true;
else if (stream[ curIndex ] == 0x00) stream[ curIndex ] = 0xFF;
}
}
int retVal = stream[ curIndex ] & readCount; // 获取当前位的值 (1 or 0)
readCount >>= 1;
return (retVal > 0 ? 1 : 0);
}
/* @brief 计算标准哈夫曼表的真实值
*/
int JpegDecoder::ComputeRealValue(int length)
{
int retVal = 0;
for (int i = 0; i < length; i++)
{
retVal = (retVal << 1) + NextBit();
}
return (retVal >= pow(2, length - 1) ? retVal : retVal - pow(2, length) + 1);
}
/* @brief 反采样
*/
void JpegDecoder::UpSample(int32_ptr CbCr, int32_ptr blk)
{
typedef int (*Ptr_CbCr)[16];
Ptr_CbCr p = reinterpret_cast<Ptr_CbCr>(CbCr); // 一位数组转二维数组
for (int i = 0; i < 16; i++)
{
for (int j =0; j< 16; j++)
{
p[i][j] = blk[(i >> 1) * 8 + (j >> 1)];
}
}
}
/* @brief Reset DC 值
*/
void JpegDecoder::ResetDC()
{
yDC = cbDC = crDC = 0x0;
}
/* @brief 查找对应的标记段的索引
* @mark: 段标记
*/
int JpegDecoder::MarkIndex(uint8_t mark)
{
int idx = 0;
while(stream[idx] != 0xFF || stream[idx + 1] != mark) idx ++;
return idx;
}
/* @brief 从二进制数据流中查找一个有效的编码值
* @table: 用于解码的哈夫曼表
*/
int JpegDecoder::FindKeyValue(tinyMap &table)
{
int key = 0;
int keyLength = 0;
while (table.find(key, keyLength) == table.end())
{
key = (key << 1) + NextBit(); // 1 or 0
keyLength++;
}
//printf("%X\n", key);
return table[key];
}
/* @brief 将一个 MacroBlock 填充到 YCbCr 缓冲区
*/
void JpegDecoder::FillYCbCr()
{
Ptr16 yPtrDst = (Ptr16)yBuf;
for (int k = 0; k < 4; k++)
{
int xOffset = (k >> 1) << 3; // equals: (k / 2) * 8;
int yOffset = (k & 0x1) << 3; // equals: (k % 2) * 8;
Ptr8 yPtrSrc = (Ptr8)yBlk[k];
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
yPtrDst[xOffset + i][yOffset + j] = yPtrSrc[i][j];
}
}
}
// 反采样
UpSample(cbBuf, cbBlk);
UpSample(crBuf, crBlk);
}
//------------------ 1.解码准备工作 -------------------------------------
/* 读取量化表 */
void JpegDecoder::ReadQuantTable()
{
int base = MarkIndex(DQT);
int baseY = base + 5;
int baseCbCr = base + 74;
for (int i = 0; i < 64; i++)
{
quantY[i] = stream[i + baseY];
quantCbCr[i] = stream[i + baseCbCr];
}
}
/* 读取图像的宽高 */
void JpegDecoder::ReadImageSize()
{
int base = MarkIndex(SOF);
m_height = (stream[base + 5] << 8) + stream[base + 6];
m_width = (stream[base + 7] << 8) + stream[base + 8];
}
/* @brief 构建哈夫曼表
* @base: 从DHT_Segment 的表头开始,到表类型字段后一个字节的偏移量
* @table: 要构建的哈夫曼表
*/
void JpegDecoder::ReBuildTable(int base, tinyMap &table)
{
int offset = 16; // offset
int key = 0x0;
int keyLength = 0;
for (int i = 0; i < 16; i++) // length of key (i.e. i = 2 means key = 000 , 001 , 010 , 011 or ...)
{
int cnt = DHT_Segment[base + i]; // number of key, which length is (i+1)
/* alignment */
if (keyLength <= i)
{
key <<= (i - keyLength + 1);
keyLength = i + 1;
}
while (cnt > 0)
{
/* value of key */
table.insert(key, keyLength, DHT_Segment[base + offset]);
offset++;
/* increment */
key++;
cnt = cnt - 1;
}
}
}
/* 构建 DC AC 哈夫曼表 */
void JpegDecoder::ComputeDHT()
{
ReBuildTable(5, DC[0]);
ReBuildTable(34, DC[1]);
ReBuildTable(63, AC[0]);
ReBuildTable(242, AC[1]);
}
/* 定位到数据头 */
void JpegDecoder::ToStartOfData()
{
curIndex = MarkIndex(SOS) + 14;
}
//------------------ 2.解码一个block ------------------------------------
/* @brief 反编码一个 8 x 8 的数据块
* @out: 返回值,一个 8 x 8 的数据块
* @dcTable: DC哈夫曼表
* @actable: AC哈夫曼表
* @dc: 前一个block的 DC 值
*/
void JpegDecoder::DecoderBlock(int32_ptr out, tinyMap &dcTable, tinyMap &acTable, int &dc)
{
// reset matrix
for (int i = 0; i < 64; i++) out[i] = 0x0;
// decoder DC of matrix
int length = FindKeyValue(dcTable);
int value = ComputeRealValue(length);
dc += value; // DC
out[0] = dc;
// decoder AC of matrix
for (int i = 1; i < 64; i++)
{
length = FindKeyValue(acTable);
if (length == 0x0) break; // 结束条件
value = ComputeRealValue(length & 0xf); // 右边 4位,实际值长度
i += (length >> 4); // 左边 4位,行程长度
out[i] = value; // AC
}
}
//------------------ 3.反量化 -------------------------------------------
/* @brief 反量化
* @out: 待量化数据,返回值(8x8)
* @quant: 量化表(8x8)
*/
void JpegDecoder::Dequant(int32_ptr out, int8_ptr quant)
{
for (int i = 0; i < 64; i++)
{
out[i] = quant[i] * out[i];
}
}
//------------------ 4.反 ZigZag 编码 -----------------------------------
void JpegDecoder::UnZigZag(int32_ptr out, int32_ptr source)
{
for (int i = 0; i < 64; i++) out[i] = source[UnZigZagTable[i]];
}
//------------------ 5.反离散余弦变换(IDCT) ---------------------------
void JpegDecoder::Transform(int32_ptr out)
{
double tmp[8][8];
Ptr8 pIn = (Ptr8)out; // 用二维数组方式访问一位数组
double tmpVal;
/* tmp = MtxIDCT * Matrix */
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
tmpVal = 0;
for (int k = 0; k < 8; k++)
{
tmpVal += MtxIDCT[i][k] * pIn[k][j];
}
tmp[i][j] = round(tmpVal);
}
}
/* Matrix = tmp * MtxDCT */
for (int i = 0; i < 8; i++)
{
for (int j = 0; j < 8; j++)
{
tmpVal =0;
for (int k = 0; k < 8; k++)
{
tmpVal +=tmp[i][k] * MtxDCT[k][j];
}
pIn[i][j] = round(tmpVal);
}
}
}
//------------------ 6.构建一个 MacroBlock ------------------------------
void JpegDecoder::DecoderMCU()
{
int tmp[64];
/* 4 个 Y 分量 */
for (int i = 0; i < 4; i++)
{
DecoderBlock(tmp, DC[0], AC[0], yDC); // 解码一个数据块
Dequant(tmp, quantY); // 反量化
UnZigZag(yBlk[i], tmp); // 反 ZigZag 编码
Transform(yBlk[i]); // 反离散余弦变换 (IDCT)
}
/* 一个 Cb 分量 */
DecoderBlock(tmp, DC[1], AC[1], cbDC);
Dequant(tmp, quantCbCr);
UnZigZag(cbBlk, tmp);
Transform(cbBlk);
/* 一个 Cr 分量 */
DecoderBlock(tmp, DC[1], AC[1], crDC);
Dequant(tmp, quantCbCr);
UnZigZag(crBlk, tmp);
Transform(crBlk);
}
//------------------ 7.颜色空间转换(from YCbCr to RGB) ----------------
/* 颜色空间转换
* @outBgr: 16x16x3 block ,返回值,BGR格式像素序列
* @row: 当前数据块的 row index , 用于越界检查
* @col: 当前数据块的 col index, 用于越界检查
*/
void JpegDecoder::ConvertClrSpace(int8_ptr outBgr, int row, int col)
{
Ptr16 Y = (Ptr16) yBuf;
Ptr16 Cb = (Ptr16) cbBuf;
Ptr16 Cr = (Ptr16) crBuf;
int R, G, B;
outBgr += (row * m_width + col) * 3; // 偏移到 第 (x,y) 块
int stride = m_width * 3; // 一次偏移一行
for (int i = 0; i < 16; i++)
{
if (row + i >= m_height) break; // 越界检查
// 计算一行
for (int j = 0; j < 16; j++)
{
if (col + j >= m_width) break; // 越界检查
int offset = j * 3;
R = Y[i][j] + 1.402 * Cr[i][j] + 128; // R
G = Y[i][j] - 0.34414 * Cb[i][j] - 0.71414 * Cr[i][j] + 128; // G
B = Y[i][j] + 1.772 * Cb[i][j] + 128; // B
if (R > 255) R = 255;
if (G > 255) G = 255;
if (B > 255) B = 255;
if (R < 0) R = 0;
if (G < 0) G = 0;
if (B < 0) B = 0;
outBgr[offset + 0] = B;
outBgr[offset + 1] = G;
outBgr[offset + 2] = R;
} // end of for_j
outBgr += stride; // 偏移到下一行首
} // end of for_i
}