作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处
Canny边缘检测原理
Canny边缘检测是一种多步骤的图像处理算法,用于提取图像中的边缘,被广泛认为是边缘检测的最优方法之一。Canny边缘检测算法的设计目标是:
- 检测尽可能多的边缘。
- 确保边缘点的定位准确。
- 确保单一边缘响应,即避免检测到虚假的边缘。
Canny边缘检测算法可以分为以下几个主要步骤:
1.高斯平滑(Gaussian Smoothing):
- 首先使用高斯滤波器对图像进行平滑处理,以减少噪声的影响。
- 高斯滤波器通过卷积运算对图像进行平滑,使用的高斯核的方差可以由用户定义。平滑处理后的图像能更好地保留实际的边缘信息,同时去除细小的噪声。
2.计算图像梯度(Gradient Calculation):
- 通过Sobel算子等方法计算图像的梯度强度和方向。
- 梯度计算主要目的是找到图像中灰度变化最大的地方,这些地方通常是边缘所在的位置。
- 计算得到的梯度方向用于在后续步骤中进行非最大抑制。
3.非最大抑制(Non-Maximum Suppression):
- 在梯度方向上对梯度幅值进行抑制,即对每个像素,检查其梯度幅值是否是梯度方向上局部的最大值,如果不是,将其设为零。
- 这一步骤的目的是细化边缘,使得边缘更加准确。
4.双阈值检测(Double Thresholding):
- 使用两个阈值(高阈值和低阈值)来确定边缘像素。
- 高于高阈值的像素被认为是强边缘。
- 低于低阈值的像素被认为是非边缘。
- 在高阈值和低阈值之间的像素,如果与强边缘相连,则被认为是弱边缘。
5.边缘连接(Edge Tracking by Hysteresis):
- 利用双阈值检测的结果,通过滞后跟踪来连接边缘。
- 从强边缘像素开始,通过追踪其四周的弱边缘像素,将这些弱边缘像素也标记为边缘。
- 最终得到精确的边缘检测结果。
环境准备
参见:Windows下用CMake编译ITK及配置测试_itk配置-CSDN博客
功能解析
1.引入必要的头文件:
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkCannyEdgeDetectionImageFilter.h>
#include <itkRescaleIntensityImageFilter.h>
#include <itkJPEGImageIOFactory.h>
#include <itkCastImageFilter.h>
2.初始化图像类型和读写器:
typedef itk::Image<unsigned char, 2> CharImageType;
typedef itk::Image<float, 2> FloatImageType;
typedef itk::ImageFileReader<CharImageType> ReaderType;
typedef itk::ImageFileWriter<CharImageType> WriterType;
itk::JPEGImageIOFactory::RegisterOneFactory();
ReaderType::Pointer reader = ReaderType::New();
WriterType::Pointer writer = WriterType::New();
其中CharImageType是常规图像格式,FloatImageType是float型图像。之所以用float是因为Canny不支持UChar类型,因此后续要进行格式转换。
3.设置文件名:
// 设置
reader->SetFileName("test.jpg"); // 要读取的文件名
writer->SetFileName("canny_output.jpg"); // 写入的文件名
reader->SetFileName("test.jpg")
设置要读取的图像文件的名称。writer->SetFileName("canny_output.jpg")
设置要写入的图像文件的名称。
4.类型转换:
// 类型转换
typedef itk::CastImageFilter<CharImageType, FloatImageType> CastFilterType;
CastFilterType::Pointer castfilter = CastFilterType::New();
castfilter->SetInput(reader->GetOutput());
5.创建Canny边缘检测过滤器,并配置参数:
// 创建Canny边缘检测过滤器
typedef itk::CannyEdgeDetectionImageFilter<FloatImageType, FloatImageType> FilterType;
FilterType::Pointer filter = FilterType::New();
filter->SetInput(castfilter->GetOutput());
// 设置参数
filter->SetVariance(3.0);
filter->SetLowerThreshold(10);
filter->SetUpperThreshold(20);
filter->SetMaximumError(0.5);
6.类型转换:
// 将浮点图像转换为unsigned char类型
typedef itk::RescaleIntensityImageFilter<FloatImageType, CharImageType> RescaleFilterType;
RescaleFilterType::Pointer castfilter2 = RescaleFilterType::New();
castfilter2->SetInput(filter->GetOutput());
7.连接过滤器输出到写入器并执行写入操作:
writer->SetInput(castfilter2->GetOutput());
try
{
writer->Update();
}
catch (itk::ExceptionObject &error)
{
std::cerr << "Error: " << error << std::endl;
return EXIT_FAILURE;
}
完整代码
#include <itkImage.h>
#include <itkImageFileReader.h>
#include <itkImageFileWriter.h>
#include <itkCannyEdgeDetectionImageFilter.h>
#include <itkRescaleIntensityImageFilter.h>
#include <itkJPEGImageIOFactory.h>
#include <itkCastImageFilter.h>
int main()
{
// 初始化
typedef itk::Image<unsigned char, 2> CharImageType;
typedef itk::Image<float, 2> FloatImageType;
typedef itk::ImageFileReader<CharImageType> ReaderType;
typedef itk::ImageFileWriter<CharImageType> WriterType;
itk::JPEGImageIOFactory::RegisterOneFactory();
ReaderType::Pointer reader = ReaderType::New();
WriterType::Pointer writer = WriterType::New();
// 设置
reader->SetFileName("test.jpg"); // 要读取的文件名
writer->SetFileName("canny_output.jpg"); // 写入的文件名
// 类型转换
typedef itk::CastImageFilter<CharImageType, FloatImageType> CastFilterType;
CastFilterType::Pointer castfilter = CastFilterType::New();
castfilter->SetInput(reader->GetOutput());
// 创建Canny边缘检测过滤器
typedef itk::CannyEdgeDetectionImageFilter<FloatImageType, FloatImageType> FilterType;
FilterType::Pointer filter = FilterType::New();
filter->SetInput(castfilter->GetOutput());
// 设置参数
filter->SetVariance(3.0);
filter->SetLowerThreshold(10);
filter->SetUpperThreshold(20);
filter->SetMaximumError(0.5);
// 将浮点图像转换为unsigned char类型
typedef itk::RescaleIntensityImageFilter<FloatImageType, CharImageType> RescaleFilterType;
RescaleFilterType::Pointer castfilter2 = RescaleFilterType::New();
castfilter2->SetInput(filter->GetOutput());
// 输出
writer->SetInput(castfilter2->GetOutput());
try
{
writer->Update();
}
catch (itk::ExceptionObject &error)
{
std::cerr << "Error: " << error << std::endl;
return EXIT_FAILURE;
}
std::cout << "Canny edge detection completed successfully." << std::endl;
return EXIT_SUCCESS;
}
测试效果
通过调整参数,可以提取不同类型的边缘信息。
如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!