天天看點

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

本次範例将要學習關于邊緣提取,圖像銳化的三個基本函數,風别是Sobel(),Laplacian(),Canny(),會從原理講起,再到代碼實作,最後會貼出運作結果,進行三種結果的對比。

1、原理及計算

Sobel:

原理:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

由上圖,你可以看到在 邊緣 ,相素值顯著的 改變 了。表示這一 改變 的一個方法是使用 導數 。 梯度值的大變預示着圖像中内容的顯著變化。

用更加形象的圖像來解釋,假設我們有一張一維圖形。下圖中灰階值的”躍升”表示邊緣的存在:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

使用一階微分求導我們可以更加清晰的看到邊緣”躍升”的存在(這裡顯示為高峰值)

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

從上例中我們可以推論檢測邊緣可以通過定位梯度值大于鄰域的相素的方法找到(或者推廣到大于一個閥值).

計算:

  1. 在兩個方向求導:
    1. 水準變化: 将 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       與一個奇數大小的核心 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       進行卷積。比如,當核心大小為3時, 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       的計算結果為:
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
    2. 垂直變化: 将I 與一個奇數大小的核心 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       進行卷積。比如,當核心大小為3時, 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       的計算結果為:
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
  2. 在圖像的每一點,結合以上兩個結果求出近似 梯度:
    學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
    有時也用下面更簡單公式代替:
    學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

注:Sobel操作,一般使用大一點的核會比小一點的核好(來自學習OpenCV),當核心大小為 3 時, 以上Sobel核心可能産生比較明顯的誤差(畢竟,Sobel算子隻是求取了導數的近似值)。 為解決這一問題,OpenCV提供了 Scharr 函數,但該函數僅作用于大小為3的核心。該函數的運算與Sobel函數一樣快,但結果卻更加精确,其核心為:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

Laplace:

原理:

  1. Sobel算子,其基礎來自于一個事實,即在邊緣部分,像素值出現”跳躍“或者較大的變化。如果在此邊緣部分求取一階導數,你會看到極值的出現。正如下圖所示:
    學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
  2. 如果在邊緣部分求二階導數會出現什麼情況?
    學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
    你會發現在一階導數的極值位置,二階導數為0。是以我們也可以用這個特點來作為檢測圖像邊緣的方法。 但是, 二階導數的0值不僅僅出現在邊緣(它們也可能出現在無意義的位置),但是我們可以過濾掉這些點。

計算

Laplacian 算子 的定義:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

核心為:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

OpenCV函數 Laplacian 實作了Laplacian算子。 實際上,由于 Laplacian使用了圖像梯度,它内部調用了 Sobel 算子。

Canny:

操作步驟:

  1. 消除噪聲。 使用高斯平滑濾波器卷積降噪。 下面顯示了一個 
    學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
     的高斯核心示例:
    學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
  2. 計算梯度幅值和方向。 此處,按照Sobel濾波器的步驟:
    1. 運用一對卷積陣列 (分别作用于 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       和 
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
       方向):
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
    2. 使用下列公式計算梯度幅值和方向:
      學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數
      梯度方向近似到四個可能角度之一(一般 0, 45, 90, 135)
  3. 非極大值 抑制。 這一步排除非邊緣像素, 僅僅保留了一些細線條(候選邊緣)。
  4. 滞後門檻值: 最後一步,Canny 使用了滞後門檻值,滞後門檻值需要兩個門檻值(高門檻值和低門檻值):
    1. 如果某一像素位置的幅值超過 高 門檻值, 該像素被保留為邊緣像素。
    2. 如果某一像素位置的幅值小于 低 門檻值, 該像素被排除。
    3. 如果某一像素位置的幅值在兩個門檻值之間,該像素僅僅在連接配接到一個高于 高 門檻值的像素時被保留。
    Canny 推薦的 高:低 門檻值比在 2:1 到3:1之間。

如果想自适應确定門檻值,那麼請查閱這篇部落格 在OpenCV中自适應确定canny算法的分割門限

2、代碼實作

#include "stdafx.h"

#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <stdlib.h>
#include <stdio.h>

using namespace cv;

/// 全局變量

Mat src, src_gray,grad,laplacian_dst;
Mat grad_x, grad_y,abs_grad_x, abs_grad_y,laplacian_dst1,dst, detected_edges;

int edgeThresh = 1;
int lowThreshold;
int slobel_lowThreshold;
int const max_lowThreshold = 100;
int ratio = 3;
int kernel_size = 3;
int scale = 1;
int delta = 0;
int ddepth = CV_16S;
char* window_name = "Edge Map";
char* sobel_window_name = "Sobel Demo - Simple Edge Detector";

/**
 * @函數 CannyThreshold
 * @簡介: trackbar 互動回調 - Canny門檻值輸入比例1:3
 */
void CannyThreshold(int, void*)
{
  /// 使用 3x3核心降噪
  blur( src_gray, detected_edges, Size(3,3) );

  /// 運作Canny算子
  Canny( detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );

  /// 使用 Canny算子輸出邊緣作為掩碼顯示原圖像
  dst = Scalar::all(0);

  src.copyTo( dst, detected_edges);
  imshow( window_name, dst );
 }
void SobelThreshold(int,void*)
{
	/// 求 X方向梯度
	Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
//	Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
	convertScaleAbs( grad_x, abs_grad_x );
	/// 求Y方向梯度
	Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
//	Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
	convertScaleAbs( grad_y, abs_grad_y );
	/// 合并梯度(近似)
	addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
	threshold(grad,grad,slobel_lowThreshold,100,THRESH_TOZERO);
	imshow(sobel_window_name,grad);

}


/** @函數 main */
int main( int argc, char** argv )
{

  /// 裝載圖像
  src = imread( "lena.png");

  if( !src.data )
  { return -1; }

  /// 使用高斯濾波消除噪聲
  GaussianBlur( src, src, Size(3,3), 0, 0, BORDER_DEFAULT );

  /// 建立與src同類型和大小的矩陣(dst)
  dst.create( src.size(), src.type() );

  /// 原圖像轉換為灰階圖像
  cvtColor( src, src_gray, CV_BGR2GRAY );

  /// 建立顯示視窗
  namedWindow( window_name, CV_WINDOW_AUTOSIZE );
  namedWindow(sobel_window_name,CV_WINDOW_AUTOSIZE);

  /// 建立trackbar
  createTrackbar( "Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold );
  createTrackbar("Sobel Threshold:",sobel_window_name,&slobel_lowThreshold,max_lowThreshold, SobelThreshold);

  /// 使用Laplace函數
  Laplacian( src_gray, laplacian_dst1, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
  convertScaleAbs( laplacian_dst1, laplacian_dst );
  imshow("laplacian",laplacian_dst);

  /// 顯示圖像
  CannyThreshold(0, 0);
   SobelThreshold(0,0);

  /// 等待使用者反應
  waitKey(0);

  return 0;
  }

           

3、運作結果

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

                                                                         圖1、 原圖

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

                                                            圖2、Sobel

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

                                                                圖3、scharr

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

                                                            圖4、laplace

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

                                                                   圖5、canny

4、使用的類和函數

Sobel:

功能:使用擴充 Sobel 算子計算一階、二階、三階或混合圖像差分

結構:

void Sobel(InputArray src, OutputArray dst, int ddepth, int xorder, int yorder, int ksize=3, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
           

src :源圖像

dst :目标圖像,和源圖像有同樣的size和通道數

ddepth :目标圖像的深度

xorder :x 方向上的差分階數

yorder :y 方向上的差分階數

ksize :擴充 Sobel 核的大小,必須是 1, 3, 5 或 7

scale :縮放因子

delta :最後加到圖像中的數值

borderType :邊界插值類型

通過适當的核對圖像進行卷積:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

如果ksize=3,x方向的核為:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

y方向的核為:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

Laplacian:

功能:計算圖像的 Laplacian 變換

結構:

void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize=1, double scale=1, double delta=0, int borderType=BORDER_DEFAULT )
           

src :源圖像

dst :目标圖像,和源圖像有同樣的size和通道數

ddepth :目标圖像的深度

ksize :核的大小,必須為奇數

scale :縮放因子

delta :最後加到圖像中的數值

borderType :邊界插值類型

函數 Laplacian 計算輸入圖像的 Laplacian變換,方法是先用 sobel 算子計算二階 x- 和 y- 差分,再求和:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

對 ksize=1 則給出最快計算結果,相當于對圖像采用如下核心做卷積:

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

Canny:

功能:采用 Canny 算法做邊緣檢測

結構:

void Canny(InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false )
           

image :單通道8位圖像

edges:目标圖像,和源圖像有同樣的size和type

threshold1 :第一個門檻值

threshold2 :第二個門檻值

apertureSize :sobel核心大小

L2gradient :當L2gradient=true,則梯度幅度采用L_2範式計算

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

,當為false時,使用L_1範式

學習OpenCV範例(十四)——sobel,laplace,canny的運用1、原理及計算2、代碼實作3、運作結果4、使用的類和函數

函數 Canny 采用 CANNY 算法發現輸入圖像的邊緣而且在輸出圖像中辨別這些邊緣。threshold1和threshold2 當中的小門檻值用來控制邊緣連接配接,大的門檻值用來控制強邊緣的初始分割。

注意:Canny隻接受單通道圖像作為輸入。

繼續閱讀