【Java版】OpenCV-Canny边缘检测和轮廓查找

一、概述

在Java环境下实现Canny边缘检测提供了便利。

通过JavaCV,我们可以轻松地将Canny边缘检测算法应用到各种图像处理项目中,充分发挥其优势。在图像处理领域,边缘检测是一项至关重要的任务。图像中的边缘包含了丰富的信息,例如物体的轮廓、区域的边界等。边缘检测算法有多种,其中 Canny边缘检测算法 以其精确性和可靠性而备受关注。Canny边缘检测算法的目标是在尽可能减少噪声影响的同时,准确地检测出图像中的边缘。

二、JavaCV环境配置

在使用JavaCV之前,需要先配置开发环境。

可以通过Maven引入JavaCV的依赖,或者手动下载Jar包。 

以下是Maven依赖添加示例:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>com.test</groupId>
    <artifactId>3d</artifactId>
    <version>1.0-SNAPSHOT</version>
 
    <dependencies>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.9</version>
        </dependency>
 
    </dependencies>
 
</project>

三、Canny算子

Canny边缘检测算子是John F. Canny于1986年开发出来的一个多级边缘检测算法。它的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:

  • 好的检测- 算法能够尽可能多地标识出图像中的实际边缘。

  • 好的定位- 标识出的边缘要与实际图像中的实际边缘尽可能接近。

  • 最小响应- 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。

为了满足这些要求Canny使用了变分法,这是一种寻找满足特定功能函数的方法。最优检测使用四个指数函数项的和表示,但是它非常近似于高斯函数的一阶导数

3.1 降噪

任何边缘检测算法都不可能在未经处理的原始数据上很好地处理,所以第一步是对原始数据与高斯平滑模板作卷积,得到的图像与原始图像相比有些轻微的模糊(blurred)。这样,单独的一个像素噪声在经过高斯平滑的图像上变得几乎没有影响。 [1]

3.2 寻找梯度

图像中的边缘可能会指向不同的方向,所以Canny算法使用4个mask检测水平、垂直以及对角线方向的边缘。原始图像与每个mask所作的卷积都存储起来。对于每个点我们都标识在这个点上的最大值以及生成的边缘的方向。这样我们就从原始图像生成了图像中每个点亮度梯度图以及亮度梯度的方向。

3.3 跟踪边缘

较高的亮度梯度比较有可能是边缘,但是没有一个确切的值来限定多大的亮度梯度是边缘多大又不是,所以Canny使用了滞后阈值。

滞后阈值需要两个阈值——高阈值与低阈值。假设图像中的重要边缘都是连续的曲线,这样我们就可以跟踪给定曲线中模糊的部分,并且避免将没有组成曲线的噪声像素当成边缘。所以我们从一个较大的阈值开始,这将标识出我们比较确信的真实边缘,使用前面导出的方向信息,我们从这些真正的边缘开始在图像中跟踪整个的边缘。在跟踪的时候,我们使用一个较小的阈值,这样就可以跟踪曲线的模糊部分直到我们回到起点。

一旦这个过程完成,我们就得到了一个二值图像,每点表示是否是一个边缘点。

四、代码

package com.test;

import org.bytedeco.javacv.CanvasFrame;
import org.bytedeco.javacv.OpenCVFrameConverter;
import org.bytedeco.opencv.global.opencv_imgcodecs;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.*;

import static org.bytedeco.opencv.global.opencv_imgcodecs.imread;
import static org.bytedeco.opencv.global.opencv_imgproc.Canny;
import static org.bytedeco.opencv.global.opencv_imgproc.GaussianBlur;
import static org.opencv.imgcodecs.Imgcodecs.IMREAD_GRAYSCALE;
//边缘检测是图像处理中的一个关键步骤,
// 它有助于识别图像中对象的轮廓
//这里我们使用了Canny边缘检测算法,它是一种广泛使用的边缘检测算法。
// Canny函数的两个参数分别为低阈值和高阈值,
// 这些阈值用于决定何种梯度强度的边缘应该被检测。

public class ImageEdgeDetectionExample {
    public static void main(String[] args) {
        // 加载图像
        Mat image = imread("/Users/csguo007/Desktop/images.jpeg");
        //Mat image = imread("/Users/csguo007/Desktop/images.jpeg", IMREAD_GRAYSCALE);
        // 检查图像是否正确加载
        if (image.empty()) {
            System.out.println("Error loading image!");
            return;
        }

        //图片灰度处理
        Mat gray = new Mat();
        opencv_imgproc.cvtColor(image, gray, opencv_imgproc.COLOR_BGR2GRAY); // 转换为灰度图,因为边缘检测通常在灰度图上进行

        // 应用高斯模糊
        Mat blurImage =new Mat();
        GaussianBlur(gray, blurImage, new Size(5, 5), 0);

        // 创建一个Mat对象用于存放边缘检测结果
        // 使用Canny算法进行边缘检测
        // 低阈值100,高阈值200
        // 低于100此值的边缘被丢弃。高于200此值的边缘保留,中间值根据连通性决定。
        Mat edges = new Mat();
        opencv_imgproc.Canny(blurImage, edges, 100, 200);

        // 查找轮廓
        Mat hierarchy = new Mat();
        MatVector contours = new MatVector();
        opencv_imgproc.findContours(edges, contours, hierarchy, opencv_imgproc.RETR_TREE, opencv_imgproc.CHAIN_APPROX_SIMPLE);

        //在原始图像上绘制轮廓
        for (int i = 0; i < contours.size(); i++) {
            Rect rect = opencv_imgproc.boundingRect(contours.get(i));
            //线条的颜色
            Scalar scalar = new Scalar(255, 0, 0, 255);
            double aspectRatio = (double) rect.width() / rect.height(); // 计算宽高比
            // 检查宽高比是否符合矩形特征
            /*if (aspectRatio >= 0.8 && aspectRatio <= 1.2) {
                opencv_imgproc.rectangle(edges, rect, scalar);
            }*/
            
            //绘制线条
            opencv_imgproc.rectangle(edges, rect, scalar);
        }

        // 创建一个窗口
        CanvasFrame canvasFrame = new CanvasFrame("Image Display");
        canvasFrame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
        canvasFrame.showImage(new OpenCVFrameConverter.ToMat().convert(edges));
    }
}

五、学习资源 

  更多学习资料,欢迎加入我们

### OpenCV-Python 轮廓检测教程 轮廓检测是计算机视觉领域的重要技术之一,广泛应用于对象检测、边缘检测以及形状分析等领域。以下是基于 OpenCV-Python轮廓检测示例代码及其说明。 #### 示例代码 以下是一个完整的 Python 代码示例,展示如何使用 `cv2.findContours()` `cv2.drawContours()` 函数进行轮廓检测并将其绘制在图像上: ```python import cv2 import numpy as np # 加载原始图像 image = cv2.imread('example.jpg') # 将图像转换为灰度图 gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 使用高斯模糊减少噪声(可选) blurred_image = cv2.GaussianBlur(gray_image, (5, 5), 0) # 应用 Canny 边缘检测算法生成二值化图像 edges = cv2.Canny(blurred_image, threshold1=30, threshold2=100) # 查找轮廓 contours, hierarchy = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 创建一个新的空白图像用于绘制轮廓 blank_image = np.zeros_like(image) # 绘制轮廓 cv2.drawContours(blank_image, contours, -1, (0, 255, 0), 2) # 显示结果 cv2.imshow('Original Image', image) cv2.imshow('Edges', edges) cv2.imshow('Detected Contours', blank_image) # 等待按键关闭窗口 cv2.waitKey(0) cv2.destroyAllWindows() ``` --- #### 代码解释 1. **加载图像**: 首先通过 `cv2.imread()` 方法加载输入图像。 2. **灰度转换**: 使用 `cv2.cvtColor()` 将彩色图像转换为灰度图像[^1]。 3. **降噪处理**: 可选地应用高斯滤波器 (`cv2.GaussianBlur`) 来平滑图像,从而降低噪声的影响。 4. **边缘检测**: 利用 Canny 边缘检测算法 (`cv2.Canny`) 获取二值化的边缘图像[^5]。 5. **查找轮廓**: 借助 `cv2.findContours` 函数提取图像中的轮廓信息[^4]。 6. **绘制轮廓**: 使用 `cv2.drawContours` 将找到的轮廓绘制到新创建的空白图像上[^2]。 7. **显示结果**: 最终通过 `cv2.imshow` 展示原图、边缘图检测后的轮廓图。 --- #### 关键点解析 - **参数设置**: - `cv2.RETR_EXTERNAL`: 表示只检索最外层的轮廓- `cv2.CHAIN_APPROX_SIMPLE`: 对轮廓进行压缩,仅保留端点坐标。 - **颜色空间转换**: 如果需要将 BGR 图像转换为 RGB 格式以便于其他库(如 Matplotlib)使用,则可以通过 NumPy 实现快速的颜色通道反转:`img_RGB = img_BGRA[:, :, ::-1]`[^3]。 - **注意事项**: - 输入给 `cv2.findContours` 的图像必须是二值图形式,因此需提前完成灰度化与阈值分割操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

常生果

喜欢我,请支持我

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值