はじめに
こんにちは。今回は、特異値分解(SVD: Singular Value Decomposition)を使って、
「暗い顔した思想クン」の顔画像を段階的に圧縮していきます。
「暗い顔した思想クン」を太宰治と仮定し、その顔画像を特異値分解(SVD)で圧縮・復元。文学的な鬱を数式で可視化し、データサイエンスの力でポジティブに。
第1章:特異値分解(SVD)とは何か?
**特異値分解(SVD:Singular Value Decomposition)**とは、任意の行列 $A$ を3つの行列の積に分解する手法です:
$$
A = U \Sigma V^\top
$$
- $A$:元の行列(たとえば画像のデータ)
- $U$:左特異ベクトル(行方向の情報、縦軸の特徴)
- $\Sigma$:特異値の対角行列(情報の重要度を表すスカラー)
- $V^\top$:右特異ベクトルの転置(列方向の情報、横軸の特徴)
これによって、画像などのデータの中に含まれる「主要な構造」や「パターン」を抽出し、重要な情報だけを保持して圧縮することができます。
第2章:画像 = 数値の行列(ピクセル値)
画像というのは、実は「画素(ピクセル)の明るさを数値として並べた2次元の行列」にすぎません。
たとえば、グレースケール画像は以下のような数値で表されます:
[[ 0 34 85 132 200 255]
[ 3 45 90 140 210 250]
[ 10 50 100 150 220 240]
...(高さ × 幅の大きさ)...
]
- 値の範囲は 0〜255(0:黒、255:白)
- 各行列要素 $a_{ij}$ は、ピクセルの明るさ(輝度)
- この2次元の数値配列をSVDで分解 → 圧縮 → 復元することで、画像の本質を保ったままデータ量を減らすことができます。
なぜ行列として扱うの?
SVDは数学的に「行列」を対象とするため、画像を数値の行列とみなすことで、
- どのピクセルが重要か(特異値の大小で)
- どんなパターンが繰り返されているか(特異ベクトルで)
「顔の情報をベクトルで抽出する」という発想であり、SVDによる顔の本質情報抽出につながります。
Pythonコード
# Step 1: 必要なライブラリのインポート
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from google.colab import files # Google Colab限定
# Step 2: 画像のアップロード
uploaded = files.upload() # 実行すると画像アップロードウィンドウが出ます
# Step 3: アップロードされたファイル名を取得(1枚を想定)
filename = list(uploaded.keys())[0]
# Step 4: グレースケールで画像読み込み & numpy配列に変換
img = Image.open(filename).convert('L') # 'L' = grayscale
img_array = np.array(img)
print("画像サイズ:", img_array.shape)
# Step 5: 特異値分解(SVD)
U, S, VT = np.linalg.svd(img_array, full_matrices=False)
print("分解完了:U, S, VT")
# Step 6: 特異値k個だけ使って近似再構成
def svd_reconstruct(k):
S_k = np.diag(S[:k])
U_k = U[:, :k]
VT_k = VT[:k, :]
return np.dot(U_k, np.dot(S_k, VT_k))
# 📌 Step 7: 元画像と再構成画像を比較表示
k_values = [10, 30, 50, 100, 200] # 使用する特異値の数
plt.figure(figsize=(15, 8))
plt.subplot(2, 3, 1)
plt.imshow(img_array, cmap='gray')
plt.title("Original")
plt.axis('off')
for i, k in enumerate(k_values):
img_recon = svd_reconstruct(k)
plt.subplot(2, 3, i+2)
plt.imshow(img_recon, cmap='gray')
plt.title(f"SVD k={k}")
plt.axis('off')
plt.tight_layout()
plt.show()
結果
Original: オリジナルの未圧縮画像。詳細が最も鮮明で、ノイズや歪みがない。
SVD k=10: 特異値10個を使用した圧縮。画像が大きくぼやけ、細部の情報が失われている。
SVD k=30: 特異値30個を使用。k=10に比べて若干鮮明だが、まだ原画像と比べて劣化が顕著。
SVD k=50: 特異値50個を使用。顔の輪郭や特徴がより明確になり、劣化が軽減。
SVD k=100: 特異値100個を使用。原画像に近いクオリティで、細部の再現性が向上。
SVD k=200: 特異値200個を使用。原画像とほぼ同等の品質に近く、圧縮による影響が最小。
結論として、SVDのk値が大きいほど、画像の情報が保持され、品質が向上します。ただし、k値が増えるとデータ量も増えるため、圧縮率と画質のトレードオフが存在します