OpenCV进阶基于OpenCV的图像着色

Posted david123_xw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV进阶基于OpenCV的图像着色相关的知识,希望对你有一定的参考价值。


有时技术会增强艺术,有时它会破坏艺术。

为黑白电影着色是一个可以追溯到 1902 年的非常古老的想法。几十年来,许多电影创作者反对为黑白电影着色的想法,并认为这是对他们艺术的破坏。今天,它被认为是对艺术形式的一种改进。

如果算法不使用任何用户输入,那不是很酷吗?

1.定义着色问题

让我们首先根据 CIE Lab 色彩空间来定义着色问题。与 RGB 颜色空间一样,它是一个 3 通道颜色空间,但与 RGB 颜色空间不同的是,颜色信息仅在 a(绿-红分量)和 b(蓝-黄分量)通道中进行编码。 L(亮度)通道仅编码强度信息。

我们想要着色的灰度图像可以看作是图像在Lab颜色空间中的L通道,我们的目标是找到a和b分量。这样得到的Lab图像可以使用标准颜色空间变换转换为RGB颜色空间。例如,在OpenCV中,这可以使用带有COLOR_BGR2Lab选项的cvtColor来实现。

为了简化计算,Lab 颜色空间的 ab 空间被量化为 313 个 bin,如下图所示。而不是为每个像素找到 a 和 b 值,因为这种量化,我们只需要找到一个介于 0 和 312之间的 bin 编号。另一种思考问题的方式是我们已经有了取值从 0 到 255 的 L 通道,我们需要找到取值在 0 到 312 之间的 ab 通道。所以颜色预测任务现在是变成了多分类问题,其中每个灰色像素有 313 个类别可供选择。

2.用于着色的 CNN 架构

Zhang 等人提出的架构是具有多个卷积块的 VGG 式网络。每个块都有两个或三个卷积层,后跟一个修正线性单元 (ReLU),并终止于一个批量归一化层(BN)。与 VGG 网络不同,它没有池化或全连接层。

输入图像被重新缩放为 224×224。让我们用 X X X表示这个重新缩放的灰度输入图像。

当它通过上图所示的神经网络时,它会被神经网络转化为 Z ^ \\hat Z Z^。在数学上,网络的这种转换可以写成 Z ^ = G ( X ) \\hat Z = G(X) Z^=G(X)

Z ^ \\hat Z Z^的维度是 H ∗ W ∗ Q H*W*Q HWQ,其中 H ( = 56 ) H(=56) H(=56) W ( = 56 ) W(=56) W(=56)是最后一个卷积层输出的高度和宽度。对于每个 H ∗ W H*W HW像素, Z ^ \\hat Z Z^包含一个 Q ( = 313 ) Q(=313) Q(=313)值向量,其中每个值表示像素属于该类的概率。我们的目标是为每个概率分布 Z ^ h , w \\hat Z_{h,w} Z^h,w找到一对 ab 通道值。

3.从 Z ^ \\hat Z Z^恢复彩色图像

上图所示的 CNN 为我们提供了来自调整大小的输入图像 X X X的分布集合 Z ^ \\hat Z Z^。让我们看看如何从 Z ^ \\hat Z Z^ 中的每个分布中恢复单个 ab 值对。

您可能会认为我们可以简单地取分布的平均值并选择与最近的量化 bin 中心相对应的 ab 对。不幸的是,这种分布不是高斯分布,分布的均值仅对应于不自然的不饱和颜色。要理解这一点,请想一想天空的颜色——它有时是蓝色的,有时是橙黄色的。天空颜色的分布是双峰的。在为天空着色时,蓝色或黄色都会产生合理的颜色。但是蓝色和黄色的平均值是一种无趣的灰色。

那么为什么不使用分布模式来获得蓝色或黄色的天空呢?当然,作者尝试过,虽然它提供了鲜艳的色彩,但有时会破坏空间一致性。他们的解决方案是在均值和模式估计之间进行插值,以获得称为退火均值的量。使用称为温度 (T) 的参数来控制插值程度。 T=0.38 的最终值用作两个极端之间的权衡。


使 用 温 度 ( T ) 的 退 火 均 值 用 于 在 分 布 的 均 值 和 众 数 之 间 进 行 插 值 。 使用温度 (T) 的退火均值用于在分布的均值和众数之间进行插值。 使(T)退

对应于 Z ^ \\hat Z Z^分布的退火均值的 ab 对表示为 Y ^ h , w \\hat Y_{h,w} Y^h,w,可以写成原始分布 Z ^ h , w \\hat Z_{h,w} Z^h,w的变换 Y ^ = H ( Z ^ ) \\hat Y=H(\\hat Z) Y^=H(Z^)

请注意,当图像通过 CNN 时,其大小减小到 56×56。因此,预测的 ab 图像 Y ^ \\hat Y Y^也具有 56×56 的维度。为了获得彩色图像,将其上采样到原始图像大小,然后添加到亮度通道 L,以生成最终的彩色图像。

4.具有颜色重新平衡的多项式损失函数

所有神经网络都是通过定义损失函数来训练的。训练过程的目标是最小化训练集的损失。在着色问题中,训练数据由数千张彩色图像及其灰度版本组成。

CNN 的输出是 Z ^ \\hat Z Z^,输入图像是 X X X。我们需要将训练集中的所有彩色图像转换为其对应的值。在数学上,我们只是想反转映射 H H H Z = H − 1 ( Y ) Z=H^{-1}(Y) Z=H1(Y)

对于输出图像 Y Y Y的每个像素 Y h , w Y_{h,w} Yh,w,我们可以简单地找到最近的 ab bin 并将 Z h , w Z_{h,w} Zh,w表示为一个独热向量,其中我们将 1 分配给最近的 ab bin,将 0 分配给所有其他 312 个 bin。但为了获得更好的结果,我们考虑了5个最近邻,并使用高斯分布来计算分布 Z h , w Z_{h,w} Zh,w,这取决于与真实值的距离。

如果您之前使用过 CNN,您可能会想使用标准交叉熵损失来比较真实值 Z Z Z和预测值 Z ^ \\hat Z Z^
L ( Z ^ , Z ) = − 1 H W ∑ h , w ∑ q Z h , w , q l o g ( Z ^ h , w , q ) L(\\hat Z, Z) = -\\frac{1}{HW}\\sum_{h,w}\\sum_qZ_{h,w,q}log(\\hat Z_{h,w,q}) L(Z^,Z)=HW1h,wqZh,w,qlog(Z^h,w,q)
不幸的是,上述损失函数会产生非常暗淡的颜色。这是因为 ImageNet 中的颜色分布在灰线周围很重。

5.彩色化的结果

作者分享了两个版本的经过训练的 Caffe 模型 - 有和没有颜色重新平衡。我们尝试了两个版本,并在下图中分享了结果。中间一列显示没有重新平衡颜色的版本,最后一列显示重新平衡的版本。

正如我们所见,色彩重新平衡使许多图像非常生动活泼。它们中的大多数都是似是而非的颜色。另一方面,有时它也会为某些图像添加一些不需要的饱和色块。

请记住,当我们尝试将灰度图像转换为彩色图像时,可能有多种合理的解决方案。因此,评估良好着色的方法不是它与基本事实的匹配程度,而是它在人眼中看起来的可信度和愉悦度。

5.1 动物

该模型在动物图像上表现非常好,尤其是猫和狗。这是因为 ImageNet 包含非常大的这些动物集合。

5.2户外场景

该模型在表现蓝天和绿色植被的户外场景方面也做得非常好。另请注意,给定一棵树的轮廓,该模型预测橙色天空表明它已捕捉到日落的概念。



5.3草图

最后,即使是草图,模型也会产生合理的着色。


6.在 OpenCV 中实现着色

作者在此位置的 GitHub 中提供了预训练模型和网络详细信息。下面,我们将回顾 Python 和 C++ 代码,使用这些预训练模型对给定的灰度图像进行着色。我们的代码基于 OpenCV 示例代码。我们使用了 OpenCV 4.5.1 版。我们还提供代码来为给定的灰度视频着色。

链接:https://pan.baidu.com/s/14_my8daL2SxgFVymQy-0Dg 
提取码:1v8m

(1)Python
图像着色代码:

# colorizeImage.py
# Usage 
# python colorizeImage.py --input greyscaleImage.png

import numpy as np
import cv2 as cv
import argparse
import os.path

parser = argparse.ArgumentParser(description='Colorize GreyScale Image')
parser.add_argument('--input', help='Path to image.', default="greyscaleImage.png")
args = parser.parse_args()

if args.input==None:
    print('Please give the input greyscale image name.')
    print('Usage example: python3 colorizeImage.py --input greyscaleImage.png')
    exit()

if os.path.isfile(args.input)==0:
    print('Input file does not exist')
    exit()

# 读取输入图像
frame = cv.imread(args.input)

# 指定 2 个模型文件的路径
protoFile = "./models/colorization_deploy_v2.prototxt"
weightsFile = "./models/colorization_release_v2.caffemodel"
# weightsFile = "./models/colorization_release_v2_norebal.caffemodel"

# 加载聚类中心
pts_in_hull = np.load('./pts_in_hull.npy')

# 将网络读入内存
net = cv.dnn.readNetFromCaffe(protoFile, weightsFile)

# 将聚类中心填充为 1x1 卷积核
pts_in_hull = pts_in_hull.transpose().reshape(2, 313, 1, 1)
net.getLayer(net.getLayerId('class8_ab')).blobs = [pts_in_hull.astype(np.float32)]
net.getLayer(net.getLayerId('conv8_313_rh')).blobs = [np.full([1, 313], 2.606, np.float32)]

#来自 opencv 示例
W_in = 224
H_in = 224

img_rgb = (frame[:,:,[2, 1, 0]] * 1.0 / 255).astype(np.float32)
img_lab = cv.cvtColor(img_rgb, cv.COLOR_RGB2Lab)
img_l = img_lab[:,:,0] # 拉出L通道

# 将亮度通道调整为网络输入大小
img_l_rs = cv.resize(img_l, (W_in, H_in)) #
img_l_rs -= 50 # 减去50中心化

net.setInput(cv.dnn.blobFromImage(img_l_rs))
ab_dec = net.forward()[0,:,:,:].transpose((1,2,0)) # 这是我们的结果

(H_orig,W_orig) = img_rgb.shape[:2] # 原始图像大小
ab_dec_us = cv.resize(ab_dec, (W_orig, H_orig))
img_lab_out = np.concatenate((img_l[:,:,np.newaxis],ab_dec_us),axis=2) # 与原始图像 L 连接
img_bgr_out = np.clip(cv.cvtColor(img_lab_out, cv.COLOR_Lab2BGR), 0, 1)

outputFile = args.input[:-4]+'_colorized.png'
cv.imwrite(outputFile, (img_bgr_out*255).astype(np.uint8))
print('Colorized image saved as '+outputFile)
print('Done !!!')

视频着色代码

# colorizeVideo.py
# Usage
# python colorizeVideo.py --input greyscaleVideo.mp4

import numpy as np
import cv2 as cv
import argparse
import os.path

parser = argparse.ArgumentParser(description='Colorize GreyScale Video')
parser.add_argument('--input', help='Path to video file.', default="greyscaleVideo.mp4")
args = parser.parse_args()

if args.input == None:
    print('Please give the input greyscale video file.')
    print('Usage example: python colorizeVideo.py --input greyscaleVideo.mp4')
    exit()

if os.path.isfile(args.input) == 0:
    以上是关于OpenCV进阶基于OpenCV的图像着色的主要内容,如果未能解决你的问题,请参考以下文章

将灰度 OpenCV 图像传递给 OpenGL 纹理

openCV进阶之二:自动校准扫描图像生成鸟瞰图

opencv进阶1

OpenCV:是不是可以使用它执行 openGL 像素着色?

如何在openCV Python中选择统一着色图的特定部分?

OpenCV进阶图像操作