计算机中的图像本质上是由无数像素组成的网格,彩色图像通常通过红(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 不同,它更贴合人眼对颜色的感知,由三个分量组成:
-
H (Hue) 色相 / 色调
- 代表是什么颜色 ,取值范围
0~179(OpenCV 中)。 - 对应色环:红→黄→绿→青→蓝→紫,循环变化。
- 代表是什么颜色 ,取值范围
-
S (Saturation) 饱和度
- 代表颜色鲜艳程度 ,取值
0~255。 - 数值越高颜色越浓郁;越低越偏向灰白,0 就是灰度。
- 代表颜色鲜艳程度 ,取值
-
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 颜色追踪、目标提取首选。
补充小知识点
- 用
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()
最常用,模拟人眼模糊,平滑效果更自然,优先用于普通降噪。 语法:
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()

选型速记(重点)
- 普通模糊 / 常规降噪 → 用 高斯滤波(最通用)
- 图片有黑白斑点 / 椒盐噪声 → 用 中值滤波
- 想模糊又不想丢轮廓(人像) → 用 双边滤波
- 简单快速平均模糊 → 用 均值滤波
补充要点
- 核尺寸越大,模糊程度越强。
- 均值、高斯会把边缘一起模糊掉;双边、中值能较好保留边缘。