1.Canny算子

Canny算子是John Canny于20世纪80年代提出的一种多级边缘检测算法。John Canny研究了最优边缘的特性,即检测到的边缘要尽可能跟实际的边缘接近并尽可能的多,同时,要尽量降低噪声对边缘检测的干扰。其计算步骤如下
1)对源图像进行高斯平滑以消除图像中噪声
2)采用差分法近似计算图像每一个像素的梯度,并计算梯度的模值和方向
3)对梯度进行"非极大抑制":图像边缘点梯度值通常在梯度方向是极大值,因此检测边缘需要将非极大值赋值0来抑制非边缘点。检测方法就是在一个局部窗口内,如果中心像素点的梯度不比梯度方向上相邻两个像素值大,那么该中心像素点梯度值赋0
4)双阈值法检测边缘和连接边缘。取两个梯度阈值high和low,将梯度图像中小于high的像素赋0得到边缘图像I1,该图像能够接近图像边缘但是可能会存在间断点;将梯度图像中小于low的像素赋0得到边缘图像I2,该图中受噪声影响比较大,但是边缘信息更多。在连接边缘时,以I1为基础,对非零点进行边缘跟踪,如果追踪过程中出现中断,则从I2对应像素点及其邻域来寻找可以连接的边缘,直至结束。
以上是Canny算子的计算步骤。
在VTK中没有实现一个专门的类来做Canny边缘检测。
但是我们可以根据以上步骤来实现:
#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<vtkJPEGReader> reader =vtkSmartPointer<vtkJPEGReader>::New();reader->SetFileName("lena.jpg");reader->Update();vtkSmartPointer<vtkImageCast> ic =vtkSmartPointer<vtkImageCast>::New();ic->SetOutputScalarTypeToFloat();ic->SetInputConnection(reader->GetOutputPort());vtkSmartPointer<vtkImageGaussianSmooth> gs =vtkSmartPointer<vtkImageGaussianSmooth>::New();gs->SetInputConnection(ic->GetOutputPort());gs->SetDimensionality(2);gs->SetRadiusFactors(1, 1, 0);vtkSmartPointer<vtkImageGradient> imgGradient =vtkSmartPointer<vtkImageGradient>::New();imgGradient->SetInputConnection(gs->GetOutputPort());imgGradient->SetDimensionality(2);vtkSmartPointer<vtkImageMagnitude> imgMagnitude =vtkSmartPointer<vtkImageMagnitude>::New();imgMagnitude->SetInputConnection(imgGradient->GetOutputPort());vtkSmartPointer<vtkImageNonMaximumSuppression> nonMax =vtkSmartPointer<vtkImageNonMaximumSuppression>::New();nonMax->SetMagnitudeInputData(imgMagnitude->GetOutput());nonMax->SetVectorInputData(imgGradient->GetOutput());nonMax->SetDimensionality(2);vtkSmartPointer<vtkImageConstantPad> pad =vtkSmartPointer<vtkImageConstantPad>::New();pad->SetInputConnection(imgGradient->GetOutputPort());pad->SetOutputNumberOfScalarComponents(3);pad->SetConstant(0);vtkSmartPointer<vtkImageToStructuredPoints> i2sp1 =vtkSmartPointer<vtkImageToStructuredPoints>::New();i2sp1->SetInputConnection(nonMax->GetOutputPort());i2sp1->SetVectorInputData(pad->GetOutput());vtkSmartPointer<vtkLinkEdgels> imgLink =vtkSmartPointer<vtkLinkEdgels>::New();imgLink->SetInputData(i2sp1->GetOutput());imgLink->SetGradientThreshold(2);vtkSmartPointer<vtkThreshold> thresholdEdgels =vtkSmartPointer<vtkThreshold>::New();thresholdEdgels->SetInputConnection(imgLink->GetOutputPort());thresholdEdgels->ThresholdByUpper(10);thresholdEdgels->AllScalarsOff();vtkSmartPointer<vtkGeometryFilter> gf =vtkSmartPointer<vtkGeometryFilter>::New();gf->SetInputConnection(thresholdEdgels->GetOutputPort());vtkSmartPointer<vtkImageToStructuredPoints> i2sp =vtkSmartPointer<vtkImageToStructuredPoints>::New();i2sp->SetInputConnection(imgMagnitude->GetOutputPort());i2sp->SetVectorInputData(pad->GetOutput());vtkSmartPointer<vtkSubPixelPositionEdgels> spe =vtkSmartPointer<vtkSubPixelPositionEdgels>::New();spe->SetInputConnection(gf->GetOutputPort());spe->SetGradMapsData(i2sp->GetStructuredPointsOutput());vtkSmartPointer<vtkStripper> strip =vtkSmartPointer<vtkStripper>::New();strip->SetInputConnection(spe->GetOutputPort());vtkSmartPointer<vtkPolyDataMapper> dsm =vtkSmartPointer<vtkPolyDataMapper>::New();dsm->SetInputConnection(strip->GetOutputPort());dsm->ScalarVisibilityOff();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;
}

该程序比较复杂,处理边缘时将其作为几何数据来进行处理。因此涉及了部分几何数据操作的filter,这里如果不明白可以先放一下,再几何数据处理部分会做详细介绍。

程序首先读入图像,计算图像的梯度和模值。
接下来按照Canny算子的步骤进行处理。
我们详细介绍用到的相应的filter:
vtkImageNonMaximumSuppression将图像中的非局部峰值设置为0,输入和输出类型都是vtkImageData:其中输入有两个,模值图像(magnitude)和向量图像,一个典型的应用就是输入梯度模值图像和梯度向量图像对梯度做非极大值抑制。
vtkImageConstantPad增加图像的大小,其输入和输出都为vtkImageData。其中函数SetOutputNumberOfScalarComponents(3)用于设置输出图像的像素数据组分个数,函数SetConstant(0)用于设置输出图像中扩大的区域像素值。而SetOutputWholeExtent()则用于设置输出图像的范围。这里的作用是将梯度图像像素的组分修改为3,方便下面vtkImageToStructuredPoints使用。
vtkImageToStructuredPoints将vtkImageData格式转换为规则点集。该类的输入类型是vtkImageData,另外还有一个可选的RGB三组分向量图像输入;输出类型是vtkStructuredPoints,当输入向量图像时,向量图像像素数据会转为输出图像的对应点的属性。
vtkLinkEdgels类根据点的相邻关系连接成连续的折线Polyline。其内部阈值变量GradientThreshold,可以用来排除输入点中梯度值小于该阈值的点。当使用vtkLinkEdgels进行Canny算子的双阈值边缘检测时,GradientThreshold可以用作较小的阈值。设置该阈值的函数是SetGradientThreshold(2)。
vtkThreshold用于获取输入任意类型数据的满足阈值条件的单元数据。该类的输入为VTK的任意数据类型,输出数据类型是不规则网格。阈值设置有:大于阈值,小于阈值和介于两个阈值之间。内部提供了两种属性模式AttributeMode设置,即阈值比较时是采用的点属性还是单元属性,默认下是点属性。而当属性为多元数据时,还需要设置阈值比较时使用哪个组分的数据。其中提供了三种模式选择,所有组分都满足阈值条件,任意一个满足阈值条件和用户指定的组分满足阈值条件。当使用点属性数据时,如果设置了AllScalars,那么单元满足阈值条件的前提会是其所有点的属性都满足阈值条件。这里将阈值设置为10,即Canny中双阈值的较大阈值。
vtkGeometryFilter将数据转换为几何数据,输出类型为vtkPolyData。该类从vtkThreshold的输出中提取图像边缘的几何数据。
vtkSubPixelPositionEdgels接收一系列连续曲线及其对应的梯度系信息作为输入,利用梯度信息来调整曲线位置。这里对前面提取的图像边缘再根据其梯度进行调整。
vtkStripper用来将输入的多边形、三角形或者线段生成三角形带或者折线段。输入的多边形数据必须是三角形,否则不会进行带化处理。因此处理多边形数据时,可以先用vtkTriangleFilter进行三角化后再使用本类。如果输入中存在孤立点的话,也不会进行任何处理。默认情况下,该filter处理后会丢弃掉属性数据。

2.参看资料

1.《C++ primer》
2.《The VTK User’s Guide – 11thEdition》
3.《The Visualization Toolkit – AnObject-Oriented Approach To 3D Graphics (4th Edition)》
4.  张晓东, 罗火灵. VTK图形图像开发进阶[M]. 机械工业出版社, 2015.

VTK修炼之道34:边缘检测_Canny算子相关推荐

  1. VTK修炼之道33:边缘检测_Sobel算子

    1.Sobel算子用于提取图像边缘 Sobel算子也是一种常用的梯度算子.Sobel算子计算稍微复杂,它采用3x3的模板.计算时模板在图像上移动,并在每个位置上计算对应中心像素的梯度值. VTK中vt ...

  2. VTK修炼之道35:边缘检测_Laplace算子

    1.拉普拉斯算子 拉普拉斯算子是一个二阶边缘算子,即梯度的散度.拉普拉斯算子的实现也是通过模板实现.常用的拉普拉斯模板定义如下: 拉普拉斯算子计算图像的二阶导数,对于图像噪声比较敏感.拉普拉斯算子的结 ...

  3. VTK修炼之道32:边缘检测_梯度算子

    1.梯度算子提取图像边缘 图像中不连续的灰度值会产生边缘,图像的边缘检测是基于边界的图像分割方法,如分水岭算法,通常是分割原图的梯度图像,梯度实际上也是反应的图像边缘信息.图像边缘一般常用图像一阶导数 ...

  4. VTK修炼之道53:图形基本操作进阶_多分辨率策略(模型细化的三种方法)

    1.模型细化 vtk中实现网格细化的累有vtkLinearSubdivisionFilter.vtkLoopsubdivisionFilter.vtkButterflySubdivisionFilte ...

  5. VTK修炼之道50:图形基本操作进阶_网格模型的特征边 与 封闭性检测

    1.封闭性检测 由于受原始数据.重建方法的限制,得到的网格模型并不是封闭的.有时为了显示或者处理某些要求,需要网格必须是封闭的. 封闭性网格应该比较好理解,比如一个球形网格. 1.1网格模型边的分类 ...

  6. VTK修炼之道39:图像平滑_各向异性滤波

    1.各向异性扩散滤波 高斯平滑方法在平滑噪声的同时,模糊了图像的重要边缘图像. 各向异性滤波是一种基于偏微分方程的滤波技术,建立于热量的各向异性扩散理论. 各向异性滤波在图像的平坦区域选择大尺度平滑, ...

  7. VTK修炼之道20:图像基本操作_图像类型转换

    1.vtkImageCast 图像数据类型转换在数字图像处理中会被频繁地用到.一些常用到的图像算子(例如梯度算子)在计算时出于精度的考虑,会将结果存储为float或者double类型.但是在图像显示时 ...

  8. python VTK(十三) ----图像边缘检测 梯度算子 sobel算子

    梯度算子 import vtk reader = vtk.vtkJPEGReader() # 读入灰度图 reader.SetFileName(r'E:\lena-gray.jpg') reader. ...

  9. VTK修炼之道83:Pipeline管线执行模型

    1.管线执行模性 请求是VTK执行管线的一个基本操作,一个管线执行模型由多个请求共同完成.管线建立完毕,当显示调用一个Filter的Update()函数时,该Filter的vtkExecutive子类 ...

最新文章

  1. Y项目轶事之入场半个月
  2. Lua 代码编写技巧
  3. if you receive money from others
  4. ftb测试软件,EXFO推出FTB-2光纤测试平台
  5. python代码300行程序_python小工具,15行代码秒出工资条
  6. 视频监控系统中的平台服务器,【视频监控主机 网络视频集中管理平台服务器】 - 太平洋安防网...
  7. 新年快乐,送一台新款华为笔记本!
  8. LeetCode 209. 长度最小的子数组
  9. c语言判断字符串合法标识符,HDU 2024 C语言合法标识符(以及一些关于输入和ctype.h的内容)...
  10. .NET Framework框架的介绍
  11. Word中插入三线格算法公式,放大不变形
  12. alfafile中转站免费_免费、不限速的文件中转站or网盘
  13. 语法分析——自顶向下分析方法
  14. centos7安装erlang
  15. 水木周平:就业难?招人更难!
  16. lattice开发错误集合
  17. android 电池容量检测,电池容量检测优化app
  18. 探讨服务端自定义生成PDF的几种方案
  19. 金融级实人认证是什么?
  20. Web全栈~26.IO

热门文章

  1. 芝诺悖论的反驳——离散与连续角度
  2. 【工业机器人】工业机器人技术最新知识大全;工业机器人驱动与控制系统
  3. Linux下time函数
  4. 华为平板 鸿蒙2.0,华为鸿蒙系统2.0版首发支持43款手机、3款华为手表和3款平板电脑...
  5. 电子产品使用感受之--Windows 10 小米笔记本Air HDMI转VGA无信号问题
  6. 对称加密和非对称加密的区别与使用
  7. java实现双向RSA + AES加密
  8. 采药问题(01背包)
  9. Word导出PDF后,PDF没有生成Word中对应的目录以及书签大纲
  10. 视觉SLAM十四讲---第一、二讲(讲解slam框架, c++编译实践)