SVD与图像处理

我们都知道SVD可以用来压缩图像。如果你用python的PIL库,进行如下操作:

  1. 将图像转化成numpy array
  2. 进行SVD分解,并形成压缩后的矩阵
  3. 用fromarray()函数还原图像。

你会发现图像不能正常还原,得到的图像无法辨识。

原因在于我们没有将numpy array中的变量类型转换成uint8。只需要加一句

r = r.astype('uint8')

然后再调用fromarray()函数就行了。

背景知识补充 :图像像素色彩信息的保存采用的是uint8变量类型。uint8可以存储0-255范围内的整数。在SVD分解和还原过程中,数据格式会变成float,所以最后要把格式转化回uint8,才能得到正确的图像。

最基本的SVD压缩代码如下:

from PIL import Image
import numpy as np

a = Image.open('神奈川冲浪里.png')#打开图像
b = a.convert('L')#转化成可以用矩阵表示的黑白格式
matrix = np.matrix(b)#转化成二维矩阵

#svd分解
s,v,d = np.linalg.svd(matrix)

#取n个最大的奇异值,还原矩阵
n = 150
r = s[:,:n]*np.diag(v[:n])*d[:n,:]

r = r.astype('uint8')#将变量类型转化为uint8
r = Image.fromarray(r)

下面分别是a(原图像)、b(黑白图像)、r(压缩后的b)。在本例中,一个638*638的图像,只取前150个最大的奇异值,就能基本还原原来的图像b。

a

b

r

也可以处理彩色照片。彩色图像的每个像素的色彩信息是由R、G、B三个uint8组成,要将原始的三维数组分拆成R、G、B三个二维数组,再分别应用SVD。代码如下。

def image_compress(image,n):
    r,g,b = image[:,:,0],image[:,:,1],image[:,:,2]
    shape = image.shape
    r,g,b = image_svd(r,n),image_svd(g,n),image_svd(b,n)
    result = np.zeros(shape)
    result[:,:,0]=r
    result[:,:,1]=g
    result[:,:,2]=b
    return result

def image_svd(image,n):
    image = np.matrix(image)
    s,v,d = np.linalg.svd(image)
    r =s[:,:n]*np.diag(v[:n])*d[:n,:]
    return r

a = np.array(Image.open('神奈川冲浪里.png'))
p = image_compress(a,600)
p = Image.fromarray(p.astype('uint8'))