线性代数
SVD奇异值分解¶
SVD奇异值分解是线性代数中一个重要的数学应用
-
其原理是由旋转-拉伸-再旋转的思想实现。
-
将一个矩阵分解成三个矩阵,分别是左奇异矩阵、对角矩阵和右奇异矩阵,其中对角矩阵是中的奇异值从大到小排列,计算时将矩阵变成多个秩一矩阵之和。通过取前k个奇异值,来近似拟合原矩阵。这种方式常常应用于图像降维压缩、噪声降噪、人脸识别中的特征脸提取场景。
-
大奇异值是主要成分,小奇异值是次要信息
以下代码是简单的灰度测试,对256*256像素的lena图像进行SVD图像压缩
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
def load_image(image_path):
img = Image.open(image_path).convert('L') # 转换为灰度图像
img_array = np.array(img)
return img_array
def svd_compress(image_array, k):
U, sigma, Vt = np.linalg.svd(image_array, full_matrices=False)
compressed_U = U[:, :k]
compressed_sigma = np.diag(sigma[:k])
compressed_Vt = Vt[:k, :]
return compressed_U, compressed_sigma, compressed_Vt
def reconstruct_image(compressed_U, compressed_sigma, compressed_Vt):
return compressed_U @ compressed_sigma @ compressed_Vt
def compression_ratio(original_size, k):
return (k * (original_size[0] + original_size[1] + 1)) / (original_size[0] * original_size[1])
def display_comparison(original, reconstructed, k):
# 确保重建图像在正确的像素范围内(可能需要转换为 uint8)
reconstructed = np.clip(reconstructed, 0, 255).astype(np.uint8)
plt.figure(figsize=(10, 5))
# 显示原始图像
plt.subplot(1, 2, 1)
plt.imshow(original, cmap='gray')
plt.title('Original Image')
plt.axis('off')
# 显示压缩后图像
plt.subplot(1, 2, 2)
plt.imshow(reconstructed, cmap='gray')
plt.title(f'Compressed Image (k={k})')
plt.axis('off')
plt.tight_layout()
plt.show()
def save_image(image_array, output_path):
img = Image.fromarray(image_array)
img.save(output_path)
def main(image_path, k, output_path):
# 读取图像
image_array = load_image(image_path)
original_size = image_array.shape
# 进行 SVD 压缩
compressed_U, compressed_sigma, compressed_Vt = svd_compress(image_array, k)
# 重建图像
reconstructed_image = reconstruct_image(compressed_U, compressed_sigma, compressed_Vt)
# 计算压缩率
ratio = compression_ratio(original_size, k)
print(f"压缩率: {ratio:.2f}")
# 显示和保存图像
display_comparison(image_array, reconstructed_image, k)
save_image(reconstructed_image, output_path)
if __name__ == "__main__":
image_path = r"D:\Desktop\code\python\lenna.jpg"
k = 100
output_path = r"D:\Desktop\code\python\compressed_image.jpeg"
main(image_path, k, output_path)
- k = 100时,图像压缩率达78%,图像特征与原图基本一致,细节仍然保留
- k = 50时,图像压缩率达39%,图像轮廓与原图基本一致
- k = 10时,图像压缩率达8%,图像只剩余大致的轮廓