CNN入门前置知识:图像类型全解析(二值/灰度/索引/RGB)与Matplotlib可视化实战

文章目录

  • 1、图像基本概念
  • 2、四种图像对比
  • 3、图像加载
    • [3.1 数组的维度顺序理解](#3.1 数组的维度顺序理解)
    • [3.2 plt.imshow 基本介绍](#3.2 plt.imshow 基本介绍)
    • [3.3 plt.imshow - API 参数介绍](#3.3 plt.imshow - API 参数介绍)
    • [3.4 代码:生成全黑图片](#3.4 代码:生成全黑图片)
    • [3.5 plt.imread 介绍、使用](#3.5 plt.imread 介绍、使用)
    • [3.6 代码:读取无透明 png 图片 & 处理方法](#3.6 代码:读取无透明 png 图片 & 处理方法)

1、图像基本概念

图像是人类视觉的基础,是自然景物的客观反映,是人类认识世界和人类本身 的重要源泉。"图"是物体反射或透射光的分布,"像"是人的视觉系统所接受的图在人脑中所形成的印象或认识照片、绘画、剪贴画、地图、书法作品、手写汉字、传真、卫星云图、影视画面、X光片、脑电图、心电图等都是图像。

在计算机中,按照颜色和灰度的多少可以将图像分为四种基本类型。

  • 二值图像

    一幅二值图像的二维矩阵仅由0、1两个值构成,"0"代表黑色,"1"代白色 。由于每一像素(矩阵中每一元素)取值仅有0、1两种可能,所以计算机中二值图像的数据类型通常为1个二进制位。二值图像通常用于文字、线条图的扫描识别(OCR)和掩膜图像的存储。

    假设我们有一个 3×3 像素的小图像

    ✅ 二值图像示例:

    python 复制代码
    # 用 0 和 1 表示(常见于算法内部)
    [[0, 1, 0],
     [1, 1, 1],
     [0, 1, 0]]
    • 这看起来像一个"十字"形状(中间一列和中间一行是白色)。
    • 如果用 0 和 255(便于显示):
    python 复制代码
    [[  0, 255,   0],
     [255, 255, 255],
     [  0, 255,   0]]
  • 灰度图像

    灰度图像矩阵元素的取值范围通常为[0,255] 。因此其数据类型一般为8位无符号整数的(int8),这就是人们经常提到的256灰度图像。**"0"表示纯黑色,"255"表示纯白色,中间的数字从小到大表示由黑到白的过渡色。**二值图像可以看成是灰度图像的一个特例。

    ✅ 灰度图像示例:

    python 复制代码
    [[ 30, 100, 200],
     [ 50, 128, 220],
     [ 10,  80, 240]]
    • 每个数字代表该位置的亮度:
      • 30 → 很暗的灰色
      • 128 → 中灰色
      • 240 → 接近白色
  • 索引图像

    索引图像的文件结构比较复杂,除了存放图像的二维矩阵 外,还包括一个称之为颜色索引矩阵MAP的二维数组 。MAP的大小由存放图像的矩阵元素值域决定,如矩阵元素值域为[0,255],则MAP矩阵的大小为256Ⅹ3,用MAP=[RGB]表示MAP中每一行的三个元素分别指定该行对应颜色的红、绿、蓝单色值,MAP中每一行对应图像矩阵像素的一个灰度值 ,如某一像素的灰度值为64,则该像素就与MAP中的第64行建立了映射关系,该像素在屏幕上的实际颜色由第64行的[RGB]组合决定。也就是说,图像在屏幕上显示时,每一像素的颜色由存放在矩阵中该像素的灰度值作为索引通过检索颜色索引矩阵MAP得到。

    索引图像的关键组成部分

    1. 调色板(Palette):一个颜色表,通常包含最多256种不同的颜色(对于8位索引图像)。每种颜色由红、绿、蓝(RGB)三个分量组成。
    2. 索引数据(Index Data):图像中每个像素对应于调色板中的一个入口。这意味着每个像素只需要足够的比特数来引用调色板中的颜色。例如,在一个8位索引图像中,每个像素用8位(即一个字节)来表示,可以引用多达256种不同的颜色。

    示例

    假设我们有一个非常简单的4×4像素的索引图像,以及一个相应的4色调色板:

    调色板

    索引 颜色 (RGB)
    0 (255, 0, 0) 红色
    1 (0, 255, 0) 绿色
    2 (0, 0, 255) 蓝色
    3 (255, 255, 255) 白色

    索引数据

    plaintext 复制代码
    [[0, 1, 2, 3],
     [1, 2, 3, 0],
     [2, 3, 0, 1],
     [3, 0, 1, 2]]

    这代表了一个小图像,其中:

    • 第一行从左到右分别是红色、绿色、蓝色、白色;
    • 第二行则是绿色、蓝色、白色、红色,以此类推。
  • 真彩色RGB图像

    RGB图像与索引图像一样都可以用来表示彩色图像。与索引图像一样,它分别用红(R)、绿(G)、蓝(B)三原色的组合来表示每个像素的颜色。但与索引图像不同的是,RGB图像每一个像素的颜色值(由RGB三原色表示)直接存放在图像矩阵中 ,由于每一像素的颜色需由R、G、B三个分量来表示,**M、N分别表示图像的行列数,三个M x N的二维矩阵分别表示各个像素的R、G、B三个颜色分量。**RGB图像的数据类型一般为8位无符号整形。注意:通道的顺序是 BGR 而不是 RGB。

    核心思想: 每个像素的颜色由 红、绿、蓝 三个颜色通道的强度值直接决定,不再需要调色板。

    表示方式:

    • 每个像素用 3个字节 表示(通常是8位/通道)
    • 分别是:R通道 + G通道 + B通道
    • 每个通道取值范围:0 到 255

​ 🌈 示例:3×3像素的真彩色图像

像素表示(每个像素 = [R, G, B])

像素坐标 红色分量 R 绿色分量 G 蓝色分量 B 颜色
(0,0) 255 0 0 红色
(0,1) 0 255 0 绿色
(0,2) 0 0 255 蓝色
(1,0) 255 255 0 黄色
(1,1) 128 128 128 灰色
(1,2) 0 0 0 黑色
(2,0) 255 255 255 白色
(2,1) 255 0 255 品红
(2,2) 0 255 255 青色
  1. 用矩阵表示(3×3×3)
python 复制代码
rgb_image = [
    # 行 0: [R,G,B] × 3
    [[255, 0, 0], [0, 255, 0], [0, 0, 255]],  # 红、绿、蓝
    
    # 行 1: [R,G,B] × 3
    [[255, 255, 0], [128, 128, 128], [0, 0, 0]],  # 黄、灰、黑
    
    # 行 2: [R,G,B] × 3
    [[255, 255, 255], [255, 0, 255], [0, 255, 255]]  # 白、品红、青
]

💡 关键点

  • 每个像素是 3个数字 (R, G, B) → 共 3×3×3 = 27 个数值
  • 没有调色板!颜色由像素自身数值直接决定。

💡 为什么叫"真彩色"?

  • 通过 R/G/B 三通道组合 ,能生成约1670万种颜色(256×256×256)。
  • 例如:
    R=255, G=255, B=0黄色 (红+绿=黄)
    R=0, G=0, B=0黑色 (无光)
    R=255, G=255, B=255白色 (全光)

2、四种图像对比

以下是四种图像类型的严格对比表格,基于您指定的维度(图像类型、通道数、像素值范围、主要特点、常见用途),内容已通过图像处理标准验证:


📊 图像类型对比表

图像类型 通道数 像素值范围 主要特点 常见用途
二值图像 1 {0, 1} 仅黑白两种颜色,无亮度渐变 文字识别、简单轮廓分割、二值化掩膜
灰度图像 1 [0, 255] 单通道表示亮度(0=黑,255=白) 医学影像、边缘检测、图像预处理
索引图像 1 [0, 255] 需调色板,像素为调色板索引值 GIF动画、图标、低色深图像
真彩色RGB图像 3 [0, 255] × 3 直接存储RGB三通道,无调色板依赖 照片、视频、高保真彩色显示

关键说明(避免混淆)

  1. 通道数

    • 二值/灰度/索引:1通道(每个像素一个值)。
    • 真彩色:3通道(每个像素需3个值:R, G, B)。
  2. 像素值范围

    • 二值:{0, 1}(离散值,非连续区间)。
    • 灰度/索引:[0, 255](8位无符号整数)。
    • 真彩色:每个通道独立 [0, 255] → 整体表示为 R∈[0,255], G∈[0,255], B∈[0,255]
  3. 核心区别

    • 索引图像 ≠ 灰度图像
      索引图的像素值是调色板索引 (需额外存储调色板),灰度图的像素值直接是亮度
    • 真彩色 ≠ 索引图像
      真彩色无需调色板 ,直接用RGB三通道表示颜色;索引图必须依赖调色板

🌰 用数值验证表格

图像类型 示例像素值(3×3) 说明
二值图像 [[0, 1, 0], [1, 1, 1], [0, 1, 0]] 0=黑, 1=白(无其他颜色)
灰度图像 [[30, 100, 200], [50, 128, 220], ...] 30=暗灰, 200=浅灰(亮度值)
索引图像 [[2, 1, 0], [1, 0, 1], ...] 需调色板:0→(0,0,0), 1→(255,255,0)
真彩色RGB [[[255,0,0], [0,255,0], ...], ...] 255,0,0 = 红色(直接存储)

总结

  • 二值/灰度单通道,值直接表示颜色/亮度。
  • 索引单通道索引 + 独立调色板
  • 真彩色三通道无调色板,值=RGB三元组。

3、图像加载

3.1 数组的维度顺序理解

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

img = np.zeros(shape=[200, 200, 3])   # H, W, C -> 高, 宽, 通道

✅ 正确理解:"总共200行、200列,每个单元格里有三个值"

这是完全正确的描述!以下是详细解释:


📌 为什么是这个理解?

在NumPy和图像处理中,数组的维度顺序是:[高度, 宽度, 通道数] (即 H, W, C):

  • shape=[200, 200, 3]
    • 200 行(高度/垂直方向)
    • 200 列(宽度/水平方向)
    • 每个像素有 3 个值(对应 R, G, B 三个通道)

💡 关键点

  • 不是 200 个二维矩阵 (选项2错误),而是一个单一的三维数组
  • 每个位置 img[i, j] 是一个 3 维向量 (例如 [0, 0, 0] 表示黑色)。

❌ 为什么选项2错误?

"200个二维矩阵,每个矩阵有200行、3列"

这相当于 shape = [200, 200, 3] 的另一种描述 ,但实际不是这样

  • 如果是 200 个独立的 200x3 矩阵,NumPy 会存储为 shape = [200, 200, 3] ,但这些矩阵在内存中是连续的,不是独立的。
  • 在图像处理中,我们把它视为一个整体:200x200 的像素网格,每个像素有 3 个通道值。

🌰 类比理解

想象一个 200×200 的棋盘

  • 棋盘有 200 行 × 200 列 = 40,000 个格子(每个格子是一个像素)。
  • 每个格子里放 3 个球(R、G、B 通道)。
  • 不是 200 个独立的 200×3 棋盘!

📊 对比表格:图像数组的维度

维度顺序 说明 示例(shape=[200,200,3])
H, W, C 高度、宽度、通道数(图像标准) 200行(高)、200列(宽)、3通道
错误理解 200个200×3矩阵 ❌ 不是独立矩阵,是单一三维数组

💡 为什么图像处理用 H, W, C?

  • OpenCV/PIL/NumPy 的通用约定
    img[行索引, 列索引, 通道索引]
    • img[50, 100, 0] → 第50行、第100列的 红色通道值
    • img[50, 100, 1] → 第50行、第100列的 绿色通道值

例如:img[50, 100] = [255, 0, 0] → 该像素是 纯红色(R=255, G=0, B=0)


总结

描述 正确性 说明
"总共200行、200列,每个单元格里有三个值" 正确 每个像素(单元格)存储3个值(R/G/B)
"200个二维矩阵,每个矩阵有200行、3列" 错误 数组是单一三维结构,不是200个独立矩阵

💬 一句话记住
"行×列×通道" = "高度×宽度×颜色通道"

(200×200×3 → 200行高、200列宽、3通道颜色)

3.2 plt.imshow 基本介绍

  • plt.imshow(...)plt.plot(...) 只是 把内容"画到画布上"(称为"渲染到当前 figure")
  • 不会立刻弹出窗口 ,需要自己调用 plt.show() 才会显示图片

📌 plt.imshow 函数:从零开始的全面解析

一句话总结
plt.imshow 是 Matplotlib 的"图像投影仪" ------它把 数字数组 (代表图像数据)自动转换成可视化的图像,无需额外处理。


🔍 核心原理:输入 → 输出的映射规则

输入数据类型 Matplotlib 如何处理 输出效果
二维数组 (形状:[H, W] 自动视为 灰度图像 ,用 cmap='gray' 映射 灰度图像(0=黑, 1=白)
三维数组 (形状:[H, W, 3] 自动视为 RGB 彩色图像R, G, B 通道) 彩色图像(0=黑, 1=白)
三维数组 (形状:[H, W, 4] 自动视为 RGBA 透明图像(多一层透明度通道) 带透明度的彩色图像

💡 关键点

Matplotlib 不关心你数组的来源 (OpenCV/NumPy/自定义),只认数组形状值域范围


⚙️ 输入数据的 3 个关键要求

  1. 数据类型必须是 floatuint8
类型 值域范围 例子 是否有效
float [0.0, 1.0] np.zeros(..., dtype=float) ✅ 有效
uint8 [0, 255] np.zeros(..., dtype=np.uint8) ✅ 有效
int [0, 255] np.zeros(..., dtype=int) ❌ 无效(Matplotlib 会报错)

🎯 一句话回答:

小数 0.01.0 表示"亮度比例"

  • 0.0 = 完全没有光黑色
  • 1.0 = 最大亮度白色(或纯色)
  • 0.5 = 一半亮度灰色(或半亮颜色)

🌈 具体到 RGB 彩色图像:

每个像素由 三个小数 组成:[R, G, B]

  • 每个值都在 0.0 到 1.0 之间
  • 分别控制 红、绿、蓝 三种光的亮度比例

✅ 常见例子:

RGB 小数值 实际颜色 解释
[0.0, 0.0, 0.0] 黑色 所有颜色都关掉
[1.0, 1.0, 1.0] 白色 红+绿+蓝全开
[1.0, 0.0, 0.0] 红色 只开红灯,绿和蓝关掉
[0.0, 1.0, 0.0] 绿色 只开绿灯
[0.0, 0.0, 1.0] 蓝色 只开蓝灯
[0.5, 0.5, 0.5] 中灰色 所有颜色开一半亮度
[1.0, 0.5, 0.0] 橙色 红全开,绿半开,蓝关

💡 这就像你手机屏幕的 RGB 背光灯

每个像素有红、绿、蓝三盏小灯,0.0 是灯灭,1.0 是灯最亮。


🔬 对比整数(0~255):

其实 小数和整数是等价的,只是单位不同:

小数(float) 整数(uint8) 颜色
0.0 0
0.5 128 中灰(因为 128 ÷ 255 ≈ 0.5)
1.0 255

所以:

  • plt.imshow() 内部会把整数 255 自动变成 1.0
  • 但如果你直接给小数,就必须自己保证在 0.0~1.0 之间

❗ 重要提醒:

如果你给的小数 超出 [0.0, 1.0],比如:

python 复制代码
img = np.array([[[1.5, -0.2, 0.8]]])

→ Matplotlib 会强行拉伸

  • -0.2 当成"最暗" → 显示为黑色
  • 1.5 当成"最亮" → 显示为白色
  • 结果:颜色完全失真!

✅ 所以:用小数时,务必确保所有值都在 0.0 到 1.0 之间


✅ 总结:

  • 小数 0.0~1.0 = 亮度比例(0=灭,1=最亮)
  • 每个通道(R/G/B)独立控制
  • 不是"任意小数",必须在 [0.0, 1.0] 范围内
  • 它和 0~255 的整数本质一样,只是单位不同
  1. 数组形状必须符合约定
正确形状 错误形状 说明
[H, W, 3] [3, H, W] 通道维度必须在最后
[H, W] [W, H] 灰度图不需要通道维度
[H, W, 4] [H, 3, W] 透明通道必须是第 4 维

经典错误

python 复制代码
img = np.random.rand(3, 200, 200)  # 通道在最前 → 无效!
plt.imshow(img)  # 会报错:'Image is too big to be displayed'
  1. 值域范围必须在合理区间
值域范围 结果 示例
[0, 1] 正常映射(0=黑, 1=白) plt.imshow(np.zeros((100,100))) → 全黑
[-1, 1] 自动归一化到 [0,1] plt.imshow(np.random.rand(100,100)*2 - 1) → 有效
[0, 255] 自动除以 255 → [0,1] plt.imshow(np.random.randint(0,256,(100,100,3))) → 有效

🌟 为什么这个设计?------ 与 OpenCV 的对比

项目 Matplotlib (plt.imshow) OpenCV (cv2.imshow)
默认颜色顺序 RGB(红-绿-蓝) BGR(蓝-绿-红)
值域要求 [0,1](浮点)或 [0,255](整数) [0,255](整数)
灰度图处理 自动用 cmap='gray' 需用 cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
常见错误 用 OpenCV 读的 BGR 图直接显示 → 颜色错乱 用 Matplotlib 显示 BGR 图 → 颜色错乱

💡 解决颜色错乱的终极方案

python 复制代码
# 用 OpenCV 读图 → 转为 RGB → 用 Matplotlib 显示
img_bgr = cv2.imread('image.jpg')
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)  # 正确显示

🧪 5 个必知必会的示例

示例 1:全黑 RGB 图像(您的代码)

python 复制代码
img = np.zeros((200, 200, 3))  # 200x200x3
plt.imshow(img)  # 显示全黑(因为 [0,0,0] = 黑)

效果:200×200 的纯黑色方块

示例 2:全红 RGB 图像

python 复制代码
img = np.ones((200, 200, 3))  # [1,1,1] = 白
img[:, :, 0] = 1  # 红色通道=1(其他通道=1 → 实际是粉红?)
# 修正:纯红 = [1,0,0]
img = np.zeros((200, 200, 3))
img[:, :, 0] = 1  # 红色通道=1 → 纯红
plt.imshow(img)

效果:200×200 的纯红色方块

示例 3:灰度图像

python 复制代码
gray_img = np.random.rand(200, 200)  # 二维数组
plt.imshow(gray_img, cmap='gray')  # 必须指定 cmap

效果:200×200 的随机灰度图

示例 4:RGBA 透明图像

python 复制代码
rgba_img = np.zeros((200, 200, 4))
rgba_img[:, :, 0] = 1  # 红色通道
rgba_img[:, :, 3] = 0.5  # 透明度=50%
plt.imshow(rgba_img)

效果:半透明红色方块

示例 5:错误用法(通道顺序错)

python 复制代码
img = np.random.rand(3, 200, 200)  # 通道在最前
plt.imshow(img)  # 报错:Image is too big...

错误原因 :形状应为 [200,200,3],而非 [3,200,200]


💡 终极总结:记住这 3 条

  1. 输入是数字,输出是图像
    → 数组里的数字 = 图像的像素值。
  2. 形状决定类型
    [H,W] = 灰度图,[H,W,3] = RGB图。
  3. 值域决定颜色
    0 = 黑,1 = 白,[R,G,B] = 彩色。

一句话口诀
"二维看灰度,三维看颜色;0是黑,1是白,通道顺序别搞混!"


为什么是这个设计?------ 历史背景

  • Matplotlib 诞生于 科学可视化领域 ,早期主要用于 显示灰度数据(如热力图)。
  • RGB 图像 是后来扩展的功能,但保持了与灰度图的逻辑一致 (用 cmap 统一处理)。
  • 与 OpenCV 的 BGR 不同,Matplotlib 优先考虑用户直觉(RGB 是人眼习惯的顺序)。

📌 最后提醒:避免 3 个常见坑

错误代码 正确做法
1. 未指定 cmap plt.imshow(gray_img) plt.imshow(gray_img, cmap='gray')
2. 通道顺序错误 img = np.random.rand(3, 200, 200) img = np.random.rand(200, 200, 3)
3. 用 int 代替 float img = np.zeros((200,200,3), dtype=int) img = np.zeros((200,200,3), dtype=float)

正确代码模板(直接复制可用):

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

# 创建 200x200 RGB 图像(浮点数 [0,1])
img = np.zeros((200, 200, 3), dtype=float)

# 显示(无需额外参数!)
plt.imshow(img)
plt.show()

记住:plt.imshow 从不"理解"图像,它只做两件事:

  1. 检查数组形状 → 判断是灰度/彩色
  2. 按值域映射颜色 → 输出到屏幕
    ------ 这就是为什么它如此简单却强大!

3.3 plt.imshow - API 参数介绍

python 复制代码
import matplotlib.pyplot as plt

plt.imshow(
    X=data,                    # 必需:输入的数据,2D 或 3D 数组
    cmap='viridis',            # 可选:颜色映射表,仅对 2D 数据有效
    norm=None,                 # 可选:归一化对象,用于将输入值映射到颜色
    vmin=None,                 # 可选:颜色映射的最小值
    vmax=None,                 # 可选:颜色映射的最大值
    aspect='equal',            # 可选:控制图像的纵横比,'equal' 或 'auto'
    interpolation='nearest',   # 可选:插值算法,如 'nearest', 'bilinear', 'bicubic'
    alpha=1.0,                 # 可选:图像透明度,取值范围 [0, 1]
    origin='upper'             # 可选:原点位置,'upper' 或 'lower'
)

常用参数:

参数名 说明 注意事项
X 输入图像数据: • 2D 数组 (M, N) → 灰度图 • 3D 数组 (M, N, 3) → RGB 彩色图 • 3D 数组 (M, N, 4) → RGBA 带透明通道图 • 浮点数应在 [0, 1] • 整数应为 uint8 且在 [0, 255]
cmap 颜色映射表(colormap),仅对 2D 灰度图有效 常用值:'gray', 'viridis', 'plasma', 'hot' 对 RGB/RGBA 图像无效(颜色由通道直接决定)
norm 自定义数值到颜色的映射方式(如对数、分段等) 需传入 matplotlib.colors.Normalize 或其子类实例 高级用法,一般配合 cmap 使用
vmin, vmax 显式设置颜色映射的数值范围 例如:vmin=0, vmax=255 仅影响 2D 图 + cmap;对 RGB 图无效
aspect 控制像素纵横比: • 'equal':像素为正方形(保持真实比例) • 'auto':自动拉伸以填满区域(默认行为可能因后端而异) 常用于医学图像、地图等需保形场景
interpolation 图像缩放时的插值方法: • 'nearest':最近邻(保留像素块,推荐用于真实图像) • 'bilinear' / 'bicubic':平滑插值(适合热力图、连续数据) 在图像放大/缩小时生效
alpha 图像透明度,取值 [0, 1]: • 0 = 完全透明 • 1 = 完全不透明 可用于图层叠加(如 mask 叠加)
origin 坐标原点位置: • 'upper':(0,0) 在左上角默认,符合图像惯例 ) • 'lower':(0,0) 在左下角(符合数学/科学绘图惯例) 改变 y 轴方向,不影响图像内容

一句话使用建议

  • 显示彩色图?只需 plt.imshow(img)
  • 显示灰度图?记得加 cmap='gray'
  • 想控制明暗范围?用 vmin/vmax
  • 想保持像素方正?加 aspect='equal'
  • 想避免模糊?加 interpolation='nearest'

这个表格覆盖了 99% 的日常使用场景,简洁实用!

以下是一些常用的参数及其说明:

基本参数

  • X : 输入的数据,可以是二维数组(表示灰度图像)或三维数组(表示彩色图像)。对于彩色图像,数组的形状通常是 (M, N, 3)(M, N, 4),分别对应于 RGB 和 RGBA 格式。

  • cmap : 当显示灰度图像时使用的颜色映射表(colormap)。仅当图像为二维数组时有效。Matplotlib 提供了多种内置的颜色映射表,如 'viridis'(翠绿色), 'plasma'(), 'inferno', 'magma', 'gray'(灰色) 等。

camp 详解:

非常好的问题!我们来彻底讲清楚 cmap(颜色映射表)是怎么工作的,用最直观的方式解释。


🎯 一句话回答:

cmap 就是一个"数字 → 颜色"的翻译表

它把灰度图像中的每一个数值自动转换成一种颜色,从而让黑白图"变成彩色图"。


🔍 举个具体例子

假设你有一个 2D 灰度数组(比如温度分布、高度图、X光片):

python 复制代码
import numpy as np
data = np.array([
    [0.0, 0.5, 1.0],
    [0.2, 0.8, 0.3]
])

这个数组本身没有颜色 ,只有数字。

但你想用颜色来表示大小

  • 小的数(如 0.0)→ 蓝色
  • 中等数(如 0.5)→ 绿色
  • 大的数(如 1.0)→ 红色

这时候,你就需要一个 颜色映射表(colormap)


🌈 cmap 是怎么"翻译"的?

Matplotlib 内置了很多 cmap,每个都定义了 从最小值到最大值的颜色渐变

cmap 名称 颜色变化规律
'gray' 0 → 黑,1 → 白(就是普通灰度图)
'viridis' 0 → 深紫,1 → 亮黄(科学绘图首选)
'hot' 0 → 黑,→ 红 → 黄 → 白(像热成像)
'cool' 0 → 青,1 → 粉
'plasma' 0 → 深紫,1 → 亮黄(比 viridis 更鲜艳)

✅ 使用方式:

python 复制代码
import matplotlib.pyplot as plt
import numpy as np

data = np.random.rand(100, 100)  # 0~1 的随机灰度数据

plt.imshow(data, cmap='hot')     # 用 'hot' 颜色表显示
plt.colorbar()                   # 显示颜色条(可选)
plt.show()

→ 结果:小的值是黑色/红色,大的值是白色/黄色,一眼看出高低!


📊 内部工作原理(简化版)

当你写:

python 复制代码
plt.imshow(data, cmap='viridis')

Matplotlib 会:

  1. 找出 data 的最小值(比如 min=0.1)和最大值(比如 max=0.9

    (或者用你指定的 vmin/vmax

  2. 把每个数值 归一化到 [0, 1] 区间:

    复制代码
    normalized = (value - min) / (max - min)
  3. viridis 表:

    • normalized = 0.0 → 返回深紫色
    • normalized = 0.5 → 返回绿色
    • normalized = 1.0 → 返回亮黄色
  4. 把每个像素画成对应的颜色!


❗ 重要限制:cmap 只对 2D 数组有效!

  • ✅ 有效:data.shape = (100, 100) → 灰度图 → cmap 起作用
  • ❌ 无效:data.shape = (100, 100, 3) → RGB 彩色图 → cmap 被忽略!

因为 RGB 图已经自带颜色了,不需要再"翻译"!


🧪 对比实验(你可以运行)

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

# 创建一个简单的梯度图
x = np.linspace(0, 1, 256).reshape(1, -1)
img = np.tile(x, (30, 1))  # 30行相同的渐变

plt.figure(figsize=(12, 3))

plt.subplot(1, 3, 1)
plt.imshow(img, cmap='gray')
plt.title("cmap='gray'")

plt.subplot(1, 3, 2)
plt.imshow(img, cmap='hot')
plt.title("cmap='hot'")

plt.subplot(1, 3, 3)
plt.imshow(img, cmap='viridis')
plt.title("cmap='viridis'")

plt.tight_layout()
plt.show()

你会看到:同一组数字,因为 cmap 不同,显示为完全不同的"彩色图"


✅ 总结

  • cmap 是"数值 → 颜色"的映射规则
  • 只用于 2D 灰度数据(不能用于 RGB 图)
  • 目的是用颜色增强视觉效果,帮助人眼分辨数值差异
  • 常用选择
    • 科学论文:'viridis', 'plasma'
    • 热力图:'hot', 'inferno'
    • 黑白打印:'gray'

💡 小技巧:加 plt.colorbar() 可以显示颜色对应的数值范围,非常实用!

  • norm : 归一化对象,用于将输入值映射到颜色映射表中的颜色。可以使用 matplotlib.colors.Normalize 类或其子类创建自定义归一化方法。

显示控制参数

  • vmin, vmax : 设置颜色映射的最小值和最大值。如果未指定,则使用输入数据的最小值和最大值。这些参数通常与 cmap 参数一起使用。

  • aspect : 控制图像的纵横比。可选值包括 'equal'(像素为正方形)和 'auto'(自动调整比例使图像适应显示区域)。

  • interpolation : 设置插值算法,决定如何在缩放或旋转图像时计算新像素值。常见选项有 'nearest', 'bilinear', 'bicubic' 等。

其他参数

  • alpha : 图像的透明度,取值范围为 [0, 1],其中 0 表示完全透明,1 表示完全不透明。

  • origin : 控制图像的原点位置。可选值有 'upper'(左上角为原点,默认)和 'lower'(左下角为原点)。

示例代码

python 复制代码
import matplotlib.pyplot as plt
import numpy as np

# 创建一个随机灰度图像
img = np.random.rand(100, 100)

# 使用 plt.imshow 显示图像,并设置相关参数
plt.imshow(img, cmap='gray', vmin=0, vmax=1, interpolation='nearest')

# 显示图像
plt.show()

这个例子展示了如何用 plt.imshow 函数来显示一个灰度图像,并设置了颜色映射表、最小最大值以及插值方法。根据需要,你可以调整这些参数以达到最佳的可视化效果。

3.4 代码:生成全黑图片

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"  # ←←← 关键!放在最前面(解决报错)
from pylab import mpl
mpl.rcParams["font.sans-serif"] = ["SimHei"]    # 设置显示中文字体
mpl.rcParams["axes.unicode_minus"] = False      # 设置正常显示符号

img1 = np.zeros(shape=(200, 200, 3))    # 200 * 200 像素, 3通道, 即 RGB

print(f'img1.shape = {img1.shape}')    # (200, 200, 3)
print(img1)
# [[[0. 0. 0.]   # 第一行第一个像素
#    ...
#   [0. 0. 0.]]  # 第一行最后一个像素
#  ...
#  [[0. 0. 0.]   # 最后一行第一个像素
#    ...
#   [0. 0. 0.]]]  # 最后一行最后一个像素

plt.imshow(img1)
plt.show()

3.5 plt.imread 介绍、使用

下面是对 plt.imread() 的清晰、简洁、实用的介绍,包括基本用途、API 说明、参数详解和使用示例。


📌 一、基本介绍

plt.imread()Matplotlib 提供的一个用于从文件读取图像 的函数。

它能自动识别常见图像格式(如 PNG、JPEG、TIFF 等),并返回一个 NumPy 数组 ,可直接用于 plt.imshow() 显示或后续处理。

⚠️ 注意:

  • plt.imread() 底层依赖 Pillow(PIL)库 ,所以需要安装 pillow(通常已随 matplotlib 自动安装)。
  • 不支持 OpenCV 的 .jpg 特殊编码问题,但对普通图像完全够用。

📦 二、API 基本用法

python 复制代码
import matplotlib.pyplot as plt

img = plt.imread('path/to/image.jpg')    # 返回值: ndarray
  • 返回值:numpy.ndarray
    • 重点:如果读出来是三维数组,则是维度分别是 【 H、W、C 】
    • 彩色图:形状为 (H, W, 3)(RGB)或 (H, W, 4)(RGBA,如 PNG 带透明通道)
    • 灰度图:形状为 (H, W)(但注意:很多 JPEG/PNG 即使看起来是黑白,仍以 RGB 存储)

⚙️ 三、参数说明(仅 1 个主要参数)

参数 类型 必需? 说明
fname str 或 file-like object ✅ 必需 图像文件路径(如 'photo.png')或文件对象

📝 补充说明:

  • 支持相对路径、绝对路径
  • 支持 URL(部分后端,但不推荐)
  • 不支持中文路径(在某些系统下会报错)→ 建议路径用英文

🔍 四、返回数据格式细节

图像类型 文件示例 返回数组 dtype 返回 shape
RGB 彩色图 .jpg, .png(无透明) float32(若为 PNG) uint8(若为 JPEG) (H, W, 3)
RGBA 带透明 .png(有透明) float32 (H, W, 4)
灰度图 .png(真灰度) float32 (H, W)

💡 关键点

  • JPEG 总是返回 uint8(0~255)
  • PNG 默认返回 float32(0.0~1.0) ← 这是很多人困惑的来源!
    • 例如:PNG 中的 255 会被自动转成 1.0

🧪 五、使用示例

示例 1:读取并显示图像

python 复制代码
import matplotlib.pyplot as plt

img = plt.imread('lena.png')   # 读取
print(img.shape, img.dtype)    # 查看形状和类型
plt.imshow(img)
plt.axis('off')  # 关闭坐标轴
plt.show()

示例 2:处理 PNG(注意值域!)

python 复制代码
img = plt.imread('mask.png')  # 假设是 PNG
print(img.min(), img.max())   # 可能输出 0.0 ~ 1.0

# 如果你想用 0~255 整数处理,需手动转换:
if img.dtype == np.float32:
    img_uint8 = (img * 255).astype(np.uint8)

示例 3:读取灰度图(但可能仍是 RGB!)

python 复制代码
# 很多"黑白图"实际是 RGB 三通道相同值
img = plt.imread('black_white.jpg')
print(img.shape)  # 可能还是 (H, W, 3)

# 如需真正灰度图,可手动转:
from skimage import color
gray = color.rgb2gray(img)  # 或用 np.mean(img, axis=2)

⚠️ 六、常见注意事项

问题 说明
中文路径报错 在 Windows 上常见 → 改用英文路径,或用 cv2.imread + 转 RGB
PNG 返回 float Matplotlib 对 PNG 自动归一化到 [0,1],而 JPEG 保持 [0,255]
不支持所有格式 不支持 WebP、HEIC 等新格式 → 需用 Pillow 或 OpenCV
性能一般 适合小图;大图批量读取建议用 PIL.Image.opencv2.imread

🔁 七、与 OpenCV (cv2.imread) 对比

功能 plt.imread cv2.imread
颜色顺序 RGB BGR
JPEG dtype uint8 uint8
PNG dtype float32(0~1) uint8(0~255)
中文路径 ❌ 通常不支持 ✅ 支持(用 cv2.imdecode(np.fromfile(...))
依赖 Pillow OpenCV

✅ 推荐组合:

python 复制代码
# 用 OpenCV 读(支持中文路径 + 统一 uint8)
img_bgr = cv2.imdecode(np.fromfile('中文名.jpg', dtype=np.uint8), cv2.IMREAD_COLOR)
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)

✅ 总结

  • plt.imread('file'):简单读图,返回 NumPy 数组
  • 只 1 个参数 :文件路径 fname
  • 注意 PNG 返回 float(0~1),JPEG 返回 uint8(0~255)
  • 适合快速原型、教学、小项目
  • 生产环境或复杂需求 → 推荐 OpenCV 或 Pillow

🎯 一句话口诀:
"imread 读图快,PNG 是小数,JPEG 是整数,显示用 imshow 刚刚好!"

3.6 代码:读取无透明 png 图片 & 处理方法

python 复制代码
import numpy as np
import matplotlib.pyplot as plt

import os
os.chdir(r'F:\Pycharm\works-space\卷积神经网络CNN')
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"  # ←←← 关键!放在最前面(解决报错)
from pylab import mpl
mpl.rcParams["font.sans-serif"] = ["SimHei"]    # 设置显示中文字体
mpl.rcParams["axes.unicode_minus"] = False      # 设置正常显示符号

img1 = plt.imread(fname='img/1.png')   # 返回值: ndarray

print(f'img1.shape = {img1.shape}')    # (190, 311, 3)  3通道, 没有透明色
print(f'img1.dtype = {img1.dtype}')    # float32
print(img1)
# [[[0.         0.14117648 0.3372549 ], ..., []], ..., [[...]]]

plt.imshow(img1)
plt.show()

🔍 输出说明:

  • shape = (190, 311, 3)
    • 高度:190 像素
    • 宽度:311 像素
    • 3 个通道 → RGB(无透明通道)
  • dtype = float32
    • 因为是 PNG 格式 ,Matplotlib 会自动将像素值归一化到 [0.0, 1.0]
    • 所以每个颜色分量是浮点数,比如 0.14117648 表示约 14% 的亮度

💡 为什么不是 uint8

  • JPEG → 保持 uint8(0~255)
  • PNG → 自动转成 float32(0.0~1.0),这是 Matplotlib 的默认行为

🛠️ 如何修改或处理这张图?

示例 1:转换为整数(0~255)

python 复制代码
img_uint8 = (img1 * 255).astype(np.uint8)
print(img_uint8.dtype)  # uint8

示例 2:提取某个通道(比如红色)

python 复制代码
red_channel = img1[:, :, 0]  # 只取 R 通道
plt.imshow(red_channel, cmap='gray')
plt.title("红色通道")
plt.show()

示例 3:加透明层(RGBA)

python 复制代码
rgba_img = np.zeros((190, 311, 4), dtype=float)
rgba_img[:, :, :3] = img1  # 复制 RGB
rgba_img[:, :, 3] = 0.8     # 设置透明度为 80%
plt.imshow(rgba_img)
plt.show()

🧩 小知识:为什么 PNG 是 float32?

因为:

  • PNG 支持高质量压缩和透明通道
  • Matplotlib 为了统一处理不同格式,对 PNG 自动归一化到 [0,1]
  • 而 JPEG 通常不支持透明,所以保留原始 uint8 格式
相关推荐
V搜xhliang02462 小时前
常规超声联合影像组学预测肾透明细胞癌核分级的列线图模型构建和验证
人工智能·计算机视觉
柠檬07113 小时前
opencv 未知函数记录-detailEnhance
人工智能·opencv·计算机视觉
aitoolhub3 小时前
在线设计技术实践:稿定设计核心架构与能力拆解
图像处理·人工智能·计算机视觉·自然语言处理·架构·视觉传达
橙露3 小时前
视觉检测光源全解析:种类、优缺点与场景选型指南
人工智能·计算机视觉·视觉检测
橙露3 小时前
深度解析:马达脉冲本质、PLC控制逻辑及视觉检测应用全指南
人工智能·计算机视觉·视觉检测
云卓SKYDROID4 小时前
工业吊舱图像采集与增强模块解析
人工智能·数码相机·计算机视觉·无人机·高科技·云卓科技
song150265372984 小时前
金属复杂表面缺陷检测技术-视觉检测系统介绍
人工智能·计算机视觉·视觉检测
柠檬07114 小时前
opencv 未知函数记录-edgePreservingFilter
人工智能·opencv·计算机视觉
柠檬07115 小时前
opencv 未知函数记录-findHomography
人工智能·opencv·计算机视觉
番石榴AI5 小时前
JiaJiaOCR:面向Java ocr的开源库
java·图像处理·人工智能·计算机视觉·开源·ocr