SVDによる圧縮の例(not 次元削減)#

  • 参考: Tutorial: Linear algebra on n-dimensional arrays

  • 全体の流れ

    • 画像ファイルをグレイスケールに変換する。(そのままだとR, G, Bの3レイヤー分の行列があるので、今回はグレイスケールに変換して1つの行列として扱う)

    • グレイスケールが保存されている行列を特異値分解する。

    • 分解によりどのぐらい近似できているのかを確認する。

    • 近似度合いがどのぐらいあるのかを可視化して確認する。

from scipy import misc
import matplotlib.pyplot as plt
import numpy as np

画像データの準備#

img = misc.face()
print(type(img))
plt.imshow(img)
<class 'numpy.ndarray'>
<matplotlib.image.AxesImage at 0x118293ac0>
../_images/92b571ae3b02ad464ce80c24558c31ca210df89c44e36fb63668574343f7f31e.png

画像データのshape確認#

print(img.shape)
print(img[:, :, 0])
plt.imshow(img[:, :, 0])
(768, 1024, 3)
[[121 138 153 ... 119 131 139]
 [ 89 110 130 ... 118 134 146]
 [ 73  94 115 ... 117 133 144]
 ...
 [ 87  94 107 ... 120 119 119]
 [ 85  95 112 ... 121 120 120]
 [ 85  97 111 ... 120 119 118]]
<matplotlib.image.AxesImage at 0x1183a2fd0>
../_images/739e2ef826015deee69081c8cc6090d04b5a69c9a2623f041de5b0ef09f71abb.png

グレイスケール変換#

  • \(grayscale = 0.2126R + 0.7152G + 0.0722B\)

img_array = img / 255
grayscale = img_array @ [0.2126, 0.7152, 0.0722]
plt.imshow(grayscale, cmap="gray")
<matplotlib.image.AxesImage at 0x1184231f0>
../_images/55935ff75ecc6459ef58a0bc686d96f330239028cf37386bc93b250ae7d78e52.png

特異値分解#

U, s, Vt = np.linalg.svd(grayscale)
print(U.shape, s.shape, Vt.shape)
(768, 768) (768,) (1024, 1024)

sを対角行列に変換#

  • sは対角行列。正方行列ならnp.diag()で変換できるが、特異値分解で出てくる特異値は行列サイズが異る(U, Vtに合わせる必要がある)。そこで、サイズを指定したゼロ行列を作成して対角成分のみコピーすることで作成している。

# 対角行列に変換
sigma = np.zeros((U.shape[1], Vt.shape[0]))
for i in range(U.shape[1]):
  sigma[i, i] = s[i]

# 特異値分解によりどのぐらい近似できているかを確認
approximation = np.dot(np.dot(U, sigma), Vt)
diff = grayscale - approximation
print(np.linalg.norm(diff))
1.5653515009052408e-12
# 特異値分解によりどのぐらい近似できているかを確認
np.allclose(grayscale, approximation)
True
# 特異値の大きさを確認
plt.plot(s)
[<matplotlib.lines.Line2D at 0x118488a30>]
../_images/4252151506b655f760a4180458109e2172dcbed2605451e68fb46ed54c820fb8.png

圧縮してみる#

  • 特異値の大きい順に「特異値1〜100位までで近似した場合」を確認。

k = 100
approximation = np.dot(np.dot(U, sigma[:, :k]), Vt[:k, :])
plt.imshow(approximation, cmap="gray")
<matplotlib.image.AxesImage at 0x1184f3a00>
../_images/c59084b8bc44249690c9c9207c84f6aa145a20d8a3fe4ae48b0a60c29ffd8b2c.png
# 特異値1位〜20位までの間で確認してみる
approximation = []
ks = [1, 5, 10, 20]
for k in ks:
  approximation.append(np.dot(np.dot(U, sigma[:, :k]), Vt[:k, :]))

# オリジナル
plt.imshow(grayscale, cmap="gray")
plt.title("original")

# 近似
fig, axes = plt.subplots(nrows=1, ncols=4, figsize=(15,5))
for ax, app, k in zip(axes, approximation, ks):
  ax.imshow(app, cmap="gray")
  ax.set_title("k = {}".format(k))
../_images/aff2a1a691f9921b56bd2355ae1a92a018061125806fb53dd6245b6e8d8c6bf1.png ../_images/a74a779b2f4e67a44a62ce087e2c9693152bf88577debb59b1081dea14b99070.png