奇异值分解(Singular Value Decomposition,简称SVD)是线性代数中一种非常重要的矩阵分解技术,广泛应用于信号处理、图像处理、统计学、计算机科学等领域。它将一个复杂的矩阵分解为三个简单矩阵的乘积,这三个矩阵分别是:
1、左奇异向量矩阵(U矩阵):一个正交矩阵,其列向量是原矩阵的左奇异向量。
2、奇异值对角矩阵( 矩阵):一个对角矩阵,对角线上的元素是原矩阵的奇异值,这些奇异值是非负的,并且按降序排列。
3、右奇异向量矩阵( 矩阵):一个正交矩阵,其列向量是原矩阵的右奇异向量。
对于任意一个 的矩阵 ,其奇异值分解可以表示为:
其中, 是一个 的正交矩阵, 是一个对角矩阵, 是一个 的正交矩阵, 是 的转置。
奇异值分解的步骤大致为:
1、计算奇异值:首先计算矩阵 的奇异值,即求解特征值问题 或 的非负平方根。
2、构造奇异值矩阵:将奇异值按降序排列,构成对角矩阵 。
3.构造奇异向量矩阵:计算 与 的关系,得到左奇异向量和右奇异向量,分别构成矩阵 和 。
奇异值分解具有以下性质和应用:
1、降维:通过保留最大的几个奇异值和对应的奇异向量,可以对矩阵进行降维,这在数据压缩和特征提取中非常有用。
2、噪声过滤:在信号处理中,奇异值分解可以用来过滤噪声,因为噪声通常对应较小的奇异值。
3、数据近似:通过保留最大的几个奇异值,可以得到原矩阵的最佳近似,这在数据压缩和图像处理中非常有用。
4、矩阵伪逆:奇异值分解可以用来计算矩阵的伪逆,这对于解决线性方程组的最小二乘问题非常有用。
奇异值分解(SVD)在图像压缩中的作用是通过减少矩阵的秩来实现压缩,同时尽量保留图像的关键视觉信息。具体来说,SVD在图像压缩中的应用可以分为以下几个步骤:
1、图像矩阵转换:首先将图像表示为一个矩阵,其中每个像素值对应矩阵的一个元素。对于彩色图像,通常需要将其转换为灰度图像或分别对RGB三个通道进行处理。
2、应用SVD:对图像矩阵进行奇异值分解,得到 三个矩阵。
3、选择奇异值:由于奇异值按从大到小的顺序排列在 对角矩阵中,较大的奇异值代表了图像的主要特征,而较小的奇异值则代表了细节和噪声。在压缩时,可以选择保留最大的 个奇异值。
4、重构图像:使用保留的 个奇异值和对应的 和 矩阵中的列向量,重构图像矩阵。具体来说,计算 ,其中 和 分别是 和 的前 列,Σ𝑘Σk 是由前 个奇异值构成的对角矩阵。
5、压缩效果和质量权衡:通过选择不同的 值,可以控制压缩的程度和图像质量之间的平衡。较小的 值意味着更高的压缩率,但可能会丢失一些图像细节;较大的 值则保留了更多的图像信息,压缩率较低。
这里以一个图片为例,图片选自电影《荒岛余生》,截取主角快要精神崩溃时刻懵逼的场景,后面还有主角的精神支柱:Vilson,(剧透一下,后来主角决意离开小岛,扎了个皮筏,在海上漂浮的过程中,Vilson被大浪吹走了,主角万念俱灰,躺在皮筏上面等死,最后........。)
原图片为:
帖子给出了详细的代码,下面逐行讲解代码。
clc; clear all;
%%
% 读取图像
img = imread('island.png');
% 将图像转换为灰度图像,如果它不是灰度图
if size(img, 3) == 3
img = rgb2gray(img);
end
% 将图像转换为双精度浮点数格式
img_double = im2double(img);
上面的代码将图片读进内存,但是matlab的imread函数读进的图片数据类型为unit8,因此需要将数据处理为浮点型数组,方便后续进行矩阵分解。
% 对图像进行奇异值分解
[U, S, V] = svd(img_double, 'econ');
这里没有自己写算法进行分解,直接调用了matlab的svd函数,返回左右奇异值矩阵和对角矩阵。
% 选择奇异值的数量来压缩图像
k = 50; % 选择一个较小的 k 值来压缩图像
k就是我们选择的前 阶特征值,这里选择了50,后续我会修改 值,观察不同的k值对压缩后的图片质量影响。
% 选择奇异值的数量来压缩图像
U_k = U(:, 1:k);
S_k = S(1:k,1:k);
V_k = V(:, 1:k);
选取前 阶特征值对左右奇异值矩阵和对角矩阵重构。
% 重构压缩后的图像
img_compressed = U_k * S_k * V_k';
生成重构后的矩阵,即生成保留前 阶特征值的图片。
% 显示原始图像和压缩后的图像
subplot(1, 2, 1);
imshow(img_double);
title('Original Image');
subplot(1, 2, 2);
imshow(img_compressed);
title(['Compressed Image with k = ', num2str(k)]);
上面的代码用与显示压缩前后的矩阵。
这张图片经过处理后的浮点矩阵大小为 ,因此,左奇异矩阵的大小为 ,右奇异矩阵的大小为 ,对角矩阵大小为 。
这张图片经过奇异值分解,共计849个特征值,我们依次保留不同的特征值,分别保留了25、50、75、100、125、15、175、200,比较压缩后的图片质量。
下面给出了原图与压缩后的图片对比图,每张图片的左边均是原图,右面是压缩后的图片,图片下方标注了相应的压缩程度。 25/849 50/849
75/849
100/849
可以发现,从这里开始,如果没有仔细分辨,已经看不出来原图和压缩后的图片的细微区别,这时截取的特征值占比为11.78%,压缩了将近百分之九十! 125/849 150/849 175/849
200/849
从上面图片的对比中发现,采用SVD对图像进行压缩,仅仅截取十分之一的特征值就达到了接近原图效果的压缩图。可见该方法在图像处理方面有极大的应用前景,并且已经有了极为广泛的应用。
下面给出了详细的matlab代码,使用的时候注意修改图片的名字。
clc; clear all;
%%
% 读取图像
img = imread('island.png');
% 将图像转换为灰度图像,如果它不是灰度图
if size(img, 3) == 3
img = rgb2gray(img);
end
% 将图像转换为双精度浮点数格式
img_double = im2double(img);
imwrite(img_double, 'origin.png');
% 对图像进行奇异值分解
[U, S, V] = svd(img_double, 'econ');
% 选择奇异值的数量来压缩图像
k = 200; % 选择一个较小的 k 值来压缩图像
U_k = U(:, 1:k);
S_k = S(1:k,1:k);
V_k = V(:, 1:k);
% 重构压缩后的图像
img_compressed = U_k * S_k * V_k';
% 显示原始图像和压缩后的图像
subplot(1, 2, 1);
imshow(img_double);
title('Original Image');
subplot(1, 2, 2);
imshow(img_compressed);
title(['Compressed Image with k = ', num2str(k)]);
% 如果需要,保存压缩后的图像
imwrite(img_compressed, string(k)+'.png');
上面的东西写的花里胡哨的,可是,这是有限元领域的公众 号啊!SVD跟有限元又有什么关系呢!?
列位看官,敬请期待!