#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include "windows.h"
#include <cmath>
using namespace std;
#define PI 3.14159262
#define N 8
FILE *imagefout;
int bmpWidth;//图像的宽
int bmpHeight;//图像的高
RGBQUAD *pColorTable;//颜色表指针
int biBitCount;//图像类型,每像素位数
unsigned char image[256*256];//读入的图像
int resultimage[256*256];//压缩后的编码表示
unsigned char resultbit[256*256];//解压缩后图像保存文件的信息流
double round(double r)
{
return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}
unsigned char * readBmp(char *file)
{
int len=0;
FILE *fp=fopen(file,"rb");
int size=sizeof(BITMAPFILEHEADER);
//跳过位图文件头结构BITMAPFILEHEADER
fseek(fp, sizeof(BITMAPFILEHEADER),0);
//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1,fp);
//获取图像宽、高、每像素所占位数等信息
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;
//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte=(bmpWidth * biBitCount/8+3)/4*4;
//灰度图像有颜色表,且颜色表表项为256
if(biBitCount==8){
//申请颜色表所需要的空间,读颜色表进内存
pColorTable=new RGBQUAD[256];
fread(pColorTable,sizeof(RGBQUAD),256,fp);
}
//申请位图数据所需要的空间,读位图数据进内存
unsigned char *pBmpBuf=new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf,1,lineByte * bmpHeight,fp);
//关闭文件
fclose(fp);
return pBmpBuf;
}
bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height,int biBitCount, RGBQUAD *pColorTable)
{
//如果位图数据指针为0,则没有数据传入,函数返回
if(!imgBuf)
return 0;
//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
int colorTablesize=0;
if(biBitCount==8)
colorTablesize=1024;
//待存储图像数据每行字节数为4的倍数
int lineByte=(width * biBitCount/8+3)/4*4;
//以二进制写的方式打开文件
FILE *fp=fopen(bmpName,"wb");
if(fp==0) return 0;
//申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp类型
//bfSize是图像文件4个组成部分之和
fileHead.bfSize= sizeof(BITMAPFILEHEADER)+ sizeof(BITMAPINFOHEADER)+ colorTablesize + lineByte*height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
//bfOffBits是图像文件前3个部分所需空间之和
fileHead.bfOffBits=54+colorTablesize;
//写文件头进文件
fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp);
//申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount=biBitCount;
head.biClrImportant=0;
head.biClrUsed=0;
head.biCompression=0;
head.biHeight=height;
head.biPlanes=1;
head.biSize=40;
head.biSizeImage=lineByte*height;
head.biWidth=width;
head.biXPelsPerMeter=0;
head.biYPelsPerMeter=0;
//写位图信息头进内存
fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp);
//如果灰度图像,有颜色表,写入文件
if(biBitCount==8)
fwrite(pColorTable, sizeof(RGBQUAD),256, fp);
//写位图数据进文件
fwrite(imgBuf, height*lineByte, 1, fp);
//关闭文件
fclose(fp);
return 1;
}
void DCT(int input[N][N],float output[N][N])
{
short i,j,p,q;
double coefficient1,coefficient2;
for(p=0;p<N;p++)
{
for(q=0;q<N;q++)
{
if(p==0)
coefficient1=sqrt(1.0/N);
else
coefficient1=sqrt(2.0/N);
if(q==0)
coefficient2=sqrt(1.0/N);
else
coefficient2=sqrt(2.0/N);
double tmp=0.0;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
tmp+=input[i][j]*cos((2*i+1)*PI*p/(2*N))*cos((2*j+1)*PI*q/(2*N));//形成新的矩阵
output[p][q]=short(round(coefficient1*coefficient2*tmp));
}
}
}
void _DCT(float input[N][N],int output[N][N])
{
short i,j,p,q;
double coefficient1,coefficient2;
for(i=0;i<N;i++)
{
for(j=0;j<N;j++)
{
double tmp=0.0;
for(p=0;p<N;p++)
for(q=0;q<N;q++)
{
if(p==0)
coefficient1=sqrt(1.0/N);
else
coefficient1=sqrt(2.0/N);
if(q==0)
coefficient2=sqrt(1.0/N);
else
coefficient2=sqrt(2.0/N);
tmp+=coefficient1*coefficient2*input[p][q]*cos((2*i+1)*PI*p/(2*N))*cos((2*j+1)*PI*q/(2*N));
}
output[i][j]=short(round(tmp));
}
}
}
void sch(int Fy[8][8],float m[8][8],int n[8][8]) //量化子程序
{//m是需要量化的系数。n是量化后的系数
for(int x=0;x<=7;x++)
for(int y=0;y<=7;y++)
{
n[x][y]=(int)(m[x][y]/Fy[x][y]); //除量化系数,取整数
}
}
void _sch(int Fy[8][8],int n[8][8],float m[8][8]) //反量化子程序
{//n是需要反量化的系数。m是反量化后的系数
for(int x=0;x<=7;x++)
for(int y=0;y<=7;y++)
{
m[x][y]=n[x][y]*Fy[x][y]; //乘以量化系数
}
}
int bianma(int n[8][8],int offset) //一维行程编码子程序
{
int k[64],a[128],i,j,t=0,e=1,d=0,l;
for(i=0;i<=7;i++) //把二维的编码数组转换成一维数组
for(j=0;j<=7;j++)
k[t++]=n[i][j];
for(i=0;i<=63;i++) //计算编码后的数据,将其存在数组a中
{
l=i+1;
if(k[i]==k[l])
e++;
else
{
a[d++]=e;
a[d++]=k[i];
e=1;
}
}
for(i=0;i<=127;i++) //输出编码后的数据
{
offset++;
resultimage[offset]=a[i];//把得到的编码后的数组a的值复制到数组resultimage中
}
return d;//返回数组a的长度
}
int _bianma(int len,int bia[])
{
int k=0;
for(int i=0;i<len;i+=2)
{
for(int j=0;j<resultimage[i];j++)
{
bia[k]=resultimage[i+1];
k++;
}
}
return k;
}
void main()
{
int totals=0;//总的原始数据量
int totalr=0;//总的编码后的数据量
int offset=-1;
float m[8][8];
int number=0,n[8][8];
/*8*8图像块数据*/
int a[8][8];
unsigned char *aaa=readBmp("lena.bmp");
for(int i=0;i<256*256;i++)
{
image[i]=aaa[i];
}
int Fy[8][8]={{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,103,77},{49,64,78,87,103,121,120,101},{72,92,95,98,112,100,103,99}};
for(i=0;i<256*256;)
{
for(int i1=0;i1<8;i1++)
{
for(int j1=0;j1<8;j1++)
{
a[i1][j1]=image[i++];
}
}
DCT(a,m); //调用DCT变换函数,a为需要变换的块,m为变换后的块。
sch(Fy,m,n); //调用量化函数,Fy是量化矩阵,m是需要量化的块,n是量化后的矩阵。
offset=offset+number;
number=bianma(n,offset); //调用编码函数,n是需要编码的矩阵
//s=8*64; //64个像素值,每个像素用八位表示。
totals=totals+8*64; //原始的总数据量
//data=number/2*3+number/2*8;
//编码后的数据总量
totalr=totalr+number*8;;
}
printf("压缩率: \n");
printf("%f%% \n",totalr*1.0/totals*100); //输出压缩率
//////////////////////////////////////////////////////////////////
/*解码过程*/
int bia[256*256];
int len=_bianma(offset,bia);//行编码解码,返回解码后的总长度
int k=0;
int X=0,Y=0;
int rb=0;
for( i=0;i<len;)
{
for(int i1=0;i1<8;i1++)
{
for(int j1=0;j1<8;j1++)
{
n[i1][j1]=bia[i++];
}
}
_sch(Fy,n,m); //反量化函数
_DCT(m,a); //反DCT变化
for(int p=0;p<8;p++)
{
for(int q=0;q<8;q++)
{
resultbit[rb]=a[p][q];
rb++;
}
}
}
if(saveBmp("decode.bmp", resultbit, bmpWidth, bmpHeight, biBitCount, pColorTable))
printf("保存成功");
else
printf("保存失败");
}