论文解读《Jointly Optimized Regressors for Image Super-resolution》

论文信息

论文标题:Jointly Optimized Regressors for Image Super-resolution
论文作者:Dengxin Dai, Radu Timofte, and Luc Van Gool
论文来源:Eurographics 2015
论文地址:PDF
论文代码:download

要解决的问题

  训练一组有代表性适用于不同低分辨率图像块(patch)的线性回归器(regressor),使得输入低分辨率图像后,模型为图像的各个块自适应选择最优回归器,并由回归计算得到对应的误差较小的高分辨率图像。

regressor   这里的regressor按照论文的意思,其实就是一个个确定好参数的回归模型,而不是回归模型中的回归子自变量,所以本文用回归器作为翻译。

解决方法

包括两个主要部分:
1)联合学习固定数量的回归器,这些回归器共同为从低分辨率(LR)到高分辨率(HR)的所有训练图像提供最小的超分辨误差;
2)自适应选择最适合输入图像块(patch)的回归器。

训练

Jointly-optimized regressors 联合优化回归器
第一步:数据收集

  对于给定的数据集,作为高分辨率图像数据,按一个固定下采样因子获取其下采样版本图像。然后将下采样图像分解成 图像块($x$),并找到与$x$对应的高分辨率图像块($y$),对应的$x$和$y$配对,构成训练集$D$: $$D={\left(x_1,y_1\right),\ldots,\left(x_L,y_L\right)}$$
上采样(UpSampling)与下采样(DownSampling)   上采样和下采样,其实就是通过像素的增加或减少来放大或缩小图像,在这个过程中对于新图像中每一个像素,都会先映射到原来图像的对应像素区域,通过原来的像素计算新的像素值。其中传统的插值方法包括最近邻、双线性和双三次插值法。

处理细节   在这一步中,论文作者认为低频信息能由双三次插值这样的快速插值很好地实现,需要更加关注高频信息的超分辨处理,所以使用了Prewitt算子([1 0 -1])和Laplacian算子([1 0 -2 0 1])分别从横纵方向,梯度滤波处理由训练图像下采样得到的图像。
下面图片演示这一步的效果,下采样因子为×2,按顺序分别为,原图、下采样图像、一阶梯度滤波、二阶梯度滤波:

第二步:训练
  目标:训练出一组固定数量\(O\)的能让训练LR图像块结果与HR图像块误差最小的回归器\(f\),每个块都会用最适合它的回归器进行重构。
  对于给定的图像对集合\(D\),将图像对划分到\(O\)个聚类中,并对每个聚类学习一个回归器。每个聚类\(o\)都与一个回归器\(f_o\)和指示向量\(c_o=\{0,1\}^l\),其中\(c_{o,l}=1(l\in o)\),需要最小化误差,即求出使得各个聚类中LR图像块回归结果与对应HR图像块向量之差中各分量平方和的开根号的结果最小的指示向量矩阵\(C\)和回归器矩阵\(\mathcal{F}\)

\[\min_{C,\mathcal{F}}\sum_{l=1}^L\sum_{o=1}^Oc_{o,l}\Vert f_o(l)-y_l\Vert^2 \]

  然后仿照EM算法,先用K-means算法获得初始的聚类指示向量矩阵\(C\),在E步利用\(C\)计算这种聚类划分下的最优回归器矩阵\(\mathcal{F}\),在M步利用\(\mathcal{F}\)求得新的指示向量矩阵\(C\),直到变化小于某个阈值,算法中并没有关于最大似然的计算。

K-means算法   K-means算法是一种划分聚类的算法,原理就是选定k个簇心向量,距离某个簇心最近的向量就分到该簇,从而划分聚类。基本实现为,随机选定k给簇心,然后遍历所有向量和这些簇心的距离,将向量分到距离最近的簇中,然后再计算每个簇的中心,尝试更新簇心,再划分簇,一直迭代到簇心不变。
  k=6的二维空间下的K-means算法的前三轮迭代如下,其中的红点为簇心,不同颜色为不同聚类:

\[P_o= Y_o (X_o^T X_O+λI)^{-1} X_o^T \]

  M步使用E步的投影矩阵,获得各个样本的投影,也就是超分辨率结果:

\[\tilde y_{o,l}= P_ox_l \]

  对每个样本l,放入误差最小的聚类中,此时误差为:

\[z_{o,l}= \Vert \tilde y_{o,l}-y_l\Vert^2 \]

  误差存为矩阵\(Z=[z_0,…,z_l ],z_l=[z_{0,l},…,z_{o,l}]^T\)
  论文中交替使用\(P_o\)\(f_o\)表示第o个回归器。

测试

Upsampling with JOR 使用联合优化回归器进行上采样
  训练完成后,HR图像块已经不再需要,各个训练LR图像块都有对应的误差向量\(z_l\)
  测试时,先将输入的LR图像用训练中使用的下采样因子分解为LR图像块,然后给每个单一的LR图像块预测其最优的回归器,按下面式子进行上采样,并将上采样图像块重组成期望图像。

\[\tilde y_{o,l}= P_ox \]

  最优回归子的自适应选择方法:
  计算输入图像块x在各个回归量下的超分辨误差\(z_o\),选择超分辨误差最小的回归器
  为了解决图像块内容与回归器注释之间的复杂关系,采用KNN算法获取输入图像块x在训练图像块中的邻域\(N(x)= {n_1,...,n_K },n_k\in\{1,…,L\}\),大小为\(K\),计算领域内累计平均误差,同时领域内各个图像块\(x_{n_k}\)的贡献为\(\frac 1k\),累计平均误差如下:

\[z_o=\frac {\sum_{k=1}^K\frac 1kz_{o,n_k}}{\sum_{k=1}^K\frac 1k} \]

KNN(K Nearest Neighbors)   KNN算法是一种分类算法,原理是对于一个测试样本,将它放到训练样本空间中,根据最近的k个样本点的类别来判断测试样本的类别。搜索算法上可以使用kd树、球树来组织训练样本,也可以用暴力搜索遍历。
  论文中使用VLFeat library中的k-d树组织训练样本来快速地完成KNN算法操作。

效果

  我们将从原文的图片的代码开始,使用原文采用的峰值信噪比(Peak signal-to-noise ratio,PSNR)作为标准,测试比较JOR算法和其他算法的优劣,然后尝试使用另一种超分辨率算法评价标准,结构相似性指数(Structural Similarity Index,SSIM),再次进行比较。

PSNR与SSIM   峰值信噪比(PSNR)用于表示信号的最大可能功率与影响其表示的保真度的破坏噪声的功率之间的比率。“信号”用原图的最大可能像素值定义,而这个“噪声”用原图与噪声图像计算得来的均方误差MSEB定义。PSNR越大,图像效果越接近原图。
  结构相似性指数(SSIM)可以衡量图片的失真程度,也可以衡量两张图片的相似程度。与均方误差MSE和PSNR衡量绝对误差不同,SSIM是感知模型,即更符合人眼的直观感受。SSIM越大,图像与原图越相似。

1、基本介绍:论文代码会将输入的图片先进行向下采样,即将图片的分辨率减小生成低分辨率图片,然后再将低分辨率图片分别使用不同的算法进行超分辨率运算,生成高分辨率图片。这样我们就可以将原始图片和生成的高分辨率图片进行比较,评价算法的好坏。我们这里以论文中的Set2图片集为例进行评价。

原始图片:

生成的低分辨率图片:

JOR算法生成的高分辨率图片:

各种算法结果比较:

注:第一个数值为PSNR,较高的 PSNR 值表示重建图像与原始图像之间的差异较小。第二个数值为处理图片时间。

  从上面的列表就可以看出,JOR算法对图像的复原效果在上面的算法中排列第二,比较不错,但缺点也很明显,图片的处理时间过长,和同等复原效果的A+算法相比,处理时间是其二十几倍。

A+算法   A+算法(Aplus算法)是一种用于图像超分辨率的深度学习算法,旨在将低分辨率图像提升到高分辨率图像。A+ 算法是基于卷积神经网络(Convolutional Neural Networks,CNN)的方法之一。
  A+ 算法使用了残差学习的思想,其中残差块(Residual Blocks)是算法的核心组件。这些残差块允许算法学习低频和高频信息之间的映射关系,从而提高图像的细节和清晰度。通过多个残差块的堆叠,A+ 算法能够逐渐恢复出高分辨率图像的细节。

2、尝试使用结构相似性指数(Structural Similarity Index,SSIM)再次进行比较。

  从上面的表格里的结果来看,JOR算法的SSIM结果对比大部分算法是很优秀的,但仍然弱于A+算法。很可惜目前还没哪个中文博客或者文章详细解读A+算法,想了解的同学可以参考google上的 CN106683067A - 一种基于残差子图像的深度学习超分辨率重建方法 - Google Patents

  下面是生成这个表格的python代码,结果将会存放在index.html中。(你需要将论文中的代码生成的结果图片文件夹改名为“result”,用来运行这个代码。如果出错,可以尝试修改路径名称)

from skimage.metrics import structural_similarity as ssim
from skimage import io
from jinja2 import Template, Environment, select_autoescape
from jinja2.ext import Extension

# 自定义扩展类
class CustomExtension(Extension):
    def __init__(self, environment):
        super().__init__(environment)

    def len(self, obj):
        return len(obj)

    @property
    def identifier(self):
        return 'custom_extension'

# 自定义环境类
class CustomEnvironment(Environment):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.add_extension(CustomExtension)

# 创建自定义环境并注册自定义函数
env = CustomEnvironment(
    autoescape=select_autoescape(['html', 'xml'])
)

# 注册自定义函数
env.globals['len'] = len

# 定义初始图像列表和待对比图像列表

from skimage.metrics import structural_similarity as ssim
from skimage import io
from jinja2 import Template, Environment, select_autoescape
from jinja2.ext import Extension

# 自定义扩展类
class CustomExtension(Extension):
    def __init__(self, environment):
        super().__init__(environment)

    def len(self, obj):
        return len(obj)

    @property
    def identifier(self):
        return 'custom_extension'

# 自定义环境类
class CustomEnvironment(Environment):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.add_extension(CustomExtension)

# 创建自定义环境并注册自定义函数
env = CustomEnvironment(
    autoescape=select_autoescape(['html', 'xml'])
)

# 注册自定义函数
env.globals['len'] = len

# 定义初始图像列表和待对比图像列表
initial_images = ['result/dog[1-Original].jpg', 'result/high-street_2585515b[1-Original].jpg', 'result/license[1-Original].jpg']
compare_images_list = [
    ['result/dog[2-Bicubic].jpg', 'result/dog[3-Yang et al.].jpg', 'result/dog[4-Zeyde et al.].jpg','result/dog[5-GR].jpg','result\dog[6-ANR].jpg','result\dog[7-NE+LS].jpg','result\dog[8-NE+NNLS].jpg','result\dog[9-NE+LLE].jpg','result\dog[10-A+].jpg','result\dog[11-SRCNN].jpg','result\dog[12-JOR].jpg' ],
    ['result/high-street_2585515b[2-Bicubic].jpg', 'result/high-street_2585515b[3-Yang et al.].jpg', 'result/high-street_2585515b[4-Zeyde et al.].jpg','result/high-street_2585515b[5-GR].jpg','result\high-street_2585515b[6-ANR].jpg','result\high-street_2585515b[7-NE+LS].jpg','result\high-street_2585515b[8-NE+NNLS].jpg','result\high-street_2585515b[9-NE+LLE].jpg','result\high-street_2585515b[10-A+].jpg','result\high-street_2585515b[11-SRCNN].jpg','result\high-street_2585515b[12-JOR].jpg'],
    ['result/license[2-Bicubic].jpg', 'result/license[3-Yang et al.].jpg','result/license[4-Zeyde et al.].jpg','result/license[5-GR].jpg','result\license[6-ANR].jpg','result\license[7-NE+LS].jpg','result\license[8-NE+NNLS].jpg','result\license[9-NE+LLE].jpg','result\license[10-A+].jpg','result\license[11-SRCNN].jpg','result\license[12-JOR].jpg']
]
# 定义自定义列名
column_names = ['Bicubic', 'Yang et al.', '4-Zeyde et al.', 'GR','ANR','NE+LS','NE+NNLS','NE+LLE','A+','SRCNN','JOR']
# 计算每个初始图像与对应待对比图像的SSIM分数
ssim_scores = []
for i in range(len(initial_images)):
    initial_image = io.imread(initial_images[i], as_gray=True)
    ssim_scores_group = []
    for compare_image in compare_images_list[i]:
        compare = io.imread(compare_image, as_gray=True)
        ssim_index = ssim(initial_image, compare)
        # 将 ssim_index 格式化为保留两位小数的字符串
        formatted_ssim_index = "{:.4f}".format(ssim_index)
        ssim_scores_group.append(formatted_ssim_index)
    ssim_scores.append(ssim_scores_group)

# 生成HTML文件
template_str = '''
<!DOCTYPE html>
<html>
<head>
    <title>SSIM Scores</title>
    <style>
        table, th, td {
            border: 1px solid black;
            border-collapse: collapse;
            padding: 5px;
        }
    </style>
</head>
<style>
    table {
        width: 800px; /* 设置表格的宽度为800像素 */
        border-collapse: collapse; /* 合并表格边框 */
    }
    th, td {
        padding: 8px; /* 设置单元格的内边距 */
        border: 1px solid black; /* 设置单元格边框 */
    }
    img {
        max-width: 100%; /* 设置图像的最大宽度为100% */
        height: auto; /* 根据比例自动调整图像的高度 */
    }
</style>
<body>
    <h1>SSIM Scores</h1>
    <table>
        <tr>
            <th>Initial Image</th>
            {% for column_name in column_names %}
                <th>{{ column_name }}</th>
            {% endfor %}
        </tr>
        {% for i in range(len(initial_images)) %}
        <tr>
            <td><img src="{{ initial_images[i] }}" width="200" height="200"></td>
            {% for score in ssim_scores[i] %}
                <td>{{ score }}</td>
            {% endfor %}
        </tr>
        {% endfor %}
    </table>
</body>
</html>
'''

# # 创建Jinja2环境并注册自定义函数
# env = Environment(
#     autoescape=select_autoescape(['html', 'xml'])
# )
# env.filters['len'] = get_length

# # 使用环境渲染模板
# template = env.from_string(template_str)
# html_output = template.render(initial_images=initial_images, compare_images_list=compare_images_list, ssim_scores=ssim_scores)
# 使用自定义环境渲染模板
template = env.from_string(template_str)
html_output = template.render(initial_images=initial_images,compare_images_list=compare_images_list, ssim_scores=ssim_scores,column_names=column_names)
# 将HTML内容写入文件
with open('index.html', 'w') as file:
    file.write(html_output)

特点总结

  1. 联合优化:该方法联合优化了一组局部线性回归变量,以最小化所有训练数据的整体超分辨误差。这意味着回归器一起学习,为所有低分辨率到高分辨率图像补丁提供最佳结果。

  2. 自适应回归选择:该方法为每个输入图像补丁自适应选择最合适的回归选择。这种选择是基于来自低分辨率图像和低分辨率补丁的邻域的信息。目标是选择能够产生具有最小误差的所需高分辨率补丁的回归器。

  3. 适应性的灵活性:该方法的框架是灵活的,可以很容易地用更复杂的替代品替换一些部件。例如,线性回归模型可以用非线性回归模型代替,k-d树可以用哈希函数代替。

  4. 训练时间优势:与SRCNN方法相比,该方法在训练时间方面具有明显的优势。在不使用任何GPU的情况下,在台式电脑上训练过程只需几十分钟,而SRCNN使用GPU则需要3天。

  5. 概念简单性:该方法在概念上比SRCNN更简单。它专注于联合优化回归器,并自适应地为每个补丁选择最佳回归器,而不是使用复杂的架构或深度学习技术。

  6. 视觉质量:与其他现有方法相比,该方法通常会产生更好的视觉结果。它生成更清晰的边缘和更少的工件,并适合纹理。

总体而言,该方法提供了一种简单有效的图像超分辨率方法,在训练速度和视觉质量方面具有良好的性能。

动手尝试——使用论文代码,得出自己的图片效果

注:需要使用MatLab运行,[MATLAB R2018b详细安装教程(附资源) - 知乎] (https://zhuanlan.zhihu.com/p/378349402)
1、先下载论文的代码
Jointly Optimized Regressors for Image Super-resolution (ethz.ch)
  这是论文补充内容的地址,有额外的算法结果对比,网页最下方是可下载的资源。
点击code和trained models 下载我们需要的代码和作者已经训练好的模型。

2、代码使用介绍
(1)
  将下载得到的两个模型(.mat文件 )放在JOR_Code的models文件夹里

(2)
  使用Matlab打开JOR_Code文件,打开run_JOR.m文件,这是主文件,会使用已有的model进行图片处理。将input_dir替换成存放自己图片的文件夹的名字。如果图片是”.bmp”文件,则需要将文件夹的名字加入到{‘Set5’,‘Set14’}里面,否则默认是“.jpg”文件。

(3)
  处理后得到的图片文件夹会出现在两个位置,一个是直接出现在JOR_Code文件夹里,一个是出现在这里你的那个图片文件夹里。这两个位置出现的图片文件夹不同之处在于JOR_Code文件夹的会有一个html文件,里面会有各个算法的性能比较,如上文中效果测试里的表格一样,你可以点击进去查看,而你的那个图片文件夹里的没有。
  每个位置又会出现两个不同的文件夹,一个是灰度图像的处理结果,一个是RGB图像的处理结果。

(4)
  假如你想训练自己的模型,可以运行“run_JOR_training.m”,更改input_dir为自己的训练图片文件夹的位置,将pattern改为自己的图片类型。(不建议在自己的电脑上运行训练程序,会导致电脑很卡,因为CPU和内存消耗很大)

  代码最后你可以改变自己的模型名称,取一个帅气的名字。

  训练结束后你就可以看到你的模型出现在models文件夹里啦。

posted @ 2023-12-25 22:36  lhb59  阅读(28)  评论(0)    收藏  举报