小白从零开始勇闯人工智能:计算机视觉初级篇(初识Opencv中)

引言

我们在上一章的学习中已经掌握了使用OpenCV进行图像读取、显示和保存的基础后,便意味着完成了计算机视觉的"睁眼"步骤。接下来,我们将迈向更富挑战的环节:引导计算机超越单纯的"观看",去"理解"图像中蕴含的形状结构与轮廓线条。本篇文件章要学习的内容------图像形态学与边缘检测,正是实现这一功能的关键。它们如同图像处理领域精密的"手术刀"与"放大镜",能让我们对图像进行精细的操控与增强,从而准确无误地剥离并提取出那些构成图像本质的关键信息与特征。

一、图像形态学 ------ 图像的"形体塑造术"

1、核心思想

形态学处理的核心在于利用一个称为结构元素(或称核)的小型矩阵,例如一个3x3且元素全为1的方块,作为基本操作工具。该结构元素会在输入图像的每一个像素位置上进行滑动遍历,并根据其覆盖区域内像素的分布情况,通过特定的逻辑运算(如与、或)来重新计算中心像素的值。这一过程能够系统地改变图像中目标的形状与拓扑结构,实现诸如连接相邻区域、消除细小空洞、平滑物体边界或分离独立对象等效果。

2、两大基本操作:腐蚀与膨胀

1、腐蚀:让物体"瘦身"

腐蚀是形态学中的一种基本操作,其核心效果如同让图像中的白色目标"瘦身"。它通过滑动一个结构元素(例如一个小矩形核)来扫描图像。在每一个位置,只有当结构元素所覆盖的区域内所有像素都符合预设条件(例如在二值图像中全部为白色)时,该区域中心的像素才会被保留为白色,否则,它将被置为黑色。这一过程可以有效地消除物体边界上相对孤立的像素点,从而导致目标整体缩小、轮廓向内收缩。它的主要作用在于去除图像中微小的、无关的白色噪点,断开因噪声而连接起来的狭窄连接处,或者分离两个本应独立的物体。

复制代码
import cv2
import numpy as np

# 读取图像,例如一张有毛刺的太阳图片'sun.png'
img = cv2.imread('sun.png')
# 定义一个3x3的结构元素(核)
kernel = np.ones((3,3), np.uint8)
# 进行腐蚀操作,iterations参数控制腐蚀次数
erosion = cv2.erode(img, kernel, iterations=1)
cv2.imshow('Original vs Erosion', np.hstack([img, erosion]))
cv2.waitKey(0)
2、膨胀:让物体"发福"

膨胀是形态学中与腐蚀相对应的基本操作,其核心作用如同让图像中的目标物体"发福"或扩张。它同样利用一个预定义的结构元素在图像上滑动。当结构元素的中心对准某个像素时,只要该元素所覆盖的区域内至少有一个像素是目标(例如在二值图像中为白色),中心像素就会被置为目标值。这个过程会将目标边缘的背景点(黑色)吸收进来,导致物体的整体尺寸增大、轮廓向外扩展。它的主要用途是填补目标内部因噪声或阈值化产生的小型空洞与裂缝,并能够连接彼此邻近但被细小间隙分开的物体。

复制代码
# 读取一张笔画断裂的文字图片'wenzi.png'
img = cv2.imread('wenzi.png')
kernel = np.ones((2,2), np.uint8)
# 进行膨胀操作
dilation = cv2.dilate(img, kernel, iterations=1)
cv2.imshow('Original vs Dilation', np.hstack([img, dilation]))
cv2.waitKey(0)

3、组合技:开运算与闭运算

1、开运算 = 先腐蚀后膨胀

开运算是一个顺序固定的两步形态学操作:先对图像进行腐蚀,紧接着对腐蚀结果进行膨胀。这一组合过程能有效平滑目标物体的外部轮廓,切断物体之间过于狭窄的连通区域,并消除图像中细小的、突出的白点噪声。它的优势在于,能够在去除白色噪点或细小干扰物(如案例中指纹图像上的细微划痕)的同时,不会显著改变目标物体原有的主体面积和整体形态,从而实现了针对性去噪与轮廓净化。

复制代码
import cv2
import numpy as np
zhiwen = cv2.imread('zhiwen.png')
cv2.imshow('src2',zhiwen)

kernel = np.ones((2,2),np.uint8)  #设置kenenel大小
zhiwen_new = cv2.morphologyEx(zhiwen,cv2.MORPH_OPEN,kernel) #开运算
cv2.imshow('zhiwen_new',zhiwen_new)
cv2.waitKey(0)
cv2.destroyAllWindows()
2、闭运算 = 先膨胀后腐蚀

闭运算是另一种顺序固定的形态学组合操作,其步骤与开运算相反:先对图像执行膨胀,再对膨胀的结果进行腐蚀。这一过程能够有效地填补目标物体内部的细小空洞与裂缝,将原本相邻且间隙微小的独立物体连接起来,并平滑物体的内部轮廓边界。它尤其擅长处理因噪声或阈值化导致的内部断裂问题,能像"补洞"和"建桥"一样,在不显著改变目标物体外部整体尺寸的前提下,强化其结构的连通性与完整性。

复制代码
import cv2
import numpy as np
zhiwen = cv2.imread('zhiwen_duan.png')
cv2.imshow('src3',zhiwen)
kernel = np.ones((4,4),np.uint8)  #设置kenenel大小
zhiwen_new = cv2.morphologyEx(zhiwen,cv2.MORPH_CLOSE,kernel) #闭运算
cv2.imshow('zhiwen_new',zhiwen_new)
cv2.waitKey(0)
cv2.destroyAllWindows()

4、高级衍生操作

1、形态学梯度 = 膨胀图 - 腐蚀图

形态学梯度是通过计算同一幅图像经过形态学膨胀后的结果与经过形态学腐蚀后的结果之间的差值而得到的。其核心公式表现为"膨胀图减去腐蚀图"。这个操作能够有效提取并突出目标的边缘轮廓,原理在于膨胀会使物体边缘向外扩张,而腐蚀则使其向内收缩,两者相减后,物体内部相似的区域被抵消,最终保留下来的正是物体外部扩张与内部收缩之间的差异部分,即其边界。这个过程实现了类似"描边"的功能,常用于图像分析中的边缘强调和物体分割。

复制代码
import cv2
import numpy as np
wenzi = cv2.imread('wenzi.png')
cv2.imshow('wenzi_new',wenzi)
kernel = np.ones((2,2),np.uint8)  #设置kenenel大小
bianyuan = cv2.morphologyEx(wenzi,cv2.MORPH_GRADIENT,kernel)
cv2.imshow('bianyuan',bianyuan)
cv2.waitKey(0)
cv2.destroyAllWindows()
2、顶帽与黑帽

顶帽变换:是原图像减去开运算后的图像,其作用是突出显示原图中比周围背景更明亮的细小区域或微小结构。由于开运算会消除亮细节,原图与开运算图相减后,这些被移除的亮部便被单独提取出来。

黑帽变换:则是闭运算后的图像减去原图像,其效果与顶帽相反,旨在突出显示原图中比周围区域更暗的细小部分或空洞。因为闭运算会填充暗细节,两者相减后,这些被填充的暗区域就得到了显著增强。

复制代码
import cv2
import numpy as np
sun = cv2.imread('sun.png')
cv2.imshow('sun_yuantu',sun)
kernel = np.ones((2,2),np.uint8)  #设置kenenel大小
#顶帽
tophat = cv2.morphologyEx(sun,cv2.MORPH_TOPHAT,kernel)
cv2.imshow('TOPHAT',tophat)
#黑帽
blackhat = cv2.morphologyEx(sun,cv2.MORPH_BLACKHAT,kernel)
cv2.imshow('BLACKHAT',blackhat)
cv2.waitKey(0)
cv2.destroyAllWindows()

二、图像边缘检测 ------ 寻找图像的"骨架"

1、为何要进行边缘检测?

边缘检测的根本目的在于从图像中提取出目标与背景之间、或不同区域之间的显著边界信息。这些轮廓线是理解图像内容最关键的结构性特征之一。作为计算机视觉中的基础与前置步骤,它为后续诸如目标识别、图像精确分割、三维场景重建等高级任务提供了重要的输入数据。

2、经典微分算子家族

1、Sobel算子:最常用的入门算子

Sobel算子是边缘检测中最基础且广泛使用的算子之一。其核心原理在于同时兼顾平滑与微分:先通过近似高斯平滑来抑制噪声,再分别进行一阶微分以计算图像在水平与垂直方向上的灰度梯度,从而综合得到边缘信息。但在实际应用时需特别注意,如使用OpenCV的cv2.Sobel()函数,其默认输出数据类型常会截断负梯度值(这些负值代表特定方向的边缘)。因此,通常建议将参数ddepth设为cv2.CV_64F以保留完整的符号信息,随后通过cv2.convertScaleAbs()函数取绝对值并转换为8位图像,即可正确显示完整的边缘结果。

复制代码
import cv2
import numpy as np
yuan = cv2.imread('yuan.png')
cv2.imshow('yuan',yuan)

# # x方向上的边缘
yuan_x = cv2.Sobel(yuan,-1,dx=1,dy=0)
cv2.imshow('yuan_x',yuan_x)

# x方向上的边缘,包括负数信息(右端),但显示不出来,因为范围是(0~255)
yuan_x_64 = cv2.Sobel(yuan,cv2.CV_64F,dx=1,dy=0)#默认uint8改为float64,可保存负数
cv2.imshow('yuan_x_64',yuan_x_64)

# #x方向上的边缘,包括负数信息,进行取绝对值的操作,右端的负值信息就可以显示出来了
yuan_x_full = cv2.convertScaleAbs(yuan_x_64)#转换为绝对值,负数转换为正数
cv2.imshow('yuan_x_full',yuan_x_full)

# # y方向上的边缘
yuan_y = cv2.Sobel(yuan,-1,dx=0,dy=1)
cv2.imshow('yuan_y',yuan_y)

# y方向上的边缘,包括负数信息(下端),但显示不出来,因为范围是(0~255)
yuan_y_64 = cv2.Sobel(yuan,cv2.CV_64F,dx=0,dy=1)#默认int8改为float64,可保存负数
yuan_y_full = cv2.convertScaleAbs(yuan_y_64)#转换为绝对值,负数转换为正数
cv2.imshow('yuan_y_full',yuan_y_full)

# #如果同时使用x,y方向的结果如何呢?(不建议使用)
yuan_xy = cv2.Sobel(yuan,-1,dx=1,dy=1)
cv2.imshow('yuan_xy',yuan_xy)

# #使用图像加权运算组合x和y方向的2个边缘。
yuan_xy_full = cv2.addWeighted(yuan_x_full,1,yuan_y_full,1,0)
cv2.imshow('yuan_xy_full',yuan_xy_full)
cv2.waitKey(0)
cv2.destroyAllWindows()
2、Scharr算子:Sobel的优化版

Scharr算子是Sobel算子在3x3卷积核尺寸下的一个优化。其核心特点是重新设计了卷积核的权重分布,使之在数学上能更精确地逼近图像梯度的方向导数。因此,在相同的较小核尺寸下,Scharr算子相比经典Sobel算子具有更高的计算精度和边缘响应灵敏度,能够更有效地检测出图像中对比度较低或更为微弱的边缘特征。

复制代码
import cv2
import numpy as np
zl = cv2.imread('zl.png',cv2.IMREAD_GRAYSCALE)
# zl=cv2.cvtColor(zl,cv2.COLOR_BGR2GRAY)
zl_x_64 = cv2.Scharr(zl,cv2.CV_64F,dx=1,dy=0)#默认int8改为float64,可保存负数
zl_x_full = cv2.convertScaleAbs(zl_x_64)#转换为绝对值,负数转换为正数
zl_y_64 = cv2.Scharr(zl,cv2.CV_64F,dx=0,dy=1)#默认int8改为float64,可保存负数
zl_y_full = cv2.convertScaleAbs(zl_y_64)#转换为绝对值,负数转换为正数
zl_xy_Scharr_full = cv2.addWeighted(zl_x_full,1,zl_y_full,1,0)
cv2.imshow('zl_xy_Scharr_full',zl_xy_Scharr_full)
cv2.waitKey(0)
3、Laplacian算子:寻找"各向同性"的边缘

Laplacian算子的核心在于利用图像强度的二阶导数来定位边缘。与依赖一阶导数的算子不同,它通过计算梯度的散度,直接响应图像灰度发生剧烈变化的区域(即二阶导数的过零点)。这种方法的特点是各向同性,其响应强度与边缘方向无关,能均匀地突出所有方向的边缘,从而产生更细的边缘线。

复制代码
import cv2
import numpy as np
zl = cv2.imread('zl.png',cv2.IMREAD_GRAYSCALE)
zl_lap = cv2.Laplacian(zl,cv2.CV_64F)
zl_lap_full = cv2.convertScaleAbs(zl_lap)#转换为绝对值,负数转换为正数
cv2.imshow('zl_lap_full',zl_lap_full)
cv2.waitKey(0)

3、王者登场:Canny边缘检测

Canny边缘检测并非单一算子,而是一个包含多个步骤的优化流程,是为了获得高质量的边缘。其流程可详细分解为四步:首先使用高斯滤波器平滑图像以抑制噪声,接着利用Sobel等算子计算梯度的幅值与方向,然后进行非极大值抑制,即沿梯度方向仅保留幅值最大的点,从而将边缘细化为单像素宽度,最后进行双阈值检测与边界跟踪:设定高低两个阈值,高于高阈值的为强边缘,低于低阈值的舍弃,介于两者之间的为弱边缘,并通过检查弱边缘是否与强边缘的8邻域相连,决定是否将其提升并保留为最终边缘。

复制代码
import cv2
import numpy as np
zl = cv2.imread('zl.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow('zl',zl)

zl_canny = cv2.Canny(zl,100,150)#低,高
cv2.imshow('zl_canny',zl_canny)
cv2.waitKey(0)

到这离我们就基本掌握了图像形态学与边缘检测,在下一篇章中我们将继续学习更多opencv的内容。

相关推荐
冬奇Lab38 分钟前
一天一个开源项目(第36篇):EverMemOS - 跨 LLM 与平台的长时记忆 OS,让 Agent 会记忆更会推理
人工智能·开源·资讯
冬奇Lab39 分钟前
OpenClaw 源码深度解析(一):Gateway——为什么需要一个"中枢"
人工智能·开源·源码阅读
AngelPP4 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年4 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼5 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS5 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区6 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈6 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang7 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk18 小时前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能