本次要整理记录的内容是利用colorization模型来将灰度图像转换为彩色图像。colorization模型是利用Lab色彩空间的L(亮度)通道来预测a、b两个通道的值,也就是说当我们手里有一张灰度图像的时候,可以利用colorization模型对这张灰度图像的亮度值进行预测,从而得到a、b两个通道的值,并将L、a、b三通道合并后得到一个彩色化的图像。
这里使用了Lab色彩空间,我们需要先对其有一定的了解:
Lab色彩空间是基于数据驱动对人类感觉的研究,它假设人类的视觉系统理想地适应自然景色的处理,由亮度分量L和两个色度分量a和b组成。其中,a表示黄-蓝通道(yellow-blue opponent),b表示红-绿通道(red-green opponent)。
Lab色彩空间各个通道的取值范围为L(0,100)、a( - 128,127)、b( - 128,127),在OpenCV中对取值范围进行量化,都归一化到 [ 0,255 ] 之间。
说白了,colorization模型所做的工作就是在Lab色彩空间中依靠灰度图像的亮度来推理出该图像在人眼视觉中可能存在的颜色,从而对图像进行自动上色。
该博文主要记录我们如何在OpenCV中利用dnn模块来加载调用这个模型,下面通过代码逐步进行演示。
首先,我们需要从文件中加载这个模型,并且设置计算后台和目标设备。
string modelTxt = "D:/opencv_c++/opencv_tutorial/data/models/colorization/colorization_deploy_v2.prototxt";
string modelBin = "D:/opencv_c++/opencv_tutorial/data/models/colorization/colorization_release_v2.caffemodel";
Net net = dnn::readNetFromCaffe(modelTxt, modelBin);
net.setPreferableBackend(DNN_BACKEND_INFERENCE_ENGINE);
net.setPreferableTarget(DNN_TARGET_CPU);
然后,我们需要定义colorzation模型的Lab量化表,这个Lab量化表是很重要的,由模型训练文件pts_in_hull.npy
给出,我们需要将这个Lab量化表作为网络中间层class8_ab
和conv8_313_rh
的输入。
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.
};
随后定义colorization网络的固定输入大小,这是由网络结构确定的,我们就将其预先定义好。
const int W_in = 224;
const int H_in = 224;
接着是同样很重要的一步,那就是要设置colorization网络中间层的输入,将先前定义的Lab量化表传入到网络中去。
int add_input[] = { 2, 313, 1, 1 };
const Mat pts_in_hull(4, add_input, 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)));
接着我们读取要进行上色的灰度图像,先将图像转换为CV_32F
类型后除以255,在转换到Lab色彩空间,通过extractChannel()
这个API来提取L通道,将L通道转换为colorization网络的输入尺寸,最后减去均值。
Mat img = imread("D:/opencv_c++/opencv_tutorial/data/images/old1.jpeg");
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;
将L通道转换为blob,传入colorization网络的输入层,并进行前向传播。
Mat inputBlob = blobFromImage(input);
net.setInput(inputBlob);
Mat result = net.forward();
再对前向传播结果进行解码。从前向传播结果中检索计算出的a,b通道
Size size(result.size[2], result.size[3]);
Mat a = Mat(size, CV_32F, result.ptr(0, 0));
Mat b = Mat(size, CV_32F, result.ptr(0, 1));
resize(a, a, img.size());
resize(b, b, img.size());
最后将得到的a、b通道与L通道进行合并,并将图像从Lab色彩空间转换回RGB色彩空间,就得到了上色后的图像了。
Mat color, Lab_channels[] = { L, a, b };
merge(Lab_channels, 3, lab);
cvtColor(lab, color, COLOR_Lab2BGR);
namedWindow("input_image", WINDOW_FREERATIO);
namedWindow("DNN-output_color_image", WINDOW_FREERATIO);
imshow("DNN-output_color_image", color);
imshow("input_image", img);
下面看看对图像自动上色后的效果:
上图中,左边是输入的灰度图像,右边是通过colorization模型自动上色后的图像,从感觉上来说,上色后的图像可能还是存在一些违和感,但大体上显示出了图像中物体的色彩,效果还是不错的。
好的那本次笔记到此结束,谢谢阅读~
PS:本人的注释比较杂,既有自己的心得体会也有网上查阅资料时摘抄下的知识内容,所以如有雷同,纯属我向前辈学习的致敬,如果有前辈觉得我的笔记内容侵犯了您的知识产权,请和我联系,我会将涉及到的博文内容删除,谢谢!