[关闭]
@fsfzp888 2018-04-15T13:23:15.000000Z 字数 2438 阅读 1082

(八)实例一:利用SVD进行图像压缩

线性代数 机器学习基础


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

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

  1. # -*- coding: utf-8 -*-
  2. import argparse
  3. import os
  4. import cv2
  5. import numpy as np
  6. curr_dir = os.path.dirname(__file__)
  7. default_pic = os.path.join(os.path.dirname(curr_dir), "pic/svd.png")
  8. SVD_TRUNC_VALUE = 8 # compress rate: 0.19616346784776903
  9. # SVD_TRUNC_VALUE = 80 # compress rate: 0.110595062335958
  10. if __name__ == '__main__':
  11. parser = argparse.ArgumentParser("SVD argument parser")
  12. parser.add_argument("-t", "--trunc_value", help="truncate value", type=int)
  13. parser.add_argument("-f", "--file", help="picture file path")
  14. args = parser.parse_args()
  15. if args.file is not None:
  16. if os.path.isfile(args.file):
  17. img_path = args.file
  18. else:
  19. img_path = default_pic
  20. else:
  21. img_path = default_pic
  22. if args.trunc_value is not None:
  23. trunc_val = args.trunc_value
  24. else:
  25. trunc_val = SVD_TRUNC_VALUE
  26. src = cv2.imread(img_path)
  27. src = cv2.cvtColor(src, cv2.COLOR_RGB2GRAY)
  28. src_matrix = np.asarray(src)
  29. U, s, V = np.linalg.svd(src_matrix, full_matrices=True)
  30. print('U shape: ', U.shape, ' s shape: ', s.shape, ' V shape: ', V.shape)
  31. s_trunc = s[s > trunc_val] # get the singular values which is larger than trunc_val
  32. U_trunc = U[:, :s_trunc.shape[0]]
  33. V_trunc = V[:s_trunc.shape[0], :]
  34. print('U_trunc shape: ', U_trunc.shape,
  35. ' Ss_trunc shape: ', s_trunc.shape,
  36. ' V_trunc shape: ', V_trunc.shape)
  37. S_trunc = np.diag(s_trunc)
  38. dst_matrix = np.dot(U_trunc, np.dot(S_trunc, V_trunc))
  39. dst = dst_matrix.astype(np.uint8)
  40. print("np.allclose result: ", np.allclose(src_matrix, dst_matrix))
  41. dst_pixel_size = S_trunc.size + U_trunc.size + s_trunc.size
  42. print("compress rate: ", dst_pixel_size / src_matrix.size)
  43. cv2.imshow('src', src)
  44. cv2.imshow('dst', dst)
  45. cv2.waitKey()
  46. file_dir = os.path.dirname(img_path)
  47. file_name_list = os.path.splitext(os.path.basename(img_path))
  48. file_name = file_name_list[0] + '_trunc_' + str(trunc_val) + file_name_list[1]
  49. cv2.imwrite(os.path.join(file_dir, file_name), dst)

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

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

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

svg.png

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

svg_trunc_8.png

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

svg_trunc_80.png

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

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

@fsfzp888
2018 年 04月 15日

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注