当前位置: 首页 > news >正文

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)表示的,其中每一个矩阵元素(也就是每一个像素)都有其颜色信息灰度值


不同类型图像中像素的含义

  1. 灰度图
  • 每个像素是一个 0 ~ 255 的数值,代表亮度(黑到白)。
  • 如:img[100, 150] = 255,表示这个点是白色。
  • 图像 shape:(H, W)
  1. 彩色图(RGB图)
  • 每个像素是一个由 3 个分量(R、G、B)组成的数组。
  • 如:img[100, 150] = [255, 0, 0],表示这个点是纯红色
  • 图像 shape:(H, W, 3)
  1. 二值图
  • 每个像素只有两个值: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 通道的 ROI
import 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
更新不易,希望得到友友的三连支持一波。收藏这篇文章,意味着你将永久拥有它,无论何时何地,都可以立即找到重新阅读;关注博主,意味着无论何时何地,博主将永久和你一起学习进步,为你带来有价值的内容。

请添加图片描述

http://www.lryc.cn/news/598261.html

相关文章:

  • 嵌入式Linux:线程的创建、终止、回收、取消和分离
  • C# system.diagnostics.debug.writeline 不在输出窗口显示
  • 测试学习之——Pytest Day5
  • Java泛型初始化ArrayList<String>()和ArrayList<>()的区别
  • 【C++】简单学——list类
  • HTTP和HTTPS复习
  • Ethereum: 从零到一为DApp开发搭建专属的私有测试网络
  • USRP X440
  • LeetCode 10:正则表达式匹配
  • Python:开启机器学习的魔法之门
  • 无源域自适应综合研究【2】
  • Android Telephony UrspRule 介绍
  • EVAL长度限制突破方法
  • C#_定时器_解析
  • Flink-1.19.0源码详解7-Flink集群端调度
  • ubuntu安装teams解决方法
  • 大模型回复数据标注优化方案
  • 系统架构师:系统安全与分析-思维导图
  • AIRIOT智慧选煤厂管理解决方案
  • 家政小程序系统开发:开启智慧家政新时代
  • Nginx 信创版本源码升级 1.22.1 升级到1.28.0
  • SSE与Websocket有什么区别?
  • uniapp nvue开发App 横竖屏切换丢失上下文导致 setTimeout和clearTimeout报错
  • 全面解析 CSS Flex 布局:从入门到精通的所有属性详解
  • 深入掌握CSS Grid布局:每个属性详解与实战示例
  • k8s通过NUMA亲和分配GPU和VF接口
  • DeepSeek-R1+豆包迭代一次完成中国象棋游戏
  • 二、计算机网络技术——第6章:应用层
  • rk3588开发板使用硬件编码处理视频
  • 国产数据库拐点已至:电科金仓用“融合+AI”重新定义下一代数据底座