Image Super-Resolution via Sparse Representation——基於稀疏表示的超解析度重建

頎周發表於2021-03-04

  經典超解析度重建論文,基於稀疏表示。下面首先介紹稀疏表示,然後介紹論文的基本思想和演算法優化過程,最後使用python進行實驗。

稀疏表示

  稀疏表示是指,使用過完備字典中少量向量的線性組合來表示某個元素。過完備字典是一個列數大於行數的行滿秩矩陣,也就是說,它的列向量有無數種線性組合來表達列向量空間中的任意點。由於它的列數通常遠大於行數,可以使用佔比很小的列向量來表示特定的向量,我們稱這種表示為稀疏表示。

  那麼如何獲得這個字典呢?它在特定的任務下有特定的取值。和煉丹類似,我們先要用大量資料來訓練這個矩陣,讓它提取出能稀疏表示這些資料的特徵,進而擁有稀疏表示其它相似資料的能力。

  訓練過完備字典的過程稱為稀疏編碼。設訓練資料集為矩陣$X=(x_1,x_2,...,x_n)\in R^{m\times n}$,待訓練矩陣為$A\in R^{m\times K}$,矩陣對每一資料的表示權重為$\alpha = (\alpha_1,\alpha_2,...,\alpha_n)\in R^{K\times n}$。進行如下優化:

\begin{align} \min\limits_{A,\alpha}\|\alpha\|_0\;\;\;s.t. \;  \|A\alpha - X\|_2^2\le \epsilon \end{align}

  容易理解,約束重建向量與原始向量差異的同時,優化表示權重$\alpha$的稀疏性。通過優化獲得所需的過完備字典後,我們就可以用它來稀疏表示新的資料了。對於某個向量$y$,我們可以進行類似的優化來獲得它在字典中的稀疏表示:

\begin{align} \min\limits_{\alpha}\|\alpha\|_0\;\;\;s.t. \;  \|A\alpha - y\|_2^2\le \epsilon \end{align}

  因為零範數的離散性,以上優化是NP-難問題,無法用正常的優化演算法解決。所以通常會對上式進行調整,以方便優化。而線上性約束下,對一範數的優化有稀疏性(點選連結理解),因此可以轉換為對一範數的優化。然後根據拉格朗日乘子法(據說如此,但我覺得這個轉換並不等價),不等式約束可以移入優化中:

\begin{align} \min\limits_{\alpha} \lambda \|\alpha\|_1 + \|A\alpha - y\|_2^2 \end{align}

  同樣,$(1)$式也可以進行類似的轉換。

  以上就是稀疏表示的流程,看到這個提取特徵然後重建的過程,我們會聯想到主成分分析(PCA)。PCA能使我們方便地找到一組“完備”基向量,但是這裡我們要做的是找到一組“過完備”的基向量來表示輸入向量。過完備基的好處是它們能更有效地找出隱含在輸入資料內部的結構與模式。然而,與PCA不同,對於過完備基來說,係數$\alpha$不再由輸入向量$y$單獨確定。因此,在稀疏編碼演算法中,我們另加一個評判標準“稀疏性”來解決因過完備而導致的退化(degeneracy)問題。

  上面這段是百度百科原話。我覺得,把過完備字典與神經網路進行對比,可以把這個待訓練的很“寬”的矩陣看做引數量很大的網路。我們知道引數量大而訓練資料不充足的時候模型很容易過擬合,為了防止過擬合就要加上正則項,以使引數能專注於學習更有共性的特徵。我們可以把上面的稀疏性看做正則化來理解,使字典的列向量能表達一些更有“特點”的資訊。

論文原理及實現流程

基本思想

  在訓練階段,論文同時對LR訓練集$Y= (y_1,y_2,...,y_n)$和對應的HR訓練集$X = (x_1,x_2,...,x_n)$分別訓練兩個過完備字典$D_l,D_h$,使得LR資料$y_i$和它對應的HR資料$x_i$能以相同的稀疏編碼$\alpha_i$分別被$D_l$和$D_h$表示。也就是

$\left\{ \begin{aligned} &D_l\alpha_i \approx y_i \\ &D_h\alpha_i \approx x_i \end{aligned} \right.$

  在測試階段,我們已經有了訓練好的$D_l$和相對應的$D_h$。對於測試影像$y_t$,首先通過優化獲得$y_t$在$D_l$中的稀疏表示$\alpha_t$,此時有$D_l\alpha_t \approx y_t$。然後用這個表示通過$D_h$對映出對應的SR影像,即$\hat{x}_t=D_h\alpha_t$。

訓練過程

  訓練過程就是訓練上述的過完備字典對。因為效能的因素,我們不可能直接對整張圖進行稀疏編碼,論文是將影像分為方形的區塊(patch)進行編碼的。因此,用於訓練的成對資料不是整張的LR-HR影像對,而是所有影像分割開來的區塊對。現在把LR訓練集的所有區塊表示為$Y=(y_1,y_2,...,y_n)\in R^{M\times n}$,相應的HR訓練集區塊表示為$X = (x_1,x_2,...,x_n)\in R^{N\times n}$。如果放大倍數為$t$倍,則有$N=t^2M$。

  優化式很直觀:

\begin{align} \min\limits_{D_l,D_h,\alpha}\frac{1}{N}\|X - D_h\alpha\|_2^2+\frac{1}{M}\|Y - D_l\alpha\|_2^2 + \lambda \|\alpha\|_1 \end{align}

  其中$D_l\in R^{M\times K},D_h\in R^{N\times K}$,分別表示待訓練的LR和HR字典,$K$表示字典的原子數;$\alpha\in R^{K\times n}$為表示矩陣;$\lambda$為平衡稀疏性和重建一致性的係數。一二兩項懲罰使用相同表示的重建差異,第三項用來優化表示的稀疏性。把前兩項合併,可得:

\begin{aligned} \min\limits_{D_c,\alpha}\|X_c - D_c\alpha\|_2^2 + \lambda \|\alpha\|_1 \end{aligned}

  其中

$X_c = \left[ \begin{aligned} &\frac{1}{\sqrt{N}}X\\ &\frac{1}{\sqrt{M}}Y \end{aligned} \right], D_c = \left[ \begin{aligned} &\frac{1}{\sqrt{N}}D_h\\ &\frac{1}{\sqrt{M}}D_l \end{aligned} \right]$

  論文說同時優化$D_c$和$\alpha$非凸,但是固定其中一個變數,然後對另一個變數的優化則是凸的。因此可以將凸優化交替進行,最終可以達到一個區域性最優點。然而我還是選擇無腦梯度下降算了。當然我們可以使用SGD,每次隨機選擇部分割槽塊執行迭代,這樣做的好處在於可以引入隨機性從而增加跳出區域性最優的可能性。引人注意的是,執行SGD時,待優化的權重是$D_c$和$\alpha$,但由於每次只選擇部分割槽塊,$\alpha$也只能選擇對應的那部分進行更新,這有點像Dropout。但是,後面實驗的時候發現,SGD的效果並沒有GD好,因此沒有分成小批量來迭代。

推理過程

  在獲得$D_l$和$D_h$後,就可以用它們對LR影像進行重建了。論文采用掃描的方式,一個區塊一個區塊從上到下、從左到右對測試影像進行超解析度重建。另外,為了使相鄰區塊之間能相互匹配,防止顏色上的衝突,前後兩個區塊之間設有重疊部分,在重建一個區塊時,重疊部分要和上一個區塊一致。具體優化方式如下。

  首先將測試影像$y$按順序劃分為$m$個區塊$(p_1,p_2,...,p_m)$,設區塊在$D_l$中的表示為$(\alpha_1,\alpha_2,...,\alpha_m)$。按順序對所有區塊執行優化,對於第$i$個區塊,優化式如下:

\begin{align} \min\limits_{\alpha_i} \lambda\|\alpha_i\|_1 + \|FD_l\alpha_i - Fp_i\|_2^2+ \|PD_h\alpha_i - w\|_2^2 \end{align}

  其中$w$表示已重建區塊和當前區塊的重疊部分,$P$表示將當前區塊對映為重疊部分的矩陣。至於$F$,論文說是一個線性提取器,使影像中感知效果更好。論文實驗時用的是一階、二階導數濾波器,但沒說清楚具體如何操作,我實驗就沒有用。

  式子意義很明顯,第一項保證表示的稀疏性,第二項優化原始LR影像的重建一致性,第三項優化SR影像相鄰區塊重疊部分的一致性。獲得SR影像所有區塊的稀疏表示後,左乘$D_h$,然後將區塊拼接起來,就是最終的SR影像了。

  除了以上步驟以外,論文還多了一個所謂全域性重建約束。用什麼反向投影法,通過迭代讓SR影像退化後能和原始影像更相似。由於說的很不清楚,這裡就不加了,而且我覺得這不是這篇論文的主要內容。

  另外,論文在執行推理過程之前,先將原圖減去自身元素的均值,以使模型能更專注於紋理的重建,在重建完的SR影像上再加回這個均值。但是這個策略只在推理章節提了一句,在訓練$D_l,D_h$時是否使用標準化的影像並沒有說明。

實驗與分析

  實驗使用LSUN資料集中的bedroom作為訓練集,從中選取1024張長寬都大於256畫素的圖片,居中裁剪至256x256,獲得HR訓練集。然後對HR使用Bicubic縮小4倍至64x64,獲得LR訓練集。將LR區塊劃分為4x4大小,HR區塊劃分為16x16大小,則每張圖片都可被劃分為16x16個區塊。另外定義$D_l$和$D_h$的原子(列向量)數為2560(算力有限),又由於彩色圖片有三個通道,因此$D_l$和$D_h$的列向量長度分別為$4\times 4 \times 3$和$16\times 16 \times 3$。

訓練

  綜上,對於$(4)$式中的各個引數,有:

\begin{equation} \left\{ \begin{aligned} &X \in R^{(16\times 16\times 3)\times (1024\times 16^2)}\\ &Y \in R^{(4\times 4\times 3)\times (1024\times 16^2)}\\ &D_h \in R^{(16\times 16\times 3)\times 2560}\\ &D_l \in R^{(4\times 4\times 3)\times 2560}\\ &\alpha \in R^{2560\times (1024\times 16^2)}\\ \end{aligned} \right. \end{equation}

  另設$(4)$式$\lambda=0.1$,使用RMSProp對$(4)$式進行優化,根據以上所列引數,Pytorch程式碼如下:

#%%
import torch,os
import numpy as np
import matplotlib.pyplot as plt
from torch import optim,cuda

#讀取影像
LR_path = r'E:\DataSets\SRTest\LR'
HR_path = r'E:\DataSets\SRTest\HR'
LR_imgs = np.zeros([1024,64,64,3])
HR_imgs = np.zeros([1024,256,256,3])

for i, j in zip(os.listdir(LR_path),range(1024)):
  img_path = os.path.join(LR_path,i)
  LR_imgs[j] = plt.imread(img_path)/255
for i, j in zip(os.listdir(HR_path),range(1024)):
  img_path = os.path.join(HR_path,i)
  HR_imgs[j] = plt.imread(img_path)/255
 
#定義各個變數
def imgs2patches(imgs, patch_size):
  #將影像集轉換為區塊集
  imgs_n = len(imgs)
  patch_n = int(imgs.shape[1]/patch_size)
  patches = np.zeros([imgs_n*patch_n**2, patch_size*patch_size*3]) 
  for i in range(patch_n): 
    for j in range(patch_n):
      t = imgs[:,i*patch_size:(i+1)*patch_size,j*patch_size:(j+1)*patch_size,:]
      t = np.reshape(t,[imgs_n,-1]) 
      now = i * patch_n + j
      patches[imgs_n*now:imgs_n*(now+1),:] = t
  return patches.T

atom_n = 2560

X = torch.tensor(imgs2patches(HR_imgs, 16),device='cuda')*255 #訓練集影像元素色值取值在[0,255]
Y = torch.tensor(imgs2patches(LR_imgs, 4), device='cuda')*255
Dh = torch.normal(0,1,[16*16*3,atom_n],device='cuda') 
Dl = torch.normal(0,1,[4*4*3,atom_n],device='cuda') 
 
alpha = torch.normal(0,1,[atom_n,1024*16*16],device='cuda') 
Dh.requires_grad_(True)
Dl.requires_grad_(True)
alpha.requires_grad_(True)
opt = optim.RMSprop([Dh,Dl,alpha]) 
#%%
#訓練模型
from torch.utils.tensorboard import SummaryWriter 

writer = SummaryWriter('logs/')
def iter_one_epoch(lamb=0.01):
  patch_n = alpha.shape[1]
  term1 = torch.sum((X - torch.matmul(Dh,alpha))**2)/256/patch_n
  term2 = torch.sum((Y - torch.matmul(Dl,alpha))**2)/16/patch_n
  term3 = lamb * torch.sum(torch.abs(alpha))/patch_n
  loss = term1 + term2 + term3
  opt.zero_grad()
  loss.backward()
  opt.step()
  return term1,term2,term3,loss
 
for i in range(1, 1500): 
  term1,term2,term3,loss = iter_one_epoch(lamb=0.1)
  print(i,loss.cpu().detach().numpy())  
  writer.add_scalar('term1', term1, int(i))
  writer.add_scalar('term2', term2, int(i))
  writer.add_scalar('term3', term3, int(i))
  writer.add_scalar('loss', loss, int(i))
  if i % 700 == 0: 
    for i in opt.param_groups: 
      i['lr'] = i['lr']*0.5 
print("儲存字典")
torch.save(Dl,'dictionaries/Dic_LR')#儲存兩個字典 
torch.save(Dh,'dictionaries/Dic_HR') 
#%%
#用Dh重建HR影像驗證訓練結果
def get_recon_LR_HR(n): 
  print(Dh.shape,Dl.shape)
 
  recon_LR = torch.matmul(Dl, alpha)
  recon_HR = torch.matmul(Dh, alpha) 
  LR = torch.zeros([1024,64,64,3],device='cuda')
  HR = torch.zeros([1024,256,256,3],device='cuda') 
  
  for i in range(n): 
    print(i)
    for j in range(16):
      for k in range(16): 
        LR[i,4*j:4*(j+1),4*k:4*(k+1),:] = recon_LR[:,i+(j*16+k)*1024].reshape([4,4,3])
        HR[i,16*j:16*(j+1),16*k:16*(k+1),:] = recon_HR[:,i+(j*16+k)*1024].reshape([16,16,3])
  return LR,HR 
lr,hr = get_recon_LR_HR(100) 
n = 10
fig = plt.figure(figsize=(15,15))
ax1,ax2,ax3,ax4 = fig.add_subplot(221),fig.add_subplot(222),fig.add_subplot(223),fig.add_subplot(224)
ax1.imshow(LR_imgs[n])
ax2.imshow(lr[n].cpu().detach()/255)
ax3.imshow(HR_imgs[n])
ax4.imshow(hr[n].cpu().detach()/255)
ax1.set_title('LR image',fontsize=20)
ax2.set_title('Reconstructed LR image',fontsize=20)
ax3.set_title('HR image',fontsize=20)
ax4.set_title('Reconstructed HR image',fontsize=20)
plt.show()   

  我另外對比過Adam和原始GD,迭代速度都沒有RMSProp快,而且loss在穩定後是最小的。演算法總共迭代了1500次,使用的是RMSProp預設的學習率,但每700次迭代都會下調為原來的一半。整個迭代在3090下用時10分鐘,loss變化如下:

  以下是訓練集的LR和它對應的HR影像的重建效果,幾乎看不出差異:

推理

順序優化區塊

  論文的推理策略是按順序重建SR影像的各個區塊,同時約束相鄰區塊的重疊部分的相似性。定義LR影像相鄰區塊之間的重疊為1畫素寬,則相應SR影像相鄰區塊之間有4畫素寬的重疊,並且影像能分成21x21個區塊。則$(5)$式各個變數的規模如下

\begin{equation} \left\{ \begin{aligned} &\alpha_{ij}\in R^{2560\times 1},\;\;i,j=1,2,...,21\\ &p_{ij}\in R^{(4\times 4\times 3) \times 1},\;\;i,j=1,2,...,21\\ &D_l\in R^{(4\times 4\times 3) \times2560}\\ &D_h\in R^{(16\times 16\times 3) \times 2560}\\ \end{aligned} \right. \end{equation}

  優化程式碼如下:

#%%
import torch
import matplotlib.pyplot as plt
from torch import optim
from torch import random 
from torch.nn import functional as F


path_lr = r'E:\DataSets\SRTest\TestImages\LR\0003.jpg'
path_hr = r'E:\DataSets\SRTest\TestImages\HR\0003.jpg'
img_lr = plt.imread(path_lr) 
img_hr = plt.imread(path_hr) 

Dl = torch.load('dictionaries/Dic_LR').reshape([4,4,3,2560])
Dh = torch.load('dictionaries/Dic_HR').reshape([16,16,3,2560])   
def img_SR(LR_img, lambda1=0.5,lambda2=1,lambda3=1,lambda4=1,epoch=100):
  '''
  LR_img取值須在[0,255],形狀為[64,64,3]
  ''' 
  LR_img = torch.tensor(LR_img,device='cuda',requires_grad=False) 
  SR_img = torch.zeros([256,256,3],device='cuda',requires_grad=False) 
  
  alpha_array = [] 
  for i in range(21):
    al = []
    for j in range(21): 
      al.append(torch.normal(0,1,[2560],device='cuda',requires_grad=True)) 
    alpha_array.append(al)
  
  def SRcompat_loss(patch, i, j):
    loss = 0
    if i > 0:
      loss += torch.mean(torch.abs(SR_img[12*i:12*i+4,12*j:12*j+16] - patch[:4]))
    if j > 0:
      loss += torch.mean(torch.abs(SR_img[12*i:12*i+16,12*j:12*j+4] - patch[:,:4]))
    return loss

  #按順序計算SR各個區塊  
  for i in range(21):
    for j in range(21): 
      alpha = alpha_array[i][j]
      opt = optim.RMSprop([alpha])
      for k in range(1, epoch): 
        L1 = torch.mean(torch.abs(alpha))
        L2 = torch.mean(torch.abs(torch.matmul(Dl,alpha) - LR_img[i*3:i*3+4,j*3:j*3+4]))
        L3 = SRcompat_loss(torch.matmul(Dh,alpha), i, j)
        down_SR = F.interpolate(torch.matmul(Dh,alpha).reshape([1,16,16,3]).permute([0,3,1,2]),size=[4,4], mode='bicubic')
        L4 = torch.mean(torch.abs(down_SR[0].permute([1,2,0]) - LR_img[i*3:i*3+4,j*3:j*3+4]))#額外的下采樣一致性
        Loss = lambda1 * L1 + lambda2 * L2 + lambda3 * L3 + lambda4 * L4
        if k%800 ==0:
          print(k,Loss)
          for l in opt.param_groups:  
            l['lr'] *= 0.5
        opt.zero_grad()
        Loss.backward()
        opt.step()
        if Loss < 5:
          print(k,Loss)
          break 
      SR_img[12*i:12*i+16,12*j:12*j+16] = torch.matmul(Dh,alpha).detach()
    plt.imshow(SR_img.detach().cpu()/255)
    plt.show()
  return SR_img

img_sr = img_SR(img_lr,0.01,1,1,1,5000).cpu()/255   
fig = plt.figure(figsize=(15,15))
ax1,ax2,ax3 = fig.add_subplot(131),fig.add_subplot(132),fig.add_subplot(133)
ax1.imshow(img_lr),ax1.set_title('LR',fontsize=20)
ax2.imshow(img_sr),ax2.set_title('SR',fontsize=20)
ax3.imshow(img_hr),ax3.set_title('HR',fontsize=20)
plt.show()

  迭代了好久,結果如下:

  噪聲很多,原因應該就是沒有用論文中提到的使用反向投影法進行精煉,以及使用$F$操作吧。

另一種推理方式

  由於上述優化有先後順序,後面的區塊可能會損失精讀來“遷就”前面已獲得的SR區塊,讓重疊部分一致。因此論文在完成這一步後又加了一個全域性一致性的約束,來調整已獲得的SR影像,就是用所謂的反向投影法,但是寫得很不明白。因此,我試驗了一種直接對所有區塊同時進行優化的方法。

  首先定義LR影像相鄰區塊的重疊為2畫素寬,也就是4x4區塊的一半。則相應的SR影像的相鄰區塊重疊為8畫素寬,同樣佔其區塊的一半。如此一來,SR相鄰區塊的相容性可以通過建立兩個“補丁”圖來約束,如下圖所示:

  即同時優化三個SR影像,第一張是最終的SR結果$x$,第二張用於約束$x$橫向相鄰區塊之間的匹配度,第三張用於約束$x$縱向相鄰區塊之間的匹配度。也就是說,第一張影像相鄰區塊各取一半拼接成的圖塊要與第二、三影像中對應的區塊一致。

  綜上,對於測試LR影像$y$,分別去掉左右、上下邊緣的2畫素寬、高的圖塊,獲得用於匹配約束的$y_r,y_c$。然後分別定義相應的表示$\alpha,\alpha_r,\alpha_c$。根據以上定義,各個引數的規模如下(LR影像區塊被展開為向量的形式):

\begin{equation} \left\{ \begin{aligned} &\alpha\in R^{2560\times (16\times 16)}\\ &\alpha_r\in R^{2560\times (16\times 15)}\\ &\alpha_c\in R^{2560\times (15\times 16)}\\ &y\in R^{(4\times 4\times 3) \times(16\times 16)}\\ &y_r\in R^{(4\times 4\times 3) \times(16\times 15)}\\ &y_c\in R^{(4\times 4\times 3) \times(15\times 16)}\\ &D_l\in R^{(4\times 4\times 3) \times2560}\\ &D_h\in R^{(16\times 16\times 3) \times 2560}\\ \end{aligned} \right. \end{equation}

  則稀疏性約束、重建一致性約束、區塊匹配度約束和最終的優化式如下:

\begin{equation} \begin{aligned} &L_{spars} = \|\alpha\|_1+\|\alpha_r\|_1+\|\alpha_c\|_1  \\ &L_{recon} = \|D_l\alpha - y\|_1+\|D_l\alpha_r - y_r\|_1+\|D_l\alpha_c - y_c\|_1 \\ &L_{comp} = \|P_1D_h\alpha - D_h\alpha_r\|_1+\|P_2D_h\alpha - D_h\alpha_c\|_1\\ &\min\limits_{\alpha,\alpha_r,\alpha_c}Loss =  \lambda_1 L_{spars}+\lambda_2L_{recon}+\lambda_3L_{comp} \end{aligned} \end{equation}

  $P_1,P_2$表示將SR影像對映到相應的匹配約束影像的操作,$\lambda_1,\lambda_2,\lambda_3$用於平衡三個約束的佔比。使用$L1$範數是因為它能生成噪聲更少的影像。另外,為了防止梯度過大,程式碼中計算的各項範數會除以元素數量,公式中沒有標明。程式碼如下:

#%%
import torch
import matplotlib.pyplot as plt
from torch import optim
from torch.utils.tensorboard import SummaryWriter 


path_lr = r'E:\DataSets\SRTest\TestImages\LR\0003.jpg'
path_hr = r'E:\DataSets\SRTest\TestImages\HR\0003.jpg'
img_lr = plt.imread(path_lr) 
img_hr = plt.imread(path_hr) 

Dl = torch.load('dictionaries/Dic_LR')
Dh = torch.load('dictionaries/Dic_HR')
def img_SR(LR_img, lambda1=0.5,lambda2=1,lambda3=1,epoch=100):
  '''
  LR_img取值須在[0,255],形狀為[64,64,3]
  ''' 
  LR_img = torch.tensor(LR_img,device='cuda',dtype=torch.float32)  
  LR_img_r = LR_img[:,2:-2]
  LR_img_c = LR_img[2:-2,:] 
  def img2patches(img):  
    patch_r = int(img.shape[0]/4)
    patch_c = int(img.shape[1]/4)
    patches = torch.zeros([4*4*3, patch_r*patch_c],device='cuda') 
    for i in range(patch_r): 
      for j in range(patch_c): 
        patches[:,i * patch_c + j] = torch.flatten(img[i*4:(i+1)*4,j*4:(j+1)*4])  
    return patches
  def patches2img(patches,row,col,ps=16):
    img = torch.zeros([row*ps, col*ps, 3],device='cuda')
    for i in range(row):
      for j in range(col):
        img[i*ps:(i+1)*ps, j*ps:(j+1)*ps] = patches[:,i*col+j].reshape([ps,ps,3])
    return img
   
  alpha = torch.normal(0,1,[2560,16*16],requires_grad=True,device='cuda') 
  alpha_r = torch.normal(0,1,[2560,16*15],requires_grad=True,device='cuda') 
  alpha_c = torch.normal(0,1,[2560,15*16],requires_grad=True,device='cuda') 
  y = img2patches(LR_img)
  y_r = img2patches(LR_img_r)
  y_c = img2patches(LR_img_c) 
 
  opt = optim.RMSprop([alpha,alpha_r,alpha_c])

  writer = SummaryWriter('InferLogs2/')
  for i in range(1, epoch):
    l_alpha = torch.mean(torch.abs(alpha))
    l_alpha_r = torch.mean(torch.abs(alpha_r))
    l_alpha_c = torch.mean(torch.abs(alpha_c))
    L1 = l_alpha + l_alpha_r + l_alpha_c

    l_rec1 = torch.mean(torch.abs(torch.matmul(Dl,alpha)-y))
    l_rec2 = torch.mean(torch.abs(torch.matmul(Dl,alpha_r)-y_r))
    l_rec3 = torch.mean(torch.abs(torch.matmul(Dl,alpha_c)-y_c))
    L2 = l_rec1 + l_rec2 + l_rec3

    l_comp1 = torch.mean(torch.abs(patches2img(torch.matmul(Dh,alpha),16,16,16)[:,8:-8] - patches2img(torch.matmul(Dh,alpha_r),16,15,16)))
    l_comp2 = torch.mean(torch.abs(patches2img(torch.matmul(Dh,alpha),16,16,16)[8:-8,:] - patches2img(torch.matmul(Dh,alpha_c),15,16,16)))
    L3 = l_comp1 + l_comp2

    Loss = lambda1 * L1 + lambda2 * L2 + lambda3 * L3

    opt.zero_grad()
    Loss.backward()
    opt.step()

    writer.add_scalar('L1',L1,i)
    writer.add_scalar('L2',L2,i)
    writer.add_scalar('L3',L3,i)
    writer.add_scalar('Loss',Loss,i)
    if i % 50 == 0:
      print(i, Loss)
      plt.imshow(patches2img(torch.matmul(Dh,alpha),16,16,16).detach().cpu()/255)
      plt.show()
      plt.imshow(patches2img(torch.matmul(Dl,alpha),16,16,4).detach().cpu()/255)
      plt.show()
    if i % 300 == 0:
      for i in opt.param_groups:
        i['lr'] *= 0.5 

  return patches2img(torch.matmul(Dl,alpha),16,16,4),patches2img(torch.matmul(Dh,alpha),16,16,16)
recon_LR_img,SR_img = img_SR(img_lr,100,1,1,epoch=1500) 
fig = plt.figure(figsize=(15,15))
ax1,ax2,ax3,ax4 = fig.add_subplot(221),fig.add_subplot(222),fig.add_subplot(223),fig.add_subplot(224)
ax1.imshow(recon_LR_img.detach().cpu()/255),ax1.set_title('Reconstructed LR',fontsize=20)
ax2.imshow(SR_img.detach().cpu()/255),ax2.set_title('SR',fontsize=20)
ax3.imshow(img_lr),ax3.set_title('LR',fontsize=20)
ax4.imshow(img_hr),ax4.set_title('HR',fontsize=20)
plt.show()

  不過實際效果也並不是很好:

總結

  綜上,單純使用稀疏表示做SR效果並不如人意。因為論文中還加了其它的方式和技巧來減少重建影像的噪聲,而我這個實驗沒有加入,並且,論文的實驗是放大3倍,區塊大小為3x3,我這裡與其並不相同,所以沒能重現出論文的效果。另外,還可能是優化演算法的緣故,論文使用的是凸優化(可能有某種方式算出解析解,但我看到L1範數就放棄了),我則是梯度下降。

  主要還是不想再做了。。。論文作者沒有給出原始碼,自己敲程式碼加寫部落格用了4天時間,想看其它論文了。

論文地址

  Image Super-Resolution via Sparse Representation

相關文章