<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      opencv源碼解析之(4):GaussianBlur()

           這一節來真正進入opencv的源碼分析中,本次分析的函數是GaussianBlur(),即高斯濾波函數。在前前面博文《opencv源碼解析之濾波前言2》:http://www.rzrgm.cn/tornadomeet/archive/2012/03/05/2379921.html 中已經闡述了這個函數的用法,即:

           其函數聲明為:

           void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT ) ;

           功能:對輸入的圖像src進行高斯濾波后用dst輸出。

           參數:src和dst當然分別是輸入圖像和輸出圖像。Ksize為高斯濾波器模板大小,sigmaX和sigmaY分別為高斯濾波在橫線和豎向的濾波系數。borderType為邊緣擴展點插值類型。

       

           接下來的工作就是進入GaussianBlur函數內部,跟蹤其函數代碼,經過分析,在該函數內部調用了很多其他的函數,其調用的函數層次結構如下圖所示:

           這里我們分析源代碼不需要深入到最底層,我們只需分析到函數createSeparableLinearFilter和getGaussianKernel這一層。

       

           那就開始我們的源碼分析工作吧!

           從函數調用層次結構圖可以看出,要分析函數GaussianBlur,必須先分析其調用過的內部函數。

           因此首先分析函數getGaussianKernel。

           功能:返回一個ksize*1的數組,數組元素滿足高斯公式:

       

           其中只有系數alpha和參數sigma未知,sigma的求法為:

           如果輸入sigma為非正,則計算公式為:sigma = 0.3*((ksize-1)*0.5 - 1) + 0.8 .

           如果輸入sigma為正,則就用該輸入參數sigma。

           最后alpha為歸一化系數,即計算出的ksize個數之和必須為1,所以后面只需求ksize個數,計算其和并求倒即可。

      其源碼及注釋如下:

      cv::Mat cv::getGaussianKernel( int n, double sigma, int ktype )
      {
      const int SMALL_GAUSSIAN_SIZE = 7;
      static const float small_gaussian_tab[][SMALL_GAUSSIAN_SIZE] =
      {
      {1.f},
      {0.25f, 0.5f, 0.25f},
      {0.0625f, 0.25f, 0.375f, 0.25f, 0.0625f},
      {0.03125f, 0.109375f, 0.21875f, 0.28125f, 0.21875f, 0.109375f, 0.03125f}
      };

      /*如果sigma小于0,且n為不大于7的奇整數,則核的濾波系數固定了,其固定在數組

      small_gaussian_tab中,根據其n的長度來選擇具體的值 ,如果不滿足上面的,則固定核為0
      固定核為0表示自己計算其核
      */

      const float* fixed_kernel = n % 2 == 1 && n <= SMALL_GAUSSIAN_SIZE && sigma <= 0 ?
      small_gaussian_tab[n>>1] : 0;

      CV_Assert( ktype == CV_32F || ktype == CV_64F );//確保核元素為32位浮點數或者64位浮點數
      Mat kernel(n, 1, ktype);//建立一個n*1的數組kernel,一個Mat矩陣包括一個矩陣頭和一個指向矩陣元素的指針
      float* cf = (float*)kernel.data;//定義指針cf指向kernel單精度浮點型數據
      double* cd = (double*)kernel.data;//定義指針cd指向kernerl雙精度浮點型數據

      double sigmaX = sigma > 0 ? sigma : ((n-1)*0.5 - 1)*0.3 + 0.8;//當sigma小于0時,采用公式得到sigma(只與n有關)
      double scale2X = -0.5/(sigmaX*sigmaX);//高斯表達式后面要用到
      double sum = 0;

      int i;
      for( i = 0; i < n; i++ )
      {
      double x = i - (n-1)*0.5;
      //如果自己算其核的話,就常用公式exp(scale2X*x*x)計算,否則就用固定系數的核
      double t = fixed_kernel ? (double)fixed_kernel[i] : std::exp(scale2X*x*x);
      if( ktype == CV_32F )
      {
      cf[i] = (float)t;//單精度要求時存入cf數組中
      sum += cf[i];//進行歸一化時要用到
      }
      else
      {
      cd[i] = t;//雙精度時存入cd數組中
      sum += cd[i];
      }
      }

      sum = 1./sum;//歸一化時核中各元素之和為1
      for( i = 0; i < n; i++ )
      {
      if( ktype == CV_32F )
      cf[i] = (float)(cf[i]*sum);//歸一化后的單精度核元素
      else
      cd[i] *= sum;//歸一化后的雙精度核元素
      }

      return kernel;//返回n*1的數組,其元素或是單精度或是雙精度,且符合高斯分布
      }

          下面該分析函數createSeparableLinearFilter了。

          功能為:創建一個圖像濾波其引擎類,其主要處理的是原圖像和目標圖像數據格式的統以及濾波器核的合成。

      其源碼及注釋如下:

      cv::Ptr<cv::FilterEngine> cv::createSeparableLinearFilter(
      int _srcType, int _dstType,
      InputArray __rowKernel, InputArray __columnKernel,
      Point _anchor, double _delta,
      int _rowBorderType, int _columnBorderType,
      const Scalar& _borderValue )//InputArray是Mat類型,表示的是輸入數組
      {
      //_rowKernel存儲其矩陣頭,_columnKernel類似
      Mat _rowKernel = __rowKernel.getMat(), _columnKernel = __columnKernel.getMat();
      _srcType = CV_MAT_TYPE(_srcType);//求矩陣的數組類型,數據類型包過通道數,深度,和數據類型3種
      _dstType = CV_MAT_TYPE(_dstType);//類似
      int sdepth = CV_MAT_DEPTH(_srcType), ddepth = CV_MAT_DEPTH(_dstType);//求矩陣元素深度
      int cn = CV_MAT_CN(_srcType);//求矩陣元素通道
      CV_Assert( cn == CV_MAT_CN(_dstType) );//源數組和目標數組的通道數必須相等
      int rsize = _rowKernel.rows + _rowKernel.cols - 1;//求行長
      int csize = _columnKernel.rows + _columnKernel.cols - 1;//求列長
      if( _anchor.x < 0 )//求被濾波點的位置
      _anchor.x = rsize/2;
      if( _anchor.y < 0 )
      _anchor.y = csize/2;

      /*getKernelType()這個函數內部就不分析了,宏觀上分析一下,其函數聲明為:
      int getKernelType(InputArray kernel, Point anchor)
      功能:根據輸入核系數矩陣kernel和被平滑點anchor來分析該核的類型,其類型主要有以下5種。
      1.普通核,沒什么特點的
      2.對稱核,anchor點在中心,且中心點2邊的系數對稱相等
      3.反對稱核,anchor點也在中心,但中心點2邊的系數對稱相反
      4.平滑核,即每個數都是非負,且所有數相加為1
      5.整數核,即核內每個系數都是整數
      */
      int rtype = getKernelType(_rowKernel,
      _rowKernel.rows == 1 ? Point(_anchor.x, 0) : Point(0, _anchor.x));//返回行矩陣核類型
      int ctype = getKernelType(_columnKernel,
      _columnKernel.rows == 1 ? Point(_anchor.y, 0) : Point(0, _anchor.y));//返回列矩陣核類型
      Mat rowKernel, columnKernel;

      /*在源代碼types_c.h中有
      #define CV_8U 0
      #define CV_8S 1
      #define CV_16U 2
      #define CV_16S 3
      #define CV_32S 4
      #define CV_32F 5
      #define CV_64F 6
      */

      int bdepth = std::max(CV_32F,std::max(sdepth, ddepth));//在sdepth,ddepth,CV_32F(即5)中選出一個最大的數
      int bits = 0;

      if( sdepth == CV_8U &&
      ((rtype == KERNEL_SMOOTH+KERNEL_SYMMETRICAL &&//行列都是平滑對稱核,且類型為8位無符號整型
      ctype == KERNEL_SMOOTH+KERNEL_SYMMETRICAL &&
      ddepth == CV_8U) ||
      ((rtype & (KERNEL_SYMMETRICAL+KERNEL_ASYMMETRICAL)) &&
      (ctype & (KERNEL_SYMMETRICAL+KERNEL_ASYMMETRICAL)) &&
      (rtype & ctype & KERNEL_INTEGER) && //或者行列都是整型對稱或反對稱核,且目標數組類型為16位有符號型
      ddepth == CV_16S)) )
      {
      bdepth = CV_32S; //重新給bdepth賦值
      bits = ddepth == CV_8U ? 8 : 0;//當目標矩陣類型為CV_8U時,位深就為8,否則為0

      /*convertTo()函數是源數組線性變換成目標數組,第二個參數為目標數組的類型*/
      _rowKernel.convertTo( rowKernel, CV_32S, 1 << bits );//將源行數組變換成32s的目標數組
      _columnKernel.convertTo( columnKernel, CV_32S, 1 << bits );//將源列數組變換成32s的目標數組
      bits *= 2;//為0或者為16
      _delta *= (1 << bits);//起放大作用?
      }
      else
      {
      if( _rowKernel.type() != bdepth )
      _rowKernel.convertTo( rowKernel, bdepth );//將源行數組深度轉換為目的數組深度
      else
      rowKernel = _rowKernel;
      if( _columnKernel.type() != bdepth )
      _columnKernel.convertTo( columnKernel, bdepth );//將源列數組深度轉換為目的數組深度
      else
      columnKernel = _columnKernel;
      }//到目前這一行為止,也只是做了一個非常簡單的工作,即把輸入的行列矩陣數據類型統一

      int _bufType = CV_MAKETYPE(bdepth, cn);//創建一個緩沖數組類型,有深度和通道數2方面的信息?

      /*Ptr<BaseRowFilter> _rowFilter表示創建一個參數為BaseRowFilter的具體類Ptr*/
      Ptr<BaseRowFilter> _rowFilter = getLinearRowFilter(
      _srcType, _bufType, rowKernel, _anchor.x, rtype);
      Ptr<BaseColumnFilter> _columnFilter = getLinearColumnFilter(
      _bufType, _dstType, columnKernel, _anchor.y, ctype, _delta, bits );//基本上也是完成數據類型的整理

      /*FilterEngine為一個通用的圖像濾波類
      */

      return Ptr<FilterEngine>( new FilterEngine(Ptr<BaseFilter>(0), _rowFilter, _columnFilter,
      _srcType, _dstType, _bufType, _rowBorderType, _columnBorderType, _borderValue ));
      //新創建一個Ptr的模板類并用類FilterEngine的構造函數來初始化它
      }

           接著分析函數createGaussianFilter。

           功能:給定濾波核大小和類型,以及2個sigma,就可以得出一個二維濾波核。兩個sigma允許輸入負數等其他不常用的輸入。

       

           其源碼及注釋如下:

      cv::Ptr<cv::FilterEngine> cv::createGaussianFilter( int type, Size ksize,
      double sigma1, double sigma2,
      int borderType )
      {
      int depth = CV_MAT_DEPTH(type);//取數組元素的深度
      if( sigma2 <= 0 )
      sigma2 = sigma1;//當第3個參數為非正時,取其與第二個參數相同的值

      // automatic detection of kernel size from sigma
      /*一般情況下滿足sigma1>0*/
      if( ksize.width <= 0 && sigma1 > 0 )//當濾波器核的寬非正時,其寬要重新經過計算
      /*根據CV_8U來計算,核寬為接近7*sigma1或者9*sigma1*/
      ksize.width = cvRound(sigma1*(depth == CV_8U ? 3 : 4)*2 + 1)|1;
      if( ksize.height <= 0 && sigma2 > 0 )
      /*同理,核高根據CV_8U來計算,為接近7*sigma2或者9*sigma2*/
      ksize.height = cvRound(sigma2*(depth == CV_8U ? 3 : 4)*2 + 1)|1;

      CV_Assert( ksize.width > 0 && ksize.width % 2 == 1 &&
      ksize.height > 0 && ksize.height % 2 == 1 );//確保核寬和核高為正奇數

      sigma1 = std::max( sigma1, 0. );//sigma最小為0
      sigma2 = std::max( sigma2, 0. );

      Mat kx = getGaussianKernel( ksize.width, sigma1, std::max(depth, CV_32F) );//得到x方向一維高斯核
      Mat ky;
      if( ksize.height == ksize.width && std::abs(sigma1 - sigma2) < DBL_EPSILON )
      ky = kx;//如果核寬和核高相等,且兩個sigma相差很小的情況下,y方向的高斯核去與x方向一樣,減少計算量
      else
      ky = getGaussianKernel( ksize.height, sigma2, std::max(depth, CV_32F) );//否則計算y方向的高斯核系數

      return createSeparableLinearFilter( type, type, kx, ky, Point(-1,-1), 0, borderType );//返回2維圖像濾波引擎
      }

           最后來看真正的高斯濾波函數GaussianBlur:

          功能:對輸入圖像_src進行濾波得到輸出圖像_dst,濾波核大小為ksize,濾波參數由sigma1和sigma2計算出,邊緣擴展模式為borderType.

          其源代碼和注釋如下:

      void cv::GaussianBlur( InputArray _src, OutputArray _dst, Size ksize,
      double sigma1, double sigma2,
      int borderType )
      {
      Mat src = _src.getMat();//創建一個矩陣src,利用_src的矩陣頭信息
      _dst.create( src.size(), src.type() );//構造與輸入矩陣同大小的目標矩陣
      Mat dst = _dst.getMat();//創建一個目標矩陣

      if( ksize.width == 1 && ksize.height == 1 )
      {
      src.copyTo(dst);//如果濾波器核的大小為1的話,則說明根本就不用濾波,輸出矩陣與輸入矩陣完全相同
      return;
      }

      if( borderType != BORDER_CONSTANT )//當邊緣擴展不是常數擴展時
      {
      if( src.rows == 1 )
      ksize.height = 1;//如果輸入矩陣是一個行向量,則濾波核的高強制為1
      if( src.cols == 1 )
      ksize.width = 1;//如果輸入矩陣是一個列向量,則濾波核的寬強制為1
      }

      /*生成一個高斯濾波器引擎f*/
      Ptr<FilterEngine> f = createGaussianFilter( src.type(), ksize, sigma1, sigma2, borderType );
      f->apply( src, dst );//調用引擎函數,完成將輸入矩陣src高斯濾波為輸出矩陣dst
      }

           至此,函數GaussianBlur源碼已經分析結束了,格式排版太累了!歡迎交流!



       

      posted on 2012-03-10 22:53  tornadomeet  閱讀(36155)  評論(12)    收藏  舉報

      阿薩德發斯蒂芬
      主站蜘蛛池模板: 亚洲国产综合精品2020| 亚洲香蕉伊综合在人在线| 国产视色精品亚洲一区二区| 亚洲一区二区三区影院| 日韩一欧美内射在线观看| 国产片av在线观看国语| 国产很色很黄很大爽的视频| 精品人妻系列无码天堂| 综1合AV在线播放| 日韩精品专区在线影院重磅| 国产乱人激情H在线观看| 久久中文字幕无码专区| 狠狠v日韩v欧美v| 久久精品国产亚洲av亚| 伊金霍洛旗| 一区二区三区国产偷拍| 亚洲中文字幕一区二区| 国产精品久久久久孕妇| 国产福利酱国产一区二区| 国产偷国产偷亚洲清高网站 | 亚洲成a人片在线观看久| av无码免费一区二区三区| 国产精品无码av不卡| 中国女人内谢69xxxx| 亚洲 卡通 欧美 制服 中文| 国产99精品成人午夜在线| 亚洲一区二区三区18禁| 在线看片免费人成视久网| 国产黑色丝袜在线播放| 国产不卡av一区二区| 天天摸夜夜摸夜夜狠狠添| 亚洲av网一区天堂福利| 国产AV大陆精品一区二区三区| 人人妻人人插视频| 国产精品美女久久久久久麻豆| 兰西县| 欧美牲交a欧美牲交aⅴ一| 天天摸夜夜摸夜夜狠狠添| 美女爽到高潮嗷嗷嗷叫免费网站 | 托里县| 骚虎视频在线观看|