JavaCV 图像边缘检测 之 Canny 算法

🧑 博主简介:CSDN博客专家历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c=1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。

在这里插入图片描述


在这里插入图片描述

JavaCV 图像边缘检测 之 Canny 算法

引言

在图像处理领域,边缘检测是一项至关重要的任务。图像中的边缘包含了丰富的信息,例如物体的轮廓区域的边界等。通过准确地检测边缘,我们可以进一步对图像进行分析、识别和理解。

边缘检测算法有多种,其中 Canny边缘检测算法 以其精确性可靠性而备受关注。Canny边缘检测算法的目标是在尽可能减少噪声影响的同时,准确地检测出图像中的边缘。它不像一些简单的边缘检测算法那样容易受到噪声干扰而产生虚假边缘,也不会遗漏重要的边缘信息。

在实际应用中,Canny边缘检测广泛应用于计算机视觉医学图像处理工业检测等众多领域。例如,在计算机视觉的目标识别任务中,准确的边缘检测可以帮助我们更好地定位和识别目标物体的形状;在医学图像处理中,对X光CT等图像进行边缘检测有助于医生发现病变区域的轮廓;在工业检测方面,可以用于检测产品的外形缺陷等。

JavaCV是一个在Java平台上用于计算机视觉的库,它为我们在Java环境下实现Canny边缘检测提供了便利。通过JavaCV,我们可以轻松地将Canny边缘检测算法应用到各种图像处理项目中,充分发挥其优势。接下来,我们将详细介绍如何使用JavaCV来实现Canny边缘检测

目录

  1. Canny 边缘检测的原理
  2. JavaCV 实现 Canny 边缘检测的 Maven 依赖
  3. JavaCV 实现 Canny 边缘检测的步骤
    • 步骤一:图像读取
    • 步骤二:高斯滤波
    • 步骤三:计算梯度幅值和方向
    • 步骤四:非极大值抑制
    • 步骤五:双阈值检测
  4. 案例分析与对比展示
  5. 总结
  6. 参考资料文献

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 对象 gradientXgradientY,分别用于存储水平方向和垂直方向上的梯度。然后,使用 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 边缘检测算法的原理和实现方法,并能够在实际项目中应用这一技术来提取图像中的边缘信息。

参考资料文献

  1. OpenCV 官方文档
  2. JavaCV 官方文档
  3. 数字图像处理(第四版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/910333.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java Agent使用

文章目录 基本使用premain使用场景 agentmain 关于tools.jar https://docs.oracle.com/en/java/javase/20/docs/specs/jvmti.html com.sun的API&#xff0c;如果使用其他厂商的JVM&#xff0c;可能没有这个API了&#xff0c;比如Eclipse的J9 https://www.ibm.com/docs/en/sdk…

解决客服打字慢的快捷回复软件

客服每天都要打字回复咨询&#xff0c;打字慢必然影响回复效率&#xff0c;聊天宝就是一款解决客服打字慢的快捷回复软件 前言 总所周知&#xff0c;客服每天都要打字回复咨询&#xff0c;打字慢必然影响回复效率&#xff0c;所以解决打字速度慢的问题&#xff0c;就是很多客服…

【2024最新版Kotlin教程】Kotlin第一行代码系列第六课-嵌套类,数据类,密封类

【2024最新版Kotlin教程】Kotlin第一行代码系列第六课-嵌套类&#xff0c;数据类&#xff0c;密封类 数据类在之前的课程已经讲了&#xff0c;嵌套类和密封类可以不学&#xff0c;很少用到的。 但为了系统的完整性还是说一下 一、嵌套类&#xff1a; 选学&#xff1a;按我经…

uniapp 开发公众号 h5 页面 错误 “redirect_uri 参数错误“

公众号 h5 支付 调错避坑 1. 一直显示以下错误 ”redirect_uri 失效了“不用管直接 打包上传服务器 2. 直接在”微信开发者工具“ 进行调试&#xff08; 使用线上地址访问 &#xff09;就能看到真实 接口 错误&#xff0c;在进行调试

闪存学习_2:Flash-Aware Computing from Jihong Kim

闪存学习_2&#xff1a;Flash-Aware Computing from Jihong Kim【1】 一、三个闪存可靠性问题二、内存的分类三、NAND 闪存和 NOR 闪存四、HDD和SSD比较Reference 一、三个闪存可靠性问题 耐性&#xff08;即寿命&#xff09;&#xff1a;最多能经受编程和擦除的次数。数据保留…

代码随想录算法训练营Day55 | 图论理论基础、深度优先搜索理论基础、卡玛网 98.所有可达路径、797. 所有可能的路径、广度优先搜索理论基础

目录 图论理论基础 深度优先搜索理论基础 卡玛网 98.所有可达路径 广度优先搜索理论基础 图论理论基础 图论理论基础 | 代码随想录 图的基本概念 图的种类 大体分为有向图和无向图。 图中的边有方向的是有向图&#xff1a; 图中的边没有方向的是无向图&#xff1a; 图…

OpenEuler 使用ffmpeg x11grab捕获屏幕流,rtsp推流,并用vlc播放

环境准备 安装x11grab(用于捕获屏幕流)和libx264(用于编码) # 基础开发环境&x11grab sudo dnf install -y \autoconf \automake \bzip2 \bzip2-devel \cmake \freetype-devel \gcc \gcc-c \git \libtool \make \mercurial \pkgconfig \zlib-devel \libX11-devel \libXext…

【Simulink仿真】混合储能平抑光伏功率波动

摘要 本文基于Simulink仿真平台&#xff0c;提出了一种混合储能系统&#xff08;Hybrid Energy Storage System, HESS&#xff09;来平抑光伏发电中的功率波动。该系统将超级电容与电池相结合&#xff0c;通过双向DC-DC变换器实现能量的动态分配。超级电容响应快&#xff0c;主…

C语言必做30道练习题

C语言练习30题&#xff08;分支循环&#xff0c;数组&#xff0c;函数&#xff0c;递归&#xff0c;操作符&#xff09; 目录 分支循环1.闰年的判断2.阅读代码&#xff0c;计算代码输出的结果3.输入一个1~7的数字&#xff0c;打印对应的星期几4.输入任意一个整数值&#xff0c;…

进程与线程+多线程优势

区别&#xff1a; 1、进程中包含线程&#xff0c;每一个进程都至少一个线程&#xff08;主线程&#xff09; 2、进程是申请系统资源的最小单位 3、进程是CPU调度的最小单位 4、线程之间共享进程申请的系统资源 5、一个线程崩溃了会影响整个进程 进程的组织方式&#xff1…

期权懂|期权策略中两边开卖方实值对冲会有盈利区间吗?

期权小懂每日分享期权知识&#xff0c;帮助期权新手及时有效地掌握即市趋势与新资讯&#xff01; 期权策略中两边开卖方实值对冲会有盈利区间吗&#xff1f; 一、期权策略中两边开卖方实值对冲的盈利区间可以‌核心策略‌分析‌&#xff1a; 期权对冲策略的核心是利用期权的特…

Follow软件的使用入门教程

开篇 看到很多兄弟还不知道怎么用这个当下爆火的浏览器&#xff01;在这里简单给需要入门的小伙伴一些建议&#xff1a; 介绍 简单解释一下&#xff0c;RSS 意思是简易信息聚合&#xff0c;用户可以通过 RSS 阅读器或聚合工具自主订阅并浏览各个平台的内容源&#xff0c;不用…

数字孪生的建构之路:从数据到智能

数字孪生是一种将物理实体系统或产品的数字化表示与其实体对应物相结合的概念&#xff0c;通过这种数字化技术&#xff0c;可以实时监测、预测和优化管理实体系统。实现数字孪生需要经历从数据采集、处理到智能化决策等多个步骤。以下是关于如何实现数字孪生的详细介绍。 1. 数…

【C#】创建一个主菜单和弹出菜单系统

文章目录 1. 创建WinForms项目2. 设计窗体3. 添加MenuStrip4. 配置菜单项5. 添加TextBox6. 编写事件处理代码7. 运行和测试 根据您提供的文件内容&#xff0c;看起来您需要在C# WinForms应用程序中设置一个窗体&#xff0c;其中包含一个文本框和几个菜单项&#xff0c;用于改变…

运维告警策略优化与实践

在运维行业中&#xff0c;告警策略的制定与执行是确保系统稳定性和业务连续性的关键环节。面对日益复杂的IT环境和不断变化的运维需求&#xff0c;如何合理制定并优化告警策略&#xff0c;成为运维团队必须面对的重要课题。本文将结合运维行业的现状、挑战及需求&#xff0c;深…

算法通关(3) -- kmp算法

KMP算法的原理 从题目引出 有两个字符串s1和s2,判断s1字符串是否包含s2字符串&#xff0c;如果包含返回s1包含s2的最左开头位置&#xff0c;不包含返回-1&#xff0c;如果是按照暴力的方法去匹配&#xff0c;以s1的每个字符作为开头&#xff0c;用s2的整体去匹配&#xff0c;…

vue3+vite搭建脚手架项目使用eletron打包成桌面应用+可以热更新

当前Node版本&#xff1a;18.12.0&#xff0c;npm版本&#xff1a;8.19.2 1.搭建脚手架项目 搭建Vue3ViteTs脚手架-CSDN博客 可删掉index.html文件的title标签 2.配置package.json {"name": "my-vite-project","private": true,"versi…

Java学习者的福音:SpringBoot教学辅助平台

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理教学辅助平台的相关信息成为必然。开发合适…

JAVA基础:数组 (习题笔记)

一&#xff0c;编码题 1&#xff0c;数组查找操作&#xff1a;定义一个长度为10 的一维字符串数组&#xff0c;在每一个元素存放一个单词&#xff1b;然后运行时从命令行输入一个单词&#xff0c;程序判断数组是否包含有这个单词&#xff0c;包含这个单词就打印出“Yes”&…

网络层5——IPV6

目录 一、IPv6 vs IPv4 1、对IPv6主要变化 2、IPv4 vs IPv6 二、IPv6基本首部 1、版本——4位 2、通信量类——8位 3、流标号——20位 4、有效载荷长度——16位 5、下一个首部——8位 6、跳数限制——8位 7、源 、 目的地址——128位 8、扩展首部 三、IPv6地址 1…