OpenCV —— color_matrix_numpy_mat_reshape
😶🌫️😶🌫️😶🌫️😶🌫️Take your time ! 😶🌫️😶🌫️😶🌫️😶🌫️
💥个人主页:🔥🔥🔥大魔王🔥🔥🔥
💥所属专栏:🔥魔王的修炼之路–Computer vision🔥
如果你觉得这篇文章对你有帮助,请在文章结尾处留下你的点赞👍和关注💖,支持一下博主。同时记得收藏✨这篇文章,方便以后重新阅读。
文章目录
- 颜色空间
- 解释
- numpy 与 颜色空间
- 图像的本质
- 三种图像
- 总结
- numpy
- 本质
- Mat
- np.ndarray
- 彩色图像
- 灰度图像
- 二值图像
- NumPy 主要能干啥?
- reshape 与 图像的关系
- 一句话理解 reshape
- 和图像类型的关系
- reshape 一般在什么时候用?
- reshape ≠ 改变图像颜色通道或语义
- 举个例子对比
- 总结
- 像素含义
- “像素”的含义
- 图像 = 像素矩阵**
- 不同类型图像中像素的含义
- 像素的“值”代表什么?
- 转换关系:
- numpy 绘制各种矩阵
- 矩阵检索与赋值
- 获取子矩阵(ROI)
- Mat
- 解释
- **深浅拷贝、三个常用属性、通道的分割与合并。**
颜色空间
解释
常用 图像颜色表示方式 即 色彩空间 / 色彩模型:
- HSV :H 为色相即色彩;S 为饱和度,即颜色纯度;V 为明度。
- HSV 与 HSL 相似,只不过在最后一个参数上有差异,HSV 的 V 明度,最大的时候颜色最明,但是 HSL 的 L 是亮度,最大的时候为白色。其他基本一样,opencv 一般用 HSV。
- RGB:opencv 的通道都是 BGR,用的时候需要注意,因为 RGB 是三个颜色通道,检测颜色的话不容易说到底是哪个颜色,opencv 常用 HSV。
- 这些色彩空间都是 0~255。
视频的话一般用 YUV。
import cv2def callback(pos):# 虽然不用回调函数,但要写一个参数,因为改变滑动条时会底层会调用 createTrackbar() 的回调函数,并传递当前值,所以需要有参数接收。passcv2.namedWindow("win", cv2.WINDOW_NORMAL)img = cv2.imread("../images/1.png")# 创建颜色空间列表,用于转换图像颜色空间 colorspaces = [cv2.COLOR_BGR2RGBA, cv2.COLOR_BGR2BGRA,cv2.COLOR_BGR2GRAY, cv2.COLOR_BGR2HSV,cv2.COLOR_BGR2YUV]# 创建一个滑动条,用于转换图像的颜色空间,opencv 一般用的就是 HSV,比较容易检测颜色,RGB 是三个颜色混合的。图片里有原理。一般不用 HSL/RGB。 cv2.createTrackbar("hsv_color", "win", 0, len(colorspaces) - 1, callback) # 第三个参数,记得是长度减一,不然就越界了。while True:# 获取滑动条当前位置:不会再去调用回调函数,它只是读取滑动条当前的位置,不会触发任何事件,也不会执行回调函数。index = cv2.getTrackbarPos("hsv_color", "win")# 颜色空间转换 APIcvt_img = cv2.cvtColor(img, colorspaces[index]) # 颜色空间转换,比如 index == 3,将 HSV 转换为 BGRcv2.imshow("win", cvt_img) # 只会显示 BGR 颜色空间key = cv2.waitKey(10)if key & 0xff == ord('q'):breakcv2.destroyAllWindows()
numpy 与 颜色空间
numpy 创建的只是普通的数字矩阵,本身没有颜色空间,BGR 还是 HSV 取决于怎么去用。
举例说明:
import numpy as npimg = np.zeros((100, 100, 3), dtype=np.uint8)
这只是一张 100×100 的、3 通道、全为 0 的图像。
它既可以是:
- 一张 BGR 图像(黑色)
- 也可以当作 HSV 图像(值全为
[0, 0, 0]
,即无色)- 也可以当作别的,比如 YUV,只要你用其他方式处理它
关键点在于 —— 颜色空间是你怎么解释这个矩阵
- 如果你用
cv2.imshow()
显示它,默认 OpenCV 按 BGR 来处理。- imshow() 只会当作 BGR 格式显示,所以如果定义的色彩空间是 HSV,要用 cvtColor(img, cv2.COLOR_HSV2BGR) 转换一下。
- imshow() 返回的对象本质也是 numpy 数组,opencv 的图片都是 numpy 多维数组。
- 如果你传给
cv2.cvtColor(..., cv2.COLOR_HSV2BGR)
,那它就是 HSV,然后通过函数转为 BGR 颜色空间。- NumPy 本身不关心图像是什么颜色空间
再明确一下:
操作 是否指定颜色空间? np.zeros()
/np.ones()
❌ 只是创建矩阵 cv2.imread()
✅ 默认返回 BGR 图像 cv2.cvtColor(img, cv2.COLOR_HSV2BGR)
✅ 转换为 HSV cv2.imshow()
✅ 默认按 BGR 显示(不管你是不是故意写的 HSV)
图像的本质
三种图像
图像本质就是一个 numpy 多维数组(np.ndarray):
如果是灰度图,那么就是一个二维数组,每个像素点就是一个数字
如果是彩色图,那么就是一个三维数组,每个像素点就是有三个元素的数组 [B, G, R]
二值图:是把灰度图像中的像素值变成 0 或 255 的过程。常见做法:给一个阈值(如 127),大于就变白(255),小于就变黑(0)。
图像类型 维度(shape) 像素值的含义 数据类型 彩色图像 (H, W, 3) 每个像素是 [B,G,R]
组成的数组uint8
灰度图像 (H, W) 每个像素是灰度值(0~255) uint8
二值图像 (H, W) 每个像素只有 0(黑)或 255(白) uint8
# 彩色图像 (H, W, 3) # ↓ cvtColor # 灰度图像 (H, W) # ↓ threshold / 自定义规则 # 二值图像 (H, W)# 实现如下:# 彩色 → 灰度 gray = cv2.cvtColor(color_img, cv2.COLOR_BGR2GRAY)# 灰度 → 二值 _, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
总结
彩色图、灰度图、二值图,本质上都是 NumPy 数组,只是维度和像素数值不同。二值图是对灰度图的进一步压缩,是最简单、最“干净”的图像表达形式。
二值化 必须基于灰度图,不能直接对彩色图做二值化(也不是不行,但默认逻辑会自动先转灰度)。
二值图和灰度图虽然 shape 相同,但灰度图是 0~255 的连续值,二值图只有 0 和 255 两种值。
所以很多图像处理函数(比如轮廓提取)要求用二值图是因为它处理起来更快、轮廓边界更清晰。
图像操作的本质
图像操作 本质是什么 读取图像 cv2.imread()
得到一个 np.ndarray
(numpy 数组)(默认读到的是 BGR)灰度/通道处理 对 NumPy 数组的变换 绘图、ROI、轮廓处理等 都是基于 NumPy 数组坐标 掩码、二值、逻辑运算等 用 NumPy 做矩阵计算
numpy
本质
OpenCV 中的图像,其实本质就是一个 NumPy 多维数组:
- 灰度图像 = 2 维矩阵(高度 × 宽度)
- 彩色图像 = 3 维矩阵(高度 × 宽度 × 通道)
- 所以你可以用 NumPy 的方式来操作图像的每个像素、通道、区域。
Mat
在 Python 中,图像数据读进来(如通过
cv2.imread()
)后,是np.ndarray
类型的对象。你可以用
np.array(...)
来手动构造这种对象(比如自己构造灰度图、小测试图像等)。如果你用 C++,那图像类型就是
cv::Mat
,是 C++ OpenCV 自己的类型。Python 中图像是
np.ndarray
类型,不再用cv::Mat
。
np.array(...)
是构造器,不是类型,它创建出的对象类型才叫np.ndarray
。
np.ndarray
np.ndarray
(即 NumPy 的核心数据类型)就是我们常说的 NumPy 数组,在图像处理中,每张图在内存中通常就是以np.ndarray
的形式存在的。- 所有图像在 OpenCV 中读入后,都是
np.ndarray
类型,只是 shape 和数据内容不同。彩色图像
import cv2 img = cv2.imread("color.jpg") print(type(img)) # <class 'numpy.ndarray'> print(img.shape) # (H, W, 3) 比如 (480, 640, 3) print(img.dtype) # uint8
灰度图像
gray = cv2.imread("gray.jpg", cv2.IMREAD_GRAYSCALE) print(type(gray)) # <class 'numpy.ndarray'> print(gray.shape) # (480, 640) print(gray.dtype) # uint8
二值图像
binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) print(type(binary)) # <class 'numpy.ndarray'> print(binary.shape) # (480, 640) print(np.unique(binary))# [0 255]
图像类型 类型 ( type
)结构 ( shape
)数据 ( dtype
)彩色图像 np.ndarray
(H, W, 3)
uint8
灰度图像 np.ndarray
(H, W)
uint8
二值图像 np.ndarray
(H, W)
uint8
(值为 0 或 255)
所以你看到的
np.array
图像,就是np.ndarray
类型的图像。可以认为
np.array(...)
是生成np.ndarray
的构造方式,而图像数据就是np.ndarray
。
NumPy 主要能干啥?
功能类别 示例 数组创建 np.zeros()
、np.ones()
、np.array()
数组索引 img[0:100, 100:200]
数学运算 np.mean()
、np.sum()
、矩阵乘法条件操作 img[img > 100] = 255
数据分析 图像统计、平均值、最大最小值等
reshape 与 图像的关系
一句话理解 reshape
reshape()
是 NumPy 中用于“改形”的操作,只改变数据的形状,不改变数据本身。
和图像类型的关系
reshape()
不能把彩色图“变成”灰度图(那叫cv2.cvtColor()
)。reshape()
只是数组结构上的“物理摆放”变化,不涉及图像语义。- 所以你不能用
reshape()
来灰度化图像、彩色化图像或者二值化图像。
reshape 一般在什么时候用?
1. 把图像展开成一维(常用于训练/特征提取)
比如:要把一个图像拉成一维向量(例如用于训练某些传统模型)
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE) # shape (480, 640) flat = img.reshape(-1) # 变成一维:(480 * 640, )
2. 做矩阵批量处理时调整维度
例如你把多张图(N 张,每张 H×W)堆叠成一个
N × H × W
的 3D 数组,但你需要变成N × (H×W)
才能送入模型:imgs = np.stack([img1, img2, img3]) # shape: (3, 480, 640) flattened = imgs.reshape(3, -1) # shape: (3, 480*640)
3. 图像保存/传输时按字节处理
# 假设 img 是彩色图 (480, 640, 3) flattened = img.reshape(-1) # (480 * 640 * 3, ),常用于写入二进制流
reshape ≠ 改变图像颜色通道或语义
用法 举例 作用 reshape()
img.reshape(-1)
改变 shape,不改内容 cv2.cvtColor()
cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
真正转换图像颜色通道 threshold()
cv2.threshold(...)
做图像二值化 np.expand_dims()
增加维度(如灰度图加通道) 如 img[None, :, :]
举个例子对比
img = cv2.imread('xxx.jpg') # 彩色图 BGR,(480, 640, 3)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # (480, 640)binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)[1] # 二值图,仍是 (480, 640)flat = binary.reshape(-1) # 拉成 (307200,)
彩色图转灰度 →
cvtColor
;灰度转二值 →threshold
;做线性代数/建模才用reshape
。
总结
reshape
是 数学操作(维度换形),不是图像语义操作。
用它的场景基本是为了满足 模型输入格式要求、做特征处理,或者为了实现某些矩阵操作,不是用来变灰度、变二值的。
方面 彩色/灰度/二值化 reshape 改变通道数? ✅ 是 ❌ 否 改变像素含义? ✅ 是 ❌ 否 改变 shape? ✅ 可能(W,H,3 → W,H) ✅ 是(但不改变通道内容) 常用在哪? 图像理解 / 预处理 数据变形 / 深度学习输入准备
像素含义
“像素”的含义
像素(Pixel, Picture Element)是图像中最小的单位。
图像 = 像素矩阵**
图像在计算机中是用一张二维或三维的矩阵(通常用
numpy.ndarray
)表示的,其中每一个矩阵元素(也就是每一个像素)都有其颜色信息或灰度值。
不同类型图像中像素的含义
- 灰度图
- 每个像素是一个
0 ~ 255
的数值,代表亮度(黑到白)。- 如:
img[100, 150] = 255
,表示这个点是白色。- 图像 shape:
(H, W)
。
- 彩色图(RGB图)
- 每个像素是一个由 3 个分量(R、G、B)组成的数组。
- 如:
img[100, 150] = [255, 0, 0]
,表示这个点是纯红色。- 图像 shape:
(H, W, 3)
。
- 二值图
- 每个像素只有两个值:0(黑)或 255(白),表示前景/背景。
- 是从灰度图阈值处理得到的。
- 图像 shape:
(H, W)
,数据类型通常是np.uint8
。
像素的“值”代表什么?
图像类型 每个像素的值 含义说明 彩色图 [R, G, B]
三个通道的颜色强度 灰度图 0 ~ 255
灰度/亮度 二值图 0 或 255
黑或白,背景或前景标志
转换关系:
- 彩色图 → 灰度图:去掉颜色信息,只保留亮度。
- 灰度图 → 二值图:通过阈值判断,把像素变成 0 或 255。
- 这些变化都不是 reshape,而是图像内容的处理。
numpy 绘制各种矩阵
创建矩阵:都可以在写完宽、高 后指定通道数,一个通道为灰度图/灰色图,只有黑色或白色。
import cv2 import numpy as npcv2.namedWindow("win", cv2.WINDOW_NORMAL)# 创建矩阵:都可以在写完宽、高 后指定通道数,一个通道为灰度图/灰色图,只有黑色或白色。# 创建 zeros 矩阵 img_zeros = np.zeros((5, 5), np.uint8)# 五行五列矩阵,像素值全为 0 print(img_zeros)# 创建 ones 矩阵 img_ones = np.ones((5, 5), np.uint8)# 像素值为 1 print(img_ones)# 创建 full 矩阵 img_full = np.full((5, 5), 100, np.uint8) # 全为 100 print(img_full)# 创建 identity 矩阵 img_identity = np.identity(5) # 单位矩阵,正方形,斜对角像素值是 1,其他为 0 print(img_identity)# 创建 eye 矩阵 # 一个参数是单位矩阵;两个参数可以是长方形矩阵;默认斜对角线,也可以通过 k = n 选择从第一行第 n 个开始的斜对角线像素值为 1,其余还是 0。 img_eye_1 = np.eye(5) print(img_eye_1)img_eye_2 = np.eye(5, 8) print(img_eye_2)img_eye_3 = np.eye(5, 8, k = 2) print(img_eye_3)key = cv2.waitKey(0) if key & 0xff == ord('q'):cv2.destroyAllWindows()
矩阵检索与赋值
检索与赋值,访问方式:[y,x] / [y, x, z]:高、宽,通道数(层数)(可选)。
import cv2 import numpy as npcv2.namedWindow("win", cv2.WINDOW_NORMAL)# 创建矩阵:都可以在写完宽、高 后指定通道数,一个通道为灰度图/灰色图,只有黑色或白色。# 创建 zeros 矩阵 img_zeros = np.zeros((100, 100, 3), np.uint8)# 五行五列矩阵,像素值全为 0 print(img_zeros) # 多通道打印函数处理的不好,会看做 100 个 这样的 高 * 长 的矩阵然后打印出来。不用在意。# 检索与赋值 print(img_zeros[50, 20]) # 打印出一个三个元素的列表,因为没有指定通道数 count_y = 0 while count_y < 80:img_zeros[count_y, 20] = [255, 255, 255] # 这里可以不指定通道数需要赋值列表,也可以指定特定通道数。count_y += 1count_y = 0 while count_y < 80:img_zeros[count_y, 40, 0] = 255 # 这里可以不指定通道数需要赋值列表,也可以指定特定通道数。count_y += 1cv2.imshow("win", img_zeros) # 自动创建窗口 "win"key = cv2.waitKey(0) if key & 0xff == ord('q'):cv2.destroyAllWindows()
获取子矩阵(ROI)
ROI(图像子区域):获取的是你给的原图中的区域 + 所有通道。
ROI 本质上只是 NumPy 切片,它默认取所有通道;如果你要指定通道,必须显式地加第三个维度的切片。
- 最常见的 ROI 获取方式就算 原图中的区域 + 所有通道:
roi = img[100:200, 100:200] # 获取这部分区域(默认包含所有通道)
- 不过也可以指定特定通道:必须在获取的时候获取指定通道,然后赋值的时候和通道个数对应。
roi = img[100:200, 150:250, 0] # 只要 B 通道 roi = img[100:200, 150:250, [1, 2]] # G 和 R
示例 意义 img[y1:y2, x1:x2]
获取所有通道的 ROI img[y1:y2, x1:x2, c]
获取第 c
通道的 ROIimport cv2 import numpy as npcv2.namedWindow("win", cv2.WINDOW_NORMAL)# 创建矩阵:都可以在写完宽、高 后指定通道数,一个通道为灰度图/灰色图,只有黑色或白色。# 创建 zeros 矩阵 img_zeros = np.zeros((200, 200, 3), np.uint8)# 五行五列矩阵,像素值全为 0 print(img_zeros) # 多通道打印函数处理的不好,会看做 100 个 这样的 高 * 长 的矩阵然后打印出来。不用在意。# ROI 获取子区域 roi = img_zeros[50:100, 50:100] # 如果想指定特定通道,只能在这,获取的时候获取特定通道,赋值的时候也要对应通道数。 roi[:, :] = [255, 255, 255] # roi 的全部区域 roi[:] = [255, 255, 255] # roi 的全部区域 roi[:, 30] = [255, 255, 255] # roi y 的全部区域,x 下标 30 的位置cv2.imshow("win", img_zeros) # 自动创建窗口 "win"key = cv2.waitKey(0) if key & 0xff == ord('q'):cv2.destroyAllWindows()
Mat
解释
- 特征类比 python 的 np.ndarray 类型就行了。
- mat 就是图像对象的载体:一个多维图像,有 属性 和 数据 两部分,底层就是定义一个类,有各种属性,比如 维度、行数、列数等等。数据就是图像矩阵对应的数值。底层采用引用计数,默认浅拷贝,修改同一块空间。
- mat 属性:
字段 说明 字段 说明 dims 维度 channels 通道数,RGB 是 3 rows 行数 size 矩阵大小 cols 列数 type 数据类型,格式为 dep+dt+chs,例如 CV_8UC3 depth 像素的位深 data 存放数据
深浅拷贝、三个常用属性、通道的分割与合并。
import cv2 import numpy as np# 创建一个 mat 对象 img = cv2.imread("../images/1.png") img2 = img # 默认浅拷贝img3 = img.copy() # 调用 img 的 copy 函数,深拷贝img2[0:100, 0:100] = [255, 255, 255] # 修改 img2 子区域, img 子区域同样被修改(浅拷贝)。img3 子区域不变。# 打印属性 print(img.shape) # 包含:高度,长度,通道数。print(img.size) # 图像占用的空间,高度 * 长度 * 通道数print(img.dtype) # data_type,每个像素的数据类型#(也可以说是每个像素的位深,uint8,8位无符号整数,所以 0~255)cv2.imshow("win", img) # 浅拷贝,改变。 cv2.imshow("win3", img3) # img3 子区域不变。# 通道的分割与合并 b, g, r = cv2.split(img3) # 接收的只是原图通道的深拷贝,需要重新合并才能影响图片。 b[:, 0:100] = 255 # 改变这个通道的子区域 img3 = cv2.merge((b, g, r)) # 生成一个新图像。 cv2.imshow("win_split", img3)cv2.waitKey(0)
- 博主长期更新,博主的目标是不断提升阅读体验和内容质量,如果你喜欢博主的文章,请点个赞或者关注博主支持一波,我会更加努力的为你呈现精彩的内容。
🌈专栏推荐
😈魔王的修炼之路–C语言
😈魔王的修炼之路–数据结构
😈魔王的修炼之路–C++
😈魔王的修炼之路–QT
😈魔王的修炼之路–算法
😈魔王的修炼之路–力扣
😈魔王的修炼之路–牛客
😈魔王的修炼之路–剑指offer
😈魔王的修炼之路–Linux
😈魔王的修炼之路–Computer vision
更新不易,希望得到友友的三连支持一波。收藏这篇文章,意味着你将永久拥有它,无论何时何地,都可以立即找到重新阅读;关注博主,意味着无论何时何地,博主将永久和你一起学习进步,为你带来有价值的内容。