構建自己的專用OpenCV----記錄一次由applyColorMap()引發的探索,本文提及代碼已經被OpenCV接受


   在編寫實際項目的過程中,我需要實現綠色主題的“偽彩色”變換。在目前提供的模板中,只有summer最為接近,但是它的顏色太淺了,看上去不是很清晰。所以我結合ocean和summer兩種現有模板,構建了deepgreen這個模板。它能夠實現綠色主題的顯著的偽彩色變換。本文記錄了我發現問題、分析問題、編寫代碼、為OpenCV提供pull request的全過程。希望能夠為有相關需求的工程師提供幫助。信息量比較大,表述不清楚的地方歡迎討論。

   本文地址:https://www.cnblogs.com/jsxyhelu/p/12873962.html

   截至發文,相關代碼已經被接受。具體可以看  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".
這個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、給出了規范的提交,並且是非常對口的。
 
這個是非常激動人心的,只要你是認認真真地去提交代碼,就會有優秀的工程師教你如何做對,比如這里給出了非常對口的指導:
 

atinfinity commented on 24 Aug 2019  

edited 

This pullrequest changes

I implemented the colormap "Turbo" proposed by Google.

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( 25630), 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;
}

colorscale_turbo

 
它來自於google的一篇研究,並且提供了參考代碼
大家看最后我的提交,基本上就是參考的#15338,以后很多地方都可以繼續參考。GitHub還有一項非常厲害的功能,就是AutoBoot(附錄6),以及《 OpenCV PR 成功的收獲和感悟》https://www.cnblogs.com/jsxyhelu/p/9638409.html
 
 
 
Contributor

asmorkalov commented 1 hour ago

五、反思小結
現在看來, 想充分使用git的功能,必須滿足:
1、持續更新;
2、網絡通暢;
3、本地部署。
 
一方面,如果希望有一個自己能夠使用的專用庫,這個庫一定要來自實際需求,如果可以。我希望能夠經過autobot檢驗,如果不可以,至少要是本地能夠編譯的;
一方面,如果下次遇到類似的問題,或者馬上提出新的問題,將其提交到opencv絕對是最好的選擇。成功的提交,才能夠給出最多的價值體驗。
 
這是我第二次為OpenCV提供代碼,從我的經驗來看:
1、使用git而不是直接zip下載的方式來使用opencv代碼,才是正確方式。但是也必須靈活使用現有工具和資源;
2、為opencv貢獻代碼是非常好的學習git和opencv的最佳途徑,這是其它場所無法提供的學習方式。

 
感謝閱讀至此,希望有所幫助。jsxyhelu 2020年5月13日06:41:41
================================ 附錄 ======================================== 附錄 ==============================================
關於這個問題的其它嘗試和一些相關的內容,放在附錄里面,有空可以看看:
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 srcOutputArray dstint colormap)
    {
        colormap::ColorMapcm =
            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名稱。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM