边缘检测算法——Canny算子
Canny算子是John Canny在20世纪80年代提出的一种多级边缘检测算法。John Canny研究了最优边缘的特性,即检测到的边缘要尽可能跟实际的边缘并尽可能的多。同时,要尽量减低噪声对边缘检测的干扰。计算步骤如下:
①.对原图像进行高斯平滑一消除图像中的噪声
②.采用查差分法近似计算图像每一个像素的梯度,并计算梯度的模值和风向
③.对梯度进行“非极大抑制”:图像边缘点梯度值通常在梯度方向是极大值,因此检测边缘需要将非极大值赋值0来抑制非边缘点。检测方法就是在一个局部窗口内,如果中心像素点的梯度不比梯度方向上相邻的两个像素值大,那么该中心像素点梯度值为0.
④.双阈值法检测边缘和连接边缘。取两个梯度阈值high和low,将梯度图像像素赋值为0得到边缘图像l1,该图像能够接近图像边缘但是可能会存在间断点;将梯度图像中小于low的像素赋值为0得到边缘图像l2,该图像中噪声干扰比较大,但是边缘信息更多。在连接边缘是时,以l1为基础,对零点进行边缘跟踪,如果跟踪过程中出现中断,则,从l2对应像素点及其邻域来寻找可以连接的边缘,直至结束。
在VTK中没有实现一个专门的类来做边缘Canny边缘检测。
实现代码`#include “Test.h”
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
#include <vtkSmartPointer.h>
#include <vtkImageData.h>
#include <vtkImageShiftScale.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleImage.h>
#include <vtkRenderer.h>
#include <vtkImageActor.h>
#include <vtkJPEGReader.h>
#include <vtkImageCast.h>
#include <vtkImageGaussianSmooth.h>
#include <vtkImageGradient.h>
#include <vtkImageMagnitude.h>
#include <vtkImageNonMaximumSuppression.h>
#include <vtkImageConstantPad.h>
#include <vtkImageToStructuredPoints.h>
#include <vtkLinkEdgels.h>
#include <vtkThreshold.h>
#include <vtkGeometryFilter.h>
#include <vtkSubPixelPositionEdgels.h>
#include <vtkCamera.h>
#include <vtkProperty.h>
#include <vtkStripper.h>
#include <vtkPolyDataMapper.h>
int main(int argc, char* argv[])
{
//首先读入图片
vtkSmartPointer reader =
vtkSmartPointer::New();
reader->SetFileName(“data/Lena.jpg”);
reader->Update();
//图片类型转换
vtkSmartPointer<vtkImageCast> ic =
vtkSmartPointer<vtkImageCast>::New();
ic->SetOutputScalarTypeToFloat();
ic->SetInputConnection(reader->GetOutputPort());
ic->Update();
//高斯算法实现图片平滑
vtkSmartPointer<vtkImageGaussianSmooth> gs =
vtkSmartPointer<vtkImageGaussianSmooth>::New();
gs->SetInputData(ic->GetOutput());
gs->SetDimensionality(2);
gs->SetRadiusFactors(1, 1, 0);
gs->Update();
//计算图像的梯度和模值
vtkSmartPointer<vtkImageGradient> imgGradient =
vtkSmartPointer<vtkImageGradient>::New();
imgGradient->SetInputData(gs->GetOutput());
imgGradient->SetDimensionality(2);
imgGradient->Update();
vtkSmartPointer<vtkImageMagnitude> imgMagnitude =
vtkSmartPointer<vtkImageMagnitude>::New();
imgMagnitude->SetInputData(imgGradient->GetOutput());
imgMagnitude->Update();
//vtkImageNonMaximumSuppression将图片中的非局部峰值设置为0,输入和输出类型都是ImageData
//其中输入有两个,模型图像和向量图像,一个典型的应用就是输入梯度模值图像梯度图像对梯度做非极大值抑制
vtkSmartPointer<vtkImageNonMaximumSuppression> nonMax =
vtkSmartPointer<vtkImageNonMaximumSuppression>::New();
nonMax->SetMagnitudeInputData(imgMagnitude->GetOutput());
nonMax->SetVectorInputData(imgGradient->GetOutput());
nonMax->SetDimensionality(2);
nonMax->Update();
//vtkImageConstantPad增加图像的大小,其输入好人输出都为VTKImageData。其中函数SetOutputNumberOfS阿炒腊肉Component是(3)用于设置输出图像的像素数据分个
//数,函数SetConstant(0)用于设置图像中扩大的区域像素值。而SetOutputWholeExtent()则用于设置输出图像的范围。这里的作用是将梯度图像像素的组分修改为3,方便下面vtkImageToStructuredPoints使用。
vtkSmartPointer<vtkImageConstantPad> pad =
vtkSmartPointer<vtkImageConstantPad>::New();
pad->SetInputConnection(imgGradient->GetOutputPort());
pad->SetOutputNumberOfScalarComponents(3);
pad->SetConstant(0);
pad->Update();
//vtkImageToStructuredPoints将VTKImageData格式转换为规则点集。该类的输入类型VTKImageData,
//另外还有一个人可选的RGB三组分向量图像输入;输入类型是VTKStructuredData类型,导航输入向量图像时,向量图像像素数据回转为输入图像的对应点
vtkSmartPointer<vtkImageToStructuredPoints> i2sp1 =
vtkSmartPointer<vtkImageToStructuredPoints>::New();
i2sp1->SetInputData(nonMax->GetOutput());
i2sp1->SetVectorInputData(pad->GetOutput());
i2sp1->Update();
//vtkLinkEdgels类根据点的相邻关系连接成连续的折线Polyline。其内部阈值变量GradientThreshold,
//可以用来排除输入点中梯度值小于该阈值的点。当使用vtkLinkEdgels进行Canny算子的双阈值边缘检测时,GradientThreshold可以用作较小的阈值。设置该阈值的函数是SetGradientThreshold(2)。
vtkSmartPointer<vtkLinkEdgels> imgLink =
vtkSmartPointer<vtkLinkEdgels>::New();
imgLink->SetInputData(i2sp1->GetOutput());
imgLink->SetGradientThreshold(2);
imgLink->Update();
vtkSmartPointer<vtkThreshold> thresholdEdgels =
vtkSmartPointer<vtkThreshold>::New();
thresholdEdgels->SetInputData(imgLink->GetOutput());
thresholdEdgels->ThresholdByUpper(10);
thresholdEdgels->AllScalarsOff();
thresholdEdgels->Update();
vtkSmartPointer<vtkGeometryFilter> gf =
vtkSmartPointer<vtkGeometryFilter>::New();
gf->SetInputConnection(thresholdEdgels->GetOutputPort());
//gf->Update();//
vtkSmartPointer<vtkImageToStructuredPoints> i2sp =
vtkSmartPointer<vtkImageToStructuredPoints>::New();
i2sp->SetInputData(imgMagnitude->GetOutput());
i2sp->SetVectorInputData(pad->GetOutput());
i2sp->Update();
vtkSmartPointer<vtkSubPixelPositionEdgels> spe =
vtkSmartPointer<vtkSubPixelPositionEdgels>::New();
spe->SetInputConnection(gf->GetOutputPort());
spe->SetGradMapsData(i2sp->GetStructuredPointsOutput());
//spe->Update();//
vtkSmartPointer<vtkStripper> strip =
vtkSmartPointer<vtkStripper>::New();
strip->SetInputConnection(spe->GetOutputPort());
//strip->Update();//
vtkSmartPointer<vtkPolyDataMapper> dsm =
vtkSmartPointer<vtkPolyDataMapper>::New();
dsm->SetInputConnection(strip->GetOutputPort());
dsm->ScalarVisibilityOff();
//dsm->Update();//
vtkSmartPointer<vtkActor> planeActor =
vtkSmartPointer<vtkActor>::New();
planeActor->SetMapper(dsm);
planeActor->GetProperty()->SetAmbient(1.0);
planeActor->GetProperty()->SetDiffuse(0.0);
planeActor->GetProperty()->SetColor(1.0, 0.0, 0.0);
vtkSmartPointer<vtkImageActor> originalActor =
vtkSmartPointer<vtkImageActor>::New();
originalActor->SetInputData(reader->GetOutput());
double originalviewport[4] = { 0.0, 0.0, 0.5, 1.0 };
double gradviewport[4] = { 0.5, 0.0, 1.0, 1.0 };
vtkSmartPointer<vtkRenderer> originalRenderer =
vtkSmartPointer<vtkRenderer>::New();
originalRenderer->SetViewport(originalviewport);
originalRenderer->AddActor(originalActor);
originalRenderer->ResetCamera();
originalRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderer> gradRenderer =
vtkSmartPointer<vtkRenderer>::New();
gradRenderer->SetViewport(gradviewport);
gradRenderer->AddActor(planeActor);
gradRenderer->ResetCamera();
gradRenderer->SetBackground(1.0, 1.0, 1.0);
vtkSmartPointer<vtkRenderWindow> renderWindow =
vtkSmartPointer<vtkRenderWindow>::New();
renderWindow->SetSize(900, 300);
renderWindow->AddRenderer(originalRenderer);
renderWindow->AddRenderer(gradRenderer);
renderWindow->Render();
renderWindow->SetWindowName("CannyExample");
vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
vtkSmartPointer<vtkRenderWindowInteractor>::New();
vtkSmartPointer<vtkInteractorStyleImage> style =
vtkSmartPointer<vtkInteractorStyleImage>::New();
renderWindowInteractor->SetInteractorStyle(style);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderWindowInteractor->Initialize();
renderWindowInteractor->Start();
return EXIT_SUCCESS;
}`