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

Posted liferecords

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se相关的知识,希望对你有一定的参考价值。

技术有时会提高艺术,但有时也会破坏艺术。着色黑白电影是一个可以追溯到1902年的古老想法。几十年来,许多电影创作者反对将黑白电影着色的想法,并将其视为对艺术的破坏。但今天它被接受为艺术形式的增强。该技术本身已经从艰苦的手工着色转变为如今的自动化技术。如下图所示。

基于传统的计算机视觉方法以及发表了许多关于图像着色的论文。我最喜欢的一篇文章是名为Colorization using Optimization的论文。它使用了一些彩色涂鸦来指导解决着色的优化问题。论文见

如果算法不需要用户输入任何参数,这不是很酷的事情吗?在过去几年中,随着深度学习的发展,着色自动化取得了巨大的飞跃。在这篇文章中,我们将了解一个这样的着色深度学习模型。我们还共享OpenCV代码,以便在Python或C ++应用程序中使用经过训练的模型。

1 彩色图像着色

在ECCV 2016中,一篇名为Colorful Image Colorization的论文,提出了一个用于着色灰度图像的卷积神经网络。论文使用ImageNet训练集的130万像素训练网络,并公开了一个训练好的基于Caffe的模型。在这篇文章中,我们将首先定义着色问题,解释论文的架构细节,最后分享代码和一些有趣的结果。

论文原文:

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个类可供选择。

1.2 CNN彩色化结构

Colorful Image Colorization这篇论文使用的CNN结构如下所示。类似与VGG网络,但是该CNN没有池化层或全连接层。

输入图像缩放为224×224,缩放后的灰度输入图像表示为X。当它通过上面显示的神经网络时,输出为:

的尺寸为H×W×Q,其中H=56和W=56是最后一个卷积层输出的高度和宽度。Q=313表示类别个数。对于每个H×W都有一个对应的值表示属于该类的概率。我们的目标是为每个概率分布

找到其对应的ab通道值。

1.3 从中恢复彩色图像

让我们看看如何从输出 恢复其对应的ab值。您可能会想到我们可以简单地采用

各个量化级别的均值,并获取与其对应的ab值。不幸的是,由于

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

的值分布特点让你得到蓝色或黄色的天空?当然,作者尝试了这一点,虽然它给出了鲜艳的色彩,但它有时会破坏空间的一致性。他们的解决方案是在

的均值和期输出值之间进行插值,以获得称为annealed-mean的量。称为温度(T)的参数用于控制插值程度。最终值T=0.38用作最优插值。

ab值所对应的annealed-mean值表示为

关系为:

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

1.4 具有颜色再平衡的多项式损失函数

通过定义损失函数来训练所有神经网络。培训过程的目标是最大限度地减少训练集的损失。在着色问题中,训练数据由数千个彩色图像及其灰度图组成。

CNN输入图像x对应的输出为

。我们需要将训练集中的所有彩色图像转换为并获得相应的

值。在数学上,我们只想反转映射H。即

对于

Y

我们可以简单地找到与其对应的ab值并表示为Z,由于ab是量化后的值,因此Y对应的ab值为1,其他312个值为0。但是为了获得更好的结果,使用高斯分布来计算

。所以通过原始图像ab值Z和

计算loss。

不幸的是,上述损失函数产生非常暗淡的颜色。这是因为ImageNet中的颜色分布与灰度图值分布接近。

为了推动算法产生鲜艳的色彩,作者将损失函数改为

其中颜色再平衡参数

表示根据颜色等级的稀有度重新平衡损失。这有助于在输出中获得更加生动和饱和的色彩。

说实话由于原文实在解释太坑的。其实模型思路很简单,输入灰度图,输出ab值的概率图Z,然后将ab映射到ab估计值图Y,使得结果更加合理。由于有了灰度图也就是输入,输出就是我们由彩色图原图转换而来的ab值图。计算loss,调整模型就行了。

详细解释可以看:

1.5 着色结果

作者分享了两种版本的训练好的Caffe模型,有无色彩再平衡。我们尝试了两个版本,并在下图中分享了结果。中间列显示没有颜色重新平衡的版本,最后一列显示具有重新平衡的版本。正如我们所看到的,色彩重新平衡使许多图像非常生动和充满活力。大多数都是合理的颜色。另一方面,有时它也可能会为某些图像添加一些不需要的饱和色块。

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

1)动物

该模型非常适合动物的图像,尤其是猫和狗。这是因为ImageNet包含了大量这些动物。

2)户外场景

该模型也很好地展示了蓝天和绿色植被的户外场景。还要注意,模型能够重新着色橙色天空,表明它已经获得了日落的特征。

3)手绘

最后,即使使用手绘草图,该模型也会产生合理的着色。

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 << "Cant read image from file: " << imageFileName << 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<dnn::Layer> class8_ab = net.getLayer("class8_ab");
    class8_ab->blobs.push_back(pts_in_hull);
    //一个防止为输出为0的层
    Ptr<dnn::Layer> conv8_313_rh = net.getLayer("conv8_313_rh");
    conv8_313_rh->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-centering

net.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 <iostream>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

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 << "Cant read image from file: " << imageFileName << 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<dnn::Layer> class8_ab = net.getLayer("class8_ab");
    class8_ab->blobs.push_back(pts_in_hull);
    //一个防止为输出为0的层
    Ptr<dnn::Layer> conv8_313_rh = net.getLayer("conv8_313_rh");
    conv8_313_rh->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 << "Time taken : " << t << " secs" << 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 << "Colorized image saved as " << str << 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 = 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] # 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-centering

net.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 <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>

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 << "Unable to open video" << 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<dnn::Layer> class8_ab = net.getLayer("class8_ab");
    class8_ab->blobs.push_back(pts_in_hull);
    Ptr<dnn::Layer> conv8_313_rh = net.getLayer("conv8_313_rh");
    conv8_313_rh->blobs.push_back(Mat(1, 313, CV_32F, Scalar(2.606)));

    int i = 0;
    for(;;)
    

        cap >> 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 << "the current frame is: " << to_string(i) << "th" << endl;
    
    cout << "Colorized video saved as " << outVideoFileName << endl << "Done !!!" << 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 = 224

i=0
while cv.waitKey(1):

    hasFrame, frame = cap.read()
    frameCopy = np.copy(frame)
    if not hasFrame:
        break

    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-centering

    net.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 !!!)

以上是关于[OpenCV实战]17 基于卷积神经网络的OpenCV图像着se的主要内容,如果未能解决你的问题,请参考以下文章

毕业设计 - 题目:基于机器视觉opencv的手势检测 手势识别 算法 - 深度学习 卷积神经网络 opencv python

联邦学习实战基于FATE框架的MNIST手写数字识别——卷积神经网络

深度学习基于卷积神经网络(tensorflow)的人脸识别项目

项目实战解析:基于深度学习搭建卷积神经网络模型算法,实现图像识别分类

人工智能------>第四天,深度学习,人工神经网络,卷积神经网络,opencv,音频采集播放,百度AI平台的使用

实战 | OpenCV实现棋盘图像识别