《信息论》的课程设计,要求完成标题所述内容
//仅供参考
使用matlab或更方便简洁,但如果想过一遍流程的话可以参考下面的代码,编译需要安装opencv
代码还有很大优化的空间,可以用向量解决代码中指针较多的问题。也可以用多线程优化运行速度,LZ编码类可以试着增加独立性,取消与霍夫曼编码的关联,建议试运行时使用较小的图片,过一下main函数。
文中参考网址及部分代码来源:
dct变换及逆变换的代码及量化部分的参考代码
https://blog.csdn.net/u014518566/article/details/46432621?ops_request_misc=&request_id=&biz_id=102&utm_term=dct%E7%A6%BB%E6%95%A3%E4%BD%99%E5%BC%A6%E5%8F%98%E6%8D%A2%E9%87%8F%E5%8C%96c++&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-1-46432621
量化矩阵的来源:
http://blog.sina.com.cn/s/blog_8bb885610102vizk.html
程序输出文件注释:
image_outi文件夹中包含使用量化矩阵i进行压缩编码的输出文件,其中:
decode.jpg为解压后的图片。
hdecode.jpg为解压后的图片的灰度统计直方图,横轴表示由0到255的灰度值,纵轴表示横轴对应灰度值在解压后中的统计次数,做了归一化处理。
hdelta1.jpg为1类误差直方图,横轴表示由-255到255的灰度值差值,纵轴表示对应差值的频率分布,做了归一化处理。
hdelta2.jpg为2类误差直方图,横轴表示原图由0到255的灰度值,纵轴表示原图对应灰度值相对解压后的图片的灰度值频数的变化,同样做了归一化处理,由于存在正负,x轴为图片中的分界线,y轴为图片左边框。
hfmlist.txt为霍夫曼编码生成的字典。
hfmout.txt为霍夫曼编码的输出序列。
ho.jpg为初始化后图片的灰度统计直方图,横轴表示由0到255的灰度值,纵轴表示横轴对应灰度值在原始图片中的统计次数,做了归一化处理。
lzlist.txt为LZ编码中信源符号的编码字典。
lzout.txt为LZ编码的输出序列。
o.jpg为初始化后的图片(原图片为三通道图长宽任意的图片,初始化后修改为单通道灰度图,长宽均为8或16的倍数的图片)。
sch.jpg为压缩量化后的图片,其中值为负值的像素由于数据溢出变更为正值。
sch.txt为压缩量化后图片各个矩阵的像素值数据,保留了负值。
1.jpg为程序运行时输入的图片,必须为三通道图片,否则报错,其它任意。
控制台输出.txt为程序运行结束后控制台输出的文字,其中包含有各步运行时间、信源符号数据,编码数据,图片数据等信息。
编译需要安装opencv。
//QQ:2048728358
//2020/05/10
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace cv;
#define NUM 8
#define PI 3.1415926
//霍夫曼树结点
class HFMP;
//霍夫曼编码器
class HFMCODE;
//霍夫曼译码器
class HFMDECODE;
//LZ编码结点
class LZP;
//LZ编码器//对霍夫曼编码具有依赖性
class LZCODE;
//LZ译码器
class LZDECODE;
//JPEG量化矩阵
//N=8时量化表
int F8[14][8][8] = {
//100
{
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1},
{1,1,1,1,1,1,1,1}
},
//98
{
{1,1,1,1,1,2,2,2},
{1,1,1,1,1,2,2,2},
{1,1,1,1,2,2,3,2},
{1,1,1,1,2,3,3,2},
{1,1,1,2,3,4,4,3},
{1,1,2,3,3,4,5,4},
{2,3,3,3,4,5,5,4},
{3,4,4,4,4,4,4,4}
},
//96
{
{1,1,1,1,2,3,4,5},
{1,1,1,2,3,5,6,4},
{1,1,1,2,3,5,6,4},
{1,1,2,2,4,7,6,5},
{1,2,3,4,5,9,8,6},
{2,3,4,5,6,8,9,7},
{4,5,6,7,8,10,10,8},
{6,7,8,8,9,8,8,8}
},
//94
{
{2,1,1,2,3,5,6,7},
{1,1,2,2,3,7,7,7},
{2,2,2,3,5,7,8,7},
{2,2,3,3,6,10,10,7},
{2,3,4,7,8,13,12,9},
{3,4,7,8,10,12,14,11},
{6,8,9,10,12,15,14,12},
{9,11,11,12,13,12,12,12}
},
//92
{
{3,2,2,3,4,6,8,10},
{2,2,2,3,4,9,10,9},
{2,2,3,4,6,9,11,9},
{2,3,4,5,8,14,13,10},
{3,4,6,9,11,17,16,12},
{4,6,9,10,13,17,18,15},
{8,10,12,14,16,19,19,16},
{12,15,15,16,18,16,16,16}
},
//90
{
{3,2,2,3,5,8,10,12},
{2,2,3,4,5,12,12,11},
{3,3,3,5,8,11,14,11},
{3,3,4,6,10,17,16,12},
{4,4,7,11,14,22,21,15},
{5,7,11,13,16,21,23,18},
{10,13,16,17,21,24,24,20},
{14,18,19,20,22,20,21,20}
},
//88
{
{4,3,2,4,6,10,12,15},
{3,3,3,5,6,14,14,13},
{3,3,4,6,10,14,17,13},
{3,4,5,7,12,21,19,15},
{4,5,9,13,16,26,25,18},
{6,8,13,15,19,25,27,22},
{12,15,19,21,25,29,26,24},
{17,22,23,24,27,24,25,24}
},
//86
{
{4,3,3,4,7,11,14,17},
{3,3,4,5,7,16,17,15},
{4,4,4,7,11,16,19,16},
{4,5,6,8,14,24,22,17},
{5,6,10,16,19,31,29,22},
{7,10,15,18,23,29,32,26},
{14,18,22,24,29,34,34,28},
{20,26,27,27,31,28,29,28}
},
//84
{
{5,4,3,5,8,13,16,20},
{4,4,4,6,8,19,19,18},
{4,4,5,8,13,18,22,18},
{4,5,7,9,16,28,26,20},
{6,7,12,18,22,35,33,25},
{8,11,18,20,26,33,36,29},
{16,20,25,28,33,39,38,32},
{23,29,30,31,36,32,33,32}
},
//82
{
{6,4,4,6,9,14,18,12},
{4,4,5,7,9,21,22,20},
{5,5,6,9,14,21,25,20},
{5,6,8,10,18,31,29,22},
{6,8,13,20,24,39,37,28},
{9,13,20,23,29,37,41,33},
{18,23,28,31,37,44,43,36},
{26,33,34,35,40,36,37,36}
},
//80
{
{6, 4, 4, 6, 10, 16, 20, 24},
{ 5,5,6,8,10,23,24,22 },
{ 6,5,6,10,16,23,28,22 },
{ 6,7,9,12,20,35,32,25 },
{ 7,9,15,22,27,44,41,31 },
{ 10,14,22,26,32,42,45,37 },
{ 20,26,31,35,41,48,48,40 },
{ 29,37,38,39,45,40,41,40 }
},
//课设矩阵
{
{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}
},
//系数保留法量化,6
{
{1,1,1,512,512,512,512,512},
{1,1,512,512,512,512,512,512},
{1,512,512,512,512,512,512,512},
{512,512,512,512,512,512,512,512},
{512,512,512,512,512,512,512,512},
{512,512,512,512,512,512,512,512},
{512,512,512,512,512,512,512,512},
{512,512,512,512,512,512,512,512}
},
//系数保留法量化,10
{
{ 1,1,1,1,512, 512, 512, 512},
{ 1,1,1,512,512,512,512,512 },
{ 1,1,512,512,512,512,512,512 },
{ 1,512,512,512,512,512,512,512 },
{ 512,512,512,512,512,512,512,512 },
{ 512,512,512,512,512,512,512,512 },
{ 512,512,512,512,512,512,512,512 },
{ 512,512,512,512,512,512,512,512 }
}
};
//实验用8*8输入矩阵
int input[8][8] = {
{89, 101, 114, 125, 126, 115, 105, 96},
{97, 115, 131, 147, 149, 135, 123, 113},
{114, 134, 159, 178, 175, 164, 149, 137},
{121, 143, 177, 196, 201, 189, 165, 150},
{119, 141, 175, 201, 207, 186, 162, 144},
{107, 130, 165, 189, 192, 171, 144, 125},
{97, 119, 149, 171, 172, 145, 117, 96},
{88, 107, 136, 156, 155, 129, 97, 75}
};//实验用输入矩阵
//对图片初始化处理
int Initimage(Mat& imagein);
//DCT变换
void DCT(int data[NUM][NUM]);
//DCT反变换
void IDCT(int data[NUM][NUM]);
//四舍五入
int around(double a);
//量化
void DCT(int data[NUM][NUM]);
//DCT反变换
void IDCT(int data[NUM][NUM]);
//量化
int SCH(int data[NUM][NUM], int k);
//反量化
int ISCH(int data[NUM][NUM], int k);
//Z字扫描
int Zscan(int datain[NUM][NUM], int dataout[NUM * NUM]);
//反Z字扫描
int IZscan(int datain[NUM * NUM], int dataout[NUM][NUM]);
//压缩
int fhasaki(Mat image, Mat& imageout1, int*& image1line, int**& image1int, int sch);
//解压缩
int fdehasaki(int length, int w, int h, int* imageline, Mat& imageout2, int sch);
//int转二进制字符
string inttoox(int x, int width);
//二进制字符转int
int oxtoint(string s);
//获取图像灰度直方图
int gethistogram(string dst, Mat image);
int gethistogram(string dst, Mat image1, Mat image2);
int gethistogram(Mat image1, Mat image2, string dst);
class HFMP {
public:
int num;//信源符号(数字)
int count;//出现次数
double p;//概率
bool tag;//标签,默认为true,用于霍夫曼树中辨别是否为原概率
string s;//霍夫曼码
HFMP* b[2];
HFMP() :num(0), count(0), p(0.0), tag(true) {
s = "";
b[0] = NULL;
b[1] = NULL;
}
~HFMP() {
if (b[0])
delete b[0];
if (b[1])
delete b[1];
if (this == NULL)
return;
}
};
class HFMCODE {
friend LZCODE;
HFMP* root;//概率分布
HFMP* code;//霍夫曼树
HFMP* temp;//临时存储点
int count;//符号出现总次数
int pnum;//符号个数
int codelength;//输出序列长度
double H;
void init() {///初始化
delete code;
delete root;
delete temp;
temp = NULL;
code = NULL;
root = new HFMP;//
root->num = 0;
pnum = 1;
count = 0;
}
//计数
void fcount(int* data)
{
cout << "正在统计数据" << endl;
cout << "共计信源符号个数:" << count << endl;
int i = 0;
for (i = 0; i < count; i++)
{
HFMP* q = findp(data[i], root);
if (q)
q->count++;
else
{
newp(data[i], root);
pnum++;
}
}
}
HFMP* findp(int data, HFMP* p) {
if (p->num == data && p->tag)
return p;
if (p->b[1] != NULL)
{
HFMP* q = findp(data, p->b[1]);
if (q != NULL)
return q;
}
if (p->b[0] != NULL)
return findp(data, p->b[0]);
return NULL;
}
void newp(int data, HFMP* p)
{
if (data < p->num && p->b[0])
newp(data, p->b[0]);
if (data > p->num && p->b[1])
newp(data, p->b[1]);
if (data < p->num && p->b[0] == NULL)
{
p->b[0] = new HFMP;
p->b[0]->num = data;
p->b[0]->count++;
}
if (data > p->num && p->b[1] == NULL)
{
p->b[1] = new HFMP;
p->b[1]->num = data;
p->b[1]->count++;
}
if (p->num == data)
cout << "霍夫曼概率计数发生错误!" << endl;
}
//概率分布排序
void sort2(HFMP* p)//不能优化为双链表,与析构函数冲突//整理到temp;
{
HFMP* q = NULL;
HFMP* qq = NULL;
if (temp == NULL)//头结点为空
{
q = new HFMP;
q->num = p->num;
q->count = p->count;
temp = q;
return;
}
if (temp->count <= p->count)//头结点概率小于或等于p
{
q = new HFMP;
q->num = p->num;
q->count = p->count;
q->b[0] = temp;
/*temp->b[1] = q;*/
temp = q;
return;
}
if (temp->b[0] == NULL)//只有头结点且头结点概率大于p
{
q = new HFMP;
q->num = p->num;
q->count = p->count;
temp->b[0] = q;
/* q->b[1] = temp;*/
return;
}
q = temp;
while (q->b[0]) {//存在概率大于p的节点
if (q->b[0]->count > p->count)
{
q = q->b[0];
continue;
}
qq = new HFMP;
qq->num = p->num;
qq->count = p->count;
qq->b[0] = q->b[0];
/* qq->b[1] = q;*/
q->b[0] = qq;
return;
}
qq = new HFMP;//所有节点概率都小于p
qq->num = p->num;
qq->count = p->count;
/*qq->b[1] = q;*/
q->b[0] = qq;
return;
}
void sort1(HFMP* p)
{
sort2(p);
if (p->b[0])
sort1(p->b[0]);
if (p->b[1])
sort1(p->b[1]);
}
void sort()//将概率分布由小到大排列为链表存到root;
{
cout << "开始概率排序" << endl;
sort1(root);
delete root;
root = temp;
temp = NULL;
}
//生成霍夫曼树
void plantsort(HFMP* p)//整理和概率结点到temp;
{
HFMP* q = p;
if (temp == NULL)//头结点为空
{
temp = p;
return;
}
if (temp->count <= p->count)//头结点概率小于或等于p
{
p->b[0] = temp;
temp = p;
return;
}
if (temp->b[0] == NULL)//只有头结点且头结点概率大于p
{
temp->b[0] = p;
/* q->b[1] = temp;*/
return;
}
q = temp;
while (q->b[0]) {//存在概率大于p的节点
if (q->b[0]->count > p->count)
{
q = q->b[0];
continue;
}
p->b[0] = q->b[0];
/* qq->b[1] = q;*/
q->b[0] = p;
return;
}
q->b[0] = p;
return;
}
void plant1(HFMP* p, HFMP** pp, int l) {
if (p->tag)
return;
plant2(p, pp, l);
if (p->b[0])
plant1(p->b[0], pp, l);
if (p->b[1])
plant1(p->b[1], pp, l);
}
void plant2(HFMP* p, HFMP** pp, int l)
{
int i;
for (i = 0; i < l; i++)
{
if ((p->count == pp[i]->count) && pp[i]->b[0] && pp[i]->b[1])
{
p->b[0] = pp[i]->b[0];
pp[i]->b[0] = NULL;
p->b[1] = pp[i]->b[1];
pp[i]->b[1] = NULL;
return;
}
}
cout << "霍夫曼编码plant2()出现问题!" << endl;
}
bool plant() {
cout << "开始生成霍夫曼树" << endl;
HFMP** proot = NULL;
temp = root;
root = NULL;
HFMP* q;
int i;
for (i = 0; temp->b[0]->b[0]->b[0];)//编码至剩下3位
{
HFMP* p = new HFMP;
HFMP* pp = new HFMP;
//最小概率
q = temp;
while (q->b[0]->b[0])
q = q->b[0];
p->b[0] = q->b[0];
q->b[0] = NULL;
//第二小概率
q = temp;
while (q->b[0]->b[0])
q = q->b[0];
p->b[1] = q->b[0];
q->b[0] = NULL;
p->count = p->b[0]->count + p->b[1]->count;
pp->count = p->count;
p->tag = false;
pp->tag = false;
if (proot == NULL)
proot = (HFMP**)malloc(sizeof(HFMP*));
else
proot = (HFMP**)realloc(proot, (i + 1) * sizeof(HFMP*));
proot[i] = p;
i++;
plantsort(pp);
p = NULL;
pp = NULL;
}
HFMP* p = new HFMP;
HFMP* pp = new HFMP;
p->b[0] = temp->b[0]->b[0];
temp->b[0]->b[0] = NULL;
p->b[1] = temp->b[0];
temp->b[0] = NULL;
p->count = p->b[0]->count + p->b[1]->count;
pp->count = p->count;
p->tag = false;
pp->tag = false;
proot = (HFMP**)realloc(proot, (i + 1) * sizeof(HFMP*));
proot[i] = p;
i++;
plantsort(pp);
pp = NULL;
p = new HFMP;
pp = new HFMP;
p->b[0] = temp->b[0];
temp->b[0] = NULL;
p->b[1] = temp;
temp = NULL;
p->count = p->b[0]->count + p->b[1]->count;
pp->count = p->count;
if (p->count != count)
{
delete p;
for (; i >= 0; i--)
{
delete proot[i];
free(proot[i]);
}
return false;
}
p->tag = false;
pp->tag = false;
temp = pp;
pp = NULL;
proot = (HFMP**)realloc(proot, (i + 1) * sizeof(HFMP*));
proot[i] = p;
i++;
p = NULL;
//至此,霍夫曼树数据已经记录完毕,开始向霍夫曼树回填数据
plant1(temp, proot, i);
code = temp;
temp = NULL;
//至此,霍夫曼树生成完毕
//删除多余数据
for (; i > 0; i--)
{
delete proot[i - 1];
proot[i - 1] = NULL;
}
free(proot);
return true;
}
//计算概率分布,生成霍夫曼树,已经包含前方所有步骤
void calculatep(int* data) {
fcount(data);
sort();
plant();
cout << "正式开始计算概率" << endl;
calculatep1(code);
}
void calculatep1(HFMP* p) {
p->p = ((double)p->count / (double)count);
if (p->tag)
{
H -= (log(p->p) / log(2)) * p->p;
}
if (p->b[0])
calculatep1(p->b[0]);
if (p->b[1])
calculatep1(p->b[1]);
}
void fcode(int l, int* data, fstream* tlist, fstream* tout) {
count = l;
calculatep(data);
fcode1(code);
cout << "正在输出霍夫曼编码表" << endl;
fcode2(code, tlist);
cout << "正在输出霍夫曼编码文件" << endl;
fout(data, tout);
}
void fcode1(HFMP* p) {//生成编码表
if (p->b[0])
{
p->b[0]->s = p->s + "0";
fcode1(p->b[0]);
}
if (p->b[1])
{
p->b[1]->s = p->s + "1";
fcode1(p->b[1]);
}
}
void fcode2(HFMP* p, fstream* t)//将字典输出到文件
{
if (p->b[1])
fcode2(p->b[1], t);
if (p->tag)
{
*t <num <<"\t"<< " p=" << p->p << "\t\t" << p->s << endl;
}
if (p->b[0])
fcode2(p->b[0], t);
}
void fout(int* data, fstream* t)
{
HFMP* p;
for (int i = 0; i < count; i++)
{
p = findp(data[i], code);
codelength = codelength + (int)p->s.length();
*t << p->s;
}
cout << "霍夫曼编码所得序列长度为:" << codelength << endl;
}
//复制一棵独立的霍夫曼树
void copyhfmtreeP(HFMP* p, HFMP* q) {
p->num = q->num;
p->count = q->count;
p->p = q->p;
p->tag = q->tag;
p->s = q->s;
p->b[0] = NULL;
p->b[1] = NULL;
}
void copyhfmtree(HFMP* frooty, HFMP* frootx) {
for (int i = 0; i < 2; i++)
{
if (frooty->b[i])
delete frooty->b[i];
frooty->b[i] = new HFMP;
if (frootx->b[i])
{
copyhfmtreeP(frooty->b[i], frootx->b[i]);
copyhfmtree(frooty->b[i], frootx->b[i]);
}
}
}
public:
int getpnum() {
return pnum;
}
HFMP* gethfmtree() {
HFMP* p = new HFMP;
copyhfmtreeP(p, code);
copyhfmtree(p, code);
return p;
}
double getH() { return H; }
HFMCODE(int l, int* data, fstream* tlist, fstream* tout) :H(0.0), count(0), codelength(0)//默认存在符号0
{
temp = NULL;
code = NULL;
root = new HFMP;
root->num = 0;
pnum = 1;
fcode(l, data, tlist, tout);
cout << "霍夫曼编码信源符号种数:" << pnum << endl;
cout << "霍夫曼编码信源符号个数:" << count << endl;
cout << "霍夫曼编码平均码长:" << (double)codelength / count << endl;
cout << "信源信息熵:" << H << endl;
cout << "霍夫曼编码编码效率:" << H / (double)codelength * count << endl;
cout << "霍夫曼编码所得压缩比:" << (double)count * 8 / codelength << endl;
}
~HFMCODE() {
delete temp;
delete code;
delete root;
}
};
class HFMDECODE {
HFMP* code;//霍夫曼树
int count;//符号出现总次数
int pnum;//符号个数
int* decode;
public:
HFMDECODE(HFMP* p, int fpnum, fstream* tin) :count(0), pnum(fpnum), decode(NULL)
{
cout << "霍夫曼译码信源符号种数:" << pnum << endl;
code = p;
p = NULL;
char ch;
string s = "";
HFMP* q = code;
int i = 0;
while (1)
{
*tin >> ch;
s = s + ch;
if (ch == '1')
q = q->b[1];
else
q = q->b[0];
if (tin->eof())
break;
if (q->tag)//
{
decode = (int*)realloc(decode, (count + 1) * sizeof(int));
if (q->s == s)
decode[count] = q->num;
else
cout << "hfm decode error!" << endl;
count++;
s = "";
q = code;
}
}
cout << "霍夫曼译码获得信源符号个数:" << count << endl;
}
~HFMDECODE() {
if (code)
delete code;
free(decode);
}
};
class LZP {//加一个前置索引则可以将译码器优化
public:
int num;//信源符号
int lastposit;//前一个位置
int posit;//当前位置
int pnum;//尾巴个数
LZP** p;//尾巴指针
LZP() :num(0), posit(0), pnum(0), p(NULL), lastposit(0) {};
~LZP() {
for (; pnum > 0; pnum--)
{
if (p[pnum - 1])
delete p[pnum - 1];
}
if (p)
free(p);
}
};
class LZCODE
{
LZP* root;//lz编码表表根
int* list;//符号编码表,坐标表示当前编号
int xnum;//信源符号个数
int** file;//被发送的信息,坐标表示当前段号,0表示前一段号,1表示末尾字符编号//处理后发送
int ynum;//段数
double H;
//查找是否有这样的信源符号串//信源符号数组,数组长度
LZP* findp1(int* numlist, int length) {
if (numlist == NULL)
return NULL;
if (length < 1)
return NULL;
LZP* q = root;
for (int i = 0; i < length; i++)
{
q = findp2(q, numlist[i]);
if (q == NULL)
return NULL;
}
return q;
}
//查找某LZ结点后方是否存在某信源符号,并返回该符号所在指针//某节点地址,某信源符号
LZP* findp2(LZP* last, int num) {
int i = 0;
for (i = 0; i < last->pnum; i++)
{
if (last->p[i])
if (last->p[i]->num == num)
return last->p[i];
}
return NULL;
}
//在某节点后增加一个符号为num,上一位置为lastposit,当前位置为posit的节点
void newp(LZP* p, int num, int lastposit, int posit) {
p->pnum++;
p->p = (LZP**)realloc(p->p, p->pnum * sizeof(LZP*));
p->p[p->pnum - 1] = new LZP;
p->p[p->pnum - 1]->num = num;
p->p[p->pnum - 1]->posit = posit;
p->p[p->pnum - 1]->lastposit = lastposit;
}
//通过霍夫曼编码树生成LZ编码信源字符字典//霍夫曼树根节点,当前列表信源符号个数
int record(HFMP* p, int i) {
if (p->tag)
{
list = (int*)realloc(list, (i + 1) * sizeof(int));
list[i] = p->num;
i++;
}
if (p->b[0])
i = record(p->b[0], i);
if (p->b[1])
i = record(p->b[1], i);
return i;
}
int findnumcode(int num)//找到信源符号编号
{
for (int i = 0; i < xnum; i++)
{
if (list[i] == num)
return i;
}
cout << "LZ编码信源编号出错" << endl;
return 0;
}
//LZ编码
void fcode(int* line, int length) {
LZP* q = NULL;
LZP* p = NULL;
int i;
q = root;
cout << "LZ编码信源符号个数:" << length << endl;
for (i = 0; i < length; i++)//最后一段可能与前面重复
{
p = findp2(q, line[i]);
if (i == length - 1 && p)
{
file = (int**)realloc(file, (ynum + 1) * sizeof(int*));
file[ynum] = (int*)malloc(2 * sizeof(int));
file[ynum][0] = q->posit;
file[ynum][1] = findnumcode(p->num);
ynum++;
return;
}
else if (p == NULL)
{
newp(q, line[i], q->posit, ynum + 1);
file = (int**)realloc(file, (ynum + 1) * sizeof(int*));
file[ynum] = (int*)malloc(2 * sizeof(int));
p = findp2(q, line[i]);
if (!p)
{
cout << "lz code error!" << endl;
return;
}
file[ynum][0] = q->posit;
file[ynum][1] = findnumcode(p->num);
ynum++;
q = root;
p = NULL;
}
else
q = p;
}
}
//输出编码表以及信源符号字典
void fout(fstream* flist, fstream* ffile)
{
cout << "LZ编码信源符号种数:" << xnum << endl;
cout << "LZ编码段数(不包括0段):" << ynum << endl;
int xl = (int)(log(xnum) / log(2)) + 1;
int yl = (int)(log(ynum) / log(2)) + 1;
int i;
for (i = 0; i < xnum; i++)
{
*flist << inttoox(i, xl) << " " << list[i] << endl;
}
for (i = 0; i < ynum; i++)
{
*ffile << inttoox(file[i][0], yl) << inttoox(file[i][1], xl);
}
cout << "LZ编码所得序列长度:" << (xl + yl) * ynum << endl;
}
//给信源符号编码
public:
LZCODE(HFMCODE& T, int* line, int length, fstream* flist, fstream* ffile) :xnum(0), list(NULL), file(NULL), ynum(0)
{
//生成信源字符字典
xnum = record(T.code, xnum);
H = T.getH();
if (xnum != T.pnum)
{
cout << xnum << " " << T.pnum << endl;
cout << "LZ code error!" << endl;
}
root = new LZP;
fcode(line, length);
fout(flist, ffile);
int xl = (int)(log(xnum) / log(2)) + 1;
int yl = (int)(log(ynum) / log(2)) + 1;
cout << "LZ编码平均码长:" << (double)ynum * (xl + yl) / (double)length << endl;
cout << "LZ编码编码效率:" << H / (double)ynum / (xl + yl) * (double)length << endl;
cout << "LZ编码所得压缩比:" << (double)T.count*8 / ynum / (xl + yl) << endl;
}
int* getlist()//有内存泄漏风险
{
int* p = (int*)malloc(xnum * sizeof(int));
for (int i = 0; i < xnum; i++)
{
p[i] = list[i];
}
return p;
}
int getxnum() { return xnum; }
int getynum() { return ynum; }
~LZCODE() {
free(list);
delete root;
for (; ynum > 0; ynum--)
free(file[ynum - 1]);
free(file);
}
};
class LZDECODE {
int** root;//LZ编码,下标为当前段号,前一段号、信源符号
int ynum;//LZ编码段数
int* list;//符号编码表//从文件接收
int xnum;//信源符号个数
int* code;//译码
int znum;//译码信源个数
//在某节点后增加一个符号为num,上一位置为lastposit,当前位置为posit的节点
public:
//信源编码字典,信源符号种类数,编码输出文件,编码段数
LZDECODE(int* flist, int fxnum, fstream* ffile, int fynum) :root(NULL), list(NULL), xnum(0), ynum(0), code(NULL), znum(0) {
list = (int*)malloc(fxnum * sizeof(int));
while (xnum < fxnum)
{
list[xnum] = flist[xnum];
xnum++;
}
int xl = (int)(log(fxnum) / log(2)) + 1;
int yl = (int)(log(fynum) / log(2)) + 1;
cout << "LZ译码信源符号种数:" << xnum << endl;
char ch;
string ss1 = "";
string ss2 = "";
root = (int**)malloc(sizeof(int*));//空出0位
ynum++;
while (1)
{
for (int i = 0; i < yl; i++)
{
ch = ffile->get();
ss1 = ss1 + ch;
}
if (ffile->eof())
break;
for (int i = 0; i < xl; i++)
{
ch = ffile->get();
ss2 = ss2 + ch;
}
root = (int**)realloc(root, (ynum + 1) * sizeof(int*));
root[ynum] = (int*)malloc(2 * sizeof(int));
root[ynum][0] = oxtoint(ss1);//段号
root[ynum][1] = oxtoint(ss2);//信源符号编号
ynum++;
ss1 = "";
ss2 = "";
}
cout << "LZ译码段号个数(包括第0段):" << ynum << endl;
int* p = NULL;
int i, j;
for (i = 1, j = 0; i < ynum; i++)
{
j = 0;
p = (int*)malloc(sizeof(int));
p[0] = list[root[i][1]];
int tempposit = root[i][0];
j++;
while (tempposit != 0)
{
p = (int*)realloc(p, (j + 1) * sizeof(int));
p[j] = list[root[tempposit][1]];
tempposit = root[tempposit][0];
j++;
}
code = (int*)realloc(code, (znum + j) * sizeof(int));
for (int k = 0; k < j; k++)
{
code[znum] = p[j - k - 1];
znum++;
}
free(p);
}
cout << "LZ译码信源符号个数:" << znum << endl;
};
~LZDECODE()
{
free(list);
free(code);
for (int i = 1; i < ynum; i++)
{
free(root[i]);
}
free(root);
}
};
int main(void)
{
int sch;
for (sch = 0; sch < 14; sch++)
{
cout << "量化表" << sch << endl;
clock_t startTime, endTime, time1, time2, time3, time4, time5, time6, time7, time8;
startTime = clock();//计时开始
//文件目录预处理
string ds = to_string(sch);
char* buffer;//当前目录
string root;//当前目录
string src = "\\image_o\\1.jpg";//原始图片目录
string dstout = "\\image_out" + ds + "\\";//输出根目录
cout << "正在获得当前路径" << endl;
//获得当前路径
if ((buffer = _getcwd(NULL, 0)) == NULL)
{
perror("_getcwderror");
return -1;
} //得到当前的工作路径
else
{
root = buffer;
free(buffer);
buffer = NULL;
src = root + src;
dstout = root + dstout;
}
cout << "已经获得当前路径" << endl;
cout << "正在加载图片" << endl;
//原始图片处理
Mat image1 = imread(src, IMREAD_UNCHANGED);//读取图片
if (image1.empty())//录入原始图片
{
cout << "图片加载失败!" << endl;
CreateDirectory(L"image_o", NULL);
cout << "请在新生成的image_o目录下放置名为1.jpg的图片" << endl;
return -1;
}
cout << "图片加载完成" << endl;
cout << "正在初始化图片" << endl;
if (Initimage(image1) == 0)//图片初始化
{
cout << "参数错误!" << endl;
return -1;
}
cout << "图片初始化完成,正在创建输出目录image_out"+ds << endl;
//创建输出文件夹
CreateDirectory(L"image_out" + (CString)ds.c_str(), NULL);
//存储格式化后的图片
imwrite(dstout + "o.jpg", image1);
//压缩,获得并存储数据
ofstream out;
string txtname = "sch.txt";
out.open(dstout + txtname, ios::out);
if (!out.is_open())
{
cout << "txt open fail!";
}
string imageoutdst2 = dstout + "sch.jpg";//量化图
string imageoutdst3 = dstout + "decode.jpg";//解压后的图片
Mat image2, image3;
int* imageline = NULL;
int** imageint = NULL;
cout << "开始压缩,请等待" << endl;
//正式开始压缩
time1 = clock();
if (fhasaki(image1, image2, imageline, imageint, sch) != 1)
{
cout << "图片压缩失败!" << endl;
return -1;
}
cout << "图片压缩完成,正在保存数据" << endl;
imwrite(imageoutdst2, image2);
time2 = clock();
cout << "压缩用时:" << (double)(time2 - time1) / CLOCKS_PER_SEC << "s" << endl;
int i, k;
for (i = 0, k = 0; i < image1.rows * image1.cols; i++, k++)
{
if (k >= NUM * NUM)
{
k = 0;
out << endl;
}
/*out << imageline[i] << " ";*/
out << setw(5) << imageline[i];
}
//霍夫曼编码
cout << "正在进行霍夫曼编码" << endl;
time3 = clock();
fstream thfmout, thfmlist;
thfmlist.open(dstout + "hfmlist.txt", ios::out);
thfmout.open(dstout + "hfmout.txt", ios::out);
HFMCODE hfm(image1.rows * image1.cols, imageline, &thfmlist, &thfmout);
thfmout.close();
thfmout.open(dstout + "hfmout.txt");
time4 = clock();
cout << "霍夫曼编码用时:" << (double)(time4 - time3) / CLOCKS_PER_SEC << "s" << endl;
cout << "正在进行霍夫曼译码" << endl;
HFMP* hfmtree = hfm.gethfmtree();
HFMDECODE dhfm(hfmtree, hfm.getpnum(), &thfmout);
hfmtree = NULL;//防火防盗防泄漏
time5 = clock();
cout << "霍夫曼译码用时:" << (double)(time5 - time4) / CLOCKS_PER_SEC << "s" << endl;
//LZ编码
cout << "正在进行LZ编码" << endl;
fstream tlzlist, tlzout;
tlzlist.open(dstout + "lzlist.txt", ios::out);
tlzout.open(dstout + "lzout.txt", ios::out);
LZCODE lz(hfm, imageline, image1.rows * image1.cols, &tlzlist, &tlzout);
tlzout.close();
time6 = clock();
cout << "LZ编码用时:" << (double)(time6 - time5) / CLOCKS_PER_SEC << "s" << endl;
tlzout.open(dstout + "lzout.txt");
cout << "正在进行LZ译码" << endl;
int* lzlist = lz.getlist();
LZDECODE dlz(lzlist, lz.getxnum(), &tlzout, lz.getynum());
time7 = clock();
cout << "LZ译码用时:" << (double)(time7 - time6) / CLOCKS_PER_SEC << "s" << endl;
free(lzlist);
//解压缩
fdehasaki(image1.rows * image1.cols, image1.cols / NUM, image1.rows / NUM, imageline, image3, sch);
time8 = clock();
cout << "解压缩用时:" << (double)(time8 - time7) / CLOCKS_PER_SEC << "s" << endl;
imwrite(imageoutdst3, image3);
//误差直方图
cout << "原图灰度值数最大值" << gethistogram(dstout + "ho.jpg", image1) << endl;
cout << "解压后灰度值数最大值" << gethistogram(dstout + "hdecode.jpg", image3) << endl;
cout << "灰度差值数最大值" << gethistogram(dstout + "hdelta1.jpg", image1, image3) << endl;
cout << "灰度值数变化量差值" << gethistogram(image1, image3, dstout + "hdelta2.jpg") << endl;
//防止内存泄露及后续扫尾工作
out.close();
thfmout.close();
thfmlist.close();
tlzlist.close();
tlzout.close();
free(imageline);
for (i = 0; i < image1.rows; i++)
{
free(*(imageint + i));
}
free(imageint);
cout << "程序结束" << endl;
endTime = clock();//计时结束
cout << "总共用时: " << (double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
}
return 0;
}
int Initimage(Mat& imagein)
{
if (NUM != 8 && NUM != 16)
return 0;
int h, w;
Mat imageout;
h = imagein.rows / NUM;//行数
w = imagein.cols / NUM;//列数
imageout = Mat::zeros(h * NUM, w * NUM, CV_8UC1);
resize(imagein, imageout, imageout.size());//休整大小
cvtColor(imageout, imageout, COLOR_RGB2GRAY);//转灰度图
if (imageout.empty())
return 0;
imagein = imageout;
return 1;
}
void DCT(int data[NUM][NUM])
{
int output[NUM][NUM];
double alpha, beta;//C(k) C(l)
int m = 0, n = 0, k = 0, l = 0;
for (k = 0; k < NUM; k++)
{
for (l = 0; l < NUM; l++)
{
if (k == 0)
{
alpha = sqrt(1.0 / NUM);
}
else
{
alpha = sqrt(2.0 / NUM);
}
if (l == 0)
{
beta = sqrt(1.0 / NUM);
}
else
{
beta = sqrt(2.0 / NUM);
}
double temp = 0.0;
for (m = 0; m < NUM; m++)
{
for (n = 0; n < NUM; n++)
{
temp += data[m][n] * cos((2 * m + 1) * k * PI / (2.0 * NUM)) * cos((2 * n + 1) * l * PI / (2.0 * NUM));
}
}
output[k][l] = around(alpha * beta * temp);
}
}
memset(data, 0, sizeof(int) * NUM * NUM);
std::memcpy(data, output, sizeof(int) * NUM * NUM);
}
void IDCT(int data[NUM][NUM])
{
int output[NUM][NUM];
double alpha, beta;
int m = 0, n = 0, k = 0, l = 0;
for (m = 0; m < NUM; m++)
{
for (n = 0; n < NUM; n++)
{
double temp = 0.0;
for (k = 0; k < NUM; k++)
{
for (l = 0; l < NUM; l++)
{
if (k == 0)
{
alpha = sqrt(1.0 / NUM);
}
else
{
alpha = sqrt(2.0 / NUM);
}
if (l == 0)
{
beta = sqrt(1.0 / NUM);
}
else
{
beta = sqrt(2.0 / NUM);
}
temp += alpha * beta * data[k][l] * cos((2 * m + 1) * k * PI / (2 * NUM)) * cos((2 * n + 1) * l * PI / (2 * NUM));
}
}
output[m][n] = around(temp);
}
}
memset(data, 0, sizeof(int) * NUM * NUM);
std::memcpy(data, output, sizeof(int) * NUM * NUM);
}
int SCH(int data[NUM][NUM], int k)
{
if (NUM == 8)
{
for (int i = 0; i < NUM; i++)
{
for (int j = 0; j < NUM; j++)
{
data[i][j] /= F8[k][i][j];
}
}
return 1;
}
else
return 0;
}
int ISCH(int data[NUM][NUM], int k)
{
if (NUM == 8)
{
for (int i = 0; i < NUM; i++)
{
for (int j = 0; j < NUM; j++)
{
data[i][j] *= F8[k][i][j];
}
}
return 1;
}
else
return 0;
}
int around(double a)
{
if (a >= 0)
{
return int(a + 0.5);
}
else
{
return int(a - 0.5);
}
}
int fhasaki(Mat image, Mat& imageout1, int*& image1line, int**& image1int, int sch) {
if (image.cols % NUM != 0 || image.rows % NUM != 0)
return -1;
int h = image.rows / NUM;
int w = image.cols / NUM;
imageout1 = Mat::zeros(h * NUM, w * NUM, CV_8UC1);
//imageout2 = Mat::zeros(h * NUM, w * NUM, CV_8UC1);
/*cout << h << w;*/
image1line = (int*)malloc(h * w * NUM * NUM * sizeof(int));//Z字扫描后所得一维序列//申请空间
image1int = (int**)malloc(h * NUM * sizeof(int*));//量化后图片的二维数组
int k;
for (k = 0; k < h * NUM; k++)
{
image1int[k] = (int*)malloc((int)w * NUM * sizeof(int));
}
int i, j, m, n;
for (i = 0; i < w; i++)
{
for (j = 0; j < h; j++)
{
int piceline[NUM * NUM];
int piceint[NUM][NUM];
for (m = 0; m < NUM; m++)
{
for (n = 0; n < NUM; n++)
{
piceint[m][n] = (int)image.at(j * NUM + m, i * NUM + n);//图片部分录入
}
}
DCT(piceint);
SCH(piceint, sch);
for (m = 0; m < NUM; m++)
{
for (n = 0; n < NUM; n++)
{
image1int[j * NUM + m][i * NUM + n] = piceint[m][n];//量化后的矩阵部分录入
imageout1.at(j * NUM + m, i * NUM + n) = (uchar)piceint[m][n];//量化后的图片部分录入
}
}
Zscan(piceint, piceline);
for (k = 0; k < NUM * NUM; k++)
{
image1line[i * h * NUM * NUM + j * NUM * NUM + k] = piceline[k];//Z字扫描后的一维数组录入
}
//ISCH(piceint);
//IDCT(piceint);
//for (m = 0; m < NUM; m++)
//{
// for (n = 0; n < NUM; n++)
// {
// imageout2.at(j * NUM + m, i * NUM + n) = (uchar)piceint[m][n];//解压后的图片录入
// }
//}
}
}
return 1;
}
int fdehasaki(int length, int w, int h, int* imageline, Mat& imageout2, int sch)
{
imageout2 = Mat::zeros(h * NUM, w * NUM, CV_8UC1);
int i, j, m, n;
for (i = 0; i < w; i++)
{
for (j = 0; j < h; j++)
{
int piceline[NUM * NUM];
int piceint[NUM][NUM];
for (int k = 0; k < NUM * NUM; k++)
{
piceline[k] = imageline[i * h * NUM * NUM + j * NUM * NUM + k];//Z字扫描后的一维数组录入
}
IZscan(piceline, piceint);
ISCH(piceint, sch);
IDCT(piceint);
for (m = 0; m < NUM; m++)
{
for (n = 0; n < NUM; n++)
{
imageout2.at(j * NUM + m, i * NUM + n) = (uchar)piceint[m][n];//解压后的图片录入
}
}
}
}
cout << "图片解压完成!" << endl;
return 0;
}
//当返回值为0时说明发生错误,返回值为1时,运行正常
int Zscan(int datain[NUM][NUM], int dataout[NUM * NUM])
{
int i, j, k, c;
for (i = 0, j = 0, k = 0, c = 0; k < NUM * NUM; k++)
{
dataout[k] = datain[i][j];
if (i == 0 && j == 0)
c = 0;
else if (c == 0 && j == 0)
c = 1;
else if (c == 0 && j == (NUM - 1))
c = 3;
else if (c == 1 && j == NUM - 1)
c = 0;
else if (c == 1 && i == 0)
c = 2;
else if (c == 1)
c = 1;
else if (c == 2 && i == 0)
c = 3;
else if (c == 2 && i == (NUM - 1))
c = 1;
else if (c == 3 && i == (NUM - 1))
c = 2;
else if (c == 3 && j == 0)
c = 0;
else if (c == 3)
c = 3;
else
return 0;
if (c == 0)//水平向右
{
i++;
}
else if (c == 1)//斜向左下
{
i--;
j++;
}
else if (c == 2)//垂直向下
{
j++;
}
else if (c == 3)//斜向右上
{
i++;
j--;
}
else
{
cout << "Zscan error" << endl;
return 0;//error
}
}
return 1;
}
int IZscan(int datain[NUM * NUM], int dataout[NUM][NUM])
{
int i, j, k, c;
for (i = 0, j = 0, k = 0, c = 0; k < NUM * NUM; k++)
{
dataout[i][j] = datain[k];
if (i == 0 && j == 0)
c = 0;
else if (c == 0 && j == 0)
c = 1;
else if (c == 0 && j == (NUM - 1))
c = 3;
else if (c == 1 && j == NUM - 1)
c = 0;
else if (c == 1 && i == 0)
c = 2;
else if (c == 1)
c = 1;
else if (c == 2 && i == 0)
c = 3;
else if (c == 2 && i == (NUM - 1))
c = 1;
else if (c == 3 && i == (NUM - 1))
c = 2;
else if (c == 3 && j == 0)
c = 0;
else if (c == 3)
c = 3;
else
return 0;
if (c == 0)//水平向右
{
i++;
}
else if (c == 1)//斜向左下
{
i--;
j++;
}
else if (c == 2)//垂直向下
{
j++;
}
else if (c == 3)//斜向右上
{
i++;
j--;
}
else
{
cout << "IZscan error" << endl;
return 0;//error
}
}
return 1;
}
string inttoox(int x, int width)
{
string s = "";
if (x == 0)
{
s = "0";
}
while (x != 0)
{
if (x % 2 == 0)
s = "0" + s;
else
s = "1" + s;
x /= 2;
}
if (s.length() < width)
{
int i = width - (int)s.length();
for (; i > 0; i--)
s = "0" + s;
}
return s;
}
int oxtoint(string s)
{
int x = 0;
for (int i = 0; i < s.length(); i++)
{
x *= 2;
x += s[i] - '0';
}
return x;
}
int gethistogram(string dst, Mat image)
{
int h = 300;
int w = 512;
int count[256] = { 0 };
if (image.channels() != 1)
Initimage(image);
int i, j;
for (i = 0; i < image.cols; i++)
for (j = 0; j < image.rows; j++)
{
count[(int)image.at(j, i)]++;
}
int max = count[0];
for (i = 0; i < 256; i++)
if (count[i] > max)
max = count[i];
for (i = 0; i < 256; i++)
{
count[i] = max - count[i];
count[i] = (count[i] * h / max);
}
Size s(w, h);
Mat out(s, CV_8UC1);
for (i = 0; i < 256; i++)
for (j = 0; j < h; j++)
{
if (j < count[i])
{
out.at(j, 2 * i) = 200;
out.at(j, 2 * i + 1) = 200;
}
else
{
out.at(j, 2 * i) = 0;
out.at(j, 2 * i + 1) = 0;
}
}
imwrite(dst, out);
return max;
}
int gethistogram(string dst, Mat image1, Mat image2)
{
double m=0;
int h = 300;
int w = 512;
int count[256 + 255] = { 0 };
if (image1.channels() != 1)
Initimage(image1);
if (image2.channels() != 1)
Initimage(image2);
if (image1.size() != image2.size())
{
cout << "图片不匹配" << endl;
return 0;
}
int i, j;
for (i = 0; i < image1.cols; i++)
for (j = 0; j < image1.rows; j++)
{
int x = image1.at(j, i) - image2.at(j, i);
count[255 + x]++;
}
int max = count[0];
for (i = 0; i < 256 + 255; i++)
{
if (count[i] > max)
max = count[i];
m += (i - 255) * (i - 255) * count[i];
}
for (i = 0; i < 256 + 255; i++)
{
count[i] = max - count[i];
count[i] = (count[i] * h / max);
}
Size s(w, h);
Mat out(s, CV_8UC1);
for (i = 0; i < 256 + 255; i++)
for (j = 0; j < h; j++)
{
if (j < count[i])
{
out.at(j, i) = 200;
}
else
{
out.at(j, i) = 0;
}
}
imwrite(dst, out);
cout << "方差:"<(j, i)]++;
}
for (i = 0; i < image1.cols; i++)
for (j = 0; j < image1.rows; j++)
{
count2[(int)image2.at(j, i)]++;
}
for (i = 0; i < 256; i++)
count[i] = count1[i] - count2[i];
int max = count[0];
int min = count[0];
for (i = 0; i < 256; i++)
{
if (count[i] > max)
max = count[i];
if (count[i] < min)
min = count[i];
}
Size s(w, h);
int v = (max*h / (max - min));
Mat out(s, CV_8UC1);
for (i = 0; i < 256; i++)
{
count[i] = -(count[i] * h / (max - min)) + v;
for (j = 0; j < h; j++)
{
if (count[i] < v)
{
if (j < count[i] || j>v)
{
out.at(j, 2 * i) = 200;
out.at(j, 2 * i + 1) = 200;
}
else
{
out.at(j, 2 * i) = 0;
out.at(j, 2 * i + 1) = 0;
}
}
else if (count[i] > v)
{
if (j > count[i] || j < v)
{
out.at(j, 2 * i) = 200;
out.at(j, 2 * i + 1) = 200;
}
else
{
out.at(j, 2 * i) = 0;
out.at(j, 2 * i + 1) = 0;
}
}
else
{
out.at(j, 2 * i) = 200;
out.at(j, 2 * i + 1) = 200;
}
}
}
imwrite(dst, out);
return max-min;
}