跳转至

线性代数

SVD奇异值分解

SVD奇异值分解是线性代数中一个重要的数学应用

  1. 其原理是由旋转-拉伸-再旋转的思想实现。

  2. 将一个矩阵分解成三个矩阵,分别是左奇异矩阵、对角矩阵和右奇异矩阵,其中对角矩阵是中的奇异值从大到小排列,计算时将矩阵变成多个秩一矩阵之和。通过取前k个奇异值,来近似拟合原矩阵。这种方式常常应用于图像降维压缩、噪声降噪、人脸识别中的特征脸提取场景。

  3. 大奇异值是主要成分,小奇异值是次要信息

以下代码是简单的灰度测试,对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%,图像特征与原图基本一致,细节仍然保留

1

  • k = 50时,图像压缩率达39%,图像轮廓与原图基本一致

1

  • k = 10时,图像压缩率达8%,图像只剩余大致的轮廓

1