奇异值分解在图形压缩中的应用
在研究奇异值分解的工程应用之前,我们得明白什么是奇异值?什么是奇异向量?
奇异值与奇异向量
概念:奇异值描述了矩阵在一组特定向量上的行为,奇异向量描述了其最大的作用方向。
奇异值分解(SVD)
对于任意 矩阵 ,都可以写成:
其中 和 为正交矩阵, 为由奇异值组成的对角矩阵。若 的秩为 ,则奇异值满足:
奇异值分解计算过程
设:
求矩阵 的 、 和 。
-
计算
-
计算奇异值
的行列式为 ,迹为 ,因此其两个特征值为 和 。奇异值是特征值的平方根:
-
计算右奇异向量
将两个特征值代入 ,并对特征向量进行归一化:
-
构造左奇异向量
根据 :
-
写出最终分解
利用奇异值分解(SVD)进行图片压缩
首先我们先找一张图片来进行实验。

通道分离
对于JPG格式的彩色图片,拥有3个颜色通道,R(红)、G(绿)、B(蓝),那么可以尝试将每个颜色通道进行分离,产生3个形状均为图像高 × 宽 的单通道剧展,即imageR,imageG,imageB。
进行通道分离,将imageArray数组中的每个通道分别单独取出来,得到3 个“高 × 宽”的二维数组。这3个二维数组中每个位置上的取值就是对应像素的某个颜色通道的取值,代码如下:
import numpy as npfrom PIL import Image
originalImage = Image.open(r'teriri.jpg', 'r')imageArray = np.array(originalImage)R = imageArray[:, :, 0]G = imageArray[:, :, 1]B = imageArray[:, :, 2]print(R)print(G)print(B)运行结果如下:
[[207 207 207 … 141 141 141]
[207 207 207 … 141 141 141]
[207 207 207 … 141 141 141]
…
[246 247 248 … 239 239 239]
[246 247 248 … 239 239 239]
[246 247 248 … 239 239 239]]
[[198 198 198 … 126 126 126]
[198 198 198 … 126 126 126]
[198 198 198 … 126 126 126]
…
[233 234 235 … 235 235 235]
[233 234 235 … 235 235 235]
[233 234 235 … 235 235 235]]
[[215 215 215 … 149 147 147]
[215 215 215 … 149 147 147]
[215 215 215 … 149 147 147]
…
[230 231 233 … 203 203 203]
[230 231 233 … 203 203 203]
[230 231 233 … 203 203 203]]
至此,我们成功得到了3个二维ndarray数组,将R、G、B三个通道成功进行了分离。
矩阵压缩
对每个单通道矩阵进行奇异值分解,按照压缩的实际需要取前k个奇异值,进行3个单通道的矩阵的压缩,最后分别形成3个压缩后的矩阵:imageRC,imageGC,imageBC,代码如下:
def imgCompress(channel,percent): U,sigma,V_T = np.linalg.svd(channel) m = U.shape[0] n = V_T.shape[0] reChannel = np.zeros((m,n)) for k in range (len(sigma)): reChannel = reChannel + sigma[k] * np.dot(U[:,k].reshape(m,1),V_T[k,:].reshape(1,n)) if float(k) / len(sigma) > percent: reChannel[reChannel < 0] = 0 reChannel[reChannel > 255] = 255 break return np.rint(reChannel).astype("unit8")图像重建
将经过奇异值分解处理的3个单通道矩阵合并,从而重构出压缩后的彩色图像。
for p in [0.001, 0.005, 0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9]: #p表示取所有奇异值的前多少比例 reR = imgCompress(R,p) reG = imgCompress(G,p) reB = imgCompress(B,p) reI = np.stack((reR,reG,reB),2) Image.fromarray(reI).save("{}".format(p)+"img.png")整体运行结果如下:
比例为0.001至0.04
>0.05至0.5
0.6至原图
总结:
- 取前0.1%奇异值重建的图像是一个非常模糊的,基本只能看到大体轮廓。
- 取前1%奇异值重建的图像就可以看到一个比较清晰的图片了。
- 随着比例的提升,图片越来越清晰,到30%的时候就基本与原图一致了。
如果这篇文章对你有帮助,欢迎分享给更多人!
部分信息可能已经过时
>0.05至0.5
