计算机中的图像本质上是由无数像素组成的网格彩色图像通常通过红R、绿G、蓝B三个独立的颜色通道来记录信息每个通道都是一个二维数字矩阵矩阵中的数值代表对应位置像素该颜色的亮度通常为 0-255三个通道的数值组合决定了像素最终呈现的颜色计算机存储的就是这些通道矩阵数据以及图像的宽、高、通道数等元信息。1.图片视频基础读取读取与展示图片安装 OpenCV 使用 imread 读取图片。pip install opencv-python读取图片核心import cv2 # 1. 彩色图默认 BGR 顺序 img cv2.imread(cat.jpg) # 2. 灰度图 img_gray cv2.imread(cat.jpg, cv2.IMREAD_GRAYSCALE) # 3. 含透明通道PNG img_alpha cv2.imread(cat.png, cv2.IMREAD_UNCHANGED)检查是否读取成功if img is None: print(读取失败检查路径或文件是否存在) else: print(正常形状, img.shape) print(灰度形状, img_gray.shape)正常形状 (180, 300, 3)灰度形状 (180, 300)显示与保存cv2.imshow(image, img) cv2.waitKey(0) cv2.destroyAllWindows() cv2.imwrite(save.jpg, img)cv2.waitKey(0)让图片窗口停住不闪退等待你按任意键继续。cv2.destroyAllWindows()关掉所有打开的图片窗口清理内存。提醒OpenCV 默认通道顺序是BGR不是 RGB。img 打印出来是一个3维数组array([[[ 6, 43, 125], [ 6, 43, 125], [ 5, 42, 122], ..., [174, 207, 222], [174, 207, 222], [173, 206, 221]]], shape(180, 300, 3), dtypeuint8)img_gray 打印出来是一个2维数组array([[ 63, 63, 62, ..., 36, 36, 36], [ 62, 62, 61, ..., 36, 36, 36], [ 59, 59, 58, ..., 36, 36, 36], ..., [202, 205, 207, ..., 206, 205, 205], [201, 199, 197, ..., 207, 206, 206], [198, 194, 193, ..., 208, 208, 207]], shape(180, 300), dtypeuint8)获取类型type(img)numpy.ndarrayimg.size162000img.dtypedtype(uint8)dtype(uint8) unsigned 8-bit integer无符号 8 位整数每个像素值用 1 字节8 位无符号整数存储范围 0–255只能存非负数没有负数。视频基础读取OpenCV 读取视频和读图片逻辑几乎一样只是把 cv2.imread 换成视频捕获对象一帧一帧读取画面。import cv2 # 1. 打开视频可以是本地视频 或 摄像头 0 cap cv2.VideoCapture(movie.mp4) # 本地视频 # cap cv2.VideoCapture(0) # 电脑摄像头直接用0 # 检查是否打开成功 if not cap.isOpened(): print(视频打开失败) exit() # 2. 循环读取每一帧 while True: # 读一帧画面 ret, frame cap.read() # 如果读不到帧视频结束退出循环 if not ret: break #img cv2.cvtColor(frame, cv2.IMREAD_COLOR) img cv2.cvtColor(frame, cv2.COLOR_RGB2GRAY) cv2.imshow(result, img) # 按 q 键退出等待1毫秒刷新 if cv2.waitKey(1) 0xFF ord(q): break # 3. 释放资源 关闭窗口 cap.release() cv2.destroyAllWindows()2.图像基本操作1.截取部分图像数据OpenCV 里截取图片区域ROI 的操作非常常用# 定义显示函数你代码里用的就是这个 def cv_show(name, img): cv2.imshow(name, img) cv2.waitKey(0) cv2.destroyAllWindows() # 读取 截取 显示 img cv2.imread(cat.jpg) cat img[0:50, 0:200] # 截左上角高50宽200 cv_show(cat, cat)关键图像切片规则img[ 行范围, 列范围 ]img[0:50, 0:200] 截取高度第 0 行第 50 行宽度第 0 列第 200 列也就是左上角一小块2.颜色通道提取把一张彩色图片拆分成 蓝、绿、红 三个独立的颜色通道。b,g,rcv2.split(img)img你读取的彩色图BGR 格式cv2.split()通道拆分得到 3 张灰度图b蓝色通道g绿色通道r红色通道r、g、b 三个数据格式如下array([[125, 125, 122, ..., 39, 39, 39], [121, 121, 120, ..., 39, 39, 39], [114, 112, 111, ..., 39, 39, 39], ..., [213, 216, 218, ..., 220, 219, 219], [212, 210, 208, ..., 221, 220, 220], [209, 205, 204, ..., 222, 222, 221]], shape(180, 300), dtypeuint8)它们的 shape 都为 (180, 300)只保留R通道cur_img img.copy() cur_img[:,:,0] 0 cur_img[:,:,1] 0 cv_show(R,cur_img)只保留G通道cur_img img.copy() cur_img[:,:,0] 0 cur_img[:,:,2] 0 cv_show(G,cur_img)只保留B通道cur_img img.copy() cur_img[:,:,1] 0 cur_img[:,:,2] 0 cv_show(B,cur_img)3.边界填充OpenCV 边界填充就是给图片四周加上一圈边框常用于深度学习预处理、图像扩展、卷积操作等用函数 cv2.copyMakeBorder() 实现。dst cv2.copyMakeBorder( img, # 原图 top, # 上边框宽度 bottom, # 下边框宽度 left, # 左边框宽度 right, # 右边框宽度 borderType, # 填充类型 value # 纯色填充时的颜色(BGR) )5 种填充类型最常用cv2.BORDER_CONSTANT纯色填充 、cv2.BORDER_REPLICATE复制边缘像素填充最常用、cv2.BORDER_REFLECT镜像反射填充、cv2.BORDER_REFLECT_101更自然的镜像填充、cv2.BORDER_WRAP平铺重复填充import cv2 # 1. 读取图片 img cv2.imread(cat.jpg) # 2. 设置填充大小上下左右各填充20像素 top bottom left right 20 # 3. 5种填充效果 # ① 纯色填充蓝色BGR(255,0,0) constant cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT, value(255,0,0)) # ② 复制边缘填充 replicate cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REPLICATE) # ③ 镜像反射 reflect cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REFLECT) # ④ 更自然的镜像 reflect101 cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_REFLECT_101) # ⑤ 平铺重复 wrap cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_WRAP)显示import matplotlib.pyplot as plt plt.subplot(231), plt.imshow(img, gray), plt.title(ORIGINAL) plt.subplot(232), plt.imshow(replicate, gray), plt.title(REPLICATE) plt.subplot(233), plt.imshow(reflect, gray), plt.title(REFLECT) plt.subplot(234), plt.imshow(reflect101, gray), plt.title(REFLECT_101) plt.subplot(235), plt.imshow(wrap, gray), plt.title(WRAP) plt.subplot(236), plt.imshow(constant, gray), plt.title(CONSTANT) plt.show()BORDER_REPLICATE复制法也就是复制最边缘像素。BORDER_REFLECT反射法对感兴趣的图像中的像素在两边进行复制例如fedcba|abcdefgh|hgfedcbBORDER_REFLECT_101反射法也就是以最边缘像素为轴对称gfedcb|abcdefgh|gfedcbaBORDER_WRAP外包装法cdefgh|abcdefgh|abcdefgBORDER_CONSTANT常量法常数值填充。最常用BORDER_REPLICATE复制边缘、BORDER_CONSTANT纯色4.数值计算OpenCV 图片是 uint8 类型数值范围 0 ~ 255。如果做 10 运算原本 ≤245 的值 → 正常加 10原本 245 的值 → 会溢出从头算25510比如255 10 9 不是 265250 10 260 → 4这叫 uint8 溢出wrap around。import cv2 img_cat cv2.imread(cat.jpg) # 读取图片dtypeuint8 (0-255) img_cat2 img_cat 10 # 每个像素值 10 img_cat[:5, :, 0] # 查看前5行、所有列、第0通道(B) 的像素值输出array([[6, 6, 5, ..., 2, 2, 2], [5, 5, 4, ..., 2, 2, 2], [4, 4, 3, ..., 2, 2, 2], [3, 3, 1, ..., 3, 3, 3], [4, 3, 2, ..., 3, 3, 3]], shape(5, 300), dtypeuint8)img_cat2[:5,:,0] 输出array([[16, 16, 15, ..., 12, 12, 12], [15, 15, 14, ..., 12, 12, 12], [14, 14, 13, ..., 12, 12, 12], [13, 13, 11, ..., 13, 13, 13], [14, 13, 12, ..., 13, 13, 13]], shape(5, 300), dtypeuint8)相当于% 256(img_cat img_cat2)[:5,:,0] 输出array([[22, 22, 20, ..., 14, 14, 14], [20, 20, 18, ..., 14, 14, 14], [18, 18, 16, ..., 14, 14, 14], [16, 16, 12, ..., 16, 16, 16], [18, 16, 14, ..., 16, 16, 16]], shape(5, 300), dtypeuint8)cv2.add(img_cat,img_cat2)[:5,:,0] 称为 OpenCV 安全加法。规则相加超过 255 就截断成 255公式min(a b, 255)例子255 10 265 → 2555.图像缩放与融合两张照片可以直接进行相加融合但是前提条件是两张照片 shape 形状相同。import cv2 img_catcv2.imread(cat.jpg) img_dogcv2.imread(dog.jpg) img_cat img_dog运行报错--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[24], line 4 1 import cv2 2 img_catcv2.imread(cat.jpg) 3 img_dogcv2.imread(dog.jpg) ---- 4 img_cat img_dog ValueError: operands could not be broadcast together with shapes (180,300,3) (193,300,3)使用import cv2 import matplotlib.pyplot as plt # 读取图片 img_cat cv2.imread(cat.jpg) img_dog cv2.imread(dog.jpg) # 把狗的尺寸改成 和猫一样大必须同尺寸才能融合 img_dog cv2.resize(img_dog, (300, 180)) # 图像融合权重相加 res cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0) # 显示 plt.imshow(res)cv2.addWeighted 图像融合公式plaintext融合图 图1 × α 图2 × β γ你写的res cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0)猫占 40%狗占 60%最后亮度偏移0这叫图像加权融合必须保证两张图宽高完全一样OpenCV 读取是 BGR 顺序matplotlib 显示是 RGB 顺序所以直接用 plt.imshow() 颜色会失真、变蓝 / 变绿只是缺少 BGR 转 RGB所以颜色不对加一行 cv2.cvtColor(res, cv2.COLOR_BGR2RGB) 就完美了res_rgb cv2.cvtColor(res, cv2.COLOR_BGR2RGB) plt.imshow(res_rgb)cv2.resize有两种用法1.指定目标尺寸cv2.resize(img, (宽, 高))2.指定缩放比例cv2.resize(img, (0,0), fx倍数, fy倍数)3.图像阈值二值化cv2.threshold 是 OpenCV 里最简单的图像分割方法把图像变成只有黑和白两种颜色二值图。ret, dst cv2.threshold(src, thresh, maxval, type)src原图最好是灰度图thresh阈值你自己设定的分割线比如 127maxval最大值一般填255type阈值类型决定怎么分割ret 返回你设定的阈值dst处理后的二值图像import cv2 import matplotlib.pyplot as plt img cv2.imread(cat.jpg) # 2. 将图片转换为灰度图阈值处理必须使用灰度图 img_gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 3. 将BGR格式转换为RGB格式方便matplotlib正常显示原图颜色 img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 4. 5种不同的阈值处理 # 二进制阈值大于127→255(白)小于等于127→0(黑) ret, thresh1 cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY) # 反二进制阈值大于127→0(黑)小于等于127→255(白) ret, thresh2 cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV) # 截断阈值大于127的部分变为127小于127不变 ret, thresh3 cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC) # 阈值化为0大于127不变小于127变为0 ret, thresh4 cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO) # 反阈值化为0大于127变为0小于127不变 ret, thresh5 cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV) # 5. 定义每个子图的标题 titles [Original Image, BINARY, BINARY_INV, TRUNC, TOZERO, TOZERO_INV] # 把所有图片放进列表中方便循环显示 images [img, thresh1, thresh2, thresh3, thresh4, thresh5] # 6. 循环绘制6张子图2行3列 for i in range(6): # 绘制子图2行3列第i1个位置 plt.subplot(2, 3, i 1) # 显示图片cmapgray表示以灰度图显示 plt.imshow(images[i], gray) # 设置子图标题 plt.title(titles[i]) # 隐藏x轴和y轴刻度 plt.xticks([]) plt.yticks([]) # 7. 显示所有图像 plt.show()效果图4.HSVHSV 是另一种色彩表达模型和 RGB/BGR 不同它更贴合人眼对颜色的感知由三个分量组成H (Hue) 色相 / 色调代表是什么颜色取值范围0~179OpenCV 中。对应色环红→黄→绿→青→蓝→紫循环变化。S (Saturation) 饱和度代表颜色鲜艳程度取值0~255。数值越高颜色越浓郁越低越偏向灰白0 就是灰度。V (Value) 明度 / 亮度代表明暗程度取值0~255。0 为纯黑255 为最亮。hsvcv2.cvtColor(img,cv2.COLOR_BGR2HSV) cv2.imshow(hsv, hsv) cv2.waitKey(0) cv2.destroyAllWindows()和 BGR/RGB 的区别BGR/RGB靠三原色混合描述颜色适合屏幕显示不适合颜色筛选。HSV把「颜色、鲜艳度、亮度」拆分开OpenCV 颜色追踪、目标提取首选。补充小知识点用cv2.imshow直接显示 HSV 图画面会看起来怪异因为窗口仍按 BGR 规则解析数据仅用于查看数组不代表真实色彩。常用场景按指定色相范围抠取特定颜色物体比如提取画面中的红色、蓝色物体。最简记忆H 什么颜色S 艳不艳V 亮不亮5.图像平滑滤波图像平滑 / 滤波主要用来降噪、模糊图像常用四类均值滤波、高斯滤波、中值滤波、双边滤波。先统一前置代码import cv2 import matplotlib.pyplot as plt # 读取图片 img cv2.imread(cat.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 转RGB方便matplotlib显示一、1. 均值滤波cv2.blur()原理邻域像素求平均值简单模糊降噪同时会丢失细节。 语法dst cv2.blur(src, ksize)ksize卷积核大小(w, h)必须为正奇数如 (3,3)、(5,5)示例# 3×3 均值滤波 blur cv2.blur(img, (3, 3))二、2. 高斯滤波cv2.GaussianBlur()按高斯分布加权平均中心像素权重更高模糊更自然像素原理图40107*25198*2226*8223*23768*219332753275/20163.75像素原理图原理和均值滤波类似但每个像素的权重由高斯核决定离中心越近权重越大特点比均值滤波更平滑是最常用的降噪方法同样会模糊边缘最常用模拟人眼模糊平滑效果更自然优先用于普通降噪。 语法dst cv2.GaussianBlur(src, ksize, sigmaX)ksize核大小宽高必须是奇数sigmaXX 方向高斯标准差控制模糊程度设为 0 会自动计算示例# 5×5 高斯核sigmaX0 gauss cv2.GaussianBlur(img, (5, 5), 0)三、3. 中值滤波cv2.medianBlur()专门处理椒盐噪声黑白噪点效果极强同时保留边缘。 原理取邻域像素中间值替换当前像素。 语法dst cv2.medianBlur(src, ksize)ksize单个数字奇数3 / 5 / 7# 3×3 中值滤波 median cv2.medianBlur(img, 3)四、方框滤波Box Filter基本和均值滤波一样可以选择是否归一化box cv2.boxFilter(img, -1, (3,3), normalizeTrue) cv2.imshow(box, box) cv2.waitKey(0) cv2.destroyAllWindows()参数说明cv2.boxFilter(src, ddepth, ksize, normalizeTrue)img输入图像-1输出图像的深度-1 表示和原图一致(3,3)卷积核大小这里是 3×3normalizeTrue是否做归一化二、和均值滤波的关系方框滤波其实是均值滤波的 “通用版”当normalizeTrue时和cv2.blur()完全等价公式输出 (邻域像素和) / (核大小)当normalizeFalse时只是做邻域像素和不除以核大小结果容易超出 0-255 范围会出现uint8溢出25510三、两种模式对比import cv2 img cv2.imread(lenaNoise.png) # 1. 归一化等价于均值滤波 box_norm cv2.boxFilter(img, -1, (3,3), normalizeTrue) # 2. 不归一化直接求和容易溢出 box_no_norm cv2.boxFilter(img, -1, (3,3), normalizeFalse) # 3. 对比均值滤波 blur cv2.blur(img, (3,3)) cv2.imshow(original, img) cv2.imshow(box normalized, box_norm) cv2.imshow(box no normalize, box_no_norm) cv2.imshow(blur, blur) cv2.waitKey(0) cv2.destroyAllWindows()四、关键结论cv2.boxFilternormalizeTruecv2.blurnormalizeFalse只在特殊场景用普通降噪推荐用归一化模式不归一化的结果很容易因为像素值过大溢出导致图像变白或出现异常完整对比代码一键运行 可视化import cv2 import matplotlib.pyplot as plt # 读图并转RGB img cv2.imread(lenaNoise.png) img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 四种滤波 blur cv2.blur(img_rgb, (3, 3)) # 均值 gauss cv2.GaussianBlur(img_rgb, (5, 5), 0) # 高斯 median cv2.medianBlur(img_rgb, 3) # 中值 box cv2.boxFilter(img, -1, (3,3), normalizeFalse) # 方框 # 绘图展示 titles [Original, Blur, Gaussian, Median, Box] imgs [img_rgb, blur, gauss, median, box] plt.figure(figsize(12,6)) for i in range(5): plt.subplot(2,3,i1) plt.imshow(imgs[i]) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()补充要点核尺寸越大模糊程度越强。均值、高斯会把边缘一起模糊掉中值能较好保留边缘。6.腐蚀操作Erosion腐蚀是形态学处理的基础操作核心作用是收缩图像中亮区域、消除细小噪声、断开粘连物体边缘常用来处理二值图像。如下是一个十字核结构B在原图上腐蚀的过程结构B的中心点会在结构A上每个位置扫一遍只要出现有一个绿色和红色点位对不上就会导致该点位被腐蚀。一、腐蚀操作原理和你之前看的卷积类似腐蚀也是用一个 ** 结构元素核** 在图像上滑动核的中心对准图像的每个像素取核覆盖区域内的最小值作为中心像素的新值对二值图来说只要核内有一个像素是 0黑色中心像素就会被 “腐蚀” 成 0相当于把亮区域的边缘 “啃掉” 一圈。二、OpenCV 核心函数cv2.erode(src, kernel, iterations1)src输入图像一般是二值图也支持灰度 / 彩色图kernel腐蚀用的结构元素核决定腐蚀的形状和大小iterations腐蚀次数次数越多图像收缩越明显常用核的创建方式# 1. 3×3 全1正方形核最常用 kernel np.ones((3,3), np.uint8) # 2. 自定义形状核如十字形、圆形用 cv2.getStructuringElement kernel cv2.getStructuringElement(cv2.MORPH_RECT, (3,3)) # 矩形核 kernel cv2.getStructuringElement(cv2.MORPH_CROSS, (3,3)) # 十字核 kernel cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3)) # 圆形核三、完整可运行示例import cv2 import numpy as np import matplotlib.pyplot as plt # 1. 读取图像推荐用带噪声/边缘粘连的二值图 img cv2.imread(noise.jpg, cv2.IMREAD_GRAYSCALE) # 2. 创建腐蚀核 kernel np.ones((3,3), np.uint8) # 3. 执行腐蚀操作iterations1腐蚀1次 erosion1 cv2.erode(img, kernel, iterations1) # 腐蚀2次 erosion3 cv2.erode(img, kernel, iterations2) # 4. 对比显示 titles [Original Binary, Erosion 1x, Erosion 3x] images [img, erosion1, erosion3] for i in range(3): plt.subplot(1,3,i1) plt.imshow(images[i], cmapgray) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()四、关键效果与用途场景效果说明去除椒盐噪声细小的白色噪点会被腐蚀消失断开粘连物体两个靠得近的物体边缘被腐蚀后会分开细化轮廓文字、线条会变细适合骨架提取前处理注意腐蚀次数不是越多越好过度腐蚀会让目标物体被过度收缩甚至消失。五、和膨胀操作的关系延伸腐蚀的 “反向操作” 是膨胀cv2.dilate它会让亮区域变大。腐蚀 膨胀组合开运算先腐蚀后膨胀去噪同时保留物体大小膨胀 腐蚀组合闭运算先膨胀后腐蚀填充孔洞、连接断裂import cv2 import numpy as np import matplotlib.pyplot as plt # 1. 读取图像推荐用带噪声/边缘粘连的二值图 img cv2.imread(noise.jpg, cv2.IMREAD_GRAYSCALE) # 2. 创建腐蚀核 kernel np.ones((3,3), np.uint8) # 3. 执行腐蚀操作iterations1腐蚀1次 erosion1 cv2.erode(img, kernel, iterations1) # 膨胀1次 dilate cv2.dilate(erosion1,kernel,iterations 1) # 4. 对比显示 titles [Original Binary, Erosion 1x, Dilate] images [img, erosion1, dilate] for i in range(3): plt.subplot(1,3,i1) plt.imshow(images[i], cmapgray) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()开运算Open Operation定义先腐蚀再膨胀公式开运算 膨胀(腐蚀(原图))作用去除小的白色噪声点同时保留物体整体大小场景去噪、断开物体间的细小粘连import cv2 import numpy as np import matplotlib.pyplot as plt # 1. 读取图像推荐用带噪声/边缘粘连的二值图 img cv2.imread(noise.jpg, cv2.IMREAD_GRAYSCALE) # 2. 创建腐蚀核 kernel np.ones((3,3), np.uint8) opening cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 4. 对比显示 titles [Original Binary, Opening] images [img, opening] for i in range(2): plt.subplot(1,2,i1) plt.imshow(images[i], cmapgray) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()闭运算Close Operation定义先膨胀再腐蚀公式闭运算 腐蚀(膨胀(原图))作用填充物体内部的小黑洞连接断裂的线条场景修复孔洞、连接断开的物体# 闭运算 closing cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)1. 形态学梯度Gradient公式膨胀 - 腐蚀效果提取物体的轮廓边缘场景边缘检测、轮廓提取gradient cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)2. 顶帽运算Top Hat公式原图 - 开运算效果提取原图中比背景亮的细小部分比如白色噪声、纹理场景提取亮细节、校正光照不均tophat cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)3. 黑帽运算Black Hat公式闭运算 - 原图效果提取原图中比背景暗的细小部分比如物体内部的小黑洞场景提取暗细节、孔洞检测blackhat cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)完整对比代码一键运行import cv2 import numpy as np import matplotlib.pyplot as plt # 1. 生成带噪声、粘连和孔洞的演示二值图 h, w 200, 300 img np.zeros((h, w), dtypenp.uint8) # 两个粘连方块 cv2.rectangle(img, (50, 50), (120, 120), 255, -1) cv2.rectangle(img, (130, 50), (200, 120), 255, -1) # 方块内部加一个小黑洞 cv2.rectangle(img, (80, 80), (90, 90), 0, -1) # 加白色噪声点 np.random.seed(0) noise (np.random.rand(h, w) 0.98) * 255 img cv2.bitwise_or(img, noise.astype(np.uint8)) # 2. 定义核 kernel np.ones((3,3), np.uint8) # 3. 各种形态学操作 opening cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 开运算 closing cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭运算 gradient cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # 梯度 tophat cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel) # 顶帽 blackhat cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel) # 黑帽 # 4. 显示对比 titles [Original, Open, Close, Gradient, TopHat, BlackHat] images [img, opening, closing, gradient, tophat, blackhat] plt.figure(figsize(12, 6)) for i in range(6): plt.subplot(2, 3, i1) plt.imshow(images[i], cmapgray) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.tight_layout() plt.show()7.图像梯度-Sobel算子Sobel 算子用于检测图像边缘原理是计算像素灰度的变化率梯度分为水平梯度X 方向和垂直梯度Y 方向。# Sobel 算子函数 dst cv2.Sobel(src, ddepth, dx, dy, ksize)参数说明src输入图像建议先转灰度图ddepth输出图像深度推荐填cv2.CV_64F。原因边缘存在正负梯度uint8 会截断负数丢失反向边缘dxX 方向梯度水平边缘1开启 /0关闭dyY 方向梯度垂直边缘1开启 /0关闭ksize卷积核大小必须是奇数常用3img cv2.imread(pie.png,cv2.IMREAD_GRAYSCALE) sobelx cv2.Sobel(img,cv2.CV_64F,1,0,ksize3) cv_show(sobelx,sobelx)二、关键知识点X 方向梯度dx1, dy0检测垂直边缘左右灰度变化大的位置Y 方向梯度dx0, dy1检测水平边缘上下灰度变化大的位置取绝对值Sobel 计算会出现负值用cv2.convertScaleAbs()取绝对值还原完整边缘。img cv2.imread(pie.png,cv2.IMREAD_GRAYSCALE) sobelx cv2.Sobel(img,cv2.CV_64F,1,0,ksize3) sobelx cv2.convertScaleAbs(sobelx) cv_show(sobelx,sobelx)三、完整可运行代码单方向 合并梯度演示import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图像并转灰度图 img cv2.imread(pie.png) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 1. X方向 Sobel检测垂直边缘 sobel_x cv2.Sobel(gray, cv2.CV_64F, dx1, dy0, ksize3) sobel_x cv2.convertScaleAbs(sobel_x) # 取绝对值 # 2. Y方向 Sobel检测水平边缘 sobel_y cv2.Sobel(gray, cv2.CV_64F, dx0, dy1, ksize3) sobel_y cv2.convertScaleAbs(sobel_y) # 3. 合并 XY 梯度完整边缘 sobel_xy cv2.addWeighted(sobel_x, 0.5, sobel_y, 0.5, 0) # 可视化对比 titles [Original Gray, Sobel X, Sobel Y, Sobel XY] images [gray, sobel_x, sobel_y, sobel_xy] plt.figure(figsize(12, 6)) for i in range(4): plt.subplot(1, 4, i1) plt.imshow(images[i], cmapgray) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()四、易错点强调不要直接用 uint8灰度由亮→暗时梯度为负数uint8会直接置 0边缘缺失。 正确流程CV_64F计算 → 取绝对值 → 转回正常图像。不建议直接dx1, dy1cv2.Sobel(gray, cv2.CV_64F, 1, 1, 3)效果差、边缘模糊 标准做法分别计算 X、Y 再加权融合。五、简化写法一行合并import cv2 import numpy as np import matplotlib.pyplot as plt # 读取图像并转灰度图 img cv2.imread(cat.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 分别求梯度再融合推荐 gx cv2.convertScaleAbs(cv2.Sobel(gray, cv2.CV_64F, 1, 0, 3)) gy cv2.convertScaleAbs(cv2.Sobel(gray, cv2.CV_64F, 0, 1, 3)) combined cv2.add(gx, gy) # 可视化对比 titles [Original Gray, combined] images [gray, combined] plt.figure(figsize(12, 6)) for i in range(2): plt.subplot(1,2, i1) plt.imshow(images[i], cmapgray) plt.title(titles[i]) plt.xticks([]), plt.yticks([]) plt.show()六、总结dx1,dy0→垂直边缘dx0,dy1→水平边缘固定搭配CV_64FconvertScaleAbs()完整边缘分方向计算后再相加 / 加权融合图像梯度-Scharr算子图像梯度-laplacian算子img cv2.imread(cat.jpg,cv2.IMREAD_GRAYSCALE) sobelx cv2.Sobel(img,cv2.CV_64F,1,0,ksize3) sobely cv2.Sobel(img,cv2.CV_64F,0,1,ksize3) sobelx cv2.convertScaleAbs(sobelx) sobely cv2.convertScaleAbs(sobely) sobelxy cv2.addWeighted(sobelx,0.5,sobely,0.5,0) scharrx cv2.Scharr(img,cv2.CV_64F,1,0) scharry cv2.Scharr(img,cv2.CV_64F,0,1) scharrx cv2.convertScaleAbs(scharrx) scharry cv2.convertScaleAbs(scharry) scharrxy cv2.addWeighted(scharrx,0.5,scharry,0.5,0) laplacian cv2.Laplacian(img,cv2.CV_64F) laplacian cv2.convertScaleAbs(laplacian) res np.hstack((sobelxy,scharrxy,laplacian)) cv_show(res,res)​​​​​​​