1. 程式人生 > >5.4.1 邊緣檢測—梯度運算元

5.4.1 邊緣檢測—梯度運算元

影象中不連續的灰度值會產生邊緣,影象的邊緣檢測是基於邊界的影象分割方法,如分水嶺演算法,通常是分割原圖的梯度影象,梯度實際上也是反應的影象邊緣資訊。影象邊緣一般常用影象一階導數和二階導數來檢測。
梯度運算元對應於影象一階導數。影象一階導數計算一般是通過差分運算來近似的。VTK中可以使用vtkImageGradient計算影象梯度。注意影象梯度是一個向量,具有方向和大小。因此vtkImageGradient的計算結果是一個梯度場,也就是每個畫素值都是一個梯度向量。顯示梯度影象時需要計算每個畫素點的梯度大小,即模值。
下面程式碼如何利用VTK怎麼計算影象梯度:

//梯度運算元
#include"vtkSmartPointer.h"
#include"vtkJPEGReader.h"
#include"vtkImageGradient.h"
#include"vtkImageMagnitude.h"
#include"vtkImageData.h"
#include"vtkImageShiftScale.h"
#include"vtkImageActor.h"
#include"vtkRenderer.h"
#include"vtkRenderWindow.h"
#include"vtkRenderWindowInteractor.h"
#include"vtkInteractorStyleImage.h"

int main()
{
	vtkSmartPointer<vtkJPEGReader> reader =	vtkSmartPointer<vtkJPEGReader>::New();
	reader->SetFileName("data\\lena-gray.jpg");
	reader->Update();

	vtkSmartPointer<vtkImageGradient> imgGradient =	vtkSmartPointer<vtkImageGradient>::New();//vtkImageGradient計算影象梯度,是一個向量
	imgGradient->SetInputConnection(reader->GetOutputPort());
	imgGradient->SetDimensionality(2);//SetDimensionality用於要計算的影象的維數

	vtkSmartPointer<vtkImageMagnitude> imgMagnitude = vtkSmartPointer<vtkImageMagnitude>::New();//imgMagnitude用於計算梯度向量的2範數(模),向量不能直接顯示
	imgMagnitude->SetInputConnection(imgGradient->GetOutputPort());
	imgMagnitude->Update();

	double Range[2];
	vtkSmartPointer<vtkImageData> getRange = vtkSmartPointer<vtkImageData>::New();
	imgMagnitude->GetOutput()->GetScalarRange(Range);//影象灰度範圍最小值、最大值

	vtkSmartPointer<vtkImageShiftScale> imgShiftScale =	vtkSmartPointer<vtkImageShiftScale>::New();//vtkImageShiftScale調整影象的資料範圍為[0,255]
	imgShiftScale->SetOutputScalarTypeToUnsignedChar(); //強制型別轉換 0~255
	imgShiftScale->SetScale(255 / Range[1]); //灰度對映間距
	imgShiftScale->SetInputConnection(imgMagnitude->GetOutputPort());
	imgShiftScale->Update();

	/////////////////////////////////////////////////////////////////////////////////
	vtkSmartPointer<vtkImageActor> origActor =	vtkSmartPointer<vtkImageActor>::New();
	origActor->SetInputData(reader->GetOutput());

	vtkSmartPointer<vtkImageActor> GradientActor =	vtkSmartPointer<vtkImageActor>::New();
	GradientActor->SetInputData(imgShiftScale->GetOutput());
	////////////////////////////////////////////////////////////////////////////////////
	double origView[4] = { 0, 0, 0.5, 1 };
	double gradientView[4] = { 0.5, 0, 1, 1 };
	vtkSmartPointer<vtkRenderer> origRender = vtkSmartPointer<vtkRenderer>::New();
	origRender->SetViewport(origView);
	origRender->AddActor(origActor);
	origRender->ResetCamera();
	origRender->SetBackground(1.0, 1, 1);

	vtkSmartPointer<vtkRenderer> gradientRender = vtkSmartPointer<vtkRenderer>::New();
	gradientRender->SetViewport(gradientView);
	gradientRender->AddActor(GradientActor);
	gradientRender->ResetCamera();
	gradientRender->SetBackground(1, 1, 1);
	////////////////////////////////////////////////////////////////////////////////////////
	vtkSmartPointer<vtkRenderWindow> rw =	vtkSmartPointer<vtkRenderWindow>::New();
	rw->AddRenderer(origRender);
	rw->AddRenderer(gradientRender);
	rw->SetSize(640, 320);
	rw->SetWindowName("Image Gradient");

	vtkSmartPointer<vtkRenderWindowInteractor> rwi = vtkSmartPointer<vtkRenderWindowInteractor>::New();
	vtkSmartPointer<vtkInteractorStyleImage> style = vtkSmartPointer<vtkInteractorStyleImage>::New();
	rwi->SetRenderWindow(rw);
	rwi->SetInteractorStyle(style);
	rwi->Initialize();
	rwi->Start();

	return 0;
}

執行結果如圖所示:

vtkImageGradient的使用比較簡單,只需要設定輸入影象即可。
計算梯度時,採用的是中間差分法,即畫素在每個方向的差分,都是利用的前後兩個畫素值之差。這樣在影象在邊界處的差分計算需要特殊處理。其內部定義了HandleBoundaries變數,通過函式SetHandleBoundaries()定賦值。當HandleBoundaries為真時運算元會特殊處理計算邊界畫素的梯度;當為假時不計算邊界畫素的梯度值,因此輸出影象大小要小於輸入影象。
另外函式SetDimensionality()用於設定要計算的影象維數,預設為二維,此時梯度向量也為二維。
前面也提到過,梯度是一個向量,不能直接顯示。
因此上面程式碼中定義了vtkImageMagnitude物件來計算梯度向量的2範數,即向量的模。
利用vtkImageShiftScale將影象的資料範圍調整到0-255然後顯示。
另外還可以通過vtkImageExtractComponents來提取每個方向的梯度分量進行顯示。
注意,彩色影象不能直接用來計算梯度,需要先轉換為灰度影象。

參考資料:

1.《The Visualization Toolkit – AnObject-Oriented Approach To 3D Graphics (4th Edition)》
2. 張曉東, 羅火靈. VTK圖形影象開發進階[M]. 機械工業出版社, 2015.

所用軟體:vtk7.0+visual studio 2013


注:此文知識學習筆記,僅記錄完整程式和實現結果,具體原理參見:

https://blog.csdn.net/www_doling_net/article/details/8541534

https://blog.csdn.net/shenziheng1/article/category/6114053/4