@fsfzp888
2018-04-15T21:23:15.000000Z
字数 2438
阅读 1193
线性代数
机器学习基础
前边一篇文章中,总结了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选项指定了多大的奇异值才保留。
这个实验使用下边的图片:
我们通过设置不同的奇异值阈值,大于阈值的奇异值会被抛弃,其中矩阵和中的不需要的向量也可以被删除,这样就达到了压缩的目的,最后恢复图像的时候,直接使用SVD的公式就可以恢复图像了。这里边对角矩阵在numpy会被返回成一个普通的数组,因为大量的元素没有必要记录。
这里首先我设置了阈值为8,也就是对角线上边大于8的值才会被保留,否则都抛弃,这个时候得到的重建图片如下所示:
可以看到,此时重建的图像基本肉眼看不出差异,但是压缩率却达到了19.6%。
但是当选择阈值为80的时候,图片变成了:
这个时候,就可以很明显的看出差异来了,此时的压缩率大约是11.1%。
当然,这个图片比较简单,所以压缩率达到19.6%很自然,当图片很复杂的时候,也许根本压缩不了多少。
这个实验代码利用了numpy内部的SVD,简单的验证了上一篇总结中的最后的说明,这个代码本身很简单,没有什么知道称道的。SVD如果有需要也可以自己实现,但是一般都有现成的库,所以如果不是必要,一般直接就拿来用了。使用Python库的好处就是可以先拿过来看看原型的效果,而不必花费过多的精力进行实验代码的书写。
@fsfzp888
2018 年 04月 15日