首页 > 解决方案 > Python SVD 修复特征值的数量以重建图像?

问题描述

我正在尝试重建以前用 SVD 分解的图像。图像是这样的:

在此处输入图像描述

我用这段代码成功地分解了图像:

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

img = Image.open('steve.jpg')
img = np.mean(img, 2)

U,s,V = np.linalg.svd(img)

s图像的奇异值数组。我取的奇异值越多,重建的图像就越类似于原始图像。
例如,如果我取 20 个奇异值:

n = 20
S = np.zeros(np.shape(img))
for i in range(0, n):
    S[i, i] = s[i]

recon_img = U@S@V

plt.imshow(recon_img)
plt.axis('off')
plt.show()

在此处输入图像描述

我想修复奇异值的最小数量以获得良好的结果:pretty与原始图像相似的图像。此外,我想看看当我采用更多的奇异值时,结果会发生多大的变化。我尝试了动画但没有成功:

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

img = Image.open('steve.jpg')
img = np.mean(img, 2)

U,s,V = np.linalg.svd(img)

fig = plt.figure()

def update(i):
    S = np.zeros(np.shape(img))
    n = 20
    for i in range(0, n):
        S[i, i] = s[i]

    recon_img = U@S@V

    plt.imshow(recon_img)
    plt.axis('off')

ani = FuncAnimation(fig = fig, func = update, frames = 20, interval = 10)
plt.show()

标签: pythonnumpymatplotlibanimationsvd

解决方案


如果您绘制s奇异值,您会看到一条非常陡峭的下降曲线,如果您对 y 轴使用对数刻度会更好:

plt.semilogy(s, 'k-')

在此处输入图像描述

如您所见,前 50 个奇异值是最重要的:几乎每个都超过 1000。从第 50 到第 250 的值要低一个数量级,并且它们的值缓慢下降:包含曲线的斜率(记住对数 y 刻度)。那个beeing说我会用前50个元素来重建你的形象。


关于动画:当动画逐帧更新时,计数器i增加1。在您的代码中,您错误地使用i切片s和定义S;你应该重命名计数器。
此外,随着动画的进行,您需要采用越来越多的奇异值,这是n您逐帧保持不变的设置。您需要n在每个循环中更新,因此您可以将其用作计数器。
此外,您需要擦除以前绘制的图像,因此您需要在函数plt.gca().cla()的开头添加一个。 检查下面的代码以供参考:update

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

img = Image.open('steve.jpg')
img = np.mean(img, 2)

U,s,V = np.linalg.svd(img)

fig, ax = plt.subplots(1, 2, figsize = (4, 4))

ax[0].imshow(img)
ax[0].axis('off')
ax[0].set_title('Original')

def init():
    ax[1].cla()
    ax[1].imshow(np.zeros(np.shape(img)))
    ax[1].axis('off')
    ax[1].set_title('Reconstructed\nn = 00')


def update(n):
    ax[1].cla()
    S = np.zeros(np.shape(img))
    for i in range(0, n):
        S[i, i] = s[i]

    recon_img = U@S@V

    ax[1].imshow(recon_img)
    ax[1].axis('off')
    ax[1].set_title(f'Reconstructed\nn = {n:02}')

ani = FuncAnimation(fig = fig, func = update, frames = 50, init_func = init, interval = 10)
ani.save('ani.gif', writer = 'imagemagick')

plt.show()

这给出了这个动画:

在此处输入图像描述

如您所见,前 50 个元素足以很好地重建您的图像。其余元素添加了一些噪音并稍微改变了背景。


推荐阅读