OpenCV基础——梯度计算、边缘检测、图像金字塔

接上期:

OpenCV基础——图像滤波和形态学操作-CSDN博客


一.梯度计算

上贴已经讲过,梯度可以浅显地理解为图像中发生颜色变化的局部区域,也即边界点。本质上是通过构造与卷积核相同的矩阵,计算边缘区域像素点的差异值——也即梯度

1.sobel算子

sobel函数中的第二个参数相当于扩充位数,意味着可以存在负数,1和0分别表示水平和垂直梯度,这里哪个为1表示算哪个,最后是核函数的大小:

sobel=cv2.Sobel(thresh,cv2.CV_64F,1,0,ksize=3)
cv_show(sobel,'sobel')

这里还用的上贴的图,需要注意的是要把图像转换为灰度图再计算边缘,这里不再赘述:

这时大家能看出来一些端倪——好像只显示了轴对称的一半?这是因为图像梯度的计算方式为核矩阵右边的元素减左边的元素,而我们选的图为外黑内白,这样的话白减黑为白色,黑减白为负由于阈值问题统一变为黑色,因此这里显示不出来,而那条竖杠也是这个道理~

因此我们要将被截断的负数取绝对值,这样另一半也能正常显示:

为了防止各位不看上贴有点蒙圈,这里再展示一下垂直方式梯度的完整代码:

import cv2
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Circle.jpg')
img=cv2.resize(img,(0,0),fx=0.7,fy=0.7)
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
sobel=cv2.Sobel(thresh,cv2.CV_64F,0,1,ksize=3)
sobel=cv2.convertScaleAbs(sobel)
cv_show(sobel,'sobel')

效果有点像水平旋转了一下:

分别得到水平和垂直的梯度,然后用我们第一帖里面提到的addweight函数叠加一下即可:

sobelx=cv2.Sobel(thresh,cv2.CV_64F,1,0,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.Sobel(thresh,cv2.CV_64F,0,1,ksize=3)
sobely=cv2.convertScaleAbs(sobely)
sobel=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)

相比单向的,缺少的线条更合理地展现了出来

注意,最好不要同时计算水平和垂直的梯度,效果不如分开算的好。


我们再换一张图整体展示一下梯度计算的效果:

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Bayern.jpg')
img=cv2.resize(img,(0,0),fx=0.7,fy=0.7)
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
sobelx=cv2.Sobel(thresh,cv2.CV_64F,1,0,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.Sobel(thresh,cv2.CV_64F,0,1,ksize=3)
sobely=cv2.convertScaleAbs(sobely)
sobel=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
tog=np.hstack((thresh,sobel))
cv_show(tog,'tog')

实际上,如果不做阈值操作效果会更好,大家自行尝试:

 

2.scharr算子

思想上来说没什么区别,只是核中的值较前者会更大一些,在边缘检测的效果中会更加敏感~

scharrx=cv2.Scharr(thresh,cv2.CV_64F,1,0)
scharry=cv2.Scharr(thresh,cv2.CV_64F,0,1)
scharr=cv2.addWeighted(scharrx,0.5,scharry,0.5,0)
tog=np.hstack((thresh,scharr))
cv_show(tog,"tog")

另一个区别是scharr中没有核大小的参数。

3.lapkacian算子

较前两者会更加敏感一些,与此同时不可避免地会对噪声点也敏感,因此效果不一定会更好,往往需要配合其他方法使用。

另一个不同是当前点要和周围四个点的值相关,因此不需要像前两者一样分别计算竖直和水平再聚合:

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Bayern.jpg')
img=cv2.resize(img,(0,0),fx=0.7,fy=0.7)
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
sobelx=cv2.Sobel(thresh,cv2.CV_64F,1,0,ksize=3)
sobelx=cv2.convertScaleAbs(sobelx)
sobely=cv2.Sobel(thresh,cv2.CV_64F,0,1,ksize=3)
sobely=cv2.convertScaleAbs(sobely)
sobel=cv2.addWeighted(sobelx,0.5,sobely,0.5,0)scharrx=cv2.Scharr(thresh,cv2.CV_64F,1,0)
scharry=cv2.Scharr(thresh,cv2.CV_64F,0,1)
scharr=cv2.addWeighted(scharrx,0.5,scharry,0.5,0)laplacian=cv2.Laplacian(thresh,cv2.CV_64F)
laplacian=cv2.convertScaleAbs(laplacian)tog=np.vstack((sobel,scharr,laplacian))
cv_show(tog,"tog")

这里直接聚合展示: 

事实上,无论哪种边缘检测,还是要结合具体应用场景和图片才能更好地评价优劣~ 

二.边缘检测

这里介绍的是Canny边缘检测算法:

1.高斯滤波降噪

先对图像进行一个平滑处理,避免噪点影响梯度计算~

满足高斯的思想,越接近中心权值越大,这里对核进行了归一化处理~

2.计算梯度强度和方向

不仅计算梯度大小,还要计算方向,这里使用了sobel算子~

有了水平和垂直的梯度直接用反三角函数就可以求出来角度大小。 

3.非极大值抑制

简单来说就是将最明显的边界值体现出来~

比较当前点和周围两个点幅值大小,如果是最大的就保存,如果不是最大就抑制掉(舍弃)~ 

4.双阈值检测完成抑制孤立的弱边缘

对边界候选值再进行一次过滤,保留更真实的一些~

总的来说就是保留更真实的边界。对于minval来说,如果相对小,则对于边缘的筛选相对放宽,较大则相对严格;maxval同样是这个规律。

5.效果

这里写法要改一下,直接读取为灰度图并且不需要阈值操作了。通过canny函数自行设置maxval和minval:

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Bayern.jpg',cv2.IMREAD_GRAYSCALE)
img=cv2.resize(img,(0,0),fx=0.7,fy=0.7)
img1=cv2.Canny(img,130,190)
img2=cv2.Canny(img,80,140)
tog=np.vstack((img,img1,img2))
cv_show(tog,'tog.jpg')

题外话,无论是直接读取为灰度图还是先读入彩色图再转换,效果都一样:

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img1=cv2.imread('D:\\Bayern.jpg',cv2.IMREAD_GRAYSCALE)
img1=cv2.resize(img1,(0,0),fx=0.7,fy=0.7)
img2=cv2.imread('D:\\Bayern.jpg')
img2=cv2.resize(img2,(0,0),fx=0.7,fy=0.7)
img2=cv2.cvtColor(img2,cv2.COLOR_BGR2GRAY)
tog=np.vstack((img1,img2))
cv_show(tog,'tog')

很明显,阈值越大则检测出来的其他边缘越多,换句话说,精度更高,但我们不一定需要~毕竟和自然规律一样,并不是事物都是越大、越多越好

我们再换一张看看:稍微归纳一下,越是对比度不明显的图片,越适合将max和min设置得小一些~

三.图像金字塔

通俗地说,将图像组装成为更像金字塔的形状:

1.高斯金字塔

验证金字塔底部往尖走是向下采样,即金字塔越来越小;向上采样则反之:本质上就是滤波~

 

原理上来说,有点像原始数据不够,通过插值法来获取更多数据的操作~ 

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Code.jpg',cv2.IMREAD_GRAYSCALE)
img=cv2.resize(img,(0,0),fx=0.5,fy=0.5)
up1=cv2.pyrUp(img)
up2=cv2.pyrUp(up1)
cv_show(up1,"up1")

但需要注意的是,上采样和下采样并不能相互抵消,因为在采样过程中又赋值为0的操作,这会损失信息

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Code.jpg',cv2.IMREAD_GRAYSCALE)
img=cv2.resize(img,(0,0),fx=0.5,fy=0.5)
newimg=cv2.pyrDown(img)
newimg=cv2.pyrUp(newimg)
tog=np.hstack((img,newimg))
cv_show(tog,'tog')

相较原来变得更加模糊:

2.拉普拉斯金字塔

原理公式如下:

和上面有点像,先down一下再up一下,然后再用原始图减去这个结果:

import cv2
import numpy as np
def cv_show(img,name):cv2.imshow(name,img)cv2.waitKey(0)cv2.destroyAllWindows()
img=cv2.imread('D:\\Code.jpg')
img=cv2.resize(img,(0,0),fx=0.5,fy=0.5)
newimg=cv2.pyrDown(img)
newimg=cv2.pyrUp(newimg)
answer=img-newimg
tog=np.hstack((img,answer))
cv_show(tog,'tog')

这里用彩色图效果更加明显:

顺便说一下,up和down的顺序相反,效果也是不同的:

up_down=cv2.pyrUp(img)
up_down=cv2.pyrDown(up_down)
down_up=cv2.pyrDown(img)
down_up=cv2.pyrUp(down_up)
tog=np.hstack((up_down,down_up))
cv_show(tog,'tog')


本帖篇幅差不多了,下期继续~

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

英语学习笔记1

目录 第一部分 例句解析 句子一 原文:Learning English is never easy but always rewarding!翻译:学习英语从来都不容易但总是有回报的! 句子二 原文:Sometimes the detailed work of understanding grammar and building v…

测试测试 测试

**非常详细的视频和文字教程,讲解常见的openmv教程包括 巡线、物体识别、圆环识别、阈值自动获取等。非常适合学习openmv、K210、K230等项目 视频合集链接在 openmv教程合集 openmv入门到项目开发 openmv和STM32通信 openmv和opencv区别 openmv巡线 openmv数字识别教…

CSS rem、vw/vh、less

目录 分辨率、视口与二倍图 一、分辨率与像素基础 1. 物理像素(Physical Pixels) 2. 逻辑像素(CSS 像素) 二、视口(Viewport)控制 1. 视口类型 2. 设置理想视口 三、二倍图(Retina/HiD…

【数电】半导体存储电路

组合逻辑电路输入和输出之间是确定关系,与之前的历史记录没有任何关系。时序逻辑电路则有相应的存储元件,要把之前的状态保存起来。 要构成时序逻辑电路,必须要有相应的存储元件,第五章讲述相应的存储元件 一、半导体存储电路概…

OPPO手机如何实时翻译会议视频?视频翻译轻松应对多语言场景

在全球化日益深入的今天,跨语言沟通已成为职场和生活中的常见需求。无论是参加国际会议、观看外语视频,还是与海外客户交流,语言障碍都可能成为效率的绊脚石。幸运的是,OPPO手机凭借其强大的功能和智能化设计,为用户提…

28_跨域

目录 promise promise的基本语法 async await try catch promise 静态方法 跨域 跨域的解决方案 1-cors ​编辑 2-jsonp方案 3-代理服务器 promise promise 是一个es6新增的语法 承诺的意思 作用:是专门用来解决回调地狱!!!! promise的基本语法 // 基本语法:// Pr…

LeetCode Hot100 刷题笔记(4)—— 二叉树、图论

目录 一、二叉树 1. 二叉树的深度遍历(DFS:前序、中序、后序遍历) 2. 二叉树的最大深度 3. 翻转二叉树 4. 对称二叉树 5. 二叉树的直径 6. 二叉树的层序遍历 7. 将有序数组转换为二叉搜索树 8. 验证二叉搜索树 9. 二叉搜索树中第 K 小的元素 …

【漏洞复现】Apache Tomcat partial PUT文件上传反序列化漏洞复现(CVE-2025-24813)

❤️博客主页: iknow181 🔥系列专栏: 网络安全、 Python、JavaSE、JavaWeb、CCNP 🎉欢迎大家点赞👍收藏⭐评论✍ 0x00 免责声明 本文所述漏洞复现方法仅供安全研究及授权测试使用;任何个人/组织须在合法合规…

BurpSuit抓包失败-基础配置

问题描述:当开启拦截抓包的时候,burpsuite没有反应,好不容易经过一通配置,浏览器出现无法访问的情况。 解决办法: 下载浏览器插件 首先下载一个代理转换插件:Omega,这样比较方便,…

求解AX=XB 方法

一、简介 一文浅谈旋转变换:旋转矩阵、旋转向量、欧拉角、四元数-CSDN博客 在机器人学、计算机视觉和几何学中,经常会遇到求解矩阵方程 AXXB 的问题。这种方程通常出现在坐标系变换、手眼标定(Hand-Eye Calibration)等场景中。理…

AnimateCC基础教学:随机抽取获奖名单及奖品-V1.0原型版

舞台界面设计: 主轴第一帧代码: this.btnObj.addEventListener("click", updateStage.bind(this)); createjs.Ticker.addEventListener("tick", updateRandom.bind(this)) var _this this; var bPlaying false; var nameList ["张三…

深入了解Linux内核:task_struct结构详解

Linux 操作系统的广袤世界里,进程管理宛如一座大厦的基石,支撑着整个系统的稳定运行与高效运转 。而task_struct结构体,无疑是进程管理这座大厦的核心支柱,它承载着进程的关键信息,贯穿于进程从诞生到消亡的整个生命周…

IsaacLab最新2025教程(7)-引入IK solver控制机器人

机器人控制可以直接给定关节角进行驱动实现功能,完成任务,但是关节角不是很直观而且做teleoperation或者是结合VLA模型时候,用eef pose会更符合直觉一些,isaacsim用的是LulaKinematics,因为IsaacLab现在是ETHZ的团队在…

Vue——常用指令总结、指令修饰符、v-model原理、computed计算属性、watch监听器、ref和$refs

文章目录 一、概念理解二、指令1. 常用内置指令总结2. 常用指令修饰符3. 自定义指令4. v-model原理表单类组件封装 三、补充1. computed计算属性2. watch监视器3. ref和$refs 一、概念理解 【事件处理函数】 事件处理函数应该写到一个跟data同级的配置项(methods&a…

求职笔试题

PDD 最长公共子序列 1143-最长公共子序列 class Solution:def longestCommonSubsequence(self, text1: str, text2: str) -> int:"""二维动态规划"""m, n len(text1), len(text2)# dp [[0]* (n1)] * (m1) 这种写法错误,m1行…

【Ragflow】6. Ragflow-plus重磅更新:增加用户后台管理系统

概述 Ragflow本身并不包含用户管理的功能,我在系列前文中,写过一个脚本,用来批量插入用户,并自动加入团队,配置默认模型设置。然而,此方式需要用户安装对应环境,对普通用户并不友好。 因此我开…

什么是贴源库

贴源库的定义与核心概念 贴源库(Operational Data Store, ODS)是数据架构中的基础层,通常作为数据仓库或数据中台的第一层,负责从业务系统直接抽取、存储原始数据,并保持与源系统的高度一致性。其核心在于“贴近源头”…

MSTP+VRRP三层架构综合实验

一、实验目的 掌握VLAN、VRRP、STP和Eth-Trunk的基本配置方法。 实现内网与外网的通信,并确保网络的高可用性和冗余性。 理解DHCP、OSPF和NAT在网络中的应用。 二、实验环境 网络拓扑:如图所示,包含两台三层交换机(SW1、SW2&a…

未来村庄智慧灯杆:点亮乡村智慧生活​

在乡村振兴与数字乡村建设的时代进程中,未来村庄智慧灯杆凭借其多功能集成与智能化特性,已成为乡村基础设施建设领域的崭新焦点,为乡村生活带来了前所未有的便利,推动着乡村生活模式的深刻变革。​ 多功能集成:一杆多能…

RedHatLinux(2025.3.22)

1、创建/www目录,在/www目录下新建name和https目录,在name和https目录下分别创建一个index.htm1文件,name下面的index.html 文件中包含当前主机的主机名,https目录下的index.htm1文件中包含当前主机的ip地址。 (1&…