[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

书史足自悦,安用勤与劬。这篇文章主要讲述[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se相关的知识,希望能为你提供帮助。
技术有时会提高艺术,但有时也会破坏艺术。着色黑白电影是一个可以追溯到1902年的古老想法。几十年来,许多电影创作者反对将黑白电影着色的想法,并将其视为对艺术的破坏。但今天它被接受为艺术形式的增强。该技术本身已经从艰苦的手工着色转变为如今的自动化技术。如下图所示。

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

基于传统的计算机视觉方法以及发表了许多关于图像着色的论文。我最喜欢的一篇文章是名为Colorization using Optimization的论文。它使用了一些彩色涂鸦来指导解决着色的优化问题。论文见
如果算法不需要用户输入任何参数,这不是很酷的事情吗?在过去几年中,随着深度学习的发展,着色自动化取得了巨大的飞跃。在这篇文章中,我们将了解一个这样的着色深度学习模型。我们还共享OpenCV代码,以便在python或C ++应用程序中使用经过训练的模型。
1 彩色图像着色在ECCV 2016中,一篇名为Colorful Image Colorization的论文,提出了一个用于着色灰度图像的卷积神经网络。论文使用ImageNet训练集的130万像素训练网络,并公开了一个训练好的基于Caffe的模型。在这篇文章中,我们将首先定义着色问题,解释论文的架构细节,最后分享代码和一些有趣的结果。
【[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se】论文原文:
1.1 定义着色问题我们首先根据CIE Lab颜色空间定义颜色问题。与RGB颜色空间一样,它是3通道颜色空间,但与RGB颜色空间不同,颜色信息仅在a(绿红分量)和b(蓝黄分量)通道中编码。L(亮度)通道仅对亮度信息进行编码。
我们想要着色的灰度图像可以被认为是Lab颜色空间中图像的L通道,我们的目标是找到a和b分量。可以使用标准颜色空间变换将该Lab图像变换为RGB颜色图像。例如,在OpenCV中,这可以使用COLOR_BGR2Lab选项的cvtColor来实现。
为了简化计算,Lab颜色空间的ab空间进行312级量化,如图2所示。OpenCV会映射到0到312,由于这种量化我们只需找到0到312的数,而不是找到每个像素的a和b值。另一种思考问题的方法是我们已经有一个L通道,取值从0到255,我们需要找到一个取0到312之间值的ab通道。所以颜色预测任务现在是变成了多项分类问题,每个灰色像素有313个类可供选择。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

1.2 CNN彩色化结构Colorful Image Colorization这篇论文使用的CNN结构如下所示。类似与VGG网络,但是该CNN没有池化层或全连接层。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

输入图像缩放为224×224,缩放后的灰度输入图像表示为X。当它通过上面显示的神经网络时,输出为:
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

的尺寸为H×W×Q,其中H=56和W=56是最后一个卷积层输出的高度和宽度。Q=313表示类别个数。对于每个H×W都有一个对应的值表示属于该类的概率。我们的目标是为每个概率分布
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

找到其对应的ab通道值。
1.3 从中恢复彩色图像
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

让我们看看如何从输出 恢复其对应的ab值。您可能会想到我们可以简单地采用
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

各个量化级别的均值,并获取与其对应的ab值。不幸的是,由于
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

的各个量化级别分布不是高斯分布,分布的均值简单地对应于去饱和色。要理解这一点,想想天空的颜色有时是蓝色,有时是橙黄色。天空的颜色分布是双峰的。在着色天空时,无论是蓝色还是黄色都会产生合理的色彩。但蓝色和黄色的平均值是无趣的灰色。那么为什么不使用
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

的值分布特点让你得到蓝色或黄色的天空?当然,作者尝试了这一点,虽然它给出了鲜艳的色彩,但它有时会破坏空间的一致性。他们的解决方案是在
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

的均值和期输出值之间进行插值,以获得称为annealed-mean的量。称为温度(T)的参数用于控制插值程度。最终值T=0.38用作最优插值。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

ab值所对应的annealed-mean值表示为
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片


[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片


[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

关系为:
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

请注意,当图像通过CNN时,其大小减小到56×56。因此,预测的ab图像也具有56×56的尺寸。为了获得彩色图像,将其上采样到原始图像尺寸,然后添加到亮度通道L,以产生最终彩色图像。
1.4 具有颜色再平衡的多项式损失函数通过定义损失函数来训练所有神经网络。培训过程的目标是最大限度地减少训练集的损失。在着色问题中,训练数据由数千个彩色图像及其灰度图组成。
CNN输入图像x对应的输出为
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

。我们需要将训练集中的所有彩色图像转换为并获得相应的
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

值。在数学上,我们只想反转映射H。即
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

对于
Y
我们可以简单地找到与其对应的ab值并表示为Z,由于ab是量化后的值,因此Y对应的ab值为1,其他312个值为0。但是为了获得更好的结果,使用高斯分布来计算
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

。所以通过原始图像ab值Z和
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

计算loss。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

不幸的是,上述损失函数产生非常暗淡的颜色。这是因为ImageNet中的颜色分布与灰度图值分布接近。
为了推动算法产生鲜艳的色彩,作者将损失函数改为
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

其中颜色再平衡参数
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

表示根据颜色等级的稀有度重新平衡损失。这有助于在输出中获得更加生动和饱和的色彩。
说实话由于原文实在解释太坑的。其实模型思路很简单,输入灰度图,输出ab值的概率图Z,然后将ab映射到ab估计值图Y,使得结果更加合理。由于有了灰度图也就是输入,输出就是我们由彩色图原图转换而来的ab值图。计算loss,调整模型就行了。
详细解释可以看:
1.5 着色结果作者分享了两种版本的训练好的Caffe模型,有无色彩再平衡。我们尝试了两个版本,并在下图中分享了结果。中间列显示没有颜色重新平衡的版本,最后一列显示具有重新平衡的版本。正如我们所看到的,色彩重新平衡使许多图像非常生动和充满活力。大多数都是合理的颜色。另一方面,有时它也可能会为某些图像添加一些不需要的饱和色块。
请记住,当我们尝试将灰度图像转换为彩色图像时,可能会有多种合理的解决方案。因此,评估良好着色的方法并不是它与真实图像的匹配程度,而是它的颜色让人看起来多么合理和令人愉悦。
1)动物
该模型非常适合动物的图像,尤其是猫和狗。这是因为ImageNet包含了大量这些动物。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

2)户外场景
该模型也很好地展示了蓝天和绿色植被的户外场景。还要注意,模型能够重新着色橙色天空,表明它已经获得了日落的特征。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

3)手绘
最后,即使使用手绘草图,该模型也会产生合理的着色。
[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se

文章图片

2 OpenCV中实现着色作者在其GitHub中提供了预先训练的模型和网络详细信息。
地址:
2.1 模型下载下面,我们将介绍在OpenCV(3.4.1以上)基于Python和C++,使用这些预先训练的模型为给定的灰度图像和灰度视频着色。
模型下载地址:
其中colorization_release_v2.caffemodel颜色再平衡 后训练的模型,colorization_release_v2_norebal.caffemodel是没有加颜色再平衡的模型。
另外python版本需要加载pts_in_hull.py文件,该文件包含ab空间的量化信息。在C++版本中,量化信息是从pts_in_hull.py复制的。
C++代码:
string imageFileName = "./image/greyscaleImage.png"; Mat img = imread(imageFileName); if (img.empty())cout & lt; & lt; "Cant read image from file: " & lt; & lt; imageFileName & lt; & lt; endl; return 2; string protoFile = "./model/colorization_deploy_v2.prototxt"; string weightsFile = "./model/colorization_release_v2.caffemodel"; //string weightsFile = "./model/colorization_release_v2_norebal.caffemodel"; double t = (double)cv::getTickCount(); // fixed input size for the pretrained network 加载模型 const int W_in = 224; const int H_in = 224; Net net = dnn::readNetFromCaffe(protoFile, weightsFile);

python:
# Specify the paths for the model files protoFile = "./models/colorization_deploy_v2.prototxt" weightsFile = "./models/colorization_release_v2.caffemodel" #weightsFile = "./models/colorization_release_v2_norebal.caffemodel"; # Read the input image frame = cv.imread("./dog-greyscale.png")W_in = 224 H_in = 224# Read the network into Memory net = cv.dnn.readNetFromCaffe(protoFile, weightsFile)

2.2 加载量化信息接下来,我们加载量化分级信息。然后就是对输出进行变换。最后,我们添加一个非零值的缩放图层。
c++代码:
// setup additional layers 在网络里额外添加两层 int sz[] =2, 313, 1, 1 ; //添加一个ab转换层 const Mat pts_in_hull(4, sz, CV_32F, hull_pts); Ptr& lt; dnn::Layer& gt; class8_ab = net.getLayer("class8_ab"); class8_ab-& gt; blobs.push_back(pts_in_hull); //一个防止为输出为0的层 Ptr& lt; dnn::Layer& gt; conv8_313_rh = net.getLayer("conv8_313_rh"); conv8_313_rh-& gt; blobs.push_back(Mat(1, 313, CV_32F, Scalar(2.606)));

python代码:
# Load the bin centers pts_in_hull = np.load(./pts_in_hull.npy)# populate cluster centers as 1x1 convolution kernel 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)]

2.3 将图像转换为CIE Lab颜色空间缩放输入RGB图像,使得值在0-1范围内,然后将其转换为Lab颜色空间,并提取出亮度通道。原始图像中的亮度通道的大小调整为网络输入大小,在这种情况下为(224,224)。通常,亮度通道的范围从0到100.因此我们减去50以0点对称。然后我们将缩放和平均居中的亮度送到网络作为前向通道的输入。前向传递的输出是图像的预测ab通道。将其缩放回原始图像大小,然后与原始大小的亮度图像(在原始分辨率中较早提取)合并,以获得输出Lab图像。然后将其转换为RGB色彩空间以获得最终的彩色图像,并保存输出图像。
C++代码:
// extract L channel and subtract mean 将原图转换为Lab空间图,并归一化 Mat lab, L, input; img.convertTo(img, CV_32F, 1.0 / 255); cvtColor(img, lab, COLOR_BGR2Lab); //提取亮度信息 extractChannel(lab, L, 0); //重置大小 resize(L, input, Size(W_in, H_in)); input -= 50; // run the L channel through the network 获得网络输出结果 Mat inputBlob = blobFromImage(input); net.setInput(inputBlob); Mat result = net.forward(); // retrieve the calculated a,b channels from the network output Size siz(result.size[2], result.size[3]); //输出为56X56 Mat a = Mat(siz, CV_32F, result.ptr(0, 0)); Mat b = Mat(siz, CV_32F, result.ptr(0, 1)); //重置大小 resize(a, a, img.size()); resize(b, b, img.size()); // merge, and convert back to BGR 合并lab,转换为RGB图像 Mat color, chn[] =L, a, b ; merge(chn, 3, lab); cvtColor(lab, color, COLOR_Lab2BGR);

python版本:
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] # pull out L channel# resize lightness channel to network input size img_l_rs = cv.resize(img_l, (W_in, H_in)) # img_l_rs -= 50 # subtract 50 for mean-centeringnet.setInput(cv.dnn.blobFromImage(img_l_rs)) ab_dec = net.forward()[0,:,:,:].transpose((1,2,0)) # this is our result(H_orig,W_orig) = img_rgb.shape[:2] # original image size 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) # concatenate with original image L img_bgr_out = np.clip(cv.cvtColor(img_lab_out, cv.COLOR_Lab2BGR), 0, 1)

3 代码算法速度很快,但是我个人觉得没加颜色再平衡的效果更好。代码分为图像版本和视频版本。但是总体效果一般般吧。
代码下载地址:
3.1 图像着色代码C++:
// OpenCV_Colorization.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 //#include "pch.h" #include & lt; iostream& gt; #include & lt; opencv2/dnn.hpp& gt; #include & lt; opencv2/imgproc.hpp& gt; #include & lt; opencv2/highgui.hpp& gt; #include & lt; iostream& gt; using namespace cv; using namespace cv::dnn; using namespace std; // the 313 ab cluster centers from pts_in_hull.npy (already transposed) static float hull_pts[] =-90., -90., -90., -90., -90., -80., -80., -80., -80., -80., -80., -80., -80., -70., -70., -70., -70., -70., -70., -70., -70., -70., -70., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 100., 100., 100., 100., 100., 100., 100., 100., 100., 100., 50., 60., 70., 80., 90., 20., 30., 40., 50., 60., 70., 80., 90., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0. ; int main()string imageFileName = "./image/greyscaleImage.png"; Mat img = imread(imageFileName); if (img.empty())cout & lt; & lt; "Cant read image from file: " & lt; & lt; imageFileName & lt; & lt; endl; return 2; string protoFile = "./model/colorization_deploy_v2.prototxt"; string weightsFile = "./model/colorization_release_v2.caffemodel"; //string weightsFile = "./model/colorization_release_v2_norebal.caffemodel"; double t = (double)cv::getTickCount(); // fixed input size for the pretrained network 加载模型 const int W_in = 224; const int H_in = 224; Net net = dnn::readNetFromCaffe(protoFile, weightsFile); // setup additional layers 在网络里额外添加两层 int sz[] =2, 313, 1, 1 ; //添加一个ab转换层 const Mat pts_in_hull(4, sz, CV_32F, hull_pts); Ptr& lt; dnn::Layer& gt; class8_ab = net.getLayer("class8_ab"); class8_ab-& gt; blobs.push_back(pts_in_hull); //一个防止为输出为0的层 Ptr& lt; dnn::Layer& gt; conv8_313_rh = net.getLayer("conv8_313_rh"); conv8_313_rh-& gt; blobs.push_back(Mat(1, 313, CV_32F, Scalar(2.606))); // extract L channel and subtract mean 将原图转换为Lab空间图,并归一化 Mat lab, L, input; img.convertTo(img, CV_32F, 1.0 / 255); cvtColor(img, lab, COLOR_BGR2Lab); //提取亮度信息 extractChannel(lab, L, 0); //重置大小 resize(L, input, Size(W_in, H_in)); input -= 50; // run the L channel through the network 获得网络输出结果 Mat inputBlob = blobFromImage(input); net.setInput(inputBlob); Mat result = net.forward(); // retrieve the calculated a,b channels from the network output Size siz(result.size[2], result.size[3]); //输出为56X56 Mat a = Mat(siz, CV_32F, result.ptr(0, 0)); Mat b = Mat(siz, CV_32F, result.ptr(0, 1)); //重置大小 resize(a, a, img.size()); resize(b, b, img.size()); // merge, and convert back to BGR 合并lab,转换为RGB图像 Mat color, chn[] =L, a, b ; merge(chn, 3, lab); cvtColor(lab, color, COLOR_Lab2BGR); //计算时间 t = ((double)cv::getTickCount() - t) / cv::getTickFrequency(); cout & lt; & lt; "Time taken : " & lt; & lt; t & lt; & lt; " secs" & lt; & lt; endl; //保存图像 string str = imageFileName; str.replace(str.end() - 4, str.end(), ""); str = str + "_colorized.png"; //反归一化 color = color * 255; color.convertTo(color, CV_8U); imwrite(str, color); cout & lt; & lt; "Colorized image saved as " & lt; & lt; str & lt; & lt; endl; return 0;

python:
import numpy as np import cv2 as cv# Read the input image frame = cv.imread("./image/greyscaleImage.png")# Specify the paths for the 2 model files protoFile = "./model/colorization_deploy_v2.prototxt" weightsFile = "./model/colorization_release_v2.caffemodel" #weightsFile = "./model/colorization_release_v2_norebal.caffemodel"# Load the cluster centers pts_in_hull = np.load(./model/pts_in_hull.npy)# Read the network into Memory net = cv.dnn.readNetFromCaffe(protoFile, weightsFile)# populate cluster centers as 1x1 convolution kernel 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)]#from opencv sample W_in = 224 H_in = 224img_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] # pull out L channel# resize lightness channel to network input size img_l_rs = cv.resize(img_l, (W_in, H_in)) # img_l_rs -= 50 # subtract 50 for mean-centeringnet.setInput(cv.dnn.blobFromImage(img_l_rs)) ab_dec = net.forward()[0,:,:,:].transpose((1,2,0)) # this is our result(H_orig,W_orig) = img_rgb.shape[:2] # original image size 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) # concatenate with original image L img_bgr_out = np.clip(cv.cvtColor(img_lab_out, cv.COLOR_Lab2BGR), 0, 1)outputFile = _colorized.png cv.imwrite(outputFile, (img_bgr_out*255).astype(np.uint8)) print(Colorized image saved as +outputFile) print(Done !!!)

3.2 视频着色代码C++:
#include "pch.h" #include & lt; opencv2/dnn.hpp& gt; #include & lt; opencv2/imgproc.hpp& gt; #include & lt; opencv2/highgui.hpp& gt; #include & lt; iostream& gt; using namespace cv; using namespace cv::dnn; using namespace std; // the 313 ab cluster centers from pts_in_hull.npy (already transposed) static float hull_pts[] = -90., -90., -90., -90., -90., -80., -80., -80., -80., -80., -80., -80., -80., -70., -70., -70., -70., -70., -70., -70., -70., -70., -70., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -60., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -50., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -40., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -30., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -20., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., -10., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 10., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 20., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 30., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 40., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 50., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 60., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 70., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 80., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 90., 100., 100., 100., 100., 100., 100., 100., 100., 100., 100., 50., 60., 70., 80., 90., 20., 30., 40., 50., 60., 70., 80., 90., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., 100., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., 90., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., 80., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., -110., -100., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0., 10., 20., 30., 40., 50., 60., 70., -90., -80., -70., -60., -50., -40., -30., -20., -10., 0. ; int main()String videoFileName = "./video/greyscaleVideo.mp4"; cv::VideoCapture cap(videoFileName); if (!cap.isOpened())cerr & lt; & lt; "Unable to open video" & lt; & lt; endl; return 1; string protoFile = "./model/colorization_deploy_v2.prototxt"; string weightsFile = "./model/colorization_release_v2.caffemodel"; //string weightsFile = "./model/colorization_release_v2_norebal.caffemodel"; Mat frame, frameCopy; int frameWidth = cap.get(CAP_PROP_FRAME_WIDTH); int frameHeight = cap.get(CAP_PROP_FRAME_HEIGHT); string str = videoFileName; str.replace(str.end()-4, str.end(), ""); string outVideoFileName = str+"_colorized.avi"; VideoWriter video(outVideoFileName, VideoWriter::fourcc(M,J,P,G), 60, Size(frameWidth,frameHeight)); // fixed input size for the pretrained network const int W_in = 224; const int H_in = 224; Net net = dnn::readNetFromCaffe(protoFile, weightsFile); // setup additional layers: int sz[] = 2, 313, 1, 1; const Mat pts_in_hull(4, sz, CV_32F, hull_pts); Ptr& lt; dnn::Layer& gt; class8_ab = net.getLayer("class8_ab"); class8_ab-& gt; blobs.push_back(pts_in_hull); Ptr& lt; dnn::Layer& gt; conv8_313_rh = net.getLayer("conv8_313_rh"); conv8_313_rh-& gt; blobs.push_back(Mat(1, 313, CV_32F, Scalar(2.606))); int i = 0; for(; ; )cap & gt; & gt; frame; if (frame.empty()) break; frameCopy = frame.clone(); // extract L channel and subtract mean Mat lab, L, input; frame.convertTo(frame, CV_32F, 1.0/255); cvtColor(frame, lab, COLOR_BGR2Lab); extractChannel(lab, L, 0); resize(L, input, Size(W_in, H_in)); input -= 50; // run the L channel through the network Mat inputBlob = blobFromImage(input); net.setInput(inputBlob); Mat result = net.forward(); // retrieve the calculated a,b channels from the network output Size siz(result.size[2], result.size[3]); Mat a = Mat(siz, CV_32F, result.ptr(0,0)); Mat b = Mat(siz, CV_32F, result.ptr(0,1)); resize(a, a, frame.size()); resize(b, b, frame.size()); // merge, and convert back to BGR Mat coloredFrame, chn[] = L, a, b; merge(chn, 3, lab); cvtColor(lab, coloredFrame, COLOR_Lab2BGR); coloredFrame = coloredFrame*255; coloredFrame.convertTo(coloredFrame, CV_8U); video.write(coloredFrame); i++; cout & lt; & lt; "the current frame is: " & lt; & lt; to_string(i) & lt; & lt; "th" & lt; & lt; endl; cout & lt; & lt; "Colorized video saved as " & lt; & lt; outVideoFileName & lt; & lt; endl & lt; & lt; "Done !!!" & lt; & lt; endl; cap.release(); video.release(); return 0;

python:
import numpy as np import cv2 as cv import argparse import os.path# Read the input video cap = cv.VideoCapture(video/greyscaleVideo.mp4) hasFrame, frame = cap.read()outputFile = colorized.avi vid_writer = cv.VideoWriter(outputFile, cv.VideoWriter_fourcc(M,J,P,G), 60, (frame.shape[1],frame.shape[0]))# Specify the paths for the 2 model files protoFile = "./model/colorization_deploy_v2.prototxt" #weightsFile = "./model/colorization_release_v2.caffemodel" weightsFile = "./model/colorization_release_v2_norebal.caffemodel"# Load the cluster centers pts_in_hull = np.load(./model/pts_in_hull.npy)# Read the network into Memory net = cv.dnn.readNetFromCaffe(protoFile, weightsFile)# populate cluster centers as 1x1 convolution kernel 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)]#from opencv sample W_in = 224 H_in = 224i=0 while cv.waitKey(1):hasFrame, frame = cap.read() frameCopy = np.copy(frame) if not hasFrame: breakimg_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] # pull out L channel# resize lightness channel to network input size img_l_rs = cv.resize(img_l, (W_in, H_in)) img_l_rs -= 50 # subtract 50 for mean-centeringnet.setInput(cv.dnn.blobFromImage(img_l_rs)) ab_dec = net.forward()[0,:,:,:].transpose((1,2,0)) # this is our result(H_orig,W_orig) = img_rgb.shape[:2] # original image size 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) # concatenate with original L channel img_bgr_out = np.clip(cv.cvtColor(img_lab_out, cv.COLOR_Lab2BGR), 0, 1)vid_writer.write((img_bgr_out*255).astype(np.uint8)) i +=1 print("the current frame is: th".format(i)) vid_writer.release()print(Colorized video saved as +outputFile) print(Done !!!)


    推荐阅读