利用SVD进行图像压缩

利用SVD进行图像压缩


前边一篇文章中,总结了SVD相关的理论支持,老是一味的搞理论没有实践毕竟也是不行的,所以本文会简单的实现一个基于Python的图像压缩示例程序,来使用SVD进行简单图片的压缩以及还原实验。

首先来看代码,完整的代码如下:

# -*- coding: utf-8 -*-
import argparse
import os

import cv2
import numpy as np

curr_dir = os.path.dirname(__file__)
default_pic = os.path.join(os.path.dirname(curr_dir), "pic/svd.png")

SVD_TRUNC_VALUE = 8  # compress rate:  0.19616346784776903
# SVD_TRUNC_VALUE = 80  # compress rate:  0.110595062335958


if __name__ == '__main__':
    parser = argparse.ArgumentParser("SVD argument parser")
    parser.add_argument("-t", "--trunc_value", help="truncate value", type=int)
    parser.add_argument("-f", "--file", help="picture file path")
    args = parser.parse_args()
    if args.file is not None:
        if os.path.isfile(args.file):
            img_path = args.file
        else:
            img_path = default_pic
    else:
        img_path = default_pic
    if args.trunc_value is not None:
        trunc_val = args.trunc_value
    else:
        trunc_val = SVD_TRUNC_VALUE
    src = cv2.imread(img_path)
    src = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)
    src_matrix = np.asarray(src)
    U, s, V = np.linalg.svd(src_matrix, full_matrices=True)
    print('U shape: ', U.shape, ' s shape: ', s.shape, ' V shape: ', V.shape)
    s_trunc = s[s > trunc_val]  # get the singular values which is larger than trunc_val
    U_trunc = U[:, :s_trunc.shape[0]]
    V_trunc = V[:s_trunc.shape[0], :]
    print('U_trunc shape: ', U_trunc.shape,
          ' Ss_trunc shape: ', s_trunc.shape,
          ' V_trunc shape: ', V_trunc.shape)
    S_trunc = np.diag(s_trunc)
    dst_matrix = np.dot(U_trunc, np.dot(S_trunc, V_trunc))
    dst = dst_matrix.astype(np.uint8)
    print("np.allclose result: ", np.allclose(src_matrix, dst_matrix))
    dst_pixel_size = S_trunc.size + U_trunc.size + s_trunc.size
    print("compress rate: ", dst_pixel_size / src_matrix.size)
    cv2.imshow('src', src)
    cv2.imshow('dst', dst)
    cv2.waitKey()
    file_dir = os.path.dirname(img_path)
    file_name_list = os.path.splitext(os.path.basename(img_path))
    file_name = file_name_list[0] + '_trunc_' + str(trunc_val) + file_name_list[1]
    cv2.imwrite(os.path.join(file_dir, file_name), dst)

假如上边的文件被保存为svn_numpy.py, 那么可以通过输入下边的命令来执行上边的Python脚本:

python svn_numpy.py -t 8 -f ../pic/svg.png

通过-f选项指定了图片文件名称,-t选项指定了多大的奇异值才保留。
这个实验使用下边的图片:

我们通过设置不同的奇异值阈值,大于阈值的奇异值会被抛弃,其中矩阵 S S S V T V^{T} VT中的不需要的向量也可以被删除,这样就达到了压缩的目的,最后恢复图像的时候,直接使用SVD的公式就可以恢复图像了。这里边对角矩阵在numpy会被返回成一个普通的数组,因为大量的 0 0 0元素没有必要记录。
这里首先我设置了阈值为8,也就是 Σ \Sigma Σ对角线上边大于8的值才会被保留,否则都抛弃,这个时候得到的重建图片如下所示:

可以看到,此时重建的图像基本肉眼看不出差异,但是压缩率却达到了19.6%。
但是当选择阈值为80的时候,图片变成了:

这个时候,就可以很明显的看出差异来了,此时的压缩率大约是11.1%。
当然,这个图片比较简单,所以压缩率达到19.6%很自然,当图片很复杂的时候,也许根本压缩不了多少。

这个实验代码利用了numpy内部的SVD,简单的验证了上一篇总结中的最后的说明,这个代码本身很简单,没有什么知道称道的。SVD如果有需要也可以自己实现,但是一般都有现成的库,所以如果不是必要,一般直接就拿来用了。使用Python库的好处就是可以先拿过来看看原型的效果,而不必花费过多的精力进行实验代码的书写。

@fsfzp888
2018 年 04月 15日

你可能感兴趣的:(机器学习基础)