在编写实际项目的过程中,我需要实现绿色主题的“伪彩色”变换。在目前提供的模板中,只有summer最为接近,但是它的颜色太浅了,看上去不是很清晰。所以我结合ocean和summer两种现有模板,构建了deepgreen这个模板。它能够实现绿色主题的显著的伪彩色变换。本文记录了我发现问题、分析问题、编写代码、为OpenCV提供pull request的全过程。希望能够为有相关需求的工程师提供帮助。信息量比较大,表述不清楚的地方欢迎讨论。
截至发文,相关代码已经通过buildbot,处于opencv memeber审核阶段。具体可以看 https://github.com/opencv/opencv/pull/17260
一、基本情况
cv::applyColorMap()能够实现预定义的伪彩色,这个是众所周知的事情。
除了这些预置的变换,如果我想实现新的变换,一般的方法是做LUT变换
cv::Mat image_gray_3c;
//单通道的灰度图,转换成R、G、B三通道值均相等的三通道图
cv::cvtColor(image_gray, image_gray_3c, cv::COLOR_GRAY2RGB);
//opencv默认的颜色排列顺序是BGR,而这里自定义的colormap的顺序是RGB
cv::cvtColor(golden_map, golden_map, cv::COLOR_BGR2RGB);
cv::Mat image_color;
cv::LUT(image_gray_3c, golden_map, image_color);
但是,这段代码只是给调用方法,没有具体说明这里的LUT色板是如何构建的,你如果想做这个调色板还是需要自己找。我在做一个实际项目的时候,甲方就反馈,希望提供一种个比较深的绿色,由此开启整个关于applyColorMap()的探索。
二、参考OpenCV的代码进行修改,达到目的
能够找到重要的参考,主要就是OpenCV自己的代码:colormap.cpp
截取其中一个colormap的实现,比如"ocean"
class Ocean : public ColorMap {
public:
Ocean() : ColorMap() {
init(256);
}
Ocean(int n) : ColorMap() {
init(n);
}
void init(int n) {
static const float r[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904762f, 0.09523809523809523f, 0.1428571428571428f, 0.1904761904761905f, 0.2380952380952381f, 0.2857142857142857f, 0.3333333333333333f, 0.3809523809523809f, 0.4285714285714285f, 0.4761904761904762f, 0.5238095238095238f, 0.5714285714285714f, 0.6190476190476191f, 0.6666666666666666f, 0.7142857142857143f, 0.7619047619047619f, 0.8095238095238095f, 0.8571428571428571f, 0.9047619047619048f, 0.9523809523809523f, 1};
static const float g[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02380952380952381f, 0.04761904761904762f, 0.07142857142857142f, 0.09523809523809523f, 0.119047619047619f, 0.1428571428571428f, 0.1666666666666667f, 0.1904761904761905f, 0.2142857142857143f, 0.2380952380952381f, 0.2619047619047619f, 0.2857142857142857f, 0.3095238095238095f, 0.3333333333333333f, 0.3571428571428572f, 0.3809523809523809f, 0.4047619047619048f, 0.4285714285714285f, 0.4523809523809524f, 0.4761904761904762f, 0.5f, 0.5238095238095238f, 0.5476190476190477f, 0.5714285714285714f, 0.5952380952380952f, 0.6190476190476191f, 0.6428571428571429f, 0.6666666666666666f, 0.6904761904761905f, 0.7142857142857143f, 0.7380952380952381f, 0.7619047619047619f, 0.7857142857142857f, 0.8095238095238095f, 0.8333333333333334f, 0.8571428571428571f, 0.8809523809523809f, 0.9047619047619048f, 0.9285714285714286f, 0.9523809523809523f, 0.9761904761904762f, 1};
static const float b[] = { 0, 0.01587301587301587f, 0.03174603174603174f, 0.04761904761904762f, 0.06349206349206349f, 0.07936507936507936f, 0.09523809523809523f, 0.1111111111111111f, 0.126984126984127f, 0.1428571428571428f, 0.1587301587301587f, 0.1746031746031746f, 0.1904761904761905f, 0.2063492063492063f, 0.2222222222222222f, 0.2380952380952381f, 0.253968253968254f, 0.2698412698412698f, 0.2857142857142857f, 0.3015873015873016f, 0.3174603174603174f, 0.3333333333333333f, 0.3492063492063492f, 0.3650793650793651f, 0.3809523809523809f, 0.3968253968253968f, 0.4126984126984127f, 0.4285714285714285f, 0.4444444444444444f, 0.4603174603174603f, 0.4761904761904762f, 0.492063492063492f, 0.5079365079365079f, 0.5238095238095238f, 0.5396825396825397f, 0.5555555555555556f, 0.5714285714285714f, 0.5873015873015873f, 0.6031746031746031f, 0.6190476190476191f, 0.6349206349206349f, 0.6507936507936508f, 0.6666666666666666f, 0.6825396825396826f, 0.6984126984126984f, 0.7142857142857143f, 0.7301587301587301f, 0.746031746031746f, 0.7619047619047619f, 0.7777777777777778f, 0.7936507936507936f, 0.8095238095238095f, 0.8253968253968254f, 0.8412698412698413f, 0.8571428571428571f, 0.873015873015873f, 0.8888888888888888f, 0.9047619047619048f, 0.9206349206349206f, 0.9365079365079365f, 0.9523809523809523f, 0.9682539682539683f, 0.9841269841269841f, 1};
Mat X = linspace(0,1,64);
this->_lut = ColorMap::linear_colormap(X,
Mat(64,1, CV_32FC1, (void*)r).clone(), // red
Mat(64,1, CV_32FC1, (void*)g).clone(), // green
Mat(64,1, CV_32FC1, (void*)b).clone(), // blue
n); // number of sample points
}
};
明显这个colormap中最主要的成分就是rgb的大矩阵,它返回的结果是LUT。关键问题是这样的矩阵如何获得?想搞懂这里的文档,需要特定的基础知识。此外,我们深入研究的话,就可以发现这里OpenCV实现的不仅仅是LUT,还有其它很多东西。比如3通道,比如插值等。为了实现这些功能,它添加了很多函数,如果想把这些函数集成过来,可能会花费较多精力。反之,我认为更直接可行的方法,就是修改现有的OpenCV代码,重新生成dll文件。
为了实现甲方要求,我套用ocean的色彩对summer进行修改,其中只是修改了一个地方,那就是将ocean中的blue通道和green通道进行了替换。
从而将绿色的成分更多的凸显出来。修改的代码通过Git的版本控制,可以比较明显地看出来(附录3).
在下图的例子中,可以发现已经正确调用,并且COLORMAP_DEEPGREEN这个常量也是自定义的。
这里实现的效果很好。但是时间长了,麻烦就来了,随着OpenCV的不断升级,后面的版本可能都需要做同样的修改。我也开始寻找相关解决方法。
一种方法,我可以构建自己专用OpenCV,这个版本的OpenCV中应该有我自己的代码,也能够更新官网代码。这样每次大的升级,我只需要重新编译一下就可以了;另一种方法,如果我能够将这段代码并到OpenCV中,那是最好的,我用起来方便、别人也可以用得着。
三、构建自己专用OpenCV
如果是自用的话,我建议使用Gitee。其主要原因是由于github的网络限制,所以直接通过其下载代码容易出现各种错误。
Gitee的一大特设,就是可以直接创建github上opencv的clone。(附录5)
而后我们可以将gitee上代码git到本地,修改,编译。
https模式可以直接clone,如果是ssh需要配密钥。通过gitee下载,我能够达到1-3m左右的速度。我们编译生成它,注意结果地址。(可以参考《记录一次关于OpenCV的CmakeLists的探索》https://www.cnblogs.com/jsxyhelu/p/12843005.html)
确保能够正确调用后,我们需要将这里的代码提交到gitee上去。
能够看到,这里的modules由于我们的修改,已经发生了变化。
由于gitee上的这个库是我们自己控制的,直接push上去,过程中需要输gitee的密码。
能够看到这次提交的对比。这样我们的代码就保存下来了。
但是同时需要注意的是,Gitee上的OpenCV是和原始库“脱轨”的,它除了一个“强制刷新”其它什么都做不了。而GitHub非常强的一个功能,是提供下图这个"behind".
四、将自己的代码并到OpenCV中(pull request)
pull request才是开源的本质,好的代码一定要融合到基库中,如果你的代码不好就融合不进去,通过这个过程中,我们才能够写出“正确、清晰、有弹性"的优秀代码。抱着”试试看“的态度,我将这里的修改提交GitHub。(由于网速问题,我基本上都是在网站上手工修改)
并且借助工具,填写英文文档。
睡了一觉以后,发现opencv member 给出了许多反馈,整理一下
1、如何使用github的方法,甚至给出了具体命令
git checkout -b feat_deep_green_colormap
2、提示同时提供文档,并且给出链接
https://github.com/opencv/opencv/pull/15388
3、给出提交代码和需要解决的问题提出相关建议,并且直指问题核心!
https://matplotlib.org/tutorials/colors/colormaps.html
4、提出代码建议:建议按照字母顺序进行排列,并且要求提供注释
5、给出了规范的提交,并且是非常对口的。
这个是非常激动人心的,只要你是认认真真地去提交代码,就会有优秀的工程师教你如何做对,比如这里给出了非常对口的指导:
This pullrequest changes
I implemented the colormap "Turbo" proposed by Google.
- https://ai.googleblog.com/2019/08/turbo-improved-rainbow-colormap-for.html
- https://gist.github.com/mikhailov-work/6a308c20e494d9e0ccc29036b28faa7a
And, I add cv::COLORMAP_TURBO
as the flag of cv::applyColorMap
.
I checked this feature using the following code.
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
int main(int argc, const char* argv[])
{
cv::Mat disp(cv::Size(256, 30), CV_8UC1, cv::Scalar(0));
for(int y = 0; y < disp.rows; y++)
{
for(int x = 0; x < disp.cols; x++)
{
disp.at<uchar>(y, x) = x;
}
}
cv::Mat turbo;
cv::applyColorMap(disp, turbo, cv::COLORMAP_TURBO);
cv::imwrite("colorscale_turbo.jpg", turbo);
return 0;
}
它来自于google的一篇研究,并且提供了参考代码
大家看最后我的提交,基本上就是参考的#15338,以后很多地方都可以继续参考。GitHub还有一项非常厉害的功能,就是AutoBoot(附录6),以及《OpenCV PR 成功的收获和感悟》https://www.cnblogs.com/jsxyhelu/p/9638409.html。
五、反思小结
现在看来,想充分使用git的功能,必须满足:
1、持续更新;
2、网络通畅;
3、本地部署。
一方面,如果希望有一个自己能够使用的专用库,这个库一定要来自实际需求,如果可以。我希望能够经过autobot检验,如果不可以,至少要是本地能够编译的;
一方面,如果下次遇到类似的问题,或者马上提出新的问题,将其提交到opencv绝对是最好的选择。成功的提交,才能够给出最多的价值体验。
使用git而不是直接zip下载的方式来使用opencv代码,才是正确方式。
为opencv贡献代码是非常好的学习git和github的途径,这是其它场所无法提供的学习方式。
感谢阅读至此,希望有所帮助。
================================附录========================================附录==============================================
关于这个问题的其它尝试和一些相关的内容,放在附录里面,有空可以看看:
1、寻求直接修改代码解决方法
这里我还是需要寻找能够和OpenCV代码共存的方式。最简单的方法就是在GOCVHelper中添加相应的修改。首先要做的,就是能够继承 public ColorMap
但是如果我想直接引用ColorMap的话,会出现这个问题
由于种种原因,这里的ColorMap只是作为一个编译期间可用的类,没有被开放出来,也就是我不能直接修改。
2、一个小tip
配置项目属性的时候,选择刚刚生成的bin目录
这样会优先使用修改后的dll,从而不会影响通用的OpenCV.dll。
3、我具体修改了什么代码?
imgproc.hpp
enum ColormapTypes
{
COLORMAP_AUTUMN = 0, //!< ![autumn](pics/colormaps/colorscale_autumn.jpg)
COLORMAP_BONE = 1, //!< ![bone](pics/colormaps/colorscale_bone.jpg)
COLORMAP_JET = 2, //!< ![jet](pics/colormaps/colorscale_jet.jpg)
COLORMAP_WINTER = 3, //!< ![winter](pics/colormaps/colorscale_winter.jpg)
COLORMAP_RAINBOW = 4, //!< ![rainbow](pics/colormaps/colorscale_rainbow.jpg)
COLORMAP_OCEAN = 5, //!< ![ocean](pics/colormaps/colorscale_ocean.jpg)
COLORMAP_SUMMER = 6, //!< ![summer](pics/colormaps/colorscale_summer.jpg)
COLORMAP_SPRING = 7, //!< ![spring](pics/colormaps/colorscale_spring.jpg)
COLORMAP_COOL = 8, //!< ![cool](pics/colormaps/colorscale_cool.jpg)
COLORMAP_HSV = 9, //!< ![HSV](pics/colormaps/colorscale_hsv.jpg)
COLORMAP_PINK = 10, //!< ![pink](pics/colormaps/colorscale_pink.jpg)
COLORMAP_HOT = 11, //!< ![hot](pics/colormaps/colorscale_hot.jpg)
COLORMAP_PARULA = 12, //!< ![parula](pics/colormaps/colorscale_parula.jpg)
COLORMAP_MAGMA = 13, //!< ![magma](pics/colormaps/colorscale_magma.jpg)
COLORMAP_INFERNO = 14, //!< ![inferno](pics/colormaps/colorscale_inferno.jpg)
COLORMAP_PLASMA = 15, //!< ![plasma](pics/colormaps/colorscale_plasma.jpg)
COLORMAP_VIRIDIS = 16, //!< ![viridis](pics/colormaps/colorscale_viridis.jpg)
COLORMAP_CIVIDIS = 17, //!< ![cividis](pics/colormaps/colorscale_cividis.jpg)
COLORMAP_TWILIGHT = 18, //!< ![twilight](pics/colormaps/colorscale_twilight.jpg)
COLORMAP_TWILIGHT_SHIFTED = 19, //!< ![twilight shifted](pics/colormaps/colorscale_twilight_shifted.jpg)
COLORMAP_TURBO = 20 ,//!< ![turbo](pics/colormaps/colorscale_turbo.jpg)
COLORMAP_DEEPGREEN = 21 //jsxyhelu 2020年5月9日
};
colormap.cpp
// Equals the colormap "deepgreen".
class DeepGreen : public ColorMap {
public:
DeepGreen() : ColorMap() {
init(256);
}
DeepGreen(int n) : ColorMap() {
init(n);
}
void init(int n) {
static const float r[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904762f, 0.09523809523809523f, 0.1428571428571428f, 0.1904761904761905f, 0.2380952380952381f, 0.2857142857142857f, 0.3333333333333333f, 0.3809523809523809f, 0.4285714285714285f, 0.4761904761904762f, 0.5238095238095238f, 0.5714285714285714f, 0.6190476190476191f, 0.6666666666666666f, 0.7142857142857143f, 0.7619047619047619f, 0.8095238095238095f, 0.8571428571428571f, 0.9047619047619048f, 0.9523809523809523f, 1 };
static const float g[] = { 0, 0.01587301587301587f, 0.03174603174603174f, 0.04761904761904762f, 0.06349206349206349f, 0.07936507936507936f, 0.09523809523809523f, 0.1111111111111111f, 0.126984126984127f, 0.1428571428571428f, 0.1587301587301587f, 0.1746031746031746f, 0.1904761904761905f, 0.2063492063492063f, 0.2222222222222222f, 0.2380952380952381f, 0.253968253968254f, 0.2698412698412698f, 0.2857142857142857f, 0.3015873015873016f, 0.3174603174603174f, 0.3333333333333333f, 0.3492063492063492f, 0.3650793650793651f, 0.3809523809523809f, 0.3968253968253968f, 0.4126984126984127f, 0.4285714285714285f, 0.4444444444444444f, 0.4603174603174603f, 0.4761904761904762f, 0.492063492063492f, 0.5079365079365079f, 0.5238095238095238f, 0.5396825396825397f, 0.5555555555555556f, 0.5714285714285714f, 0.5873015873015873f, 0.6031746031746031f, 0.6190476190476191f, 0.6349206349206349f, 0.6507936507936508f, 0.6666666666666666f, 0.6825396825396826f, 0.6984126984126984f, 0.7142857142857143f, 0.7301587301587301f, 0.746031746031746f, 0.7619047619047619f, 0.7777777777777778f, 0.7936507936507936f, 0.8095238095238095f, 0.8253968253968254f, 0.8412698412698413f, 0.8571428571428571f, 0.873015873015873f, 0.8888888888888888f, 0.9047619047619048f, 0.9206349206349206f, 0.9365079365079365f, 0.9523809523809523f, 0.9682539682539683f, 0.9841269841269841f, 1 };
static const float b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02380952380952381f, 0.04761904761904762f, 0.07142857142857142f, 0.09523809523809523f, 0.119047619047619f, 0.1428571428571428f, 0.1666666666666667f, 0.1904761904761905f, 0.2142857142857143f, 0.2380952380952381f, 0.2619047619047619f, 0.2857142857142857f, 0.3095238095238095f, 0.3333333333333333f, 0.3571428571428572f, 0.3809523809523809f, 0.4047619047619048f, 0.4285714285714285f, 0.4523809523809524f, 0.4761904761904762f, 0.5f, 0.5238095238095238f, 0.5476190476190477f, 0.5714285714285714f, 0.5952380952380952f, 0.6190476190476191f, 0.6428571428571429f, 0.6666666666666666f, 0.6904761904761905f, 0.7142857142857143f, 0.7380952380952381f, 0.7619047619047619f, 0.7857142857142857f, 0.8095238095238095f, 0.8333333333333334f, 0.8571428571428571f, 0.8809523809523809f, 0.9047619047619048f, 0.9285714285714286f, 0.9523809523809523f, 0.9761904761904762f, 1 };
Mat X = linspace(0, 1, 64);
this->_lut = ColorMap::linear_colormap(X,
Mat(64, 1, CV_32FC1, (void*)r).clone(), // red
Mat(64, 1, CV_32FC1, (void*)g).clone(), // green
Mat(64, 1, CV_32FC1, (void*)b).clone(), // blue
n); // number of sample points
}
};
以及
void applyColorMap(InputArray src, OutputArray dst, int colormap)
{
colormap::ColorMap* cm =
colormap == COLORMAP_AUTUMN ? (colormap::ColorMap*)(new colormap::Autumn) :
colormap == COLORMAP_BONE ? (colormap::ColorMap*)(new colormap::Bone) :
colormap == COLORMAP_CIVIDIS ? (colormap::ColorMap*)(new colormap::Cividis) :
colormap == COLORMAP_COOL ? (colormap::ColorMap*)(new colormap::Cool) :
colormap == COLORMAP_HOT ? (colormap::ColorMap*)(new colormap::Hot) :
colormap == COLORMAP_HSV ? (colormap::ColorMap*)(new colormap::HSV) :
colormap == COLORMAP_INFERNO ? (colormap::ColorMap*)(new colormap::Inferno) :
colormap == COLORMAP_JET ? (colormap::ColorMap*)(new colormap::Jet) :
colormap == COLORMAP_MAGMA ? (colormap::ColorMap*)(new colormap::Magma) :
colormap == COLORMAP_OCEAN ? (colormap::ColorMap*)(new colormap::Ocean) :
colormap == COLORMAP_PARULA ? (colormap::ColorMap*)(new colormap::Parula) :
colormap == COLORMAP_PINK ? (colormap::ColorMap*)(new colormap::Pink) :
colormap == COLORMAP_PLASMA ? (colormap::ColorMap*)(new colormap::Plasma) :
colormap == COLORMAP_RAINBOW ? (colormap::ColorMap*)(new colormap::Rainbow) :
colormap == COLORMAP_SPRING ? (colormap::ColorMap*)(new colormap::Spring) :
colormap == COLORMAP_SUMMER ? (colormap::ColorMap*)(new colormap::Summer) :
colormap == COLORMAP_TURBO ? (colormap::ColorMap*)(new colormap::Turbo) :
colormap == COLORMAP_TWILIGHT ? (colormap::ColorMap*)(new colormap::Twilight) :
colormap == COLORMAP_TWILIGHT_SHIFTED ? (colormap::ColorMap*)(new colormap::TwilightShifted) :
colormap == COLORMAP_VIRIDIS ? (colormap::ColorMap*)(new colormap::Viridis) :
colormap == COLORMAP_DEEPGREEN ? (colormap::ColorMap*)(new colormap::DeepGreen) :
colormap == COLORMAP_WINTER ? (colormap::ColorMap*)(new colormap::Winter) : 0;
这里的几个修改,都是比较简单的。其中注意不能有中文(包括注释,不符合编码习惯)
重新生成的时候,生成install就可以。注意lib/dll/include都需要使用最新的
4、vs调用的配置使用。(三个地方,分别对应dll include lib)
5、码云有一个“强制同步”这个是可以用的,主要用于维护自己的库和远程下载,我们可以将GITHUB上的库clone到码云上下载,一般速度较快。
此外,很多常见库,码云官方也都提供了下载。
附录6,实现成功autobot的要点
注意!一定要采用特征分支!比如我这里
是jsxyhelu:add_deepgreen_colormap 向opencv:3.4中进行pr。在网站中创建特征分支的方法是
直接在这里输入你的新bransh名称。
一、基本情况
cv::applyColorMap()能够实现预定义的伪彩色,这个是众所周知的事情。
除了这些预置的变换,如果我想实现新的变换,一般的方法是做LUT变换
cv::Mat image_gray_3c;
//单通道的灰度图,转换成R、G、B三通道值均相等的三通道图
cv::cvtColor(image_gray, image_gray_3c, cv::COLOR_GRAY2RGB);
//opencv默认的颜色排列顺序是BGR,而这里自定义的colormap的顺序是RGB
cv::cvtColor(golden_map, golden_map, cv::COLOR_BGR2RGB);
cv::Mat image_color;
cv::LUT(image_gray_3c, golden_map, image_color);
但是,这段代码只是给调用方法,没有具体说明这里的LUT色板是如何构建的,你如果想做这个调色板还是需要自己找。我在做一个实际项目的时候,甲方就反馈,希望提供一种个比较深的绿色,由此开启整个关于applyColorMap()的探索。
二、参考OpenCV的代码进行修改,达到目的
能够找到重要的参考,主要就是OpenCV自己的代码:colormap.cpp
截取其中一个colormap的实现,比如"ocean"
class Ocean : public ColorMap {
public:
Ocean() : ColorMap() {
init(256);
}
Ocean(int n) : ColorMap() {
init(n);
}
void init(int n) {
static const float r[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904762f, 0.09523809523809523f, 0.1428571428571428f, 0.1904761904761905f, 0.2380952380952381f, 0.2857142857142857f, 0.3333333333333333f, 0.3809523809523809f, 0.4285714285714285f, 0.4761904761904762f, 0.5238095238095238f, 0.5714285714285714f, 0.6190476190476191f, 0.6666666666666666f, 0.7142857142857143f, 0.7619047619047619f, 0.8095238095238095f, 0.8571428571428571f, 0.9047619047619048f, 0.9523809523809523f, 1};
static const float g[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02380952380952381f, 0.04761904761904762f, 0.07142857142857142f, 0.09523809523809523f, 0.119047619047619f, 0.1428571428571428f, 0.1666666666666667f, 0.1904761904761905f, 0.2142857142857143f, 0.2380952380952381f, 0.2619047619047619f, 0.2857142857142857f, 0.3095238095238095f, 0.3333333333333333f, 0.3571428571428572f, 0.3809523809523809f, 0.4047619047619048f, 0.4285714285714285f, 0.4523809523809524f, 0.4761904761904762f, 0.5f, 0.5238095238095238f, 0.5476190476190477f, 0.5714285714285714f, 0.5952380952380952f, 0.6190476190476191f, 0.6428571428571429f, 0.6666666666666666f, 0.6904761904761905f, 0.7142857142857143f, 0.7380952380952381f, 0.7619047619047619f, 0.7857142857142857f, 0.8095238095238095f, 0.8333333333333334f, 0.8571428571428571f, 0.8809523809523809f, 0.9047619047619048f, 0.9285714285714286f, 0.9523809523809523f, 0.9761904761904762f, 1};
static const float b[] = { 0, 0.01587301587301587f, 0.03174603174603174f, 0.04761904761904762f, 0.06349206349206349f, 0.07936507936507936f, 0.09523809523809523f, 0.1111111111111111f, 0.126984126984127f, 0.1428571428571428f, 0.1587301587301587f, 0.1746031746031746f, 0.1904761904761905f, 0.2063492063492063f, 0.2222222222222222f, 0.2380952380952381f, 0.253968253968254f, 0.2698412698412698f, 0.2857142857142857f, 0.3015873015873016f, 0.3174603174603174f, 0.3333333333333333f, 0.3492063492063492f, 0.3650793650793651f, 0.3809523809523809f, 0.3968253968253968f, 0.4126984126984127f, 0.4285714285714285f, 0.4444444444444444f, 0.4603174603174603f, 0.4761904761904762f, 0.492063492063492f, 0.5079365079365079f, 0.5238095238095238f, 0.5396825396825397f, 0.5555555555555556f, 0.5714285714285714f, 0.5873015873015873f, 0.6031746031746031f, 0.6190476190476191f, 0.6349206349206349f, 0.6507936507936508f, 0.6666666666666666f, 0.6825396825396826f, 0.6984126984126984f, 0.7142857142857143f, 0.7301587301587301f, 0.746031746031746f, 0.7619047619047619f, 0.7777777777777778f, 0.7936507936507936f, 0.8095238095238095f, 0.8253968253968254f, 0.8412698412698413f, 0.8571428571428571f, 0.873015873015873f, 0.8888888888888888f, 0.9047619047619048f, 0.9206349206349206f, 0.9365079365079365f, 0.9523809523809523f, 0.9682539682539683f, 0.9841269841269841f, 1};
Mat X = linspace(0,1,64);
this->_lut = ColorMap::linear_colormap(X,
Mat(64,1, CV_32FC1, (void*)r).clone(), // red
Mat(64,1, CV_32FC1, (void*)g).clone(), // green
Mat(64,1, CV_32FC1, (void*)b).clone(), // blue
n); // number of sample points
}
};
明显这个colormap中最主要的成分就是rgb的大矩阵,它返回的结果是LUT。关键问题是这样的矩阵如何获得?想搞懂这里的文档,需要特定的基础知识。此外,我们深入研究的话,就可以发现这里OpenCV实现的不仅仅是LUT,还有其它很多东西。比如3通道,比如插值等。为了实现这些功能,它添加了很多函数,如果想把这些函数集成过来,可能会花费较多精力。反之,我认为更直接可行的方法,就是修改现有的OpenCV代码,重新生成dll文件。
为了实现甲方要求,我套用ocean的色彩对summer进行修改,其中只是修改了一个地方,那就是将ocean中的blue通道和green通道进行了替换。
从而将绿色的成分更多的凸显出来。修改的代码通过Git的版本控制,可以比较明显地看出来(附录3).
在下图的例子中,可以发现已经正确调用,并且COLORMAP_DEEPGREEN这个常量也是自定义的。
这里实现的效果很好。但是时间长了,麻烦就来了,随着OpenCV的不断升级,后面的版本可能都需要做同样的修改。我也开始寻找相关解决方法。
一种方法,我可以构建自己专用OpenCV,这个版本的OpenCV中应该有我自己的代码,也能够更新官网代码。这样每次大的升级,我只需要重新编译一下就可以了;另一种方法,如果我能够将这段代码并到OpenCV中,那是最好的,我用起来方便、别人也可以用得着。
除了这些预置的变换,如果我想实现新的变换,一般的方法是做LUT变换
cv::Mat image_gray_3c;
//单通道的灰度图,转换成R、G、B三通道值均相等的三通道图
cv::cvtColor(image_gray, image_gray_3c, cv::COLOR_GRAY2RGB);
//opencv默认的颜色排列顺序是BGR,而这里自定义的colormap的顺序是RGB
cv::cvtColor(golden_map, golden_map, cv::COLOR_BGR2RGB);
cv::Mat image_color;
cv::LUT(image_gray_3c, golden_map, image_color);
但是,这段代码只是给调用方法,没有具体说明这里的LUT色板是如何构建的,你如果想做这个调色板还是需要自己找。我在做一个实际项目的时候,甲方就反馈,希望提供一种个比较深的绿色,由此开启整个关于applyColorMap()的探索。
二、参考OpenCV的代码进行修改,达到目的
能够找到重要的参考,主要就是OpenCV自己的代码:colormap.cpp
截取其中一个colormap的实现,比如"ocean"
class Ocean : public ColorMap {
public:
Ocean() : ColorMap() {
init(256);
}
Ocean(int n) : ColorMap() {
init(n);
}
void init(int n) {
static const float r[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904762f, 0.09523809523809523f, 0.1428571428571428f, 0.1904761904761905f, 0.2380952380952381f, 0.2857142857142857f, 0.3333333333333333f, 0.3809523809523809f, 0.4285714285714285f, 0.4761904761904762f, 0.5238095238095238f, 0.5714285714285714f, 0.6190476190476191f, 0.6666666666666666f, 0.7142857142857143f, 0.7619047619047619f, 0.8095238095238095f, 0.8571428571428571f, 0.9047619047619048f, 0.9523809523809523f, 1};
static const float g[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02380952380952381f, 0.04761904761904762f, 0.07142857142857142f, 0.09523809523809523f, 0.119047619047619f, 0.1428571428571428f, 0.1666666666666667f, 0.1904761904761905f, 0.2142857142857143f, 0.2380952380952381f, 0.2619047619047619f, 0.2857142857142857f, 0.3095238095238095f, 0.3333333333333333f, 0.3571428571428572f, 0.3809523809523809f, 0.4047619047619048f, 0.4285714285714285f, 0.4523809523809524f, 0.4761904761904762f, 0.5f, 0.5238095238095238f, 0.5476190476190477f, 0.5714285714285714f, 0.5952380952380952f, 0.6190476190476191f, 0.6428571428571429f, 0.6666666666666666f, 0.6904761904761905f, 0.7142857142857143f, 0.7380952380952381f, 0.7619047619047619f, 0.7857142857142857f, 0.8095238095238095f, 0.8333333333333334f, 0.8571428571428571f, 0.8809523809523809f, 0.9047619047619048f, 0.9285714285714286f, 0.9523809523809523f, 0.9761904761904762f, 1};
static const float b[] = { 0, 0.01587301587301587f, 0.03174603174603174f, 0.04761904761904762f, 0.06349206349206349f, 0.07936507936507936f, 0.09523809523809523f, 0.1111111111111111f, 0.126984126984127f, 0.1428571428571428f, 0.1587301587301587f, 0.1746031746031746f, 0.1904761904761905f, 0.2063492063492063f, 0.2222222222222222f, 0.2380952380952381f, 0.253968253968254f, 0.2698412698412698f, 0.2857142857142857f, 0.3015873015873016f, 0.3174603174603174f, 0.3333333333333333f, 0.3492063492063492f, 0.3650793650793651f, 0.3809523809523809f, 0.3968253968253968f, 0.4126984126984127f, 0.4285714285714285f, 0.4444444444444444f, 0.4603174603174603f, 0.4761904761904762f, 0.492063492063492f, 0.5079365079365079f, 0.5238095238095238f, 0.5396825396825397f, 0.5555555555555556f, 0.5714285714285714f, 0.5873015873015873f, 0.6031746031746031f, 0.6190476190476191f, 0.6349206349206349f, 0.6507936507936508f, 0.6666666666666666f, 0.6825396825396826f, 0.6984126984126984f, 0.7142857142857143f, 0.7301587301587301f, 0.746031746031746f, 0.7619047619047619f, 0.7777777777777778f, 0.7936507936507936f, 0.8095238095238095f, 0.8253968253968254f, 0.8412698412698413f, 0.8571428571428571f, 0.873015873015873f, 0.8888888888888888f, 0.9047619047619048f, 0.9206349206349206f, 0.9365079365079365f, 0.9523809523809523f, 0.9682539682539683f, 0.9841269841269841f, 1};
Mat X = linspace(0,1,64);
this->_lut = ColorMap::linear_colormap(X,
Mat(64,1, CV_32FC1, (void*)r).clone(), // red
Mat(64,1, CV_32FC1, (void*)g).clone(), // green
Mat(64,1, CV_32FC1, (void*)b).clone(), // blue
n); // number of sample points
}
};
明显这个colormap中最主要的成分就是rgb的大矩阵,它返回的结果是LUT。关键问题是这样的矩阵如何获得?想搞懂这里的文档,需要特定的基础知识。此外,我们深入研究的话,就可以发现这里OpenCV实现的不仅仅是LUT,还有其它很多东西。比如3通道,比如插值等。为了实现这些功能,它添加了很多函数,如果想把这些函数集成过来,可能会花费较多精力。反之,我认为更直接可行的方法,就是修改现有的OpenCV代码,重新生成dll文件。
为了实现甲方要求,我套用ocean的色彩对summer进行修改,其中只是修改了一个地方,那就是将ocean中的blue通道和green通道进行了替换。
从而将绿色的成分更多的凸显出来。修改的代码通过Git的版本控制,可以比较明显地看出来(附录3).
在下图的例子中,可以发现已经正确调用,并且COLORMAP_DEEPGREEN这个常量也是自定义的。
这里实现的效果很好。但是时间长了,麻烦就来了,随着OpenCV的不断升级,后面的版本可能都需要做同样的修改。我也开始寻找相关解决方法。
一种方法,我可以构建自己专用OpenCV,这个版本的OpenCV中应该有我自己的代码,也能够更新官网代码。这样每次大的升级,我只需要重新编译一下就可以了;另一种方法,如果我能够将这段代码并到OpenCV中,那是最好的,我用起来方便、别人也可以用得着。
三、构建自己专用OpenCV
如果是自用的话,我建议使用Gitee。其主要原因是由于github的网络限制,所以直接通过其下载代码容易出现各种错误。
Gitee的一大特设,就是可以直接创建github上opencv的clone。(附录5)
而后我们可以将gitee上代码git到本地,修改,编译。
https模式可以直接clone,如果是ssh需要配密钥。通过gitee下载,我能够达到1-3m左右的速度。我们编译生成它,注意结果地址。(可以参考《记录一次关于OpenCV的CmakeLists的探索》https://www.cnblogs.com/jsxyhelu/p/12843005.html)
确保能够正确调用后,我们需要将这里的代码提交到gitee上去。
能够看到,这里的modules由于我们的修改,已经发生了变化。
由于gitee上的这个库是我们自己控制的,直接push上去,过程中需要输gitee的密码。
能够看到这次提交的对比。这样我们的代码就保存下来了。
但是同时需要注意的是,Gitee上的OpenCV是和原始库“脱轨”的,它除了一个“强制刷新”其它什么都做不了。而GitHub非常强的一个功能,是提供下图这个"behind".
四、将自己的代码并到OpenCV中(pull request)
pull request才是开源的本质,好的代码一定要融合到基库中,如果你的代码不好就融合不进去,通过这个过程中,我们才能够写出“正确、清晰、有弹性"的优秀代码。抱着”试试看“的态度,我将这里的修改提交GitHub。(由于网速问题,我基本上都是在网站上手工修改)
并且借助工具,填写英文文档。
睡了一觉以后,发现opencv member 给出了许多反馈,整理一下
1、如何使用github的方法,甚至给出了具体命令
git checkout -b feat_deep_green_colormap
2、提示同时提供文档,并且给出链接
https://github.com/opencv/opencv/pull/15388
3、给出提交代码和需要解决的问题提出相关建议,并且直指问题核心!
https://matplotlib.org/tutorials/colors/colormaps.html
4、提出代码建议:建议按照字母顺序进行排列,并且要求提供注释
5、给出了规范的提交,并且是非常对口的。
这个是非常激动人心的,只要你是认认真真地去提交代码,就会有优秀的工程师教你如何做对,比如这里给出了非常对口的指导:
This pullrequest changesI implemented the colormap "Turbo" proposed by Google.
And, I add
|
它来自于google的一篇研究,并且提供了参考代码
大家看最后我的提交,基本上就是参考的#15338,以后很多地方都可以继续参考。GitHub还有一项非常厉害的功能,就是AutoBoot(附录6),以及《OpenCV PR 成功的收获和感悟》https://www.cnblogs.com/jsxyhelu/p/9638409.html。
五、反思小结
现在看来,想充分使用git的功能,必须满足:
1、持续更新;
2、网络通畅;
3、本地部署。
一方面,如果希望有一个自己能够使用的专用库,这个库一定要来自实际需求,如果可以。我希望能够经过autobot检验,如果不可以,至少要是本地能够编译的;
一方面,如果下次遇到类似的问题,或者马上提出新的问题,将其提交到opencv绝对是最好的选择。成功的提交,才能够给出最多的价值体验。
使用git而不是直接zip下载的方式来使用opencv代码,才是正确方式。
为opencv贡献代码是非常好的学习git和github的途径,这是其它场所无法提供的学习方式。
感谢阅读至此,希望有所帮助。
3、我具体修改了什么代码?
imgproc.hpp
enum ColormapTypes
{
COLORMAP_AUTUMN = 0, //!< ![autumn](pics/colormaps/colorscale_autumn.jpg)
COLORMAP_BONE = 1, //!< ![bone](pics/colormaps/colorscale_bone.jpg)
COLORMAP_JET = 2, //!< ![jet](pics/colormaps/colorscale_jet.jpg)
COLORMAP_WINTER = 3, //!< ![winter](pics/colormaps/colorscale_winter.jpg)
COLORMAP_RAINBOW = 4, //!< ![rainbow](pics/colormaps/colorscale_rainbow.jpg)
COLORMAP_OCEAN = 5, //!< ![ocean](pics/colormaps/colorscale_ocean.jpg)
COLORMAP_SUMMER = 6, //!< ![summer](pics/colormaps/colorscale_summer.jpg)
COLORMAP_SPRING = 7, //!< ![spring](pics/colormaps/colorscale_spring.jpg)
COLORMAP_COOL = 8, //!< ![cool](pics/colormaps/colorscale_cool.jpg)
COLORMAP_HSV = 9, //!< ![HSV](pics/colormaps/colorscale_hsv.jpg)
COLORMAP_PINK = 10, //!< ![pink](pics/colormaps/colorscale_pink.jpg)
COLORMAP_HOT = 11, //!< ![hot](pics/colormaps/colorscale_hot.jpg)
COLORMAP_PARULA = 12, //!< ![parula](pics/colormaps/colorscale_parula.jpg)
COLORMAP_MAGMA = 13, //!< ![magma](pics/colormaps/colorscale_magma.jpg)
COLORMAP_INFERNO = 14, //!< ![inferno](pics/colormaps/colorscale_inferno.jpg)
COLORMAP_PLASMA = 15, //!< ![plasma](pics/colormaps/colorscale_plasma.jpg)
COLORMAP_VIRIDIS = 16, //!< ![viridis](pics/colormaps/colorscale_viridis.jpg)
COLORMAP_CIVIDIS = 17, //!< ![cividis](pics/colormaps/colorscale_cividis.jpg)
COLORMAP_TWILIGHT = 18, //!< ![twilight](pics/colormaps/colorscale_twilight.jpg)
COLORMAP_TWILIGHT_SHIFTED = 19, //!< ![twilight shifted](pics/colormaps/colorscale_twilight_shifted.jpg)
COLORMAP_TURBO = 20 ,//!< ![turbo](pics/colormaps/colorscale_turbo.jpg)
COLORMAP_DEEPGREEN = 21 //jsxyhelu 2020年5月9日
};
colormap.cpp
// Equals the colormap "deepgreen".
class DeepGreen : public ColorMap {
public:
DeepGreen() : ColorMap() {
init(256);
}
DeepGreen(int n) : ColorMap() {
init(n);
}
void init(int n) {
static const float r[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.04761904761904762f, 0.09523809523809523f, 0.1428571428571428f, 0.1904761904761905f, 0.2380952380952381f, 0.2857142857142857f, 0.3333333333333333f, 0.3809523809523809f, 0.4285714285714285f, 0.4761904761904762f, 0.5238095238095238f, 0.5714285714285714f, 0.6190476190476191f, 0.6666666666666666f, 0.7142857142857143f, 0.7619047619047619f, 0.8095238095238095f, 0.8571428571428571f, 0.9047619047619048f, 0.9523809523809523f, 1 };
static const float g[] = { 0, 0.01587301587301587f, 0.03174603174603174f, 0.04761904761904762f, 0.06349206349206349f, 0.07936507936507936f, 0.09523809523809523f, 0.1111111111111111f, 0.126984126984127f, 0.1428571428571428f, 0.1587301587301587f, 0.1746031746031746f, 0.1904761904761905f, 0.2063492063492063f, 0.2222222222222222f, 0.2380952380952381f, 0.253968253968254f, 0.2698412698412698f, 0.2857142857142857f, 0.3015873015873016f, 0.3174603174603174f, 0.3333333333333333f, 0.3492063492063492f, 0.3650793650793651f, 0.3809523809523809f, 0.3968253968253968f, 0.4126984126984127f, 0.4285714285714285f, 0.4444444444444444f, 0.4603174603174603f, 0.4761904761904762f, 0.492063492063492f, 0.5079365079365079f, 0.5238095238095238f, 0.5396825396825397f, 0.5555555555555556f, 0.5714285714285714f, 0.5873015873015873f, 0.6031746031746031f, 0.6190476190476191f, 0.6349206349206349f, 0.6507936507936508f, 0.6666666666666666f, 0.6825396825396826f, 0.6984126984126984f, 0.7142857142857143f, 0.7301587301587301f, 0.746031746031746f, 0.7619047619047619f, 0.7777777777777778f, 0.7936507936507936f, 0.8095238095238095f, 0.8253968253968254f, 0.8412698412698413f, 0.8571428571428571f, 0.873015873015873f, 0.8888888888888888f, 0.9047619047619048f, 0.9206349206349206f, 0.9365079365079365f, 0.9523809523809523f, 0.9682539682539683f, 0.9841269841269841f, 1 };
static const float b[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.02380952380952381f, 0.04761904761904762f, 0.07142857142857142f, 0.09523809523809523f, 0.119047619047619f, 0.1428571428571428f, 0.1666666666666667f, 0.1904761904761905f, 0.2142857142857143f, 0.2380952380952381f, 0.2619047619047619f, 0.2857142857142857f, 0.3095238095238095f, 0.3333333333333333f, 0.3571428571428572f, 0.3809523809523809f, 0.4047619047619048f, 0.4285714285714285f, 0.4523809523809524f, 0.4761904761904762f, 0.5f, 0.5238095238095238f, 0.5476190476190477f, 0.5714285714285714f, 0.5952380952380952f, 0.6190476190476191f, 0.6428571428571429f, 0.6666666666666666f, 0.6904761904761905f, 0.7142857142857143f, 0.7380952380952381f, 0.7619047619047619f, 0.7857142857142857f, 0.8095238095238095f, 0.8333333333333334f, 0.8571428571428571f, 0.8809523809523809f, 0.9047619047619048f, 0.9285714285714286f, 0.9523809523809523f, 0.9761904761904762f, 1 };
Mat X = linspace(0, 1, 64);
this->_lut = ColorMap::linear_colormap(X,
Mat(64, 1, CV_32FC1, (void*)r).clone(), // red
Mat(64, 1, CV_32FC1, (void*)g).clone(), // green
Mat(64, 1, CV_32FC1, (void*)b).clone(), // blue
n); // number of sample points
}
};
以及
void applyColorMap(InputArray src, OutputArray dst, int colormap)
{
colormap::ColorMap* cm =
colormap == COLORMAP_AUTUMN ? (colormap::ColorMap*)(new colormap::Autumn) :
colormap == COLORMAP_BONE ? (colormap::ColorMap*)(new colormap::Bone) :
colormap == COLORMAP_CIVIDIS ? (colormap::ColorMap*)(new colormap::Cividis) :
colormap == COLORMAP_COOL ? (colormap::ColorMap*)(new colormap::Cool) :
colormap == COLORMAP_HOT ? (colormap::ColorMap*)(new colormap::Hot) :
colormap == COLORMAP_HSV ? (colormap::ColorMap*)(new colormap::HSV) :
colormap == COLORMAP_INFERNO ? (colormap::ColorMap*)(new colormap::Inferno) :
colormap == COLORMAP_JET ? (colormap::ColorMap*)(new colormap::Jet) :
colormap == COLORMAP_MAGMA ? (colormap::ColorMap*)(new colormap::Magma) :
colormap == COLORMAP_OCEAN ? (colormap::ColorMap*)(new colormap::Ocean) :
colormap == COLORMAP_PARULA ? (colormap::ColorMap*)(new colormap::Parula) :
colormap == COLORMAP_PINK ? (colormap::ColorMap*)(new colormap::Pink) :
colormap == COLORMAP_PLASMA ? (colormap::ColorMap*)(new colormap::Plasma) :
colormap == COLORMAP_RAINBOW ? (colormap::ColorMap*)(new colormap::Rainbow) :
colormap == COLORMAP_SPRING ? (colormap::ColorMap*)(new colormap::Spring) :
colormap == COLORMAP_SUMMER ? (colormap::ColorMap*)(new colormap::Summer) :
colormap == COLORMAP_TURBO ? (colormap::ColorMap*)(new colormap::Turbo) :
colormap == COLORMAP_TWILIGHT ? (colormap::ColorMap*)(new colormap::Twilight) :
colormap == COLORMAP_TWILIGHT_SHIFTED ? (colormap::ColorMap*)(new colormap::TwilightShifted) :
colormap == COLORMAP_VIRIDIS ? (colormap::ColorMap*)(new colormap::Viridis) :
colormap == COLORMAP_DEEPGREEN ? (colormap::ColorMap*)(new colormap::DeepGreen) :
colormap == COLORMAP_WINTER ? (colormap::ColorMap*)(new colormap::Winter) : 0;
这里的几个修改,都是比较简单的。其中注意不能有中文(包括注释,不符合编码习惯)
重新生成的时候,生成install就可以。注意lib/dll/include都需要使用最新的
4、vs调用的配置使用。(三个地方,分别对应dll include lib)
注意!一定要采用特征分支!比如我这里
是jsxyhelu:add_deepgreen_colormap 向opencv:3.4中进行pr。在网站中创建特征分支的方法是
直接在这里输入你的新bransh名称。
atinfinity commentedon 24 Aug 2019 ?
edited