用 SVD 进行图像压缩
\(A\) 为 \(m \times n\) 实矩阵, 记 SVD 的一般形式为
\[A = U\Sigma V',
\]
其中 \(U=(u_1,\dots,u_m)\), \(V=(v_1,\dots,v_n)\) 为正交阵,
\[\Sigma = \begin{pmatrix} S & O\\ O & O \end{pmatrix},\quad\text{where $S = {\rm diag}\{\sigma_1,\dots,\sigma_r\}$ },
\]
\(\sigma_1\ge\dots\sigma_r> 0\) 是 \(A\) 的非零奇异值.
\(\sigma_j\) 越大意味着对应的 \(A'A\) 的特征值 \(\sigma_j^2\) 越大, 从而其主成分 (principal component) \(Av_j\) 的样本方差越大, 我们把方差大视为提供了更多信息.
\(A\) 可写为
\[A = \sum_{k=1}^r \sigma_r u_kv_k',
\]
把带有较少信息的小的奇异值扔掉便达到了压缩 (减少需要存储的东西) 的目的.
import numpy as np
from numpy.linalg import svd
from PIL import Image
import matplotlib.pyplot as plt
path = r'Lenna_(test_image).png'
image = Image.open(path)
image = np.array(image)
print(np.shape(image))
plt.imshow(image)
(512, 512, 3)
3 表示 RGB 各有一个矩阵, 下面分别对 RGB 做 SVD.
# 保留前 k 个奇异值
def compression(image, k):
image2 = np.zeros_like(image)
for i in range(image.shape[2]):
U, S, Vt = svd(image[:,:,i])
image2[:,:,i] = U[:,:k].dot(np.diag(S[:k])).dot(Vt[:k,:])
plt.imshow(image2)
plt.title('k = %s' % k)
plt.figure(figsize=(20,10))
k = image.shape[0]/2
for i in range(8):
k = int(k/2)
pos = 241+i
plt.subplot(pos)
compression(image, k)
References
[1] 姚慕生, 吴泉水, 谢启鸿. (2014). 高等代数学 (第三版) (pp. 414-416). 上海: 复旦大学出版社.
[2] Friedman, J., Hastie, T., & Tibshirani, R. (2001). The elements of statistical learning (pp. 62). New York: Springer series in statistics.