【OpenCV入門教程之九】 非線性濾波專場:中值濾波、雙邊濾波

二、深入——OpenCV源碼分析溯源

首先讓我們一起領略medianBlur()函數的源碼,其於…opencvsourcesmodulesimgprocsrcsmooth.cpp的第1653行開始。

[cpp] view plain copy

  1. //-----------------------------------【medianBlur()函數中文注釋版源代碼】-------------------------
  2. //代碼作用:進行中值濾波操作的函數
  3. //說明:以下代碼為來自於計算機開源視覺庫OpenCV的官方源代碼
  4. //OpenCV源代碼版本:2.4.8
  5. //源碼路徑:…opencvsourcesmodulesimgprocsrcsmooth.cpp
  6. //源文件中如下代碼的起始行數:1653行
  7. //中文注釋by淺墨
  8. //--------------------------------------------------------------------------------------------------------
  9. voidcv::medianBlur(InputArray_src0,OutputArray_dst,intksize)
  10. {
  11. //拷貝形參Mat數據到臨時變數,用於稍後的操作
  12. Matsrc0=_src0.getMat();
  13. _dst.create(src0.size(),src0.type());
  14. Matdst=_dst.getMat();
  15. //處理特定的ksize值的不同情況
  16. if(ksize<=1)
  17. {
  18. src0.copyTo(dst);
  19. return;
  20. }
  21. CV_Assert(ksize%2==1);
  22. //若之前有過HAVE_TEGRA_OPTIMIZATION優化選項的定義,則執行宏體中的tegra優化版函數並返回
  23. #ifdefHAVE_TEGRA_OPTIMIZATION
  24. if(tegra::medianBlur(src0,dst,ksize))
  25. return;
  26. #endif
  27. booluseSortNet=ksize==3||(ksize==5
  28. #if!CV_SSE2//若CV_SSE2為假,則執行宏體中語句
  29. &&src0.depth()>CV_8U
  30. #endif
  31. );
  32. //開始正式進行濾波操作
  33. Matsrc;
  34. if(useSortNet)
  35. {
  36. if(dst.data!=src0.data)
  37. src=src0;
  38. else
  39. src0.copyTo(src);
  40. //根據不同的位深,給medianBlur_SortNet函數取不同的模板類型值
  41. if(src.depth()==CV_8U)
  42. medianBlur_SortNet<MinMax8u,MinMaxVec8u>(src,dst,ksize);
  43. elseif(src.depth()==CV_16U)
  44. medianBlur_SortNet<MinMax16u,MinMaxVec16u>(src,dst,ksize);
  45. elseif(src.depth()==CV_16S)
  46. medianBlur_SortNet<MinMax16s,MinMaxVec16s>(src,dst,ksize);
  47. elseif(src.depth()==CV_32F)
  48. medianBlur_SortNet<MinMax32f,MinMaxVec32f>(src,dst,ksize);
  49. else
  50. CV_Error(CV_StsUnsupportedFormat,"");
  51. return;
  52. }
  53. else
  54. {
  55. cv::copyMakeBorder(src0,src,0,0,ksize/2,ksize/2,BORDER_REPLICATE);
  56. intcn=src0.channels();
  57. CV_Assert(src.depth()==CV_8U&&(cn==1||cn==3||cn==4));
  58. doubleimg_size_mp=(double)(src0.total())/(1<<20);
  59. if(ksize<=3+(img_size_mp<1?12:img_size_mp<4?6:2)*(MEDIAN_HAVE_SIMD&&checkHardwareSupport(CV_CPU_SSE2)?1:3))
  60. medianBlur_8u_Om(src,dst,ksize);
  61. else
  62. medianBlur_8u_O1(src,dst,ksize);
  63. }
  64. }

仔細閱讀源碼我們可以發現,正式進入濾波操作時,根據圖像不同的位深,我們會給medianBlur_SortNet函數模板取不同的模板類型值,或者調用medianBlur_8u_Om或medianBlur_8u_O1來進行操作。

上面我們剛說到,medianBlur_SortNet 是一個函數模板,其源碼於smooth.cpp的1439行開始,由於其函數體很長,我們在此只貼出它的函數聲明。

[cpp] view plain copy

  1. template<classOp,classVecOp>
  2. staticvoidmedianBlur_SortNet(constMat&_src,Mat&_dst,intm);

另外,bilateralFilter函數的源碼也比較冗長,在D:Program Filesopencvsourcesmodulesimgprocsrcsmooth.cpp源碼文件中。

從1714行到2273行都是。我們在這裡只給出路徑,和一張概況圖,大家有興趣自己去看源代碼。

再提一點,smooth.cpp源碼的第2275行到2552行是OpenCV中自適應雙邊濾波器(adaptiveBilateralFilter)的源代碼,有興趣和精力的童鞋可以去探究探究。

三、淺出——API函數快速上手3.1中值濾波——medianBlur函數

medianBlur函數使用中值濾波器來平滑(模糊)處理一張圖片,從src輸入,而結果從dst輸出。

且對於多通道圖片,每一個通道都單獨進行處理,並且支持就地操作(In-placeoperation)。

[cpp] view plain copy

  1. C++:voidmedianBlur(InputArraysrc,OutputArraydst,intksize)

參數詳解:

  • 第一個參數,InputArray類型的src,函數的輸入參數,填1、3或者4通道的Mat類型的圖像;當ksize為3或者5的時候,圖像深度需為CV_8U,CV_16U,或CV_32F其中之一,而對於較大孔徑尺寸的圖片,它只能是CV_8U。
  • 第二個參數,OutputArray類型的dst,即目標圖像,函數的輸出參數,需要和源圖片有一樣的尺寸和類型。我們可以用Mat::Clone,以源圖片為模板,來初始化得到如假包換的目標圖。
  • 第三個參數,int類型的ksize,孔徑的線性尺寸(aperture linear size),注意這個參數必須是大於1的奇數,比如:3,5,7,9 ...
  • 調用範例:

    [cpp] view plain copy

    1. //載入原圖
    2. Matimage=imread("1.jpg");
    3. //進行中值濾波操作
    4. Matout;
    5. medianBlur(image,out,7);

    用上面三句核心代碼架起來的完整程序代碼:

    [cpp] view plain copy

    1. //-----------------------------------【程序說明】----------------------------------------------
    2. //說明:【中值濾波medianBlur函數的使用示常式序】
    3. //開發所用OpenCV版本:2.4.8
    4. //2014年4月3日Createby淺墨
    5. //------------------------------------------------------------------------------------------------
    6. //-----------------------------------【頭文件包含部分】---------------------------------------
    7. //描述:包含程序所依賴的頭文件
    8. //----------------------------------------------------------------------------------------------
    9. #include"opencv2/core/core.hpp"
    10. #include"opencv2/highgui/highgui.hpp"
    11. #include"opencv2/imgproc/imgproc.hpp"
    12. //-----------------------------------【命名空間聲明部分】---------------------------------------
    13. //描述:包含程序所使用的命名空間
    14. //-----------------------------------------------------------------------------------------------
    15. usingnamespacecv;
    16. //-----------------------------------【main()函數】--------------------------------------------
    17. //描述:控制台應用程序的入口函數,我們的程序從這裡開始
    18. //-----------------------------------------------------------------------------------------------
    19. intmain()
    20. {
    21. //載入原圖
    22. Matimage=imread("1.jpg");
    23. //創建窗口
    24. namedWindow("中值濾波【原圖】");
    25. namedWindow("中值濾波【效果圖】");
    26. //顯示原圖
    27. imshow("中值濾波【原圖】",image);
    28. //進行中值濾波操作
    29. Matout;
    30. medianBlur(image,out,7);
    31. //顯示效果圖
    32. imshow("中值濾波【效果圖】",out);
    33. waitKey(0);
    34. }

    運行效果圖(孔徑的線性尺寸為7):

    3.2 雙邊濾波——bilateralFilter函數

    用雙邊濾波器來處理一張圖片,由src輸入圖片,結果於dst輸出。

    [cpp] view plain copy

    1. C++:voidbilateralFilter(InputArraysrc,OutputArraydst,intd,doublesigmaColor,doublesigmaSpace,intborderType=BORDER_DEFAULT)
  • 第一個參數,InputArray類型的src,輸入圖像,即源圖像,需要為8位或者浮點型單通道、三通道的圖像。
  • 第二個參數,OutputArray類型的dst,即目標圖像,需要和源圖片有一樣的尺寸和類型。
  • 第三個參數,int類型的d,表示在過濾過程中每個像素鄰域的直徑。如果這個值我們設其為非正數,那麼OpenCV會從第五個參數sigmaSpace來計算出它來。
  • 第四個參數,double類型的sigmaColor,顏色空間濾波器的sigma值。這個參數的值越大,就表明該像素鄰域內有更寬廣的顏色會被混合到一起,產生較大的半相等顏色區域。
  • 第五個參數,double類型的sigmaSpace坐標空間中濾波器的sigma值,坐標空間的標註方差。他的數值越大,意味著越遠的像素會相互影響,從而使更大的區域足夠相似的顏色獲取相同的顏色。當d>0,d指定了鄰域大小且與sigmaSpace無關。否則,d正比於sigmaSpace。
  • 第六個參數,int類型的borderType,用於推斷圖像外部像素的某種邊界模式。注意它有默認值BORDER_DEFAULT。
  • 調用代碼示範如下:

    [cpp] view plain copy

    1. //載入原圖
    2. Matimage=imread("1.jpg");
    3. //進行雙邊濾波操作
    4. Matout;
    5. bilateralFilter(image,out,25,25*2,25/2);

    用一個完整的示常式序把bilateralFilter函數熟悉一下:

    [cpp] view plain copy

    1. //-----------------------------------【程序說明】----------------------------------------------
    2. //說明:【雙邊濾波bilateralFilter函數的使用示常式序】
    3. //開發所用OpenCV版本:2.4.8
    4. //2014年4月3日Createby淺墨
    5. //------------------------------------------------------------------------------------------------
    6. //-----------------------------------【頭文件包含部分】---------------------------------------
    7. //描述:包含程序所依賴的頭文件
    8. //----------------------------------------------------------------------------------------------
    9. #include"opencv2/core/core.hpp"
    10. #include"opencv2/highgui/highgui.hpp"
    11. #include"opencv2/imgproc/imgproc.hpp"
    12. //-----------------------------------【命名空間聲明部分】---------------------------------------
    13. //描述:包含程序所使用的命名空間
    14. //-----------------------------------------------------------------------------------------------
    15. usingnamespacecv;
    16. //-----------------------------------【main()函數】--------------------------------------------
    17. //描述:控制台應用程序的入口函數,我們的程序從這裡開始
    18. //-----------------------------------------------------------------------------------------------
    19. intmain()
    20. {
    21. //載入原圖
    22. Matimage=imread("1.jpg");
    23. //創建窗口
    24. namedWindow("雙邊濾波【原圖】");
    25. namedWindow("雙邊濾波【效果圖】");
    26. //顯示原圖
    27. imshow("雙邊濾波【原圖】",image);
    28. //進行雙邊濾波操作
    29. Matout;
    30. bilateralFilter(image,out,25,25*2,25/2);
    31. //顯示效果圖
    32. imshow("雙邊濾波【效果圖】",out);
    33. waitKey(0);
    34. }

    運行效果圖:

    四、綜合示例——在實戰中熟稔

    依然是每篇文章都會配給大家的一個詳細注釋的博文配套示常式序,把這篇文章中介紹的知識點以代碼為載體,展現給大家。

    這個示常式序中可以用軌跡條來控制各種濾波(方框濾波、均值濾波、高斯濾波、中值濾波、雙邊濾波)的參數值,通過滑動滾動條,就可以控制圖像在各種平滑處理下的模糊度,有一定的可玩性。廢話不多說,上代碼吧:

    [cpp] view plain copy

    1. //-----------------------------------【程序說明】----------------------------------------------
    2. //程序名稱::《【OpenCV入門教程之九】非線性濾波專場:中值濾波、雙邊濾波》博文配套源碼
    3. //開發所用IDE版本:VisualStudio2010
    4. //開發所用OpenCV版本:2.4.8
    5. //2014年4月8日Createby淺墨
    6. //------------------------------------------------------------------------------------------------
    7. //-----------------------------------【頭文件包含部分】---------------------------------------
    8. //描述:包含程序所依賴的頭文件
    9. //----------------------------------------------------------------------------------------------
    10. #include<opencv2/core/core.hpp>
    11. #include<opencv2/highgui/highgui.hpp>
    12. #include<opencv2/imgproc/imgproc.hpp>
    13. #include<iostream>
    14. //-----------------------------------【命名空間聲明部分】---------------------------------------
    15. //描述:包含程序所使用的命名空間
    16. //-----------------------------------------------------------------------------------------------
    17. usingnamespacestd;
    18. usingnamespacecv;
    19. //-----------------------------------【全局變數聲明部分】--------------------------------------
    20. //描述:全局變數聲明
    21. //-----------------------------------------------------------------------------------------------
    22. Matg_srcImage,g_dstImage1,g_dstImage2,g_dstImage3,g_dstImage4,g_dstImage5;
    23. intg_nBoxFilterValue=6;//方框濾波內核值
    24. intg_nMeanBlurValue=10;//均值濾波內核值
    25. intg_nGaussianBlurValue=6;//高斯濾波內核值
    26. intg_nMedianBlurValue=10;//中值濾波參數值
    27. intg_nBilateralFilterValue=10;//雙邊濾波參數值
    28. //-----------------------------------【全局函數聲明部分】--------------------------------------
    29. //描述:全局函數聲明
    30. //-----------------------------------------------------------------------------------------------
    31. //軌跡條回調函數
    32. staticvoidon_BoxFilter(int,void*);//方框濾波
    33. staticvoidon_MeanBlur(int,void*);//均值塊濾波器
    34. staticvoidon_GaussianBlur(int,void*);//高斯濾波器
    35. staticvoidon_MedianBlur(int,void*);//中值濾波器
    36. staticvoidon_BilateralFilter(int,void*);//雙邊濾波器
    37. //-----------------------------------【main()函數】--------------------------------------------
    38. //描述:控制台應用程序的入口函數,我們的程序從這裡開始
    39. //-----------------------------------------------------------------------------------------------
    40. intmain()
    41. {
    42. system("color5E");
    43. //載入原圖
    44. g_srcImage=imread("1.jpg",1);
    45. if(!g_srcImage.data){printf("Oh,no,讀取srcImage錯誤~!
      ");returnfalse;}
    46. //克隆原圖到四個Mat類型中
    47. g_dstImage1=g_srcImage.clone();
    48. g_dstImage2=g_srcImage.clone();
    49. g_dstImage3=g_srcImage.clone();
    50. g_dstImage4=g_srcImage.clone();
    51. g_dstImage5=g_srcImage.clone();
    52. //顯示原圖
    53. namedWindow("【<0>原圖窗口】",1);
    54. imshow("【<0>原圖窗口】",g_srcImage);
    55. //=================【<1>方框濾波】=========================
    56. //創建窗口
    57. namedWindow("【<1>方框濾波】",1);
    58. //創建軌跡條
    59. createTrackbar("內核值:","【<1>方框濾波】",&g_nBoxFilterValue,50,on_BoxFilter);
    60. on_MeanBlur(g_nBoxFilterValue,0);
    61. imshow("【<1>方框濾波】",g_dstImage1);
    62. //=====================================================
    63. //=================【<2>均值濾波】==========================
    64. //創建窗口
    65. namedWindow("【<2>均值濾波】",1);
    66. //創建軌跡條
    67. createTrackbar("內核值:","【<2>均值濾波】",&g_nMeanBlurValue,50,on_MeanBlur);
    68. on_MeanBlur(g_nMeanBlurValue,0);
    69. //======================================================
    70. //=================【<3>高斯濾波】===========================
    71. //創建窗口
    72. namedWindow("【<3>高斯濾波】",1);
    73. //創建軌跡條
    74. createTrackbar("內核值:","【<3>高斯濾波】",&g_nGaussianBlurValue,50,on_GaussianBlur);
    75. on_GaussianBlur(g_nGaussianBlurValue,0);
    76. //=======================================================
    77. //=================【<4>中值濾波】===========================
    78. //創建窗口
    79. namedWindow("【<4>中值濾波】",1);
    80. //創建軌跡條
    81. createTrackbar("參數值:","【<4>中值濾波】",&g_nMedianBlurValue,50,on_MedianBlur);
    82. on_MedianBlur(g_nMedianBlurValue,0);
    83. //=======================================================
    84. //=================【<5>雙邊濾波】===========================
    85. //創建窗口
    86. namedWindow("【<5>雙邊濾波】",1);
    87. //創建軌跡條
    88. createTrackbar("參數值:","【<5>雙邊濾波】",&g_nBilateralFilterValue,50,on_BilateralFilter);
    89. on_BilateralFilter(g_nBilateralFilterValue,0);
    90. //=======================================================
    91. //輸出一些幫助信息
    92. cout<<endl<<" 嗯。好了,請調整滾動條觀察圖像效果~

      "

    93. <<" 按下「q」鍵時,程序退出~!
      "
    94. <<"

      by淺墨";

    95. while(char(waitKey(1))!="q"){}
    96. return0;
    97. }
    98. //-----------------------------【on_BoxFilter()函數】------------------------------------
    99. //描述:方框濾波操作的回調函數
    100. //-----------------------------------------------------------------------------------------------
    101. staticvoidon_BoxFilter(int,void*)
    102. {
    103. //方框濾波操作
    104. boxFilter(g_srcImage,g_dstImage1,-1,Size(g_nBoxFilterValue+1,g_nBoxFilterValue+1));
    105. //顯示窗口
    106. imshow("【<1>方框濾波】",g_dstImage1);
    107. }
    108. //-----------------------------【on_MeanBlur()函數】------------------------------------
    109. //描述:均值濾波操作的回調函數
    110. //-----------------------------------------------------------------------------------------------
    111. staticvoidon_MeanBlur(int,void*)
    112. {
    113. blur(g_srcImage,g_dstImage2,Size(g_nMeanBlurValue+1,g_nMeanBlurValue+1),Point(-1,-1));
    114. imshow("【<2>均值濾波】",g_dstImage2);
    115. }
    116. //-----------------------------【on_GaussianBlur()函數】------------------------------------
    117. //描述:高斯濾波操作的回調函數
    118. //-----------------------------------------------------------------------------------------------
    119. staticvoidon_GaussianBlur(int,void*)
    120. {
    121. GaussianBlur(g_srcImage,g_dstImage3,Size(g_nGaussianBlurValue*2+1,g_nGaussianBlurValue*2+1),0,0);
    122. imshow("【<3>高斯濾波】",g_dstImage3);
    123. }
    124. //-----------------------------【on_MedianBlur()函數】------------------------------------
    125. //描述:中值濾波操作的回調函數
    126. //-----------------------------------------------------------------------------------------------
    127. staticvoidon_MedianBlur(int,void*)
    128. {
    129. medianBlur(g_srcImage,g_dstImage4,g_nMedianBlurValue*2+1);
    130. imshow("【<4>中值濾波】",g_dstImage4);
    131. }
    132. //-----------------------------【on_BilateralFilter()函數】------------------------------------
    133. //描述:雙邊濾波操作的回調函數
    134. //-----------------------------------------------------------------------------------------------
    135. staticvoidon_BilateralFilter(int,void*)
    136. {
    137. bilateralFilter(g_srcImage,g_dstImage5,g_nBilateralFilterValue,g_nBilateralFilterValue*2,g_nBilateralFilterValue/2);
    138. imshow("【<5>雙邊濾波】",g_dstImage5);
    139. }

    放出一些效果圖。

    原圖:

    方框濾波(盒式濾波):

    均值濾波:

    高斯濾波:

    中值濾波(參數調的有些猛,妹子完全面目全非- -):

    雙邊濾波(和原圖差別不大,要仔細看才看得出效果):

    OK,就放出這些吧,具體更多的運行效果大家就自己下載示常式序回去玩吧。


    推薦閱讀:

    模型試驗與非線性分析哪個更模擬?——楊博士每日一問匯總【貳】
    非線性無源控制理論
    對非線性科學的幾點思考

    TAG:教程 | OpenCV | 入門教程 | 非線性 | 入門 |