% 基于SVD的图像压缩演示
clear; clc; close all;
%% 1. 读取并显示原始图像
% 读取图像(请将 'your_image.jpg' 替换为你的图像路径)
original_img = imread('ex2.jpg');
% 如果图像是彩色的,转换为灰度图
if size(original_img, 3) == 3
original_img = rgb2gray(original_img);
end
% 转换为double类型以便计算
A = double(original_img);
% 显示原始图像
figure('Position', [100, 100, 1200, 800]);
subplot(2, 3, 1);
imshow(original_img, []);
title('原始图像', 'FontSize', 12);
xlabel(['尺寸: ', num2str(size(A, 1)), '×', num2str(size(A, 2))]);
%% 2. 对图像矩阵进行SVD分解
fprintf('正在进行SVD分解...\n');
[U, S, V] = svd(A);
% 获取奇异值
singular_values = diag(S);
total_singular_values = length(singular_values);
fprintf('SVD分解完成!总奇异值个数: %d\n', total_singular_values);
%% 3. 使用不同数量的奇异值重建图像
k_values = [1, 5, 20, 50, 100]; % 选择不同的k值进行测试
% 计算原始图像需要存储的元素数量
original_elements = numel(A);
fprintf('原始图像需要存储的元素数量: %d\n', original_elements);
for i = 1:length(k_values)
k = k_values(i);
% 使用前k个奇异值重建图像
A_compressed = U(:, 1:k) * S(1:k, 1:k) * V(:, 1:k)';
% 计算压缩比
compressed_elements = size(U, 1)*k + k + size(V, 1)*k;
compression_ratio = compressed_elements / original_elements;
% 显示重建后的图像
subplot(2, 3, i+1);
imshow(uint8(A_compressed), []);
title(sprintf('k = %d (压缩比: %.1f%%)', k, compression_ratio*100), 'FontSize', 10);
xlabel(sprintf('需要存储: %d个元素', compressed_elements));
% 计算并显示PSNR(峰值信噪比)来衡量重建质量
mse = mean((A(:) - A_compressed(:)).^2);
psnr_val = 20*log10(255/sqrt(mse));
text(10, 20, sprintf('PSNR: %.1f dB', psnr_val), 'Color', 'white', 'FontSize', 8, 'BackgroundColor', 'black');
end
%% 4. 绘制奇异值衰减曲线
figure('Position', [100, 500, 800, 400]);
% 绘制奇异值
subplot(1, 2, 1);
semilogy(1:min(100, total_singular_values), singular_values(1:min(100, total_singular_values)), 'b-', 'LineWidth', 2);
xlabel('奇异值序号');
ylabel('奇异值大小(对数尺度)');
title('前100个奇异值');
grid on;
% 绘制累积能量
subplot(1, 2, 2);
cumulative_energy = cumsum(singular_values.^2) / sum(singular_values.^2);
plot(1:min(100, total_singular_values), cumulative_energy(1:min(100, total_singular_values)), 'r-', 'LineWidth', 2);
xlabel('使用的奇异值数量 (k)');
ylabel('累积能量比例');
title('信息保留程度 vs k值');
grid on;
% 标记几个关键点
hold on;
marker_k = [1, 5, 20, 50];
for k = marker_k
if k <= length(cumulative_energy)
plot(k, cumulative_energy(k), 'ro', 'MarkerSize', 8, 'MarkerFaceColor', 'red');
text(k, cumulative_energy(k)-0.1, sprintf('k=%d\n%.1f%%', k, cumulative_energy(k)*100), ...
'HorizontalAlignment', 'center', 'FontSize', 8);
end
end
%% 5. 显示压缩效果对比
fprintf('\n=== 压缩效果总结 ===\n');
fprintf('k值\t压缩比\t\t信息保留\n');
for k = [1, 5, 20, 50, 100]
if k <= total_singular_values
compressed_elements = size(U, 1)*k + k + size(V, 1)*k;
compression_ratio = compressed_elements / original_elements;
info_retained = cumulative_energy(k) * 100;
fprintf('%d\t%.2f%%\t\t%.1f%%\n', k, compression_ratio*100, info_retained);
end
end
fprintf('\n程序运行完成!\n');
