在Python使用奇异值分解 (SVD) 进行图像重建
奇异值分解又名 SVD 是众多矩阵分解技术之一,它将矩阵分解为 3 个子矩阵,即 U、S、V,其中 U 是左特征向量,S 是奇异值的对角矩阵,V 称为右特征向量。我们可以使用 NumPy 模块的linalg.svd()方法重建图像的 SVD。
Syntax:
linalg.svd(matrix, full_matrices=True, compute_uv=True, hermitian=False)
Parameters:
- matrix : A real or complex matrix of size > 2.
- full_matrices: If True the size of u and v matrics are m x n , if False then the shape of u and v matrices are m x k , where k is non-zero values only.
- compute_uv: Takes in boolean value to compute u and v matrices along with s matrix.
- hermitian: By default matrix is assumed to be Hermitian if it contains real-values, this is used internally for efficiently computing the singular values.
使用的图像:
Python3
# import module
import requests
import cv2
import numpy as np
import matplotlib.pyplot as plt
# assign and open image
url = 'https://media.geeksforgeeks.org/wp-content/cdn-uploads/20210401173418/Webp-compressed.jpg'
response = requests.get(url, stream=True)
with open('image.png', 'wb') as f:
f.write(response.content)
img = cv2.imread('image.png')
# Converting the image into gray scale for faster
# computation.
gray_image = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Calculating the SVD
u, s, v = np.linalg.svd(gray_image, full_matrices=False)
# inspect shapes of the matrices
print(f'u.shape:{u.shape},s.shape:{s.shape},v.shape:{v.shape}')
Python3
# import module
import seaborn as sns
var_explained = np.round(s**2/np.sum(s**2), decimals=6)
# Variance explained top Singular vectors
print(f'variance Explained by Top 20 singular values:\n{var_explained[0:20]}')
sns.barplot(x=list(range(1, 21)),
y=var_explained[0:20], color="dodgerblue")
plt.title('Variance Explained Graph')
plt.xlabel('Singular Vector', fontsize=16)
plt.ylabel('Variance Explained', fontsize=16)
plt.tight_layout()
plt.show()
Python3
# plot images with different number of components
comps = [3648, 1, 5, 10, 15, 20]
plt.figure(figsize=(12, 6))
for i in range(len(comps)):
low_rank = u[:, :comps[i]] @ np.diag(s[:comps[i]]) @ v[:comps[i], :]
if(i == 0):
plt.subplot(2, 3, i+1),
plt.imshow(low_rank, cmap='gray'),
plt.title(f'Actual Image with n_components = {comps[i]}')
else:
plt.subplot(2, 3, i+1),
plt.imshow(low_rank, cmap='gray'),
plt.title(f'n_components = {comps[i]}')
输出:
u.shape:(3648, 3648),s.shape:(3648,),v.shape:(3648, 5472)
解释:
上面的输出形状表明该图像中有 3648 个线性无关的特征向量。
现在让我们以图形方式查看在奇异向量上使用的图像的方差:
蟒蛇3
# import module
import seaborn as sns
var_explained = np.round(s**2/np.sum(s**2), decimals=6)
# Variance explained top Singular vectors
print(f'variance Explained by Top 20 singular values:\n{var_explained[0:20]}')
sns.barplot(x=list(range(1, 21)),
y=var_explained[0:20], color="dodgerblue")
plt.title('Variance Explained Graph')
plt.xlabel('Singular Vector', fontsize=16)
plt.ylabel('Variance Explained', fontsize=16)
plt.tight_layout()
plt.show()
输出:
说明:上面的方差解释图清楚地表明,大约 99.77% 的信息是由第一个特征向量及其相应的特征值本身解释的。因此,非常建议仅使用前几个特征向量本身来重建图像。
在基于上述讨论的以下程序中,我们使用 SVD 重建图像:
蟒蛇3
# plot images with different number of components
comps = [3648, 1, 5, 10, 15, 20]
plt.figure(figsize=(12, 6))
for i in range(len(comps)):
low_rank = u[:, :comps[i]] @ np.diag(s[:comps[i]]) @ v[:comps[i], :]
if(i == 0):
plt.subplot(2, 3, i+1),
plt.imshow(low_rank, cmap='gray'),
plt.title(f'Actual Image with n_components = {comps[i]}')
else:
plt.subplot(2, 3, i+1),
plt.imshow(low_rank, cmap='gray'),
plt.title(f'n_components = {comps[i]}')
输出:
解释:
- 尽管第一个特征向量包含 99.77% 的信息,但仅从其重建图像并不能给出清晰的图片。
- 使用前 15 个向量进行图像重建可以提供足够好的近似值。同样在 3648 个向量中,这大大减少了计算量,并且还压缩了图像。