Canny算子

news2024/7/6 19:28:25

Canny算子_百度百科 (baidu.com)icon-default.png?t=N7T8https://baike.baidu.com/item/Canny%E7%AE%97%E5%AD%90/8821789?fr=ge_ala

图像处理中最经典的边沿检测算法: Canny边缘检测_哔哩哔哩_bilibiliicon-default.png?t=N7T8https://www.bilibili.com/video/BV1U4411277i/?spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=7c3bfbf39d037fe80c97234396acc524

读取图像

需要处理成灰度图,因为输出会根据图片的尺寸来决定输出的位置以及窗口的大小,所以我这里自己初始化固定了窗口大小有输出的位置。

import numpy as np
import cv2

# 读取图像
image = cv2.imread('input_image.jpg', cv2.IMREAD_GRAYSCALE)

# 将灰度图像调整为固定大小
gray_resized = cv2.resize(image, (500, 400), interpolation=cv2.INTER_NEAREST)

# 获取屏幕尺寸
screen_height, screen_width = 1080, 1920

# 计算窗口左上角位置
window_x = int((screen_width - 500) / 2)
window_y = int((screen_height - 400) / 2)

# 创建窗口并显示结果
cv2.namedWindow('GrayImage', cv2.WINDOW_NORMAL)  # 设置窗口大小可调整
cv2.resizeWindow('GrayImage', 500, 400)
cv2.moveWindow('GrayImage', window_x, window_y)
cv2.imshow('GrayImage', gray_resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

降噪(高斯滤波)

高斯滤波_百度百科 (baidu.com)icon-default.png?t=N7T8https://baike.baidu.com/item/%E9%AB%98%E6%96%AF%E6%BB%A4%E6%B3%A2/9032353?fr=ge_ala

OpenCV高斯滤波函数--GaussianBlur()参数说明_gaussianblur参数-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/qq_28126689/article/details/105953157用于平滑图像以消除噪声,使得后续的梯度计算更加稳定。

# 高斯滤波
image_blur = cv2.GaussianBlur(image, (5, 5), 1)

梯度计算

计算水平和垂直方向的梯度,因为是向量的形式他这个只有四条线来划分,假设计算出水平方向是指向0,垂直方向是指向90,他结果就是指向45,如果角度是30也会变成40。

# 计算梯度
gradient_x = cv2.Sobel(image_blur, cv2.CV_64F, 1, 0, ksize=3)
gradient_y = cv2.Sobel(image_blur, cv2.CV_64F, 0, 1, ksize=3)

gradient_magnitude = np.sqrt(gradient_x ** 2 + gradient_y ** 2)
gradient_direction = np.arctan2(gradient_y, gradient_x) * 180 / np.pi

非极大值抑制

将梯度图像中的局部最大值保留,以消除非边缘像素。对于每个像素,只有梯度方向上的像素值是局部最大值,才会保留,其他像素将被抑制。

就是根据前面梯度的方向,假设他梯度方向是垂直的,如果当前位置上面的元素和下面位置的元素都小于当前位置的元素,则当前位置就是局部最大值,就应该保留下来,反之就会被抑制。

 # 非极大值抑制
gradient_magnitude_suppressed = np.zeros_like(gradient_magnitude)
    for i in range(1, gradient_magnitude.shape[0] - 1):
        for j in range(1, gradient_magnitude.shape[1] - 1):
            angle = gradient_direction[i, j]
            if (0 <= angle < 22.5) or (157.5 <= angle <= 180) or (-22.5 <= angle < 0) or (-180 <= angle < -157.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i, j + 1]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i, j - 1]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]
            elif (22.5 <= angle < 67.5) or (-157.5 <= angle < -112.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i - 1, j + 1]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i + 1, j - 1]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]
            elif (67.5 <= angle < 112.5) or (-112.5 <= angle < -67.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i - 1, j]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i + 1, j]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]
            elif (112.5 <= angle < 157.5) or (-67.5 <= angle < -22.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i - 1, j - 1]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i + 1, j + 1]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]

双阈值检测

像素梯度大于高阈值,则将其标记为强边缘;如果像素梯度介于低阈值和高阈值之间,则将其标记为弱边缘;否则,将其标记为非边缘。

介于两条线之间的就是可能保留可能淘汰还要看后续的处理,高分数线以上的直接保留,低分数线没达到的直接淘汰。

# 双阈值检测
gradient_magnitude_suppressed_normalized = cv2.normalize(gradient_magnitude_suppressed, None, 0, 255,
                                                             cv2.NORM_MINMAX, cv2.CV_8U)
    low_threshold = int(low_threshold)
    high_threshold = int(high_threshold)

    strong_edges = (gradient_magnitude_suppressed_normalized >= high_threshold)
    weak_edges = (gradient_magnitude_suppressed_normalized >= low_threshold) & (
                gradient_magnitude_suppressed_normalized < high_threshold)

边缘连接

将弱边缘连接到强边缘,以形成完整的边缘。如果弱边缘与任何一个强边缘相连,则将其标记为边缘;否则,将其标记为非边缘。

就是画了两条分数线,低分数线以下的直接淘汰,高分数以上的直接录取,如果介于两条线之间的,看有没有关系,如果有关系就保留,没有关系就淘汰。

# 边缘连接
strong_edges_idx = np.argwhere(strong_edges)
weak_edges_idx = np.argwhere(weak_edges)

edge_image = np.zeros_like(gradient_magnitude_suppressed_normalized)
edge_image[strong_edges] = 255

for i, j in weak_edges_idx:
    if np.any(strong_edges[i - 1:i + 2, j - 1:j + 2]):
       edge_image[i, j] = 255

代码 

import numpy as np
import cv2


def CannyEdgeDetection(image, sigma=1, low_threshold=20, high_threshold=50):
    # 1. 高斯滤波
    image_blur = cv2.GaussianBlur(image, (5, 5), sigma)

    # 2. 计算梯度
    gradient_x = cv2.Sobel(image_blur, cv2.CV_64F, 1, 0, ksize=3)
    gradient_y = cv2.Sobel(image_blur, cv2.CV_64F, 0, 1, ksize=3)

    gradient_magnitude = np.sqrt(gradient_x ** 2 + gradient_y ** 2)
    gradient_direction = np.arctan2(gradient_y, gradient_x) * 180 / np.pi

    # 3. 非极大值抑制
    gradient_magnitude_suppressed = np.zeros_like(gradient_magnitude)
    for i in range(1, gradient_magnitude.shape[0] - 1):
        for j in range(1, gradient_magnitude.shape[1] - 1):
            angle = gradient_direction[i, j]
            if (0 <= angle < 22.5) or (157.5 <= angle <= 180) or (-22.5 <= angle < 0) or (-180 <= angle < -157.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i, j + 1]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i, j - 1]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]
            elif (22.5 <= angle < 67.5) or (-157.5 <= angle < -112.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i - 1, j + 1]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i + 1, j - 1]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]
            elif (67.5 <= angle < 112.5) or (-112.5 <= angle < -67.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i - 1, j]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i + 1, j]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]
            elif (112.5 <= angle < 157.5) or (-67.5 <= angle < -22.5):
                if (gradient_magnitude[i, j] >= gradient_magnitude[i - 1, j - 1]) and (
                        gradient_magnitude[i, j] >= gradient_magnitude[i + 1, j + 1]):
                    gradient_magnitude_suppressed[i, j] = gradient_magnitude[i, j]

    # 4. 双阈值检测
    gradient_magnitude_suppressed_normalized = cv2.normalize(gradient_magnitude_suppressed, None, 0, 255,
                                                             cv2.NORM_MINMAX, cv2.CV_8U)
    low_threshold = int(low_threshold)
    high_threshold = int(high_threshold)

    strong_edges = (gradient_magnitude_suppressed_normalized >= high_threshold)
    weak_edges = (gradient_magnitude_suppressed_normalized >= low_threshold) & (
                gradient_magnitude_suppressed_normalized < high_threshold)

    # 5. 边缘连接
    strong_edges_idx = np.argwhere(strong_edges)
    weak_edges_idx = np.argwhere(weak_edges)

    edge_image = np.zeros_like(gradient_magnitude_suppressed_normalized)
    edge_image[strong_edges] = 255

    for i, j in weak_edges_idx:
        if np.any(strong_edges[i - 1:i + 2, j - 1:j + 2]):
            edge_image[i, j] = 255

    return edge_image


# 读取图像
image = cv2.imread('input_image.jpg', cv2.IMREAD_GRAYSCALE)

# 使用Canny算子进行边缘检测
edges = CannyEdgeDetection(image)

# 将边缘图像调整为固定大小
edges_resized = cv2.resize(edges, (500, 400), interpolation=cv2.INTER_NEAREST)

# 获取屏幕尺寸
screen_height, screen_width = 1080, 1920

# 计算窗口左上角位置
window_x = int((screen_width - 500) / 2)
window_y = int((screen_height - 400) / 2)

# 创建窗口并显示结果
cv2.namedWindow('Canny Edge Detection', cv2.WINDOW_NORMAL)  # 设置窗口大小可调整
cv2.resizeWindow('Canny Edge Detection', 500, 400)
cv2.moveWindow('Canny Edge Detection', window_x, window_y)
cv2.imshow('Canny Edge Detection', edges_resized)
cv2.waitKey(0)
cv2.destroyAllWindows()

 这个代码是用chatGpt生成的,思路大概是没错的,但是不知道能不能对的上,还有就是识别出来的图像不对,可能很稀疏的几个点,也可能很密集,这个就需要调整高阈值和低阈值来处理。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【静态分析】在springboot使用太阿(Tai-e)02

参考&#xff1a;使用太阿&#xff08;Tai-e&#xff09;进行静态代码安全分析&#xff08;spring-boot篇二&#xff09; - 先知社区 本文章使用的被分析代码为GitHub - JoyChou93/java-sec-code: Java web common vulnerabilities and security code which is base on springb…

docxtemplater避坑!!! 前端导出word怎么插入本地图片或base64 有完整示例

用docxtemplater库实现前端通过模板导出word&#xff0c;遇到需求&#xff0c;要插图片并转成word并导出&#xff0c;在图片转换这块遇到了问题&#xff0c;网上查示例大多都跑不通&#xff0c;自己琢磨半天&#xff0c;总算搞明白了。 附上清晰完整示例&#xff0c;供参考。 …

医卫兰大药学试题及答案,分享几个实用搜题和学习工具 #媒体#微信

这些软件以其强大的搜索引擎和智能化的算法&#xff0c;为广大大学生提供了便捷、高效的解题方式。下面&#xff0c;让我们一起来了解几款备受大学生欢迎的搜题软件吧&#xff01; 1.彩虹搜题 这个是公众号 题库内容丰富全面&#xff0c;细致分类整理。找题再也不费力&#…

数据库|基于T-SQL添加默认约束、外键约束、内连接查询

哈喽&#xff0c;你好啊&#xff0c;我是雷工&#xff01; 前边学习了基于T-SQL48_47.基于T-SQL添加数据、CRUD操作、标识列详解&#xff1a;《数据库|基于T-SQL向数据库数据表中添加、修改、删除数据》 接下来接着学习基于T-SQL添加默认约束、外键约束、内连接查询&#xff0c…

多个文本如何一键导出二维码?在线批量生码的制作方法

当存在多条文本数据并且需要将每条数据生成单独的二维码来使用&#xff0c;很多小伙伴可能还在用一个一个来制作的方法&#xff0c;在二维码生成器上将文本转二维码。这种方式操作起来比较的繁琐&#xff0c;需要浪费大量的时间&#xff0c;那么有什么方法可以简化这个过程吗&a…

【Python】 如何将列表转换为字符串

基本原理 在Python中&#xff0c;列表&#xff08;list&#xff09;和字符串&#xff08;string&#xff09;是两种不同的数据类型。列表是一个有序的元素集合&#xff0c;可以包含不同类型的元素&#xff0c;而字符串则是一个字符序列。有时&#xff0c;我们可能需要将列表中…

记录一次cnvd事件型证书漏洞挖掘

事件起因是因为要搞毕设了&#xff0c;在为这个苦恼&#xff0c;突然负责毕设的老师说得到cnvd下发的证书结合你的漏洞挖掘的过程是可以当成毕设的&#xff0c;当时又学习了一段时间的web渗透方面的知识&#xff0c;于是踏上了废寝忘食的cnvd证书漏洞挖掘的日子。 前言&#x…

卷径计算(PID输出补偿法 SCL源代码)

卷径计算有很多方法,这里我们提供另一个思路,这里我们采用的是通过速度控制间接控制张力通过线速度和系统卷径我们可以计算出我们的速度前馈量(主速度)。具体收放卷前馈量计算可以参考下面文章链接: 收放卷前馈量计算FC(梯形图+SCL代码)-CSDN博客文章浏览阅读584次。这篇博…

Hexo最新实战:(一)Hexo7.0+GitHub Pages博客搭建

前言 很多平台都能写博客还有创作激励&#xff0c;为什么我又要搭一个&#xff1f;为什么这次要选择用Hexo框架&#xff1f; 对应的原因是流量自由和省钱&#xff0c;第一个&#xff0c;很多平台能写但不是都有收益&#xff0c;而且平台有自身的规则&#xff0c;比如会屏蔽一…

鸿蒙大厂目前政策变现沉淀思考

鸿蒙引擎定制优化 鸿蒙端hotfix&#xff1a; 技术栈太大了&#xff0c;但是鸿蒙需要学习什么呢&#xff1f; 什么最有价值&#xff1f; 这就是接下来需要表达下我的观点&#xff1a; 1、APP开发 2、应用市场技术专员 【游戏、电商重型APP性能的处理 SmartPerf、构建自己的工…

如果一个开发初学者从今天开始,他们应该学习什么?

What should a beginner developer learn if they were to start today? by Scott Hanselman 如果从今天才开始学习&#xff0c;新手开发者要学习什么&#xff1f; 新的开发人员今天应该从哪里开始&#xff1f; 他们应该学习什么来为自己的职业生涯做好准备&#xff1f;Sco…

做微火全域运营服务商要注意的风险有哪些?

随着全域运营赛道日渐兴起&#xff0c;微火全域运营服务商申请人不断增多&#xff0c;逐渐成为众多新兴职业中的一大热门。 所谓微火全域运营服务商&#xff0c;顾名思义&#xff0c;就是通过操作微火全域运营系统做全域运营的一类群体&#xff0c;其业务范围为公域和私域内所有…

IC开发——Verilator

1. 简介 Verilator 是一个开源的 Verilog 和 SystemVerilog 硬件描述语言 (HDL) 仿真器。它是一个高性能的仿真器,可以将 Verilog 和 SystemVerilog 代码转换为 C/SystemC 代码,并生成可执行的仿真模型。 Verilator 的主要特点包括: 高性能:Verilator 生成的仿真模型具有非常…

Bootstrap 3.x 版本基础引入指南

Bootstrap 是一款广受欢迎的前端框架&#xff0c;它简化了网页设计与开发流程&#xff0c;帮助开发者快速创建响应式布局和美观的网页界面。本文将向您介绍如何在项目中引入 Bootstrap 3.x 版本的基本步骤&#xff0c;包括 CSS 和 JavaScript 文件的引用&#xff0c;以及必要的…

关于Java Agent的使用、工作原理、及hotspot源码 解析

说明&#xff1a; 本文很长&#xff0c;长到超出了掘金编辑器的限制字符数 10万&#xff0c;所以我在最后边只是图解&#xff0c;没有更多的文字和代码描述了&#xff0c;本文知识点较多&#xff0c;如果没接触过agent那必然大概率会懵&#xff08;大部分知识点讲解完后&#x…

瑞_Windows环境下使用bat重启jar包等服务

文章目录 命令示例重启ray-project.jar重启redis服务 &#x1f64a; 前言&#xff1a;经验分享——Windows环境下使用.bat批处理文件重启 jar 包等服务。在学习或者工作日常中&#xff0c;有时候会需要在 Windows 系统环境下去启动 jar 包或其它服务&#xff0c;此时如果使用关…

百世慧入选第七届数字中国建设峰会“2024企业数字化转型典型应用案例”

5月24日-25日&#xff0c;第七届数字中国建设峰会在福州举行。本届峰会是国家数据工作体系优化调整后首次举办的数字中国建设峰会&#xff0c;主题为“释放数据要素价值&#xff0c;发展新质生产力”。 为了全方位展示各领域数字化最新成果&#xff0c;共创数字中国美好未来&a…

mail发送调用接口如何与三方服务无缝对接?

mail发送调用接口的性能怎么样&#xff1f;调用邮件接口的技巧&#xff1f; 为了提高效率和自动化水平&#xff0c;企业通常会选择使用mail发送调用接口。然而&#xff0c;仅仅使用这些接口还不够&#xff0c;如何与各种第三方服务无缝对接同样至关重要。AokSend将探讨如何有效…

Golang性能分析工具pprof--远程分析时无法定位源代码行数问题解决方案

场景 通过命令行模式的list命令&#xff0c;为了查看指标消耗在具体哪一行&#xff0c;需要源代码。但实际程序是部署在线上或者程序的源代码目录变了&#xff0c;则pprof从默认路径找不到代码&#xff0c;无法显示是哪一行的问题。 通过浏览器模式的source页面&#xff0c;有…

Linux java jni调用C++封装动态库

由于项目中java需要调用第三方提供的C动态库&#xff1b;由于第三方动态库传入的参数较多&#xff0c;还伴随着指针传入操作&#xff0c;导致java调用极为不便&#xff01;因此催生出对于第三方的C动态库进行二次封装。java调用只需按结构传入一个结构化的string即可。话不多说…