超解像再構成

意義

対象世界のシーンには豊富で多彩な情報が含まれていますが、ハードウェア機器の撮影条件や撮影方法の制限により、元のシーンのすべての情報を取得することは困難です。さらに、ハードウェア デバイスの解像度の制限により、画像から高周波の詳細情報が一部失われることは避けられません。情報化が急速に進む現代において、衛星リモートセンシング、医療画像、マルチメディアビデオなどの分野における画質への要求はますます高くなっており、その要求に応えるべく映像の高画質化、高解像度化が常に求められています。 . .

空間解像度のサイズは画質の重要な指標であり、画像を現実の生活に適用するための重要なパラメータの 1 つです。解像度が高いほど、画像に含まれる情報がより詳細になり、画像の鮮明さが向上し、実際のアプリケーションでさまざまなターゲットの識別と判断がより正確になります。

ただし、ハードウェアのパフォーマンスを向上させて画像の解像度を上げるにはコストがかかります。そこで、ハードウェアのコストを増加させずに画像の解像度の要求に応えるために、時代の要請に応じてソフトウェア手法による画像の超解像再構成が登場しました。

超解像度画像再構成とは、ノイズが多く、ぼやけ、サンプリングが不足している一連の低解像度画像シーケンスから高解像度画像を復元するプロセスを指します。既存のイメージング システムに共通する低解像度の欠陥を考慮して、特定のアルゴリズムを使用して、取得される低解像度画像の品質を向上させることができます。したがって、超解像再構成アルゴリズムの研究には幅広い開発余地があります。

具体的な方法の詳細

評価指標
ピーク信号対雑音比

ピーク信号対雑音比は、圧縮再構成画像の品質を測定するための信号の最大電力と信号対雑音電力の比であり、通常はデシベルで表されます。 PSNR インデックス値が高いほど、画質が良くなります。

SSIM の計算式は次のとおりです。

PSNR=10\ast lg\frac{MAX_I^2}{MSE}

MSE は、2 つの画像間の対応するピクセル間の差の二乗の平均を表します。

MAX^2_I画像内のピクセルの最大値を表します。 8 ビット画像の場合、通常は 255 です。

MSE=\frac{1}{M\ast N} \displaystyle \sum_{i=1}^{N} \sum_{j=1}^{M}(f_{ij}-f'_{ij })^2

f_{ij} ij における画像 X のピクセル値を表します

f'_{ij} ij における画像 Y のピクセル値を表します

構造類似性評価

構造類似性指数は、2 つの画像間の類似性を 0 から 1 の範囲の値で測定する指数です。 SSIM インデックスの値が大きいほど、画像の歪みが小さくなり、画質が良くなります。

SSIM の計算式は次のとおりです。

L(X,Y)=\frac{2\mu X\mu Y +C_1}{\mu ^2_X + \mu ^2_Y + C_1}

C(X,Y)=\frac{2\sigma X\sigma Y +C_2}{\sigma ^2_X + \sigma ^2_Y + C_2}

S(X,Y)=\frac{\sigma _{XY} + C_3}{\sigma _X \sigma _Y + C_3}

SSIM(X,Y)=L(X,Y) \ast C(X,Y) \ast S(X,Y)

 これら 2 つの方法は、一般に再構成効果をより正確に評価できます。しかし、結局のところ、人間の目の認識は複雑で豊かであるため、時には特定の偏差が発生することがあります。

EDSR

画像

SRResNet は、より深いネットワークを実現するために SR 作業に残余ブロックを導入しましたが、EDSR は SRResNet の改良版であり、その最も重要なモデル パフォーマンスの向上は、SRResNet の冗長モジュール (BN 層) の削除です。

画像-20211229150541634

EDSR では、バッチ正規化 (BN) 操作が削除されます。

論文によると、元の ResNet はもともと分類や検出などの高レベルのコンピュータ ビジョン問題を解決するために提案されたもので、ResNet の構造を超解像度などの低レベルのコンピュータ ビジョン問題に直接適用するのは明らかに最適ではありません。バッチ正規化層はその前の畳み込み層と同じサイズのメモリを消費するため、このステップを削除した後、EDSR はより多くのネットワーク層をスタックしたり、同じコンピューティング リソースを使用して層ごとにより多くの機能を抽出したりできるため、パフォーマンスが向上します。 EDSR は、L1 損失関数を使用してネットワーク モデルを最適化します。

1. データセットを解凍します。

トレーニング時間はそれほど長くない可能性があるため、ここでは BSD100 を使用しますが、自分で DIV2K や coco に変更することもできます。

#  !unzip -o /home/aistudio/data/data121380/DIV2K_train_HR.zip -d train
# !unzip -o  /home/aistudio/data/data121283/Set5.zip -d test
 2. データセットを定義する
import os
from paddle.io import Dataset
from paddle.vision import transforms
from PIL import Image
import random
import paddle
import PIL
import numbers
import numpy as np
from PIL import Image
from paddle.vision.transforms import BaseTransform
from paddle.vision.transforms import functional as F
import matplotlib.pyplot as plt


class SRDataset(Dataset):

    def __init__(self, data_path, crop_size, scaling_factor):
        """
        :参数 data_path: 图片文件夹路径
        :参数 crop_size: 高分辨率图像裁剪尺寸  (实际训练时不会用原图进行放大,而是截取原图的一个子块进行放大)
        :参数 scaling_factor: 放大比例
        """

        self.data_path=data_path
        self.crop_size = int(crop_size)
        self.scaling_factor = int(scaling_factor)
        self.images_path=[]

        # 如果是训练,则所有图像必须保持固定的分辨率以此保证能够整除放大比例
        # 如果是测试,则不需要对图像的长宽作限定

        # 读取图像路径
        for name in os.listdir(self.data_path):
            self.images_path.append(os.path.join(self.data_path,name))

        # 数据处理方式
        self.pre_trans=transforms.Compose([
                                # transforms.CenterCrop(self.crop_size),
                                transforms.RandomCrop(self.crop_size),
                                transforms.RandomHorizontalFlip(0.5),
                                transforms.RandomVerticalFlip(0.5),
                                # transforms.ColorJitter(brightness=0.3, contrast=0.3, hue=0.3),
                                ])

        self.input_transform = transforms.Compose([
                                transforms.Resize(self.crop_size//self.scaling_factor),
                                transforms.ToTensor(),
                                transforms.Normalize(mean=[0.5],std=[0.5]),
                                ])

        self.target_transform = transforms.Compose([
                                transforms.ToTensor(),
                                transforms.Normalize(mean=[0.5],std=[0.5]),
                                ])


    def __getitem__(self, i):
        # 读取图像
        img = Image.open(self.images_path[i], mode='r')
        img = img.convert('RGB')
        img=self.pre_trans(img)

        lr_img = self.input_transform(img)
        hr_img = self.target_transform(img.copy())
        
        return lr_img, hr_img


    def __len__(self):
        return len(self.images_path)

テストデータセット

# 单元测试

train_path='train/DIV2K_train_HR'
test_path='test'
ds=SRDataset(train_path,96,2)
l,h=ds[1]

# print(type(l))
print(l.shape)
print(h.shape)

l=np.array(l)
h=np.array(h)
print(type(l))
l=l.transpose(2,1,0)
h=h.transpose(2,1,0)
print(l.shape)
print(h.shape)

plt.subplot(1, 2, 1)
plt.imshow(((l+1)/2))
plt.title('l')
plt.subplot(1, 2, 2)
plt.imshow(((h+1)/2))
plt.title('h')
plt.show()

ネットワーク構造を定義する

rsresnet と比較すると、正規化層が少なく、残差ブロックが深くなります。

from paddle.nn import Layer
from paddle import nn
import math


n_feat = 256
kernel_size = 3

# 残差块 尺寸不变
class _Res_Block(nn.Layer):
    def __init__(self):
        super(_Res_Block, self).__init__()
        self.res_conv = nn.Conv2D(n_feat, n_feat, kernel_size, padding=1)
        self.relu = nn.ReLU()

    def forward(self, x):
        y = self.relu(self.res_conv(x))
        y = self.res_conv(y)
        y *= 0.1
        # 残差加入
        y = paddle.add(y, x)
        return y


class EDSR(nn.Layer):
    def __init__(self):
        super(EDSR, self).__init__()

        in_ch = 3
        num_blocks = 32

        self.conv1 = nn.Conv2D(in_ch, n_feat, kernel_size, padding=1)
        # 扩大
        self.conv_up = nn.Conv2D(n_feat, n_feat * 4, kernel_size, padding=1)
        self.conv_out = nn.Conv2D(n_feat, in_ch, kernel_size, padding=1)

        self.body = self.make_layer(_Res_Block, num_blocks)
        # 上采样
        self.upsample = nn.Sequential(self.conv_up, nn.PixelShuffle(2))

    # 32个残差块
    def make_layer(self, block, layers):
        res_block = []
        for _ in range(layers):
            res_block.append(block())
        return nn.Sequential(*res_block)

    def forward(self, x):

        out = self.conv1(x)
        out = self.body(out)
        out = self.upsample(out)
        out = self.conv_out(out)

        return out

パドルがGPUを使用できるかどうかを確認する

import paddle
print(paddle.device.get_device())


paddle.device.set_device('gpu:0')

トレーニングには通常、良好な効果が得られるまでに 4 時間かかります。セット 5 では、psnr は約 27 に達します。もちろん、この時間はまだ短すぎます。

import os
from math import log10
from paddle.io import DataLoader
import paddle.fluid as fluid
import warnings
from paddle.static import InputSpec

if __name__ == '__main__':
    warnings.filterwarnings("ignore", category=Warning)  # 过滤报警信息

    train_path='train/DIV2K_train_HR'
    test_path='test'

    crop_size = 96      # 高分辨率图像裁剪尺寸
    scaling_factor = 2  # 放大比例

    # 学习参数
    checkpoint = './work/edsr_paddle'   # 预训练模型路径,如果不存在则为None
    batch_size = 30    # 批大小
    start_epoch = 0     # 轮数起始位置
    epochs = 10000        # 迭代轮数
    workers = 4         # 工作线程数
    lr = 1e-4           # 学习率

    # 先前的psnr
    pre_psnr=32.35

    try:
        model = paddle.jit.load(checkpoint)
        print('加载先前模型成功')
    except:
        print('未加载原有模型训练')
        model = EDSR()

    # 初始化优化器
    scheduler = paddle.optimizer.lr.StepDecay(learning_rate=lr, step_size=1, gamma=0.99, verbose=True)
    optimizer = paddle.optimizer.Adam(learning_rate=scheduler,
                                    parameters=model.parameters())

    criterion = nn.MSELoss()

    train_dataset = SRDataset(train_path, crop_size, scaling_factor)
    test_dataset = SRDataset(test_path, crop_size, scaling_factor)

    train_loader = DataLoader(train_dataset,
        batch_size=batch_size,
        shuffle=True,
        num_workers=workers,
        )

    test_loader = DataLoader(test_dataset,
        batch_size=batch_size,
        shuffle=False,
        num_workers=workers,
        )

    for epoch in range(start_epoch, epochs+1):

        model.train()  # 训练模式:允许使用批样本归一化
        train_loss=0
        n_iter_train = len(train_loader)
        train_psnr=0
        # 按批处理
        for i, (lr_imgs, hr_imgs) in enumerate(train_loader):
            lr_imgs = lr_imgs
            hr_imgs = hr_imgs

            sr_imgs = model(lr_imgs)
            loss = criterion(sr_imgs, hr_imgs)  
            optimizer.clear_grad()
            loss.backward()
            optimizer.step()
            train_loss+=loss.item()
            psnr = 10 * log10(1 / loss.item())
            train_psnr+=psnr

        epoch_loss_train=train_loss / n_iter_train
        train_psnr=train_psnr/n_iter_train

        print(f"Epoch {epoch}. Training loss: {epoch_loss_train} Train psnr {train_psnr}DB")


        model.eval()  # 测试模式
        test_loss=0
        all_psnr = 0
        n_iter_test = len(test_loader)

        with paddle.no_grad():
            for i, (lr_imgs, hr_imgs) in enumerate(test_loader):
                lr_imgs = lr_imgs
                hr_imgs = hr_imgs

                sr_imgs = model(lr_imgs)
                loss = criterion(sr_imgs, hr_imgs)

                psnr = 10 * log10(1 / loss.item())
                all_psnr+=psnr
                test_loss+=loss.item()
        
        epoch_loss_test=test_loss/n_iter_test
        epoch_psnr=all_psnr / n_iter_test

        print(f"Epoch {epoch}. Testing loss: {epoch_loss_test} Test psnr{epoch_psnr} dB")

        if epoch_psnr>pre_psnr:
            paddle.jit.save(model, checkpoint,input_spec=[InputSpec(shape=[1,3,48,48], dtype='float32')])
            pre_psnr=epoch_psnr
            print('模型更新成功')

        scheduler.step()

テストするには、低解像度の画像を自分でアップロードする必要があります。

import paddle
from paddle.vision import transforms
import PIL.Image as Image
import numpy as np


imgO=Image.open('img_003_SRF_2_LR.png',mode="r")  #选择自己图片的路径
img=transforms.ToTensor()(imgO).unsqueeze(0)

#导入模型
net=paddle.jit.load("./work/edsr_paddle")

source = net(img)[0, :, :, :]
source = source.cpu().detach().numpy()  # 转为numpy
source = source.transpose((1, 2, 0))  # 切换形状
source = np.clip(source, 0, 1)  # 修正图片
img = Image.fromarray(np.uint8(source * 255))

plt.figure(figsize=(9,9))
plt.subplot(1, 2, 1)
plt.imshow(imgO)
plt.title('input')
plt.subplot(1, 2, 2)
plt.imshow(img)
plt.title('output')
plt.show()

img.save('./sr.png')

EDSR_X2効果

バイリニア補間増幅効果

 EDSR_X2増幅効果

 バイリニア補間増幅効果

EDSR_X2増幅効果

原文: EDSR 画像の超解像度再構成

おすすめ

転載: blog.csdn.net/qq_39312146/article/details/134608463