特征值&奇异值

如果说一个向量 是方阵 的特征向量,将一定可以表示成下面的形式:

这时候 就被称为特征向量 对应的特征值,一个矩阵的一组特征向量是一组正交向量。特征值分解是将一个矩阵分解成下面的形式:

总结一下,特征值分解可以得到特征值与特征向量,特征值表示的是这个特征到底有多重要,而特征向量表示这个特征是什么。但是特征值分解也有很多的局限,比如说变换的矩阵必须是方阵。

对于普通非方阵普通方阵,描述其特征,我们使用奇异值分解。对于任意一个 的矩阵 总是一个 阶的对称矩阵, 而且其特征值都为非负实数。将 的特征值表示为非负实数的平方,并按照递减的顺序排列:

存在 阶的正交矩阵 阶的正交矩阵 使得:

这个分解就称为 A 的奇异值分解。

压缩图片

图片可以看作一个矩阵,图片像素大小即为矩阵的大小,每个像素值对应的即为矩阵的每个元素。我们记这个由图片组成的矩阵为 ,假设其为 的矩阵。对 进行奇异值分解,那么得到的 是一个 的方阵, 是一个 的矩阵, 是一个 的矩阵

奇异值 跟特征值类似,在矩阵 中也是从大到小排列,而且 的减少特别的快,在很多情况下,前10%甚至1%的奇异值的和就占了全部的奇异值之和的99%以上了。于是我们就可以使用前几项大的奇异值来近似整个矩阵(图片),达到压缩图片的目的。

右边的三个矩阵相乘的结果将会是一个接近于 的矩阵,在这儿, 越接近于 ,则相乘的结果越接近于 。而这三个矩阵的面积之和(在存储观点来说,矩阵面积越小,存储量就越小)要远远小于原始的矩阵 ,于是仅需要记录 即可代替原本的像素矩阵。

理论存在,实践开始

我们将一个黑白颜色的图像表示为一个 矩阵,其中每个条目要么是 ,代表黑色像素,要么是 ,代表白色。因此,矩阵中有 个条目

矩阵M

进行奇异值分解,发现只有三个非零奇异值

则矩阵可表示为

有三个向量 ,每个向量有 个条目,三个向量 ,每个向量都有 个条目,以及三个奇异值 。可以仅使用 个数字而不是矩阵中出现的 个数字来表示矩阵,从而达到压缩图像的目的。

实现代码

使用工具:python(函数库真香),你需要准备以下内容:

  • pillow 库:用于图像处理
  • numpy 库:储存处理数组
  • 一张jpg图片,不要太大,放置于与源代码相同的文件夹内,重命名为 1.jpg
# 引入 pillow
# 引入 numpy
import numpy as np
from PIL import Image

# 奇异值分解函数
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("uint8")

# 请在源文件文件夹中放置一张图片:1.jpg
oriImage = Image.open(r'1.jpg', 'r')
imgArray = np.array(oriImage)

# 3个颜色通道,R、G、B,将其分离
R = imgArray[:, :, 0]
G = imgArray[:, :, 1]
B = imgArray[:, :, 2]


a=float(input("请输入压缩数值(值越大越清晰):"))
# 对单通道颜色矩阵进行奇异值分解,并取前a%的奇异值
reR = imgCompress(R, a)
reG = imgCompress(G, a)
reB = imgCompress(B, a)
reI = np.stack((reR, reG, reB), 2)

Image.fromarray(reI).save("{}".format(a)+"compress.jpg")

压缩效果还可以的样子,前50%(0.5)奇异值压缩

就是图片大了压缩时间有一点小长,以及占用我CPU 100%

偶不堪重负的CPU
偶不堪重负的CPU
这是原图(407kb)
这是原图(407kb)
这是压缩后的图(186kb)
这是压缩后的图(186kb)

陕ICP备2022011813 | 由又拍云提供CDN加速
| 基于 Stellar 主题
十年之约