🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,
15年
工作经验,精通Java编程
,高并发设计
,Springboot和微服务
,熟悉Linux
,ESXI虚拟化
以及云原生Docker和K8s
,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
JavaCV 图像边缘检测 之 Canny 算法
引言
在图像处理领域,边缘检测是一项至关重要的任务。图像中的边缘包含了丰富的信息,例如物体的轮廓、区域的边界等。通过准确地检测边缘,我们可以进一步对图像进行分析、识别和理解。
边缘检测算法有多种,其中 Canny边缘检测
算法 以其精确性和可靠性而备受关注。Canny边缘检测算法的目标是在尽可能减少噪声影响的同时,准确地检测出图像中的边缘。它不像一些简单的边缘检测算法那样容易受到噪声干扰而产生虚假边缘,也不会遗漏重要的边缘信息。
在实际应用中,Canny边缘检测
广泛应用于计算机视觉、医学图像处理、工业检测等众多领域。例如,在计算机视觉的目标识别任务中,准确的边缘检测可以帮助我们更好地定位和识别目标物体的形状;在医学图像处理中,对X光
、CT等图像
进行边缘检测有助于医生发现病变区域的轮廓;在工业检测方面,可以用于检测产品的外形缺陷等。
JavaCV
是一个在Java平台上用于计算机视觉的库,它为我们在Java环境
下实现Canny边缘检测
提供了便利。通过JavaCV
,我们可以轻松地将Canny边缘检测算法
应用到各种图像处理项目中,充分发挥其优势。接下来,我们将详细介绍如何使用JavaCV
来实现Canny边缘检测
。
目录
- Canny 边缘检测的原理
- JavaCV 实现 Canny 边缘检测的 Maven 依赖
- JavaCV 实现 Canny 边缘检测的步骤
- 步骤一:图像读取
- 步骤二:高斯滤波
- 步骤三:计算梯度幅值和方向
- 步骤四:非极大值抑制
- 步骤五:双阈值检测
- 案例分析与对比展示
- 总结
- 参考资料文献
Canny 边缘检测的原理
Canny 边缘检测是一种基于多阶段算法的边缘检测方法,其目标是找到图像中强度变化最为显著的位置,即边缘。以下是 Canny 边缘检测的主要步骤和原理:
1. 高斯滤波
在进行边缘检测之前,首先对图像进行高斯滤波。这一步的目的是减少噪声对边缘检测的影响。高斯滤波是一种线性平滑滤波器,它通过对图像中的每个像素点与其周围的像素点进行加权平均来实现平滑效果。权重是根据高斯分布函数确定的,离中心像素点越近的像素点权重越大,离中心像素点越远的像素点权重越小。
2. 计算梯度幅值和方向
接下来,通过使用一阶偏导数的有限差分来近似计算图像的梯度幅值和方向。梯度幅值表示图像中像素点的强度变化程度,梯度方向表示强度变化的方向。具体来说,对于图像中的每个像素点,计算其在水平方向和垂直方向上的偏导数,然后根据这两个偏导数计算出梯度幅值和方向。
3. 非极大值抑制
非极大值抑制是为了细化边缘,只保留梯度方向上的局部最大值作为边缘点。在这一步中,对于每个像素点,将其梯度幅值与沿梯度方向上的两个相邻像素点的梯度幅值进行比较。如果该像素点的梯度幅值不是局部最大值,则将其置为 0,否则保持不变。这样可以去除一些非边缘点,使边缘更加清晰。
4. 双阈值检测
最后,通过双阈值检测来确定最终的边缘。设置一个高阈值和一个低阈值,高于高阈值的像素点确定为边缘点,低于低阈值的像素点被排除,介于两者之间的像素点如果与确定的边缘点相连则也被视为边缘点。这样可以去除一些弱边缘,同时保留一些强边缘和与强边缘相连的弱边缘,使边缘更加连续和准确。
JavaCV 实现 Canny 边缘检测的 Maven 依赖
为了在 Java 项目中使用 JavaCV 实现 Canny 边缘检测,需要在项目的 pom.xml 文件中添加以下 Maven 依赖:
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.7</version>
</dependency>
JavaCV 实现 Canny 边缘检测的步骤
步骤一:图像读取
首先,我们需要读取一张图像。可以使用 JavaCV 中的 Imgcodecs
类来实现图像的读取。以下是读取图像的代码示例:
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
public class CannyEdgeDetection {
public static void main(String[] args) {
// 读取图像
Mat image = Imgcodecs.imread("input.jpg");
if (image.empty()) {
System.out.println("无法读取图像");
return;
}
}
}
在上述代码中,我们使用 Imgcodecs.imread()
方法读取了一张名为 input.jpg
的图像,并将其存储在一个 Mat
对象中。如果图像读取失败,则输出错误信息并返回。
步骤二:高斯滤波
接下来,对图像进行高斯滤波。可以使用 JavaCV 中的 Imgproc
类来实现高斯滤波。以下是对图像进行高斯滤波的代码示例:
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class CannyEdgeDetection {
public static void main(String[] args) {
// 读取图像
Mat image = Imgcodecs.imread("input.jpg");
if (image.empty()) {
System.out.println("无法读取图像");
return;
}
// 高斯滤波
Mat blurredImage = new Mat();
Imgproc.GaussianBlur(image, blurredImage, new org.opencv.core.Size(5, 5), 0);
}
}
在上述代码中,我们首先创建了一个新的 Mat
对象 blurredImage
,用于存储滤波后的图像。然后,使用 Imgproc.GaussianBlur()
方法对图像进行高斯滤波。该方法的参数分别为输入图像、输出图像、高斯核的大小和标准差。在这个例子中,我们使用了一个大小为 5x5
的高斯核,标准差为 0。
步骤三:计算梯度幅值和方向
计算图像的梯度幅值和方向可以使用 JavaCV 中的 Imgproc
类的 Sobel()
方法。以下是计算梯度幅值和方向的代码示例:
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class CannyEdgeDetection {
public static void main(String[] args) {
// 读取图像
Mat image = Imgcodecs.imread("input.jpg");
if (image.empty()) {
System.out.println("无法读取图像");
return;
}
// 高斯滤波
Mat blurredImage = new Mat();
Imgproc.GaussianBlur(image, blurredImage, new Size(5, 5), 0);
// 计算梯度幅值和方向
Mat gradientX = new Mat();
Mat gradientY = new Mat();
Imgproc.Sobel(blurredImage, gradientX, -1, 1, 0);
Imgproc.Sobel(blurredImage, gradientY, -1, 0, 1);
// 计算梯度幅值和方向
Mat magnitude = new Mat();
Mat direction = new Mat();
Imgproc.cartToPolar(gradientX, gradientY, magnitude, direction);
}
}
在上述代码中,我们首先创建了两个新的 Mat
对象 gradientX
和 gradientY
,分别用于存储水平方向和垂直方向上的梯度。然后,使用 Imgproc.Sobel()
方法分别计算水平方向和垂直方向上的梯度。该方法的参数分别为输入图像、输出图像、输出图像的深度、水平方向上的导数阶数和垂直方向上的导数阶数。在这个例子中,我们将输出图像的深度设置为 -1
,表示与输入图像的深度相同。水平方向上的导数阶数为 1
,垂直方向上的导数阶数为 0
,表示计算水平方向上的梯度。同样地,我们可以计算垂直方向上的梯度。
接下来,我们使用 Imgproc.cartToPolar()
方法计算梯度幅值和方向。该方法的参数分别为水平方向上的梯度、垂直方向上的梯度、输出的梯度幅值和输出的梯度方向。
步骤四:非极大值抑制
非极大值抑制可以使用 JavaCV 中的 Imgproc
类的 Canny()
方法来实现。以下是进行非极大值抑制的代码示例:
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class CannyEdgeDetection {
public static void main(String[] args) {
// 读取图像
Mat image = Imgcodecs.imread("input.jpg");
if (image.empty()) {
System.out.println("无法读取图像");
return;
}
// 高斯滤波
Mat blurredImage = new Mat();
Imgproc.GaussianBlur(image, blurredImage, new Size(5, 5), 0);
// 计算梯度幅值和方向
Mat gradientX = new Mat();
Mat gradientY = new Mat();
Imgproc.Sobel(blurredImage, gradientX, -1, 1, 0);
Imgproc.Sobel(blurredImage, gradientY, -1, 0, 1);
// 计算梯度幅值和方向
Mat magnitude = new Mat();
Mat direction = new Mat();
Imgproc.cartToPolar(gradientX, gradientY, magnitude, direction);
// 非极大值抑制
Mat edges = new Mat();
Imgproc.Canny(magnitude, edges, 50, 150);
}
}
在上述代码中,我们首先创建了一个新的 Mat
对象 edges
,用于存储非极大值抑制后的边缘图像。然后,使用 Imgproc.Canny()
方法进行非极大值抑制。该方法的参数分别为输入的梯度幅值图像、输出的边缘图像、低阈值和高阈值。在这个例子中,我们将低阈值设置为 50
,高阈值设置为 150
。
步骤五:双阈值检测
双阈值检测已经在非极大值抑制的步骤中完成了,因为 Imgproc.Canny()
方法会自动进行双阈值检测。
案例分析与对比展示
为了更好地理解 Canny 边缘检测的效果,我们可以使用一张图片进行案例分析,并对比展示处理前后的图像。
以下是一个完整的示例代码,用于读取一张图片,进行 Canny 边缘检测,并显示处理前后的图像:
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.core.Core;
import org.opencv.highgui.HighGui;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
public class CannyEdgeDetection {
public static void main(String[] args) {
// 读取图像
Mat image = Imgcodecs.imread("input.jpg");
if (image.empty()) {
System.out.println("无法读取图像");
return;
}
// 高斯滤波
Mat blurredImage = new Mat();
Imgproc.GaussianBlur(image, blurredImage, new Size(5, 5), 0);
// 计算梯度幅值和方向
Mat gradientX = new Mat();
Mat gradientY = new Mat();
Imgproc.Sobel(blurredImage, gradientX, -1, 1, 0);
Imgproc.Sobel(blurredImage, gradientY, -1, 0, 1);
// 计算梯度幅值和方向
Mat magnitude = new Mat();
Mat direction = new Mat();
Core.cartToPolar(gradientX, gradientY, magnitude, direction);
// 非极大值抑制
Mat edges = new Mat();
Imgproc.Canny(magnitude, edges, 50, 150);
// 保存边缘检测后的图像
Imgcodecs.imwrite("path/to/your/edge_detection_result.jpg", edges);
}
}
在上述代码中,我们首先读取了一张名为 input.jpg
的图片。然后,对图像进行了高斯滤波、计算梯度幅值和方向、非极大值抑制等操作,得到了边缘检测后的图像。最后,使用 HighGui.imshow()
方法显示原始图像和边缘检测后的图像,并使用 HighGui.waitKey()
方法等待用户按下任意键退出程序。
以下是一张原始图像和经过 Canny 边缘检测后的图像对比:
原始图像 | 边缘检测后的图像 |
---|---|
从对比图中可以看出,经过 Canny 边缘检测
后,图像中的边缘更加清晰,物体的轮廓更加明显。
总结
本文详细介绍了 JavaCV
图像边缘检测之 Canny 边缘检测
算法。首先阐述了 Canny 边缘检测
的原理,包括高斯滤波、计算梯度幅值和方向、非极大值抑制以及双阈值检测等关键步骤。然后介绍了在 Java 项目中使用 JavaCV 实现 Canny 边缘检测
的 Maven 依赖。接着,通过详细的代码示例展示了 Canny 边缘检测
的每个关键步骤,并对代码进行了注释说明。最后,通过案例分析和图像对比展示了 Canny 边缘检测
的实际效果。通过本文的学习,读者可以掌握 Canny 边缘检测算法
的原理和实现方法,并能够在实际项目中应用这一技术来提取图像中的边缘信息。
参考资料文献
- OpenCV 官方文档
- JavaCV 官方文档
- 数字图像处理(第四版)