用 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.

posted @ 2019-03-07 13:51  resolvent  阅读(...)  评论(...编辑  收藏