掌握DPCM编解码系统的基本原理。初步掌握实验用C++语言编程实现DPCM编码器,并分析其压缩效率。
本次实验代码包含在以下几个文件中,下面分别进行展示和分析(以8bit量化为例)。
头文件声明了主函数要用到的方法函数。
#pragma once int prob(int height, int width, unsigned char* inbuf, double* outpro); int dpcm(int height, int width, unsigned char* oldbuf, unsigned char* newbuf, unsigned char* difbuf, int dep); int psnr(int height, int width, unsigned char* oldbuf, unsigned char* newbuf, int dep);
主函数的内容即本次实验的步骤:
#define _CRT_SECURE_NO_DEPRECATE #include<iostream> #include<math.h> #include"method.h" using namespace std; int main() { //相关参数 int height = 768; //高 int width = 512; //宽 int dep = 8; //量化比特数 //缓冲区开辟 unsigned char* orbuf = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 1.5); unsigned char* rebuf = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 1.5); unsigned char* errbuf = (unsigned char*)malloc(sizeof(unsigned char) * height * width * 1.5); double* orpro = (double*)malloc(sizeof(double) * 256); double* errpro = (double*)malloc(sizeof(double) * 256); //文件打开 FILE* orfile = fopen("Birds.yuv", "rb"); FILE* refile = fopen("outBirds.yuv", "wb"); FILE* errfile = fopen("quanBirds.yuv", "wb"); FILE* ortxt = fopen("orpro.txt", "wb"); FILE* errtxt = fopen("errpro.txt", "wb"); if (orfile == NULL || refile == NULL || errfile == NULL || ortxt == NULL || errtxt == NULL) { cout << "文件打开失败!" << endl; return 0; } //文件读取 fread(orbuf, 1, height * width * 1.5, orfile); //dpcm dpcm(height, width, orbuf, rebuf, errbuf, dep); //psnr计算 psnr(height, width, orbuf, rebuf, dep); //灰度值概率计算 prob(height, width, orbuf, orpro); //原图概率分布 prob(height, width, errbuf, errpro); //预测误差图概率分布 //文件写入 fwrite(rebuf, 1, height * width * 1.5, refile); fwrite(errbuf, 1, height * width * 1.5, errfile); for (int i = 0; i < 256; i++) { fprintf(ortxt, "%lf\n", *(orpro + i)); fprintf(errtxt, "%lf\n", *(errpro + i)); } //文件关闭 fclose(orfile); fclose(refile); fclose(errfile); fclose(ortxt); fclose(errtxt); return 0; }
#include<iostream> #include<math.h> using namespace std; int dpcm(int height, int width, unsigned char* orbuf, unsigned char* rebuf, unsigned char* errbuf, int dep) { //dpcm编解码 for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { if (j == 0) { *errbuf = (*orbuf - 128 + 255) / pow(2, 9 - dep); *rebuf = 128 + (*errbuf * pow(2, 9 - dep) - 255); rebuf++; orbuf++; errbuf++; } else { *errbuf = (*orbuf - *(rebuf - 1) + 255) / pow(2, 9 - dep); *rebuf = *(rebuf - 1) + (*errbuf * pow(2, 9 - dep) - 255); if (*rebuf < 0) *rebuf = 0; if (*rebuf > 255) *rebuf = 255; rebuf++; orbuf++; errbuf++; } } } //uv分量不处理,差值设为128 for (int i = 0; i < height * width * 0.5; i++) { *rebuf = *orbuf; *errbuf = 128; rebuf++; orbuf++; errbuf++; } //指针归位 for (int i = 0; i < height * width * 1.5; i++) { errbuf--; rebuf--; orbuf--; } return 0; }
平平无奇统计频次,计算概率。
#include<iostream> #include<math.h> using namespace std; int prob(int height, int width, unsigned char* inbuf, double* outpro) { //变量定义 double size = height * width * 1.5; //总像素数 int num[256] = { 0 }; double pro[256] = { 0 }; //计算各个灰度出现概率 for (int i = 0; i < size; i++) { num[(int)*(inbuf + i)]++; pro[(int)*(inbuf + i)] = num[(int)*(inbuf + i)] / size; } //输出概率 for (int i = 0; i < 256; i++) *(outpro + i) = pro[i]; return 0; }
根据实验原理中PSNR计算方法来实现。
#include<iostream> #include<math.h> using namespace std; int psnr(int height, int width, unsigned char* orbuf, unsigned char* rebuf, int dep) { double max = 255; double mse = 0; double psnr; for (int i = 0; i < height; i++) for (int j = 0; j < width; j++) { mse += (orbuf[i * width + j] - rebuf[i * width + j]) * (orbuf[i * width + j] - rebuf[i * width + j]); } mse = mse / (double)(width * height); psnr = 10 * log10((double)(max * max) / mse); cout << dep << "比特量化时PSNR = " << psnr << endl; return 0; }
本来打算如第一次实验那样复制一大堆进数组,这次探索了一下如何获取txt文件内容,探索成功了!上次就是懒
orpro = importdata('D:\CUC\SJYS\SJYS_L8\orpro.txt'); errpro = importdata('D:\CUC\SJYS\SJYS_L8\errpro.txt'); %原图概率分布 figure(1) plot(orpro); title('orBirds.yuv概率分布'); xlabel('灰度值'); ylabel('概率'); axis([0 256 0 0.02]); %预测误差图概率分布 figure(2) plot(errpro); title('errBirds.yuv概率分布'); xlabel('灰度值'); ylabel('概率'); axis([0 256 0 0.55]);
① 运行代码得到4个输出文件,依次为预测误差图像、重建图像、预测误差图像概率分布、原图像概率分布。
② YUVviewer查看图像,matlab输出概率分布图像。
原图及概率分布:
原图 | 概率分布图 (matlab输出) |
---|---|
不同量化比特数下的结果:
8bit | 4bit | 2bit | 1bit | |
---|---|---|---|---|
预测误差图像 | ||||
重建图像 | ||||
psnr值 | ||||
预测误差图像概率分布 |
运用老师发来的huffcode.exe
分别对原图和预测误差图像进行熵编码。
运行批处理文件后得到:
图片 | 大小 / KB | 压缩比 |
---|---|---|
原图 | 576 | 1 |
仅熵编码 | 520 | 1.108 |
8bit DPCM+熵编码 | 193 | 2.984 |
4bit DPCM+熵编码 | 111 | 5.189 |
2bit DPCM+熵编码 | 124 | 4.645 |
1bit DPCM+熵编码 | 117 | 4.923 |
压缩质量 | 8bit DPCM | 4bit DPCM | 2bit DPCM | 1bit DPCM |
---|---|---|---|---|
psnr值 / dB | 51.1417 | 22.8232 | 8.53407 | 9.11363 |
综上所述,DPCM+熵编码压缩效果整体优于仅使用熵编码,控制合适的量化比特数的情况下,DPCM+熵编码在压缩比和压缩质量(目测)上均有优势,虽然损失了一些质量,但对于人眼来说无伤大雅。
fwrite
不太行,毕竟概率是double
类型的,查了查资料问了问同学采用了fprintf
流式写入的方法。PS.您现在看到的下面