送书 | 使用奇异值分解进行图像压缩

送书 | 使用奇异值分解进行图像压缩_第1张图片

奇异值分解(Singular Value Decomposition,SVD)是线性代数中一种重要的矩阵分解。在很多情况下,数据中的一部分携带了数据集中的大部分信息,而剩下的信息则要么是噪声,要么是毫不相关的信息。利用SVD实现,能够用小得多的数据集来表示原始数据集。这样做,实际上是去除了噪声和冗余数据。同样,当我们试图节省空间时,去除信息也是很有用了。可以使用这个思想对图像进行压缩。

在Python中,SVD分解非常简单,可利用np.linalg.svd()函数,比如u,sigma,v=np.linalg.svd(A),则u,v分别返回A的左右奇异向量,而sigma返回的并不是系数矩阵,而是一个奇异值从大到小排列的一个向量。

然后对于图像而言,一般的彩色图像其实就是RGB三个图层上矩阵的叠加,每个元素的值 为0到255之间的整数,在Python中读取图像可以通过plt.imread()函数,这样直接得到了一个ab3的矩阵,然后对三个图层分别处理就行。

利用SVD分解进行图片压缩的基本步骤如下:

1 读取图片,分解成RGB三个矩阵。
2 对三个矩阵分别进行SVD分解,得到对应的奇异值和奇异向量。
3 按照一定标准进行奇异值的筛选(整体数量的一定百分比,或者奇异值和的一定百分比)
4 恢复矩阵,并将RGB三个矩阵叠加起来。
5 保存图像。

实现步骤如下:

由于在python中需要读取图像,因此需要在命令行下安装如下库:

pip install matplotlib pillow

程序首先导入下面两个库:

import numpy as np   
from matplotlib import pyplot as plt

然后定义下面函数用于读入图像,并生成数据矩阵:

def svdimage(filename,percent):  
    original=plt.imread(filename)    #读取图像
    R0=np.array(original[:,:,0])     #获取第一层矩阵数据
    G0=np.array(original[:,:,1])     #获取第二层矩阵数据
    B0=np.array(original[:,:,2])     #获取第三层矩阵数据
    u0,sigma0,v0=np.linalg.svd(R0)   #对第一层数据进行SVD分解
    u1,sigma1,v1=np.linalg.svd(G0)   #对第二层数据进行SVD分解
    u2,sigma2,v2=np.linalg.svd(B0)   #对第三层数据进行SVD分解
    R1=np.zeros(R0.shape)  
    G1=np.zeros(G0.shape)  
    B1=np.zeros(B0.shape)  
    total0=sum(sigma0)  
    total1=sum(sigma1)  
    total2=sum(sigma2)  
    sd=0  
    for i,sigma in enumerate(sigma0):     #用奇异值总和的百分比来进行筛选。
        R1+=sigma*np.dot(u0[:,i].reshape(-1,1),v0[i,:].reshape(1,-1))  
        sd+=sigma  
        if sd>=percent*total0:  
            break  
    sd=0  
    for i,sigma in enumerate(sigma1): #用奇异值总和的百分比来进行筛选
        G1+=sigma*np.dot(u1[:,i].reshape(-1,1),v1[i,:].reshape(1,-1))  
        sd+=sigma  
        if sd>=percent*total1:  
            break  
    sd=0  
    for i,sigma in enumerate(sigma2): #用奇异值总和的百分比来进行筛选
        B1+=sigma*np.dot(u2[:,i].reshape(-1,1),v2[i,:].reshape(1,-1))  
        sd+=sigma  
        if sd>=percent*total2:  
            break  
    final=np.stack((R1,G1,B1),2)  
    final[final>255]=255  
    final[final<0]=0  
    final=np.rint(final).astype('uint8')  
    return final  

最后,可以调用这个函数,调用时指定需要的百分比。如下图所示:

for p in np.arange(.1,1,.1):  
        after=svdimage(filename,p)  
        plt.imsave(str(p)+'_1.jpg',after)

原始图像如下:

送书 | 使用奇异值分解进行图像压缩_第2张图片

下图给出了保留奇异值50%的压缩信息的图像。

送书 | 使用奇异值分解进行图像压缩_第3张图片

下图给出了保留奇异值90%的压缩信息的图像。

送书 | 使用奇异值分解进行图像压缩_第4张图片

可以看出在不同的奇异值保留程度下,图像的压缩比例也不一样。

送书 | 使用奇异值分解进行图像压缩_第5张图片


本文部分观点节选自北京大学出版社出版的《Python 3 数据分析与机器学习实战》图书,这本书现在在京东参加春季优惠满100减50的活动,点击阅读原文,即可了解更多。

送书 | 使用奇异值分解进行图像压缩_第6张图片


留言回复你在机器学习方面做过哪些有趣的应用,我们会在留言中随机抽取一位读者免费送出上述图书一本。

你可能感兴趣的:(送书 | 使用奇异值分解进行图像压缩)