兹于2017年11月,应《多媒体技术基础》课程实验的要求,本人就基于JPEG图像压缩解压算法做了较为深入的理解,用C++语言实现JPEG图像压缩解压算法。

JPEG图像压缩解压算法


一、实验目的

1.掌握JPEG的压缩原理;

2.熟知FDCT变换方法;

3.掌握JPEG所采用的行程编码、霍夫曼编码、差分编码等压缩技术;

4.掌握JPEG解压原理。


二、实验设备与环境

Windows 7 操作系统,Dev-C ++,MATLAB


三、实验内容、程序清单及运行结果

实验要求:

根据书本P76-P83内容,用Dev-C ++编写JPEG压缩与解压算法。(没有《多媒体技术基础(第三版)》,可以在这里下载PDF电子书,也可在中国知网查阅类似文章如:《用查表法快速实现二维8×8离散余弦逆变换的研究——纪秀花 张彩明 韩慧健》和《基于JPEG标准的静态图像压缩算法研究——张元伟 刘彦隆》等)

1. 实现JPEG压缩编码程序。

2. 实现JPEG解压程序。

实验操作和步骤:

1.打开Dev-C ++,进入编程环境,新建一个源代码文件,文件名任意;

2.将下面我所编写的代码拷贝到所新建的源代码文件中;

3.点击编译运行。

算法简介:

该算法操作是把一个单独的彩色图像分量分成8×8的图像块,即是对源图像的一个分量样本分成8×8的数据,然后进行压缩编码和解码。

自创C++源代码

/**
*   作者:戴文治
*   时间:2017年11月17日
*   描述:JPEG压缩与解压算法
*   测试环境:Dev-C++ 5.9.2
*/#include<iostream>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#define MAX 100
#define N 8     //N为每个图像分量的矩阵大小
using namespace std; /*亮度量化值表*/
struct  BrightnessQuantizedValueTable{int Q[N][N];BrightnessQuantizedValueTable(){int x[N][N]={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};for(int i=0;i<N;i++){for(int j=0;j<N;j++){Q[i][j] = x[i][j];}} }
};
BrightnessQuantizedValueTable brightnessQuantizedValueTable;//定义一个亮度量化值表/*亮度DC差值码表*/
struct BrightnessDC_DifferenceTableList{string brightnessDC_DifferenceTable[12];//亮度DC差值码表  类别(数组下标)与码字映射BrightnessDC_DifferenceTableList(){brightnessDC_DifferenceTable[0]="00";brightnessDC_DifferenceTable[1]="010";brightnessDC_DifferenceTable[2]="011";brightnessDC_DifferenceTable[3]="100";brightnessDC_DifferenceTable[4]="101";brightnessDC_DifferenceTable[5]="110";brightnessDC_DifferenceTable[6]="1110";brightnessDC_DifferenceTable[7]="11110";brightnessDC_DifferenceTable[8]="111110";brightnessDC_DifferenceTable[9]="1111110";brightnessDC_DifferenceTable[10]="11111110";brightnessDC_DifferenceTable[11]="111111110";}
};
BrightnessDC_DifferenceTableList brightnessDC_DifferenceTableList;//定义一个亮度DC差值码表/*AC系数熵编码时的中间符号*/
struct AC_EntropyCoding_MiddleSymbol{string R_S;int temp;
};
AC_EntropyCoding_MiddleSymbol ac_EntropyCoding_MiddleSymbol[N*N];//由于用函数返回结构体数组,里面的字符串会出现一些无法处理的乱码,故定义为全局变量 /*熵编码时的编码输出*/
struct EntropyCoding{string strTemp1;string strTemp;
};
EntropyCoding ac_EntropyCodingStr[N*N];//由于用函数返回结构体数组,里面的字符串会出现一些无法处理的乱码,故定义为全局变量 /* R/S与码字映射结点*/
struct StringMap{string key;string value;
}; /*亮度AC码表  R/S与码字映射表*/
/**特别注意,以下的亮度AC码表只适用于给出的测试数据,如要其它的测试数据,则必须补全亮度AC码表**/
//网上找到一个fantasy 的博客http://menmory.blog.163.com/blog/static/12690012620114535032530/ 这里面有比较详细的亮度AC码表等
struct StringMapList{StringMap stringMap[N*N];int partNum;  //该亮度AC码表中的条数 StringMapList(){//部分常用亮度AC码表stringMap[0].key = "0/0(EOB)";stringMap[0].value = "1010";stringMap[1].key = "0/1";stringMap[1].value = "00"; stringMap[2].key = "1/1";stringMap[2].value = "1100";stringMap[3].key = "1/2";stringMap[3].value = "11011";stringMap[4].key = "2/1";stringMap[4].value = "11100";stringMap[5].key = "3/2";stringMap[5].value = "111110111";stringMap[6].key = "F/0(ZRL)";stringMap[6].value = "11111111001";stringMap[7].key = "F/F";stringMap[7].value = "1111111111111110";partNum = 8;}
};
StringMapList stringMapList;//定义一个 部分常用亮度AC码表/*DC差值范围表,本人通过对表找规律发现如下规律 */
int DC_Difference(int temp){int temp1;if(temp == 0){temp1 = 0;}else{for(int i=1;i<=11;i++){if(abs(temp)<pow(2,i)){temp1 = i;break;}}}return temp1;
}/*AC系数范围表,本人通过对表找规律发现如下规律*/
int AC_Difference(int temp){int temp1;if(temp == 0){temp1 = 0;}else{for(int i=1;i<=10;i++){if(abs(temp)<pow(2,i)){temp1 = i;break;}}}return temp1;
}/*将正整数十进制转换成二进制*/
string TenToTwo(int temp){string strTemp="";//旧方法
//  for(int k=abs(temp);k>0;k=k/2){
//      strTemp = strTemp + (k%2==1?'1':'0');
//  }
//  //倒置
//  int len = strTemp.length();
//  for(int k=0;k<len/2;k++){
//      char t = strTemp[k];
//      strTemp[k] = strTemp[len-1-k];
//      strTemp[len-1-k] = t;
//  }//新方法 char str[N*N];itoa(temp,str,2);strTemp = str; return strTemp;
}/*将正整数二进制转换成十进制*/
int TwoToTen(string strTemp){int temp=0;for(int i=0;i<strTemp.length();i++){temp = temp*2+strTemp[i]-'0';}return temp;
} /*将一个负数的二进制串逐位取反*/
string ConvertToComplement(string strTemp){string str = "";for(int i=0;i<strTemp.length();i++){str = str + (strTemp[i]=='1'?'0':'1');}return str;
} /*DC系数编码*/
EntropyCoding DC_EntropyCoding(int &temp,int &temp1){//对DC系数生成中间符号(中间符号(temp1,temp))//查DC差值表temp1 = DC_Difference(temp);/*测试*/
//  cout<<temp1<<endl;//对中间符号通过查表进行符号编码 //对 temp1通过查亮度DC差值码表进行熵编码EntropyCoding dc_EntropyCodingStr; dc_EntropyCodingStr.strTemp1 = brightnessDC_DifferenceTableList.brightnessDC_DifferenceTable[temp1];//对 temp进行转换成补码 //先将 temp转换成二进制串dc_EntropyCodingStr.strTemp = TenToTwo(abs(temp));//转换成补码if(temp<0){dc_EntropyCodingStr.strTemp = ConvertToComplement(dc_EntropyCodingStr.strTemp);   }/*测试*/
//  cout<<dc_EntropyCodingStr.strTemp1<<"\t"<<dc_EntropyCodingStr.strTemp<<endl;return dc_EntropyCodingStr;
}/*AC系数编码*/
bool AC_EntropyCoding(int F_[N][N],int &index){//对AC系数生成中间符号中/后的部分 int SSSS[N][N];//查AC系数范围表,本人通过对表找规律发现如下规律 for(int i=0;i<N;i++){for(int j=0;j<N;j++){SSSS[i][j] = AC_Difference(F_[i][j]);}}/*测试*/
//  for(int i=0;i<N;i++){
//      for(int j=0;j<N;j++){
//          cout<<SSSS[i][j]<<" ";
//      }
//      cout<<endl;
//  }//Z字形编码int count = 0;//计算0的个数int i,j,t; for(i=0,j=1,t=1;t<=N-2;t++){//以下语句设为一个周期,大概要执行N-2个周期(这里N=8,通过观察发现每一下一上为一周期,则有6个周期+半段) //向左下方向 for(;i<N&&j>=0;i++,j--){if(F_[i][j]==0){count++;}else{char countString[N*N];itoa(count,countString,10);//将整数count转换为字符串并保存在countString(以10进制方式,也可指定2、8、10、16等进制实现进制转换,进制转换新玩法) string strTemp = "/";strTemp = countString + strTemp;//cout<<"--"<<strTemp<<"--"<<endl;char SSSS_String[N*N];itoa(SSSS[i][j],SSSS_String,10);strTemp = strTemp + SSSS_String;//cout<<"**"<<strTemp<<"**"<<endl;//中间符号 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp;ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j];index++;count = 0;//置为0 ,计算下个不为0的数前面0的个数 }}if(i>=N&&j<0){//当出现正中间往下时,挪回正规 i--;j = j+2;}else if(i>=N){//当出现往下突出时,挪回正规 i--;j = j+2;}else if(j<0){//当出现往左突出时,挪回正规j++; }//向右上方向 for(;i>=0&&j<N;i--,j++){if(F_[i][j]==0){count++;}else{char countString[N*N];itoa(count,countString,10);string strTemp = "/";strTemp = countString + strTemp;//cout<<"--"<<strTemp<<"--"<<endl;char SSSS_String[N*N];itoa(SSSS[i][j],SSSS_String,10);strTemp = strTemp + SSSS_String;//cout<<"**"<<strTemp<<"**"<<endl;//中间符号 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp;ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j];index++;count = 0;//置为0 ,计算下个不为0的数前面0的个数}}if(i<0&&j>=N){//当出现正中间往上时,挪回正规 j--;i = i+2;  }else if(i<0){//当出现往上突出时,挪回正规 i++;}else if(j>=N){//当出现往右突出时,挪回正规 j--;i = i+2;}}//剩下半个周期的编码 //向左下方向 for(;i<N&&j>=0;i++,j--){if(F_[i][j]==0){count++;}else{char countString[N*N];itoa(count,countString,10);string strTemp = "/";strTemp = countString + strTemp;//cout<<"--"<<strTemp<<"--"<<endl;char SSSS_String[N*N];itoa(SSSS[i][j],SSSS_String,10);strTemp = strTemp + SSSS_String;//cout<<"**"<<strTemp<<"**"<<endl;//中间符号 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp;ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j];index++;count = 0;//置为0 ,计算下个不为0的数前面0的个数}}if(i>=N){//当出现往下突出时,挪回正规i--;j = j+2;}if(F_[i][j]==0){//最后一个点count++;ac_EntropyCoding_MiddleSymbol[index].R_S = "0/0(EOB)";ac_EntropyCoding_MiddleSymbol[index].temp = INT_MAX;index++; }else{char countString[N*N];itoa(count,countString,10);string strTemp = "/";strTemp = countString + strTemp;//cout<<"--"<<strTemp<<"--"<<endl;char SSSS_String[N*N];itoa(SSSS[i][j],SSSS_String,10);strTemp = strTemp + SSSS_String;//cout<<"**"<<strTemp<<"**"<<endl;//中间符号 ac_EntropyCoding_MiddleSymbol[index].R_S = strTemp+"(EOB)";ac_EntropyCoding_MiddleSymbol[index].temp = F_[i][j];index++;}/*测试*/
//  for(int k=0;k<index;k++){
//      cout<<ac_EntropyCoding_MiddleSymbol[k].R_S<<"\t"<<ac_EntropyCoding_MiddleSymbol[k].temp<<endl;
//  }//对中间符号进行符号编码 //对R/S通过查亮度AC码表进行熵编码for(int u=0;u<index;u++){for(int v=0;v<stringMapList.partNum;v++){if(ac_EntropyCoding_MiddleSymbol[u].R_S == stringMapList.stringMap[v].key){ac_EntropyCodingStr[u].strTemp1 = stringMapList.stringMap[v].value;}}//对 temp进行转换成补码 //先将 temp转换成二进制串if(ac_EntropyCoding_MiddleSymbol[u].R_S!="0/0(EOB)"){ac_EntropyCodingStr[u].strTemp = TenToTwo(abs(ac_EntropyCoding_MiddleSymbol[u].temp));//转换成补码if(ac_EntropyCoding_MiddleSymbol[u].temp<0){ac_EntropyCodingStr[u].strTemp = ConvertToComplement(ac_EntropyCodingStr[u].strTemp); }/*测试*/
//          cout<<"**********"<<ac_EntropyCodingStr[u].strTemp<<endl;}else{ac_EntropyCodingStr[u].strTemp = ""+'\0';/*测试*/
//          cout<<"**********+"<<ac_EntropyCodingStr[u].strTemp<<endl;}}/*测试*/
//  for(int k=0;k<index;k++){
//      cout<<ac_EntropyCodingStr[k].strTemp1<<" "<<ac_EntropyCodingStr[k].strTemp<<endl;
//  }return true;
}int main(){cout<<"压缩编码过程:"<<endl; /*压缩编码*/const double PI = acos(-1);
//  double f[N][N]={139,144,149,153,155,155,155,155,
//                  144,151,153,156,159,156,156,156,
//                  150,155,160,163,158,156,156,156,
//                  159,161,162,160,160,159,159,159,
//                  159,160,161,162,162,155,155,155,
//                  161,161,161,161,160,157,157,157,
//                  162,162,161,163,162,157,157,157,
//                  162,162,161,161,163,158,158,158};double f[N][N];double ff[N][N],F[N][N];/*输入图像的一个分量样本*/for(int i=0;i<N;i++){for(int j=0;j<N;j++){cin>>f[i][j];}}cout<<"源图像的一个分量样本:"<<endl; /*输出—源图像的一个分量样本*/for(int i=0;i<N;i++){for(int j=0;j<N;j++){cout<<f[i][j]<<"\t";}cout<<endl;}//图像的一个分量样本-128后 for(int i=0;i<N;i++){for(int j=0;j<N;j++){ff[i][j] = f[i][j]-128;}}cout<<"源图像的一个分量样本-128后:"<<endl; /*输出—图像的一个分量样本-128后*/for(int i=0;i<N;i++){for(int j=0;j<N;j++){cout<<ff[i][j]<<"\t";}cout<<endl;}//由公式计算DCT变化后的系数矩阵for(int u=0;u<N;u++){for(int v=0;v<N;v++){double temp = 0.0;for(int i=0;i<N;i++){for(int j=0;j<N;j++){temp = temp + ff[i][j]*cos((2*i+1)*u*PI*1.0/16)*cos((2*j+1)*v*PI*1.0/16);}}F[u][v] = 1.0/4*(u==0?1.0/sqrt(2):1)*(v==0?1.0/sqrt(2):1)*temp;}}/*输出—DCT变化后的系数矩阵*///DCT变化后的系数矩阵cout<<"DCT变化后的系数矩阵:"<<endl;for(int u=0;u<N;u++){for(int v=0;v<N;v++){printf("%.1f\t",F[u][v]);//cout<<F[u][v]<<" ";}cout<<endl;}//利用公式将DCT变化后的系数矩阵转换为规格化量化系数矩阵 int F_[N][N];//规格化量化系数矩阵 for(int i=0;i<N;i++){for(int j=0;j<N;j++){//二维数组Q 为亮度量化值表 F_[i][j] = (int)((F[i][j]/brightnessQuantizedValueTable.Q[i][j])>0.0)?floor((F[i][j]/brightnessQuantizedValueTable.Q[i][j]) + 0.5) : ceil((F[i][j]/brightnessQuantizedValueTable.Q[i][j]) - 0.5);//进行量化,然后进行四舍五入 }}/*输出—规格化量化系数矩阵*///规格化量化系数矩阵 cout<<"规格化量化系数:"<<endl;for(int u=0;u<N;u++){for(int v=0;v<N;v++){cout<<F_[u][v]<<"\t";}cout<<endl;}//对DC系数生成中间符号(temp1,temp)int temp = F_[0][0];int temp1;EntropyCoding dc_EntropyCodingStr = DC_EntropyCoding(temp,temp1);cout<<"中间符号:"<<endl; /*输出—DC系数中间符号*/cout<<temp1<<"\t"<<temp<<endl; int index=0; //AC系数生成中间符号的个数 //对AC系数生成中间符号AC_EntropyCoding(F_,index);/*输出—AC系数中间符号*/for(int k=0;k<index;k++){cout<<ac_EntropyCoding_MiddleSymbol[k].R_S<<"\t";if(ac_EntropyCoding_MiddleSymbol[k].R_S!="0/0(EOB)"){cout<<ac_EntropyCoding_MiddleSymbol[k].temp;}cout<<endl;}cout<<"熵编码输出:"<<endl; /*输出—DC系数熵编码输出*/cout<<dc_EntropyCodingStr.strTemp1<<"\t"<<dc_EntropyCodingStr.strTemp<<endl;/*输出—AC系数熵编码输出*/for(int k=0;k<index;k++){cout<<ac_EntropyCodingStr[k].strTemp1<<"\t"<<ac_EntropyCodingStr[k].strTemp<<endl;}cout<<"----------------------------------------------------------------------"<<endl<<endl;cout<<"解码过程:"<<endl; //下面的解码过程只用到了压缩过程传输过来的熵编码和熵编码中AC系数的个数index(也可以通过一个循环来计算出这个index) cout<<"待解码的熵编码:"<<endl;/*输出—DC系数熵编码输出*/cout<<dc_EntropyCodingStr.strTemp1<<"\t"<<dc_EntropyCodingStr.strTemp<<endl;/*输出—AC系数熵编码输出*/for(int k=0;k<index;k++){cout<<ac_EntropyCodingStr[k].strTemp1<<"\t"<<ac_EntropyCodingStr[k].strTemp<<endl;}/*解码*///将DC系数熵编码的编码转换为中间符号//对strTemp1进行反向查找亮度DC差值码表得到temp1int Itemp1;for(int i=0;i<11;i++){if( dc_EntropyCodingStr.strTemp1 == brightnessDC_DifferenceTableList.brightnessDC_DifferenceTable[i]){Itemp1 = i;}}//对strTemp进行反向补码得到temp,可通过查找规律发现若是负数转换为补码后首个数字必为0,正数必不为0 int Itemp;if(dc_EntropyCodingStr.strTemp[0]=='0'){//为负数,先取反 string tempStr = ConvertToComplement(dc_EntropyCodingStr.strTemp);//转换为10进制Itemp = TwoToTen(tempStr);//加负号Itemp = -Itemp;} else{//为正数,直接转换为10进制即可 Itemp = TwoToTen(dc_EntropyCodingStr.strTemp);}cout<<"中间符号:"<<endl; /*输出—DC中间符号*/cout<<Itemp1<<"\t"<<Itemp<<endl;//将AC系数熵编码的编码转换为中间符号AC_EntropyCoding_MiddleSymbol Iac_EntropyCoding_MiddleSymbol[N*N];//遍历所有的AC系数熵编码的编码对strTemp1进行反向查找亮度AC码表得到R_Sfor(int i=0;i<index;i++){for(int u=0;u<stringMapList.partNum;u++){if(ac_EntropyCodingStr[i].strTemp1==stringMapList.stringMap[u].value){Iac_EntropyCoding_MiddleSymbol[i].R_S = stringMapList.stringMap[u].key;}}//对strTemp进行反向补码得到temp,可通过查找规律发现若是负数转换为补码后首个数字必为0,正数必不为0if(ac_EntropyCodingStr[i].strTemp[0]=='0'){//为负数,先取反 string tempStr = ConvertToComplement(ac_EntropyCodingStr[i].strTemp);//转换为10进制Iac_EntropyCoding_MiddleSymbol[i].temp = TwoToTen(tempStr);//加负号Iac_EntropyCoding_MiddleSymbol[i].temp = -Iac_EntropyCoding_MiddleSymbol[i].temp;} else{//为正数,直接转换为10进制即可 Iac_EntropyCoding_MiddleSymbol[i].temp = TwoToTen(ac_EntropyCodingStr[i].strTemp);}}/*输出—AC中间符号*/for(int i=0;i<index;i++){cout<<Iac_EntropyCoding_MiddleSymbol[i].R_S<<"\t";if(Iac_EntropyCoding_MiddleSymbol[i].R_S!="0/0(EOB)"){cout<<Iac_EntropyCoding_MiddleSymbol[i].temp;}cout<<endl;} //规格化量化系数 int IF_[N][N];//初始化矩阵 for(int u=0;u<N;u++){for(int v=0;v<N;v++){IF_[u][v]=0; }}//DC系数//还原编码 IF_[0][0] = Itemp;//AC系数//Z字形还原编码int count;//计算0的个数int a=0,b=1; //初始位置for(int h=0;h<index;h++){//将/前的字符串转换为整数count = 0;for(int w=0;Iac_EntropyCoding_MiddleSymbol[h].R_S[w]!='/';w++){count = count*10 + Iac_EntropyCoding_MiddleSymbol[h].R_S[w]-'0';} /*测试*/
//      cout<<"count:"<<count<<endl; while(Iac_EntropyCoding_MiddleSymbol[h].R_S!="0/0(EOB)"){//根据查找规律发现,当行+列为奇数时向左下方向,当行+列为偶数时为右上方向 if((a+b)%2==0){//偶数,向右上方向 for(;count>=0&&a>=0&&b<N;a--,b++){if(count==0){//此时放temp IF_[a][b] = Iac_EntropyCoding_MiddleSymbol[h].temp;count--;break; }else{//此时放0 IF_[a][b] = 0;count--;}}if(count<0){//向右上移动 a--;b++;}if(a<0&&b>=N){//当出现正中间往上时,挪回正规  b--;a = a+2;}else if(a<0){//当出现往上突出时,挪回正规a++; }else if(b>=N){//当出现往右突出时,挪回正规 b--;a = a+2;}if(count<0){//跳出到第一层循环 break; }} else{//奇数,向左下方向 for(;count>=0&&a<N&&b>=0;a++,b--){if(count==0){//此时放temp IF_[a][b] = Iac_EntropyCoding_MiddleSymbol[h].temp;count--;break; }else{//此时放0 IF_[a][b] = 0;count--;}}if(count<0){//向左下移动 a++;b--;}if(a>=N&&b<0){//当出现正中间往下时,挪回正规 a--;b = b+2;}else if(a>=N){//当出现往下突出时,挪回正规 a--;b = b+2;}else if(b<0){//当出现往左突出时,挪回正规b++;}if(count<0){//跳出到第一层循环 break; }}}}/*输出—规格化量化系数矩阵*///规格化量化系数矩阵 cout<<"规格化量化系数:"<<endl;for(int u=0;u<N;u++){for(int v=0;v<N;v++){cout<<IF_[u][v]<<"\t";}cout<<endl;}//利用公式将规格化量化系数矩阵转换为逆量化后的系数矩阵  double IF[N][N];//逆量化后的系数矩阵for(int i=0;i<N;i++){for(int j=0;j<N;j++){//二维数组Q 为亮度量化值表IF[i][j] = 1.0*IF_[i][j]*brightnessQuantizedValueTable.Q[i][j];}}/*输出—逆量化后的系数矩阵*/cout<<"逆量化后的系数矩阵:"<<endl;for(int i=0;i<N;i++){for(int j=0;j<N;j++){cout<<IF[i][j]<<"\t";}cout<<endl;}//由公式计算IDCT变化后的系数矩阵double Iff[N][N];for(int i=0;i<N;i++){for(int j=0;j<N;j++){double sum = 0.0;for(int u=0;u<N;u++){for(int v=0;v<N;v++){sum = sum + (u==0?1.0/sqrt(2.0):1.0)*(v==0?1.0/sqrt(2.0):1.0)*IF[u][v]*cos((2*i+1)*u*PI*1.0/16)*cos((2*j+1)*v*PI*1.0/16);  }}Iff[i][j] = 1.0/4*sum;}}/*输出—IDCT变化后的系数矩阵*/cout<<"IDCT变化后的系数矩阵:"<<endl;for(int i=0;i<N;i++){for(int j=0;j<N;j++){//cout<<Iff[i][j]<<"\t";printf("%.0f\t",Iff[i][j]);}cout<<endl;}cout<<"源图像的一个分量样本的重构图像:"<<endl; /*IDCT变化后的系数矩阵+128后变成源图像的一个分量样本的重构图像*/double If[N][N]; for(int i=0;i<N;i++){for(int j=0;j<N;j++){If[i][j] = Iff[i][j]+128;}}/*输出—源图像的一个分量样本的重构图像*/for(int i=0;i<N;i++){for(int j=0;j<N;j++){//cout<<If[i][j]<<"\t";printf("%.0f\t",If[i][j]);}cout<<endl;}return 0;
} 

测试用例(书本P83):

139 144 149 153 155 155 155 155

144 151 153 156 159 156 156 156

150 155 160 163 158 156 156 156

159 161 162 160 160 159 159 159

159 160 161 162 162 155 155 155

161 161 161 161 160 157 157 157

162 162 161 163 162 157 157 157

162 162 161 161 163 158 158 158

压缩编码:

解码:

利用MATLAB将测试用例的数据生成jpeg格式图片

将得到后的图片放大:

再将经过JPEG压缩解压后的数据生成jpeg格式图片

将得到后的图片放大:

四、实验结论、实验体会

本次实验算是我目前“算法”编码生涯中编码最长的一次,一共700多行的代码。在编码途中我遇到了非常多的问题。

1. 系统问题:在我初次编完750行左右代码的时候,代码突然抽疯,丢失了很多代码,只剩下600行左右,而且有些后面的代码跑到中间去了。点返回键显示返回栈错误,此时我的内心是崩溃的,但是没办法只能重新编写丢失的代码,并对部分代码进行优化修改。然后每写完一段代码,马上新建一个TXT文件保存代码,以防再次发生这种情况。

2. 编程语言问题:在这次编码过程,我愈发的感觉C++/C 类库真的太少了,不利于快速编程。例如:

2.1 string的字符串操作能力及其低下,很多都要自己编写,甚至进行字符串连接时也是非常苛刻,不能将两个常量字符串进行连接、其它类型的数据不能直接跟字符串连接,可用操作函数极少等等,与JAVA语言完全没得比。其次,C++语言的矩阵操作能力也是比较低下的,与MATLAB语言完全没得比,而且我觉得此次的实验更应该用MATLAB语言实现,这样会节省大量的工作;

2.2 用函数返回结构体数组,里面的字符串会出现一些无法处理的乱码,最后不得不将其定义为全局变量;

3. 参照表格繁多复杂、C++/C语言数据类型较为贫乏:有一些可以通过观察找到规律编写,而有一些则是另外算法得到的结果,因此为方便起见,将一些找不到规律的表直接用映射方法进行存储。

4. 课本出错问题:课本上的IDCT逆变换公式有问题,通过在网上查找正确的公式进行编码;其次课本上给出的测试用例也有一些问题;

5. 编码问题:Z字形编码,编码时通过模拟其轨迹,以及查找规律;

6. 编码问题:代码命名重复,导致一些结果不正确或编译出问题,要时刻明白每个变量的生命周期,尽最大努力缩短变量的生命周期,减少不必要的冲突;

7. 编码问题:对变量的数据类型要时刻有个清晰的认识;

8. 编码规范问题:尽量多的注释,以及对不同类别注释的区分;

9. 不同平台不兼容问题:本次实验我在Dev-C ++开发,而当我把代码放到VS2010时却出现了许多编译错误,需要进行适当修改,VS对语法检查非常苛刻。

最后,在此次编码过程中我收获颇多,编写了很多非常巧妙的算法,精简了很多常用算法,使得代码效率更高、更简洁、更方便。比如:十进制转换为其它进制的算法,只需要几条非常简短的代码就能实现,根本不用自己编写繁琐的代码,函数原型为char*itoa(int value,char*string,int radix); int value 被转换的整数,char *string 转换后储存的字符数组,int radix 转换进制数,如2,8,10,16 进制等;功能:将任意类型的数字转换为字符串,使用时必须包含<cstdlib>头文件,开启进制转换新玩法。而至于二进制转换为十进制以及二进制串逐位取反的算法也进行了巧妙的设计。

    本来想直接将一张JPEG格式的图片进行压缩编码,然后解压输出,但是经过尝试发现需要编写更庞大的代码,对代码要进行一些修改、增加更多的数据,在时间上是不太允许的,故而没有实现这个想法。
写的不是很好,望各位大神多多指正,不喜勿喷。

JPEG图像压缩解压算法——C++实现相关推荐

  1. 文件压缩c语言程序代码,C语言程序设计之RLE压缩解压算法

    先介绍一下RLE压缩算法: 游程编码(Run-Length Encoding, RLE)又称行程长度编码或者变动长度编码法,在控制理论中对于二值图像而言是一种编码方法,对连续的黑,白向像素以不同的码字 ...

  2. libjpeg库的简单使用使用----jpeg图片解压

    libjpeg库其实已经可以满足我们日常处理日常简单图片之间的转换了.下面就来介绍一下jpeg库的解压和压缩过程. 首先来讲解压操作过程: 1.分配jpeg对象结构体空间,并初始化. 2.指定解压数据 ...

  3. RLE压缩解压算法的完整实现

    和第四题一样同样是各种东拼西凑的结果,希望对一部分人有帮助.要用的话请至少改一下变量名和函数顺序并且搞懂为什么,不要直接抄袭.在此感谢陈德创大佬的无私帮助以及陈万庆老师提供的音频测试文件 看之前请先搞 ...

  4. 字符串压缩、解压算法

    1.字符串压缩作用: 减轻传输压力 简单加密传输内容 2.压缩算法进行字符串压缩测试效率对比结果 deflater > gzip 3.常用加密工具方法demo deflater压缩工具类 imp ...

  5. JS LZW算法压缩与解压

    JS LZW算法压缩与解压,一个JavaScript二进制接口源码,LZW 压缩解压算法,压缩比确实不错,代码不超过200行. <html> <head> <meta h ...

  6. 基于DCT变换的JPEG图像压缩

    基于DCT变换的JPEG图像压缩 摘 要:图像和视频通常在计算机中表示会占用非常大的空间,而出于节省硬盘空间的考虑,往往要进行压缩.而随着网络的发展,图像压缩技术越来越被人所重视.DCT变换是图像压缩 ...

  7. C++ 项目实战:跨平台的文件与视频压缩解压工具的设计与实现

    C++实战:跨平台文件与视频压缩解压工具的设计与实现 一.引言(Introduction) 1.1 项目背景与目标 1.2 技术选型:C++.FFmpeg.libarchive.libzip.Qt C ...

  8. JPEG2000压缩DICOM文件的解压(三)

    DICOM文件JPEG2000压缩与解压(三) QQ:2556741313 关键词:DCMTK.DICOM.JPEG2000 前面两篇关于DICOM文件JPEG2000解压的内容很简单,主要是没有那么 ...

  9. ZIP 也能边下载边解压?流式解压技术揭秘!

    对于一个 ZIP 文件,由于标准的解压方式总是从读取文件的末尾开始的,因此必须下载完整个 ZIP 解压后才能访问.当用户通过网络访问 ZIP 文件时,下载解压所带来的耗时将大大降低用户体验.那么能不能 ...

最新文章

  1. 【欧拉回路】解题报告:luogu P6066 [USACO]Watchcow (欧拉回路详解)【模板】
  2. 18.11 LVS DR模式搭建 18.12 keepalived + LVS
  3. 阿里云全球布局逾200个数据中心,成立海外孵化中心深耕本地市场
  4. 在Windows C程序中使用Unicode编码
  5. ARM中C语言和汇编语言的混合编程
  6. 2017/06/23 linux软件管理构建本地源
  7. CF1404C:Fixed Point Removal(离线)(树状数组二分)
  8. linux中gid和groups区别,linux用户与组管理
  9. IntelliJ IDEA 2020.1 快速查找文件
  10. 《CATIA V5 从入门到精通(第二版)》——2.5 草绘可视化设置(Visualization)
  11. 一维数组求平均值c语言编程软件,请问一道c++编程:求一维数组中全部元素的平均值?...
  12. 有多少人乘坐公交车时用NFC付钱?
  13. windows的回车换行“\r\n“,Linux的回车换行“\n“
  14. 孙鑫-MFC笔记七--文件与注册表
  15. 逻辑结构与存储结构关系
  16. LayUI分页查询展示数据(SSH框架)
  17. 安卓系统强制旋转屏幕实现横竖屏切换
  18. 互联网自动化赚钱的方法
  19. 常见的hash函数算法
  20. -20dB谱宽是什么意思?

热门文章

  1. python图像倾斜校正_python 图像倾斜校正
  2. 微信小程序实例:开发showToast消息提示接口
  3. Domoticz添加实时天气信息显示
  4. win7双屏幕,双任务栏
  5. Java实现输出特殊偏旁的汉字的功能
  6. 避免过多if - else的新姿势:策略模式、工厂 + 策略
  7. android平台的开源框架的思考
  8. python如何计算平均分_python脚本如何输入成绩求平均分?
  9. [WebView] - WebView leaked
  10. 别错过,教你如何用电脑玩手机