图像压缩编码发展学习

一、早期发展

1、基于信息的统计概率的无失真编码技术的熵编码

熵编码有很多种:Huffman编码、行程编码 (RLE)、算术编码、Lempel-Ziv字典编码、基于上下文的自适应变长编码(CAVLC)、基于上下文的自适应二进制算术编码(CABAC)

  1. 霍夫曼编码
    霍夫曼编码是一种编码方法,霍夫曼编码是可变字长编码(VLC)的一种。
    哈夫曼编码思想是:出现频率高的字母使用较短的编码,反之出现频率低的则使用较长的编码,这便使编码之后的字符串的平均长度、期望值降低,从而达到无损压缩数据的目的。这样在对整个信息进行编码时,就可以进行大幅度压缩。
    1)将信源符号的概率按减小的顺序排队。
    2)把两个最小的概率相加,并重复操作,始终将较高的概率分支放在右边,直到最后概率为1。
    3)画出从概率1处到每个信源符号的路径,顺序记下沿路径的0和1,所得就是该符号的霍夫曼码字。
    4)将每对组合的左边一个指定为0,右边一个指定为1(或相反)。
    在这里插入图片描述
    最后我们可以看到出现次数(概率)越多的会越在上层,编码也越短,出现频率越少的就越在下层,编码也越长。
    局限:哈夫曼确实可以对数据进行压缩,但是无法逼近香农提出的熵极限。因为哈夫曼是采用整数进行符号编码的;输入符号数受限于可实现的码表尺寸;译码复杂;需要实现知道输入符号集的概率分布;没有错误保护功能。

  2. 算术编码
    早在1948年,香农就提出将信源符号依其出现的概率降序排序,用符号序列累计概率的二进值作为对芯源的编码,并从理论上论证了它的优越性。1960年, Peter Elias发现无需排序,只要编、解码端使用相同的符号顺序即可,提出了算术编码的概念。Elias没有公布他的发现,因为他知道算术编码在数学上虽然成 立,但不可能在实际中实现。1976年,R. Pasco和J. Rissanen分别用定长的寄存器实现了有限精度的算术编码。1979年Rissanen和G. G. Langdon一起将算术编码系统化,并于1981年实现了二进制编码。1987年Witten等人发表了一个实用的算术编码程序,即CACM87(后用 于ITU-T的H.263视频压缩标准)。同期,IBM公司发表了著名的Q-编码器(后用于JPEG和JBIG图像压缩标准)。从此,算术编码迅速得到了广泛的注意。

    算术编码的本质思想,也是对于高频的字符进行短编码,但是它的实现方式,完全和哈夫曼不同。算术编码的基本原理是将编码的消息表示成实数0和1之间的一个间隔(Interval),消息越长,编码表示它的间隔就越小,表示这一间隔所需的二进制位就越多。

    按照信源信号的频率为将[0, 1)分解成为若干区间。那么这一步骤完成之后,我们得到以下的表格:
    在这里插入图片描述
    此时对CADACDB这个信号进行编码:
    在这里插入图片描述
    对于算术编码,解码的过程其实和编码的过程恰恰相反,解码是给定一个[0, 1)中的浮点数,通过解码操作之后就能完全获得原始的信号串。

2、图像相邻像素之间存在很强的相关性,去除冗余可以简化图像数据

  1. 预测编码:线性预测、非线性预测、自适应预测
    利用以往的样本值对新样本值进行预测,将新样本值的实际值与其预测值相减,得到误差值,对该误差值进行编码,传送此编码即可。
    理论上数据源可以准确地用一个数学模型表示,使其输出数据总是与模型的输出一致,因此可以准确地预测数据,但是实际上预测器不可能找到如此完美的数学模型;
    预测本身不会造成失真。误差值的編码可以采用无失真压縮法或失真压縮法。
    有损预测编码原理(差分脉冲调制预测DPCM):
    在这里插入图片描述
  2. 内插编码:亚抽样法、亚行法、亚场法、亚帧法

3、面向空间域的压缩方法初露端倪

块变换编码:DCT变换、DFT变换、KLT变换、Walsh-Hadamard变换
块变换编码是指将图像分成若干个块,对每一块经过数学转换后映射至另一值域后再进行编码处理,是属于变换编码的一种。因为经常与量化一起使用,属于有损数据压缩。

  1. DCT编码(摘自CSDN)
clear;
%% 显示原始图像
A=imread('D:\\Picture\\1.jpg');%这里换成相应的路径即可
[Imgm,Imgn]=size(A);
Target = imcrop(A, [0,0,floor(Imgn/8)*8,floor(Imgm/8)*8]);%为了用blockproc函数
I=double(rgb2gray(Target));%将彩色图像变为灰度图像
figure(1);
imshow(uint8(I));
title('原始图像');
%% dct变换,并且显示图像
T=zeros(8,8); %变换矩阵A,也可以通过函数dctmtx(n)求得
[m,n]=size(T);
for i=0:m-1
    for j=0:m-1
        if i==0
            a=sqrt(1/m);
        else
            a=sqrt(2/m);
        end
        T(i+1,j+1)=a*cos(pi*(j+0.5)*i/m);
    end
end
%----------------------------------------------------------
% B = blockproc(A,[m n],fun)
% 输入灰度图像A,返回图像B,按照尺寸m× n滑动邻域,利用运算函数fun处理后得到结果
% [m n] : 图像以m*n为分块单位,对图像进行处理(如8像素*8像素)
% fun: 应用此函数对分别对每个m*n分块的像素进行处理
%----------------------------------------------------------
fun = @(block_struct) T * block_struct.data * T';
I1=blockproc(I,[8 8],fun);
figure(2);
imshow(uint8(I1));
title('DCT变换');
%% 区域编码,并且显示图像
a=[]; 
a0=ones(1,8); 
a1=zeros(1,8);
%区域编码保留前50%个系数
for i=1:8   
    if i<=4    
        a(i,:)=a0;   
    else
        a(i,:)=a1;
    end
end;
fun = @(block_struct) a.*block_struct.data;
areaCode=blockproc(I1,[8 8],fun); 
figure(3); 
imshow(uint8(I1)); 
title('对DCT区域编码');
%% 区域编码解码,并且显示图像
fun = @(block_struct) T' * block_struct.data * T;
areaCodeDecode=blockproc(areaCode,[8 8], fun); 
figure(4); 
imshow(uint8(areaCodeDecode)); 
title('对DCT区域编码后反DCT变换');
%% 阈值编码,并且显示图像
[Im,In]=size(I1);
%----------------------------------------------------------
% B=reshape(A,m,n) 
% 返回m-by-n矩阵B,它的元素从A的第一列开始取到A的最后一列(一列取完再取第二列)
% 如果A没有m*n元素,得到一个错误结果
%----------------------------------------------------------
list=reshape(I1,1,Im*In);%将I1变成一个1*(Im*In)的列向量

%----------------------------------------------------------
% B=median(M)
% 每一列返回一个值,为M该列的从大到小排列的中间值.
%----------------------------------------------------------
mid=median(abs(list));%即取出g中绝对值的中间值,因为DCT变换正负并不影响大小

%阈值编码保留大的50%个系数
for i=1:m
    for j=1:n
        if(abs(I1(i,j))<mid)
            a(i,j)=0;
        end;
    end;
end;
fun = @(block_struct) a.* block_struct.data;
thresholdCode=blockproc(I1,[8 8],fun);
figure(5);
imshow(uint8(thresholdCode));
title('对DCT阈值编码');

%% 阈值编码解码,并且显示图像
fun = @(block_struct) T' * block_struct.data * T;
thresholdCodeDecode=blockproc(thresholdCode,[8 8],fun);
figure(6);
imshow(uint8(thresholdCodeDecode));
title('对DCT阈值编码后反DCT变换');

DCT变换具有如下优点:用单片集成电路就可以实现,可将最多信息装入最少的系数中,并且在子图像间的边界变得可见时,可使出现的称为块缺陷的块效应最小化。DCT在信息携带能力和计算复杂性之间提供了较好的折中。

二、中期发展

  1. 第二代图像编码:方向分解编码、纹理轮廓的编码、区域基编码

  2. 混合编码:一种方法很难去除图像存在的多种相关性,多种编码有机结合能够对图像数据进行更大的压缩;

  3. JPEG图像压缩标准:
    (摘自:https://zhuanlan.zhihu.com/p/575387988)
    JPEG图像压缩标准于1992年被采纳为国际标准,至今已走过30年的历史,仍然保持着高度的活力和市场占有率。JPEG在对图像压缩时包含了很多步骤,其中的每个步骤对数据的压缩都有一定贡献。
    在这里插入图片描述
    上图展示了JPEG图像压缩编码器(encoder)工作的主要步骤。如果将图中的每一个箭头反过来,我们就成功得到了一个JPEG解码器(decoder)。从上图中看,JPEG的主要步骤有下面这些:

    • 将图像从RGB色彩转到YCbCr空间,并进行4:2:0下采样 (有损)
    • 按固定大小的块对YCbCr进行离散余弦变换得到频域矩阵 (无损)
    • 对频域矩阵进行量化 (有损)
    • 按之字形对量化后的频域矩阵进行行程编码 (无损)
    • 对上一步得到的结果进行熵编码 (无损)
    • 构造文件头,将结果写入数据文件 (无损)
      从主要步骤上看,JPEG虽然是有损压缩,但真正有损的步骤只有2个:一是YCbCr的下采样,因为在这其中会丢失一部分原有的Cb和Cr数据;二是对离散余弦变换得到的频域矩阵进行量化操作,这一步主要是在过滤掉图像中原有的高频信号。
  4. DCT离散余弦变换

  5. 量化:有损编码的信息损失都是在量化阶段产生的;如何设计最优的性能的量化器一直是各阶段重要研究课题;1959年香农提出矢量量化(VQ)的性能总是优于标量量化的(SQ);LBG算法极大推进了矢量量化的研究
    矢量量化:多级VQ、均值/残差VQ、增益/波形VQ、树结构VQ

近期发展

1.小波变换编码:

所谓的小波的小是针对傅里叶波而言,傅里叶波指的是在时域空间无穷震荡的正弦(或余弦波)。相对而言,小波指的是一种能量在时域非常集中的波,它的能量有限,都集中在某一点附近,而且积分的值为零,这说明它与傅里叶波一样是正交波。小波变换步骤如下:

​ 1)把小波 w(t)和原函数 f(t)的开始部分进行比较,计算系数 C。系数 C 表示该部分函数与小波的相似程度;

​ 2)把小波向右移 k 单位,得到小波 w(t-k),重复 1。重复该步骤直至函数 f 结束;

​ 3)扩展小波 w(t),得到小波 w(t/2),重复步骤 1,2;

​ 不断扩展小波,重复 1,2,3。

图像的二维离散小波分解和重构过程如下图所示,

分解过程可描述为:首先对图像的每一行进行 1D-DWT,获得原始图像在水平方向上的低频分量 L 和高频分量 H,然后对变换所得数据的每一列进行 1D-DWT,获得原始图像在水平和垂直方向上的低频分量 LL、水平方向上的低频和垂直方向上的高频 LH、水平方向上的高频和垂直方向上的低频 HL 以及水平和垂直方向上的的高频分量 HH。

重构过程可描述为:首先对变换结果的每一列进行以为离散小波逆变换,再对变换所得数据的每一行进行一维离散小波逆变换,即可获得重构图像。由上述过程可以看出,图像的小波分解是一个将信号按照低频和有向高频进行分离的过程,分解过程中还可以根据需要对得到的 LL 分量进行进一步的小波分解,直至达到要求。
在这里插入图片描述
# pip安装 pip install matplotlib numpy opencv-python PyWavelets

import cv2
import numpy as np
import matplotlib.pyplot as plt
# 需要安装python小波分析库PyWavelets
from pywt import dwt2, idwt2

def put(path):
    # 0是表示直接读取灰度图
    img = cv2.imread(path, 0)
    
    # 对img进行haar小波变换:,haar小波
    cA, (cH, cV, cD) = dwt2(img, 'haar')

    # 小波变换之后,低频分量对应的图像:
    a = np.uint8(cA / np.max(cA) * 255)
    # 小波变换之后,水平方向高频分量对应的图像:
    b = np.uint8(cH / np.max(cH) * 255)
    # 小波变换之后,垂直平方向高频分量对应的图像:
    c = np.uint8(cV / np.max(cV) * 255)
    # 小波变换之后,对角线方向高频分量对应的图像:
    d = np.uint8(cD / np.max(cD) * 255)

    # 根据小波系数重构回去的图像
    rimg = idwt2((cA, (cH, cV, cD)), 'haar')

    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.subplot(231), plt.imshow(img, 'gray'), plt.title('原始图像'), plt.axis('off')
    plt.subplot(232), plt.imshow(a, 'gray'), plt.title('低频分量'), plt.axis('off')
    plt.subplot(233), plt.imshow(b, 'gray'), plt.title('水平方向高频分量'), plt.axis('off')
    plt.subplot(234), plt.imshow(c, 'gray'), plt.title('垂直平方向高频分量'), plt.axis('off')
    plt.subplot(235), plt.imshow(d, 'gray'), plt.title('对角线方向高频分量'), plt.axis('off')
    plt.subplot(236), plt.imshow(rimg, 'gray'), plt.title('重构图像'), plt.axis('off')

    # plt.savefig('3.new-img.jpg')
    plt.show()


# 图像处理函数,要传入路径
put(r'../image/image1.jpg')

猜你喜欢

转载自blog.csdn.net/weixin_45178274/article/details/129778280
今日推荐