在网上看了好多解析JPEG图片的文章,多多少少都有问题,下面是我参考过的文章链接:

首先,解析的步骤

1.读取文件的信息2.Huffman编码解码3.直流交流编码解析然而,读取多少个8×8矩阵才能解析出一个MCU呢?

4.反量化5.反Zig-Zag变化6.反DCT变化8.YCbCr转RGB效果图

1.读取文件的信息

JPEG格式中信息是以段(数据结构)来存储的。 段的格式如下

名称字节数数据说明段标识1FF每个新段的开始标识段类型1类型编码(称作“标记码”)段长度2包括段内容和段长度本身,不包括段标识和段类型段内容≤65533字节

其余具体信息请见以下链接,我就不当复读机了。 JPEG标记的说明 格式介绍 值得注意的一点是一个字节的高位在左边,而且直流分量重置标记一共有8个,其他的格式说明在第二个链接中已经足够详细了

这些段中必须要读取的段:SOS, DHT, DQT, SOF, DRI,其他的只是锦上添花 这里面可能会出现多个SOF段,我们需要拿到这几个段中图片高度和宽度的最大值,和YCbCr的水平,垂直采样因子的最大值分别记为Hmax,Vmax,之后会用到 DRI中的开始间隔指的就是直流分量重置间隔,我们记为reset

2.Huffman编码解码

首先Huffman编码分直流表(DC)和交流表(AC),他们一般各自有两张表,具体使用哪张表是通过SOS里面的对应关系来的,一般Y对应第一张表,CbCr对应第二、三张表。

因为规定huffman编码最多16位,所以huffman编码的最大值位65535 以下代码为我的解码方式,直流交流均如此

int curPos = 16, curCode = 0;

for (int i = 0; i < 16; i++) {

int count = temp[i];//count为二进制位数为i+1的个数

curCode <<= 1; //curCode为当前huffman编码数值

while (count--) { //一次循环生成一个

uint16_t code=curCode;

uint8_t bit=i+1;//比特位有几位 00为2位

uint8_t weight=temp[curPos];//权重是按照顺序排列的,如比特位为两位的编码有两个,设为00,01,后面权重排列为1,2,则00对应1,01对应2

pair t1(bit,weight);

//

pair> t2(curCode,t1);

table.insert(t2);

curCode++;

curPos++;

}

}

3.直流交流编码解析

SOS段之后就是真正的图片压缩数据了,可以选择一次性读取到内存中,也可以边读数据边做后面的解析步骤,我是选择了第二种。每读取一个MCU后做一次解析(我使用的是缓存队列)。在图片编码的时候需要划分MCU(最小编码单位),每个MCU由多个8×8矩阵组成,通过编码将二维数组转换为一维的,所以当读取的数据达到了64个,就代表一个8×8的块解析完成,直到读取到0xFFD9结束

然而,读取多少个8×8矩阵才能解析出一个MCU呢?

MCU里面的8×8矩阵个数,如果从编码角度来说的话,8×8矩阵个数是Hmax*Vmax个,但是从解码角度来说,因为此时的YCbCr已经分开成为了三张表,所以8×8矩阵个数应该是三个分量的水平、垂直采样因子的乘积之和(先乘积,再求和)记为SUMmcu,所以读取一次要读取SUMmcu个8×8矩阵(此时这里面有YCbCr三种表,之后通过公式将YCbCr转换为RGB数值)

好了,到这里我们知道了要读取多少个8×8的矩阵 (实际上,因为没有反Zig-Zag编码,此时还是有64个数据的一维数组) 接下来开始解析,解析需要使用上一步解码出来的Huffman编码。 解析方式如下: 1、对于直流(差分编码),按照一个比特位来读取图片压缩数据,若在Huffman表中发现该编码,并且位数相等,则读取该编码所对应的权重,该权重代表接下来读取多少个比特位作为直流分量的值,你以为这就完了?还要加上差分矫正变量 (YCbCr每张表都有一个,所以一共有3个)。 2、对于交流(游程编码),其他部分都一样(这个没有差分矫正变量),不同的地方举个例子,设读取的直流分量为0x37,则低4位(这里为7)代表接下来7个比特位是该交流分量的值,而高4位(此处为3)代表此交流分量前有3个0 (这里就不用加上前面的了)。注意直流交流使用的Huffman表不同 3、接下来就是循环读取交流分量了,那么什么时候退出呢? 有两个条件,只要达成一个就可以退出

读取了63个交流分量交流分量的权值为0,此位后面全是0

对于根据权重所读出来的值(不区分直流交流),对于最高位(最左边)若为0则是负数,否则为正数,判断代码如下,curValue为读取的值,curValueLength为读取的值有多少位

curValue = (curValue >= pow(2, curValueLength - 1) ? curValue : curValue - pow(2, curValueLength) + 1);

这里面还有两个坑(若DRI读出来的直流分量重置间隔reset为0,不用管这步)

假设reset为332(这是我图片的间隔),就是隔了332个MCU(也就是332×SUMmcu个8×8的矩阵),需要将 所有差分矫正变量全部置为0,并且当这332个MCU读取完后,你要读取两个字节(这两个字节是一个段),这两个字节应该正好是0xFF 0xD0~0xD7,并且D0到D7是按顺序出现的,例如,上一个是0xFFD0那么下一个肯定是0xD1,到D7后下一个是D0,若对不上那就有问题了。还有,这读出来的两个字节不是图片的压缩数据不需要解码若读取到了0xFF00则00忽略

到此,我们得到了一个有64个元素的一维数组

4.反量化

我们用之前读出来的量化表(也是64个元素的,你说巧不巧嘿嘿)与上面解码得到的元素对应项相乘,反量化完成!!!

5.反Zig-Zag变化

编码方式如下

我使用的模拟法,将一维数组转为8×8矩阵 函数如下,写的不好

double** UnZigZag(int* originArray){

double** table=new double*[ROW];

for(int i=0;i

int cur=0,x=0,y=0;

bool flag = true;//true是右上 false是左下

while (cur < 64) {

table[y][x] = originArray[cur++];

if (flag) { x++; y--; }

else { x--; y++; }

if (x < 0 || y < 0 || x>7 || y>7) flag = !flag;

if (x < 0 && y>7) { x = 1; y = 7; }

if (x < 0) x = 0;

else if (x > 7) { x = 7; y += 2; }

if (y < 0) y = 0;

else if (y > 7) { y = 7; x += 2; }

}

return table;

也可以使用另外一种方法,手动记录一个数组,将位置写好,转换只需要4行代码

6.反DCT变化

那个公式太慢了,有这个公式的简化版本,公式可以化为矩阵乘法,只需要一个转换矩阵 矩阵我是用下面的代码计算得到的

double** JPEGData::createDCTAndIDCTArray(int row){

double** res=new double*[row];

for(int i=0;i

// cout<

for(int i=0;i

for(int j=0;j

double t=0;

if(i==0) t=sqrt(1.0/row);

else t=sqrt(2.0/row);

res[i][j]=t*cos(M_PI*(j+0.5)*i/row);

// cout<

}

// cout<

}

return res;

}

//设返回的矩阵为A

//DCT原理 Y=A*X*A'(X为正变换输入,Y是输出)

//IDCT原理X=A'*Y*A(Y是逆变换输入,X是输出'是转置)

void JPEGData::IDCT(double** originMatrix){

vector> temp(ROW,vector(COL,0));

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=DCTAndIDCTArray[k][i]*originMatrix[k][j];

}

temp[i][j]=sum;

}

}

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=temp[i][k]*DCTAndIDCTArray[k][j];

}

originMatrix[i][j]=sum;

}

}

}

void JPEGData::DCT(double** originMatrix){

vector> temp(ROW,vector(COL,0));

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=DCTAndIDCTArray[i][k]*originMatrix[k][j];

}

temp[i][j]=sum;

}

}

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=temp[i][k]*DCTAndIDCTArray[j][k];

}

originMatrix[i][j]=sum;

}

}

}

8.YCbCr转RGB

公式如下,这个是真好使 R=128+y+1.402 cr G=128+y-0.71414cr-0.34414*cb B=128+y+1.772 *cb

struct RGB{

uint8_t red;

uint8_t green;

uint8_t blue;

};

RGB** JPEGData::YCbCrToRGB(const int* YUV){

RGB **res = new RGB *[ROW * max_v_samp_factor];

int matrixCount = YUV[0] + YUV[1] + YUV[2];

int crCount = 0, cbCount = 0;

//1=Y, 2=Cb, 3=Cr

//式子 scale*x,scale*y

double cb_h_samp_scale=component[1].h_samp_factor*1.0/max_h_samp_factor,

cb_v_samp_scale=component[1].v_samp_factor*1.0/max_v_samp_factor,

cr_h_samp_scale=component[2].h_samp_factor*1.0/max_h_samp_factor,

cr_v_samp_scale=component[2].v_samp_factor*1.0/max_v_samp_factor;

for (int i = 0; i < ROW * max_v_samp_factor; i++)

res[i] = new RGB[COL * max_h_samp_factor];

//此处直接生成rgb值

//注意,此处YCbCr的对应关系与采样因子有关

//这个ycbcr存的是一个MCU,假设YUV为411,那么ycbcr有6个

//这种方式转换不了YUV为420的,因为数组越界了,不过可以加个判断,我懒得改了

// cout<

for(int j=0;j

for(int k=0;k

int yPos = (j / ROW) * component[0].h_samp_factor + (k / COL);

int cbPos = YUV[0] + (int)((k / ROW) * cb_v_samp_scale) + (int)((j / COL) * cb_h_samp_scale);

int crPos = YUV[0] + YUV[1] + (int)((k / ROW) * cr_v_samp_scale) + (int)((j / COL) * cr_h_samp_scale);

double y = ycbcr[yPos][j % ROW][k % COL];

double cb = ycbcr[cbPos][(int)(j * cb_v_samp_scale)][(int)(k * cb_h_samp_scale)];

double cr = ycbcr[crPos][(int)(j * cr_v_samp_scale)][(int)(k * cr_h_samp_scale)];

res[j][k].red =RGBValueLimit(128+y+1.402 *cr);

res[j][k].green =RGBValueLimit(128+y-0.71414*cr-0.34414*cb);

res[j][k].blue =RGBValueLimit(128+y+1.772 *cb);

// 输出当前选择的矩阵

//cout<

// cout<

// <

// <

}

// cout<

}

// cout<

return res;

}

效果图

这个是JPEG 这是位图

最最后,如何把图片显示出来呢?,我将信息转换为位图,就能看见了。 下面源码附上 Image.h

#pragma once

#define _USE_MATH_DEFINES

#include

#include

#include

#include

#ifndef _IMAGE_

#define _IMAGE_

#include "Util.h"

#include

#include

#include

using namespace std;

NAME_SPACE_START(myUtil)

#define ROW 8

#define COL 8

#define HUFFMAN_DECODE_DEQUE_CACHE 64//单位:位

// #define _DEBUG_

// #define _DEBUGOUT_

#define FREE_VECTOR_LP(vectorName) \

for(auto item : vectorName){ \

for(int i=0;i

delete [] item[i];\

delete [] item; \

}\

vectorName.clear();

//释放二维指针

#define FREE_LP_2(lpName,row) \

for(int i=0;i

delete [] lpName[i];\

}\

delete [] lpName;

//段类型

enum JPEGPType{

SOF0 = 0xC0, //帧开始

SOF1 = 0xC1, //帧开始

SOF2 = 0xC2, //帧开始

DHT = 0xC4, //哈夫曼表

SOI = 0xD8, //文件头

EOI = 0xD9, //文件尾

SOS = 0xDA, //扫描行开始

DQT = 0xDB, //定义量化表

DRI = 0xDD, //定义重新开始间隔

APP0 = 0xE0, //定义交换格式和图像识别信息

APP1 = 0xE1, //定义交换格式和图像识别信息

APP2 = 0xE2, //定义交换格式和图像识别信息

COM = 0xFE //注释

};

//将一维数组变为二维数组

double** UnZigZag(int* originArray);

struct RGB{

uint8_t red;

uint8_t green;

uint8_t blue;

};

//SOS

class JPEGScan{

public:

//componentId,

map> componentHuffmanMap;

bool Init(fstream& file,uint16_t len);

};

//APP

class JPEGInfo{

public:

uint16_t version;

};

//DHT

class JPEGHuffmanCode{

public:

using iterator = map>::iterator;

//

map> table;

//init huffman table

bool Init(fstream& file,uint16_t len);

//find-true not find-false

bool findKey(const uint16_t& code,const uint8_t& bit,iterator& it);

};

//DQT

//quality table

class JPEGQuality{

public:

uint8_t precision;

uint8_t id;

vector table;

bool Init(fstream& file,uint16_t len);

};

//SOF segment

class JPEGComponent{

public:

//1=Y, 2=Cb, 3=Cr, 4=I, 5=Q

uint8_t colorId;

uint8_t h_samp_factor;

uint8_t v_samp_factor;

uint8_t qualityId;

bool Init(fstream& file,uint16_t len);

};

class JPEGData{

int max_h_samp_factor;//行MCU

int max_v_samp_factor;//列MCU

int width;

int height;

int precision;

bool isYUV411=false;

bool isYUV422=false;

bool isYUV111=false;

uint8_t curDRI=0;//当前重置直流分量标识,这里只取个位方便计算

uint16_t resetInterval=0;//单位是MCU

int preDCValue[3]={0}; //用于直流差分矫正

//量化表

vector quality;

//huffman码表

vector dc_huffman;

vector ac_huffman;

//component每个颜色分量

vector component;

JPEGScan scan;

//vector deHuffman;

vector ycbcr;

vector rgb;

double** DCTAndIDCTArray;

streampos pos;

bool EOI{false};

public:

JPEGData():

max_h_samp_factor(0),

max_v_samp_factor(0),

width(0),

height(0),

precision(0){

DCTAndIDCTArray=createDCTAndIDCTArray(ROW);

}

~JPEGData(){

FREE_LP_2(DCTAndIDCTArray,ROW-1)

// FREE_LP_2(DCTArray,ROW-1)

// FREE_LP_2(IDCTArray,ROW-1)

FREE_VECTOR_LP(rgb)

}

bool readJPEG(const char* filePath);

int getWidth() const {return width;}

int getHeight() const {return height;}

vector getRGB() const {return rgb;}

int getMaxHSampFactor() const {return max_h_samp_factor;}

int getMaxVSampFactor() const {return max_v_samp_factor;}

double** createDCTAndIDCTArray(int row);

//double** createIDCTArray(int row);

void DCT(double** originMatrix);

void IDCT(double** originMatrix);

protected:

bool readSOF(fstream& file,uint16_t len);

bool readData(fstream& file);

bool huffmanDecode(fstream& file);

void deQuality(double** originMatrix,int qualityID);

//隔行正负纠正

void PAndNCorrect(double** originMatrix);

RGB** YCbCrToRGB(const int* YUV);

//标记位检查 是否结束,是否重置直流矫正数值,返回要添加的数值

string FlagCkeck(fstream& file,int byteInfo);

uint16_t ReadByte(fstream& file,int len);

uint16_t findHuffmanCodeByBit(fstream& file,int& length,int& pos,string& deque,int curValue,int& curValLen);

};

NAME_SPACE_END()

#endif //!_IMAGE_

Image.cpp

#include "Image.h"

#include "Util.h"

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

NAME_SPACE_START(myUtil)

int RGBValueLimit(double input){

if(input<0) return 0;

else if(input>255) return 255;

// 四舍五入、取整均可

// return (int)(input);

return round(input);

}

void print(double** originMatrix){

cout<

for(int i=0;i

for(int j=0;j

cout<

}

cout<

}

cout<

}

double** UnZigZag(int* originArray){

double** table=new double*[ROW];

for(int i=0;i

int cur=0,x=0,y=0;

bool flag = true;//true是右上 false是左下

while (cur < 64) {

table[y][x] = originArray[cur++];

if (flag) { x++; y--; }

else { x--; y++; }

if (x < 0 || y < 0 || x>7 || y>7) flag = !flag;

if (x < 0 && y>7) { x = 1; y = 7; }

if (x < 0) x = 0;

else if (x > 7) { x = 7; y += 2; }

if (y < 0) y = 0;

else if (y > 7) { y = 7; x += 2; }

}

return table;

}

bool JPEGScan::Init(fstream &file, uint16_t len){

try {

uint8_t count=file.get();

len--;

while(count--){

uint8_t componentId=file.get();

uint8_t table=file.get();

uint8_t dcId=table>>4;

uint8_t acId=table&0x0f;

pair info1(dcId,acId);

pair> info2(componentId,info1);

componentHuffmanMap.insert(info2);

}

} catch (...) {

return false;

}

return true;

}

bool JPEGHuffmanCode::Init(fstream &file, uint16_t len){

try{

vector temp;

while(len--){

int info=file.get();

temp.push_back(info);

}

int curPos = 16, curCode = 0;

for (int i = 0; i < 16; i++) {

int count = temp[i];

curCode <<= 1;

while (count--) {

uint16_t code=curCode;

uint8_t bit=i+1;

uint8_t weight=temp[curPos];

pair t1(bit,weight);

pair> t2(curCode,t1);

table.insert(t2);

curCode++;

curPos++;

}

}

}

catch(...){

return false;

}

return true;

}

bool JPEGHuffmanCode::findKey(const uint16_t& code,const uint8_t& bit,iterator& it)

{

it=table.find(code);

if(it==table.end()) return true;

return it->second.first!=bit;

}

bool JPEGQuality::Init(fstream &file, uint16_t len){

try{

int info=file.get();

precision=info>>4;

id=info&0x0f;

len--;

while(len--){

int t=file.get();

table.push_back(t);

}

}

catch(...){

return false;

}

return true;

}

bool JPEGComponent::Init(fstream &file, uint16_t len){

try {

int info1=file.get();

int info2=file.get();

int info3=file.get();

colorId=info1;

h_samp_factor=info2>>4;

v_samp_factor=info2&0x0f;

qualityId=info3;

} catch (...) {

return false;

}

return true;

}

bool JPEGData::readJPEG(const char *filePath){

fstream file(filePath,ios::in|ios::binary);

if(file.fail()) return false;

file.seekg(0,ios::end);

pos = file.tellg();

file.seekg(2,ios::beg);

dc_huffman.resize(2);

ac_huffman.resize(2);

try {

//do read data through using other method

uint16_t pLen=0;

uint16_t pMarker=0xFF;

uint16_t pType=0x00;

while(!file.eof()){

pMarker=file.get();

pType=file.get();

if(pType==EOI) break;

pLen=file.get();

pLen=(pLen<<8)+file.get();

// cout<

if(pMarker!=0xFF) throw exception();

bool flag=true;

switch (pType) {

case SOF0:

case SOF1:

case SOF2:{

flag=readSOF(file, pLen-2);

break;

}

case DHT:{

JPEGHuffmanCode huf;

int info=file.get();

int tableId=info&0x0f;

// cout<

flag=huf.Init(file, pLen-3);

if((info>>4)&1) ac_huffman[tableId]=huf;

else dc_huffman[tableId]=huf;

break;

}

//case SOI:

//case EOI:

case SOS:{

flag=scan.Init(file, pLen-2);

int count=3;

// cout<

while(count--) file.get();

// cout<

//正式读取数据

if(!flag) break;

flag=readData(file);

break;

}

case DQT:{

JPEGQuality q;

flag=q.Init(file, pLen-2);

quality.push_back(q);

break;

}

case DRI:{

resetInterval=ReadByte(file, 2);

break;

}

case APP0:

case APP1:

case APP2:

case COM:{

pLen-=2;

while(pLen--){

file.get();

}

break;

}

default:

pLen-=2;

while(pLen--){

file.get();

}

break;

}

if(!flag) throw exception();

// cout<

}

} catch (...) {

file.close();

return false;

}

file.close();

return true;

}

bool JPEGData::readSOF(fstream& file,uint16_t len){

try {

precision=file.get();

height=max(height,(int)ReadByte(file, 2));

width=max(width,(int)ReadByte(file, 2));

int count=ReadByte(file, 1);

if(count!=3) return false;

len-=6;

component.resize(count);

for(int i=0;i

JPEGComponent com;

com.Init(file, len/3);

max_h_samp_factor=max(max_h_samp_factor,(int)com.h_samp_factor);

max_v_samp_factor=max(max_v_samp_factor,(int)com.v_samp_factor);

component[i]=com;

}

if((component[0].h_samp_factor*component[0].v_samp_factor)

/(component[1].h_samp_factor*component[1].v_samp_factor)==4){

isYUV411=true;

}

else if((component[0].h_samp_factor*component[0].v_samp_factor)

/(component[1].h_samp_factor*component[1].v_samp_factor)==2){

isYUV422=true;

}

else if((component[0].h_samp_factor*component[0].v_samp_factor)

/(component[1].h_samp_factor*component[1].v_samp_factor)==1){

isYUV111=true;

}

} catch (...) {

return false;

}

return true;

}

bool JPEGData::readData(fstream& file){

bool flag=true;

try{

//使用huffman表来解出RLE编码,接着转回长度为64的矩阵

flag=huffmanDecode(file);

if(!flag) return false;

//反量化,即上面的64矩阵×对应位置的量化表

//flag=deQuantity();

//if(!flag) return false;

//反zig-zag排序

//flag=deZSort();

//if(!flag) return false;

//反离散余弦变换

//if(!flag) return false;

//YCbCr转RGB

//if(!flag) return false;

}

catch(...){

return false;

}

return true;

}

bool JPEGData::huffmanDecode(fstream& file){

try {

//原图像一个MCU有多少8*8矩阵(此时是YCbCr还没有分开)

//int MCUBlockCount=max_h_samp_factor*max_v_samp_factor;

//顺序YCbCr

int YUV[]={component[0].h_samp_factor*component[0].v_samp_factor,

component[1].h_samp_factor*component[1].v_samp_factor,

component[2].h_samp_factor*component[2].v_samp_factor};

int curMCUCount=1; //当前MCU数量

int curValueLength=0; //当前值有多少位

int curValue=0; //当前的值

int curBitDequeLength=8;//当前curBitDeque长度

int curBitPos=0; //当前string读取到第几位

int restart=resetInterval;//直流分量重置

string curBitDeque=""; //用来存储读出来的2进制数

//一次循环解析一个MCU

curBitDeque.append(bitset<8>(file.get()).to_string());

curBitDequeLength=8;

// cout<

while(!EOI||(pos-file.tellg())!=2){

// cout<

int count=1;

for(int i=0;i<3;i++){

for(int j=0;j

// cout<

int matrix[64]={0};

int valCount=0;

uint8_t dcID = scan.componentHuffmanMap[component[i].colorId].first;

uint8_t acID = scan.componentHuffmanMap[component[i].colorId].second;

int qualityId=component[i].qualityId;

if(qualityId>=quality.size()) qualityId=0;

// cout<

while(valCount<64){

//用curBitDeque和curBit去找权重,curValue作为当前键值

JPEGHuffmanCode::iterator it;

JPEGHuffmanCode &huffman = valCount==0?dc_huffman[dcID]:ac_huffman[acID];

while(curValueLength<=16&&huffman.findKey(curValue,curValueLength,it)){

curValue=findHuffmanCodeByBit(file,curBitDequeLength,curBitPos,curBitDeque,curValue,curValueLength);

}

if(curValueLength>16)

return true;

#ifdef _DEBUGOUT_

//cout<

cout<<"key="<

}

cout<

}

cout<

#endif

}

}

// if(count!=6){

// cout<<" ";

// }

RGB** lpRGB = YCbCrToRGB(YUV);

FREE_VECTOR_LP(ycbcr)

rgb.push_back(lpRGB);

// 直流分量重置间隔不为0的

if(restart>0){

resetInterval--;

if(resetInterval==0){

resetInterval=restart;

curDRI+=1;

curDRI&=0x7;

//需要在此处读取两字节信息,看是否为重置标识

file.get();

if(file.get()==0xD9) EOI=true;

curBitPos=curBitDequeLength;

preDCValue[0]=0;

preDCValue[1]=0;

preDCValue[2]=0;

}

}

// cout<<"curMCUCount="<

if(pos-file.tellg()==2) break;

}

cout<<"\nsuccessfully\n";

} catch (exception ex) {

cout<

return false;

}

return true;

}

RGB** JPEGData::YCbCrToRGB(const int* YUV){

RGB **res = new RGB *[ROW * max_v_samp_factor];

int matrixCount = YUV[0] + YUV[1] + YUV[2];

int crCount = 0, cbCount = 0;

//1=Y, 2=Cb, 3=Cr

//式子 scale*x,scale*y

double cb_h_samp_scale=component[1].h_samp_factor*1.0/max_h_samp_factor,

cb_v_samp_scale=component[1].v_samp_factor*1.0/max_v_samp_factor,

cr_h_samp_scale=component[2].h_samp_factor*1.0/max_h_samp_factor,

cr_v_samp_scale=component[2].v_samp_factor*1.0/max_v_samp_factor;

for (int i = 0; i < ROW * max_v_samp_factor; i++)

res[i] = new RGB[COL * max_h_samp_factor];

//此处直接生成rgb值

//注意,此处YCbCr的对应关系与采样因子有关

// cout<

for(int j=0;j

for(int k=0;k

int yPos = (j / ROW) * component[0].h_samp_factor + (k / COL);

int cbPos = YUV[0] + (int)((k / ROW) * cb_v_samp_scale) + (int)((j / COL) * cb_h_samp_scale);

int crPos = YUV[0] + YUV[1] + (int)((k / ROW) * cr_v_samp_scale) + (int)((j / COL) * cr_h_samp_scale);

double y = ycbcr[yPos][j % ROW][k % COL];

double cb = ycbcr[cbPos][(int)(j * cb_v_samp_scale)][(int)(k * cb_h_samp_scale)];

double cr = ycbcr[crPos][(int)(j * cr_v_samp_scale)][(int)(k * cr_h_samp_scale)];

res[j][k].red =RGBValueLimit(128+y+1.402 *cr);

res[j][k].green =RGBValueLimit(128+y-0.71414*cr-0.34414*cb);

res[j][k].blue =RGBValueLimit(128+y+1.772 *cb);

// 输出当前选择的矩阵

//cout<

// cout<

// <

// <

}

// cout<

}

// cout<

return res;

}

double** JPEGData::createDCTAndIDCTArray(int row){

double** res=new double*[row];

for(int i=0;i

// cout<

for(int i=0;i

for(int j=0;j

double t=0;

if(i==0) t=sqrt(1.0/row);

else t=sqrt(2.0/row);

res[i][j]=t*cos(M_PI*(j+0.5)*i/row);

// cout<

}

// cout<

}

return res;

}

void JPEGData::DCT(double** originMatrix){

//原理 Y=A*X*A'

vector> temp(ROW,vector(COL,0));

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=DCTAndIDCTArray[i][k]*originMatrix[k][j];

}

temp[i][j]=sum;

}

}

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=temp[i][k]*DCTAndIDCTArray[j][k];

}

originMatrix[i][j]=sum;

}

}

}

void JPEGData::IDCT(double** originMatrix){

//原理X=A'*Y*A

vector> temp(ROW,vector(COL,0));

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=DCTAndIDCTArray[k][i]*originMatrix[k][j];

}

temp[i][j]=sum;

}

}

for(int i=0;i

for(int j=0;j

double sum=0;

for(int k=0;k

sum+=temp[i][k]*DCTAndIDCTArray[k][j];

}

originMatrix[i][j]=sum;

}

}

}

void JPEGData::deQuality(double** originMatrix,int qualityID){

for(int i=0;i

for(int j=0;j

originMatrix[i][j]*=quality[qualityID].table[i*ROW+j];

}

}

}

void JPEGData::PAndNCorrect(double** originMatrix){

for(int i=0;i

if(i%2==1)

for(int j=0;j

originMatrix[i][j]=-originMatrix[i][j];

}

string JPEGData::FlagCkeck(fstream& file,int byteInfo){

if(byteInfo==0xff){

uint8_t info=file.get();

string res=bitset<8>(0xFF).to_string();

if(info==0xD9) {EOI=true;return "false";}

else if(info==0x00) return res;

return res + bitset<8>(info).to_string();

}

return bitset<8>(byteInfo).to_string();

}

uint16_t JPEGData::ReadByte(fstream& file,int len){

uint16_t res=file.get();

if(len!=1){

res=(res<<8)+(uint8_t)file.get();

}

return res;

}

uint16_t JPEGData::findHuffmanCodeByBit(fstream& file,int& length,int& pos,string& deque,int curValue,int& curValLen){

if(pos==length&&length>=HUFFMAN_DECODE_DEQUE_CACHE){//达到最大缓存

deque = deque.substr(pos);

int info=file.get();

string res=FlagCkeck(file,info);

string str=bitset<8>(info).to_string();

if(res=="false") res=bitset<8>(file.get()).to_string();

deque.append(res);

length = deque.length();

pos = 0;

}

else if(length==0 || pos>=length){

if(length==0){

deque="";

pos=0;

}

int info=file.get();

string res=FlagCkeck(file,info);

string str=bitset<8>(info).to_string();

if(res=="false") res=bitset<8>(file.get()).to_string();

deque.append(res);

length+=8;

}

curValue = (curValue << 1) + (uint8_t)(deque.at(pos++) - '0');

curValLen++;

return curValue;

}

NAME_SPACE_END()

BmpEncoder.h

#pragma once

#include

#include

#include "Image.h"

using namespace myUtil;

/* Bitmap Header, 54 Bytes */

static

unsigned char BmpHeader[54] =

{

0x42, 0x4D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x28, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1B, 0x01, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00,

0x00, 0x00, 0x60, 0xCD, 0x04, 0x00, 0x23, 0x2E, 0x00, 0x00, 0x23, 0x2E, 0x00, 0x00, 0x00, 0x00,

0x00, 0x00, 0x00, 0x00, 0x00, 0x00

};

void SetBitmapInfo(unsigned int size, int height, int width)

{

for (int i = 0; i < 4; i++)

{

// size of image ( header + data )

BmpHeader[2 + i] = size & 0xff;

size >>= 8;

// width of image

BmpHeader[18 + i] = width & 0xff;

width >>= 8;

// height of image

BmpHeader[22 + i] = height & 0xff;

height >>= 8;

}

}

/* BGR format 这是我粘来的改了映射部分代码 */

unsigned char *Encoder(const vector& buf, int height, int width, int mcu_height, int mcu_width, int &size)

{

uint8_t *bitmap = nullptr;

int rowSize = (24 * width + 31) / 32 * 4;

// compute the size of total bytes of image

size = rowSize * height + 54; // data size + header size

bitmap = new uint8_t [ size ];

// set the header info

SetBitmapInfo(size, height, width);

// fill the header area

for (int i = 0; i < 54; i++)

{

bitmap[i] = BmpHeader[i];

}

// fill the data area

for (int i = 0; i < height; i++)

{

// compute the offset of destination bitmap and source image

int idx = height - 1 - i;

int offsetDst = idx * rowSize + 54; // 54 means the header length

// int offsetSrc = i * width;

int offsetHeight = (int)floor(i*1.0/mcu_height)*(int)ceil(width*1.0/mcu_width);

// fill data

for (int j = 0; j < width * 3; j++)

{

int pos=(j/3)/mcu_width+offsetHeight;

if(pos>=buf.size()) pos=buf.size()-1;

RGB temp=buf[pos][i%mcu_height][(j/3)%mcu_height];

if(j%3==0) bitmap[offsetDst + j] = temp.blue;

else if(j%3==1) bitmap[offsetDst + j] = temp.green;

else if(j%3==2) bitmap[offsetDst + j] = temp.red;

// cout<

}

// fill 0x0, this part can be ignored

for (int j = width * 3; j < rowSize; j++)

{

bitmap[offsetDst +j] = 0x0;

}

}

return bitmap;

}

/* Save to file */

void Write(const char *fileName, uint8_t *buf, int &size)

{

FILE *fp = fopen(fileName, "wb+");

fwrite(buf, 1, size, fp);

fclose(fp);

}

主程序

#include

#include

#include

#include

#include

#include

#include

#include "Image.h"

#include "BmpEncoder.h"

using namespace std;

using namespace myUtil;

// void print(double** input){

// cout<

// for(int i=0;i<8;i++){

// for(int j=0;j<8;j++){

// cout<

// }

// cout<

// }

// cout<

// }

int main(){

string str="../img/Image/3.jpg";

JPEGData data;

clock_t startTime=clock();

data.readJPEG(str.c_str());

int size;

unsigned char *bitmap = Encoder(data.getRGB(), data.getHeight(), data.getWidth(),

8*data.getMaxHSampFactor(),

8*data.getMaxVSampFactor(), size);

Write("out.bmp", bitmap, size);

cout<

// DCT正反变换测试

// JPEGData data;

// double** arr=new double*[8];

// for(int i=0;i<8;i++){

// arr[i]=new double[8];

// for(int j=0;j<8;j++){

// arr[i][j]=(int)(rand()%100);

// }

// }

// print(arr);

// data.DCT(arr);

// print(arr);

// data.IDCT(arr);

// print(arr);

// FREE_LP_2(arr,8)

return 0;

}

项目环境gcc 7.3.0 工具CMake,源码链接

好文推荐

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: