OpenCV(Python 版)

计算机中的图像本质上是由无数像素组成的网格,彩色图像通常通过红(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), dtype=uint8)

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), dtype=uint8)

获取类型

复制代码
type(img)

numpy.ndarray

复制代码
img.size

162000

复制代码
img.dtype

dtype('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,r=cv2.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), dtype=uint8)

它们的 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|hgfedcb

BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba

BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg

BORDER_CONSTANT:常量法,常数值填充。

最常用:BORDER_REPLICATE(复制边缘)、BORDER_CONSTANT(纯色)

4.数值计算

OpenCV 图片是 uint8 类型,数值范围 0 ~ 255。如果做 +10 运算:

原本 ≤245 的值 → 正常加 10

原本 >245 的值 → 会溢出从头算(255+1=0)

比如:

255 + 10 = 9 (不是 265!)

250 + 10 = 260 → 4

这叫 uint8 溢出(wrap around)。

复制代码
import cv2
img_cat = cv2.imread('cat.jpg')  # 读取图片,dtype=uint8 (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), dtype=uint8)

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), dtype=uint8)

相当于% 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), dtype=uint8)

cv2.add(img_cat,img_cat2):5,:,0 称为 OpenCV 安全加法。

规则:相加超过 255 就截断成 255

公式:min(a + b, 255)

例子:255 + 10 = 265 → 255

5.图像缩放与融合

两张照片可以直接进行相加融合,但是前提条件是两张照片 shape 形状相同。

复制代码
import cv2
img_cat=cv2.imread('cat.jpg')
img_dog=cv2.imread('dog.jpg')
img_cat + img_dog

运行报错

复制代码
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[24], line 4
      1 import cv2
      2 img_cat=cv2.imread('cat.jpg')
      3 img_dog=cv2.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阈值(你自己设定的分割线,比如 127)

maxval最大值(一般填 255

type阈值类型(决定怎么分割)

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列,第i+1个位置
    plt.subplot(2, 3, i + 1)
    # 显示图片,cmap='gray'表示以灰度图显示
    plt.imshow(images[i], 'gray')
    # 设置子图标题
    plt.title(titles[i])
    # 隐藏x轴和y轴刻度
    plt.xticks([])
    plt.yticks([])

# 7. 显示所有图像
plt.show()

效果图:

4.HSV

HSV 是另一种色彩表达模型,和 RGB/BGR 不同,它更贴合人眼对颜色的感知,由三个分量组成:

  1. H (Hue) 色相 / 色调

    • 代表是什么颜色 ,取值范围 0~179(OpenCV 中)。
    • 对应色环:红→黄→绿→青→蓝→紫,循环变化。
  2. S (Saturation) 饱和度

    • 代表颜色鲜艳程度 ,取值 0~255
    • 数值越高颜色越浓郁;越低越偏向灰白,0 就是灰度。
  3. V (Value) 明度 / 亮度

    • 代表明暗程度 ,取值 0~255
    • 0 为纯黑,255 为最亮。

    hsv=cv2.cvtColor(img,cv2.COLOR_BGR2HSV)

    cv2.imshow("hsv", hsv)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


和 BGR/RGB 的区别

  • BGR/RGB :靠三原色混合描述颜色,适合屏幕显示;不适合颜色筛选
  • HSV :把「颜色、鲜艳度、亮度」拆分开,OpenCV 颜色追踪、目标提取首选

补充小知识点

  1. cv2.imshow 直接显示 HSV 图,画面会看起来怪异,因为窗口仍按 BGR 规则解析数据,仅用于查看数组,不代表真实色彩
  2. 常用场景:按指定色相范围抠取特定颜色物体(比如提取画面中的红色、蓝色物体)。

最简记忆

  • 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()

最常用,模拟人眼模糊,平滑效果更自然,优先用于普通降噪。 语法:

复制代码
dst = cv2.GaussianBlur(src, ksize, sigmaX)
  • ksize:核大小,宽高必须是奇数
  • sigmaX:X 方向高斯标准差,控制模糊程度;设为 0 会自动计算

示例:

复制代码
# 5×5 高斯核,sigmaX=0
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)


四、4. 双边滤波 cv2.bilateralFilter()

美颜级滤波 :降噪 + 保留边缘,适合人像、细节不能丢的场景,速度偏慢。 语法:

复制代码
dst = cv2.bilateralFilter(src, d, sigmaColor, sigmaSpace)
  • d:邻域直径

  • sigmaColor:颜色空间标准差

  • sigmaSpace:坐标空间标准差

    bilateral = cv2.bilateralFilter(img, 5, 75, 75)


完整对比代码(一键运行 + 可视化)

复制代码
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)            # 中值
bilateral = cv2.bilateralFilter(img_rgb, 5, 75, 75) # 双边

# 绘图展示
titles = ["Original", "Blur", "Gaussian", "Median", "Bilateral"]
imgs = [img_rgb, blur, gauss, median, bilateral]

plt.figure(figsize=(12,6))
for i in range(5):
    plt.subplot(2,3,i+1)
    plt.imshow(imgs[i])
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

选型速记(重点)

  1. 普通模糊 / 常规降噪 → 用 高斯滤波(最通用)
  2. 图片有黑白斑点 / 椒盐噪声 → 用 中值滤波
  3. 想模糊又不想丢轮廓(人像) → 用 双边滤波
  4. 简单快速平均模糊 → 用 均值滤波

补充要点

  • 核尺寸越大,模糊程度越强
  • 均值、高斯会把边缘一起模糊掉;双边、中值能较好保留边缘。
相关推荐
暴躁小师兄数据学院14 小时前
【AI大模型应用开发工程师特训笔记】第04讲(第6章):复合数据类型
人工智能·windows·笔记·python
2501_9400417414 小时前
脱离 CRUD 舒适区:硬核全栈实战项目
人工智能
Elastic 中国社区官方博客14 小时前
Elasticsearch 9.4 推动 Elastic AI 生态系统的下一阶段:Dell AI Data Platform 与 NVIDIA
大数据·人工智能·elasticsearch·搜索引擎·全文检索
IT北辰14 小时前
树形层级数据平铺术:用 Python 将分类父子表展开为全路径宽表
大数据·python·分类
可涵不会debug14 小时前
最近又挖到 MuMu 模拟器的新活,跟 AI 搭上线了
人工智能
qcx2314 小时前
【系统学AI】08 Plan-then-Execute范式:先想好再做,比ReAct强在哪
前端·人工智能·react.js·ai·react·plan execute
有一个好名字14 小时前
CrewAI 高级04:输出格式、缓存与工作流编排
人工智能·ai agent
TE-茶叶蛋14 小时前
Graph RAG Agent 系统深度分析
后端·python·flask
前端开发小透明14 小时前
Harness Engineering 实战:从前端开发视角,让AI Agent真正落地生产
人工智能