第 02 讲《图像数字化:像素、通道与张量》——模型看到的不是照片:一文搞懂像素、通道与张量
模型看到的不是照片:一文搞懂像素、通道与张量
第 02 讲《图像数字化:像素、通道与张量》公众号整理版
整理说明:本文基于 B 站视频《第02讲〈图像数字化:像素、通道与张量〉》的公开信息、课程课件主线,并结合 OpenCV、PIL、PyTorch 与 YOLO 输入预处理常见流程进行原创化整理。本文不是逐字稿,重点是把“图片如何变成模型能算的数字”整理成科研小白可以照着学、照着查、照着跑的教程。
你在屏幕上看到一张图片,可能是一只猫、一辆车、一张医学影像,也可能是交通路口。
但模型看到的不是“猫”,不是“车”,也不是“病灶”。模型看到的是一组排好队的数字。
所以第 02 讲只解决一个核心问题:
模型看到的不是照片,而是一组怎样排列的数字?
这件事一旦讲清楚,后面学 CNN、YOLO26、数据增强、训练报错都会顺很多。因为几乎所有视觉模型的第一步,都是把图片变成张量。
02_图像数字化:像素、通道与张量
01 为什么小白总卡在 shape 上?
很多同学刚开始学 YOLO 或深度学习,最容易卡在 4 个地方:
- 图片明明能打开,为什么到模型里变成
3 x 640 x 640? RGB、BGR、HWC、CHW、BCHW到底谁在前谁在后?- 图片尺寸、通道顺序、归一化写错,为什么代码不一定报错,但结果会很离谱?
- 同一张图在 OpenCV、PIL、PyTorch 里为什么读出来不一样?
这几个问题本质上都指向同一件事:
图片不是直接进入模型的。它要先被解码成像素,再整理成通道,再变成张量,最后按照模型要求的 shape 输入网络。
把这条链路记住:
图片文件 → 像素矩阵 → 通道排列 → 尺寸统一 → 数值缩放 → 维度转换 → batch 张量 → 进入网络
以后遇到输入错误,先沿着这条链往回查,不要第一反应就怀疑模型结构。
02 六个核心概念,一次说清楚
| 概念 | 严格一点的说法 | 小白理解 |
|---|---|---|
| 像素 Pixel | 图像的最小采样单元,每个位置记录颜色或强度 | 一张图由很多小格子组成 |
| 通道 Channel | 颜色或特征的维度 | RGB 图像有红、绿、蓝三层 |
| 张量 Tensor | 多维数组,是神经网络处理数据的基本容器 | 模型能计算的数字盒子 |
| Shape | 张量每个维度的长度 | 1 x 3 x 640 x 640里的每个数字 |
| Batch | 一次送入模型的一组样本 | batch=8 表示一次处理 8 张图 |
| 归一化 | 把数值缩放到稳定范围 | 常见做法是把 0-255 除以 255 |
初学阶段先别急着背复杂公式。你只要能回答三件事:
- 这个数字从哪里来?
- 它现在是什么形状?
- 下一步要送到哪里?
能把这三件事讲明白,shape 就不再是玄学。
03 像素:一张图首先是一张数字表
一张640 x 480的灰度图,可以理解为一个480 行 x 640 列的二维矩阵。
每个位置有一个数,表示这个位置的亮度。常见 8 位图像的像素范围是 0 到 255:
| 数值 | 含义 |
|---|---|
| 0 | 最暗,接近黑色 |
| 255 | 最亮,接近白色 |
| 中间值 | 不同程度的灰度 |
彩色图像多了颜色通道。最常见的 RGB 图像,每个像素不是一个数,而是三个数:
[R, G, B]例如:
| 颜色 | RGB 数值 |
|---|---|
| 红色 | [255, 0, 0] |
| 绿色 | [0, 255, 0] |
| 蓝色 | [0, 0, 255] |
| 白色 | [255, 255, 255] |
这就是“模型看到的是数字”的第一层含义:图片先变成一堆像素值。
04 2 x 2 RGB 小图,手算一次图片变张量
为了彻底看懂,我们构造一张最小彩色图:
| 位置 | 颜色 | RGB |
|---|---|---|
| 左上 | 红色 | [255, 0, 0] |
| 右上 | 绿色 | [0, 255, 0] |
| 左下 | 蓝色 | [0, 0, 255] |
| 右下 | 白色 | [255, 255, 255] |
如果用HWC表示,它可以写成:
[[[255,0,0],[0,255,0]],[[0,0,255],[255,255,255]]]这个 shape 是:
H x W x C = 2 x 2 x 3其中:
| 字母 | 含义 | 这里的值 |
|---|---|---|
| H | Height,高度,有几行 | 2 |
| W | Width,宽度,有几列 | 2 |
| C | Channel,通道数 | 3 |
但深度学习框架里,模型常常更喜欢CHW或BCHW:
HWC: 2 x 2 x 3 CHW: 3 x 2 x 2 BCHW: 1 x 3 x 2 x 2注意,数字总量没有变,只是排列顺序变了。
05 HWC、CHW、BCHW 到底怎么记?
最简单的记法:
| 格式 | 常见场景 | 例子 | 读法 |
|---|---|---|---|
H x W x C | 图片存储、OpenCV/PIL 转 NumPy 后常见 | 640 x 640 x 3 | 高、宽、通道 |
C x H x W | 单张图送入 PyTorch 模型前常见 | 3 x 640 x 640 | 通道、高、宽 |
B x C x H x W | 模型训练和推理常见 | 8 x 3 x 640 x 640 | batch、通道、高、宽 |
为什么要加B?
因为训练时通常不是一张一张喂给模型,而是一批一批喂。8 x 3 x 640 x 640的意思是:
| 维度 | 含义 |
|---|---|
| 8 | 一次处理 8 张图 |
| 3 | 每张图 3 个通道 |
| 640 | 高度 640 |
| 640 | 宽度 640 |
一句话总结:
HWC 像图片,CHW 像单张模型输入,BCHW 像一批模型输入。
06 OpenCV、PIL、PyTorch 为什么容易混?
最常见的坑是通道顺序。
| 工具 | 常见读取结果 | 通道习惯 | 小白提醒 |
|---|---|---|---|
| OpenCV | NumPy 数组 | 默认 BGR | 显示或送模型前常需要转 RGB |
| PIL | Image 对象 | 常见 RGB | 转 NumPy 后通常是 HWC |
| PyTorch | Tensor | 常用 CHW/BCHW | 通常要permute或transpose |
举个例子:
importcv2 img_bgr=cv2.imread("test.jpg")img_rgb=cv2.cvtColor(img_bgr,cv2.COLOR_BGR2RGB)如果忘了 BGR 转 RGB,代码可能不报错,但颜色语义已经变了。对人来说只是颜色怪,对模型来说就是输入分布变了。
这也是为什么很多检测结果“看起来很玄”:模型还没开始推理,输入已经悄悄错了。
07 归一化:为什么要除以 255?
常见图片像素范围是 0 到 255,但神经网络训练更喜欢比较稳定的数值范围。
所以经常会做:
x_norm=x/255.0这样像素会从:
0-255变成:
0-1这一步不是为了“好看”,而是为了让后续计算更稳定。数值尺度差太大,可能让激活值、梯度和优化过程变得不舒服。
但要注意:
训练时怎么归一化,推理时也要保持一致。
如果训练时输入是 0-1,推理时却喂 0-255,模型结果很可能异常。
08 Resize 和 Letterbox:尺寸统一不是简单拉伸
模型通常要求统一输入尺寸,例如640 x 640。
但真实图片可能是横图、竖图、方图,尺寸各不相同。常见处理有两类:
| 方法 | 做法 | 风险 |
|---|---|---|
| 直接 resize | 强行拉到指定大小 | 可能改变目标比例 |
| letterbox | 保持比例缩放,再补边 | 更适合检测任务 |
目标检测尤其要注意比例。因为检测任务不仅要认出类别,还要预测位置。如果几何比例乱了,框的位置学习也会被影响。
所以第一次做自己的数据集时,请记录:
- 原图尺寸是多少?
- 训练输入尺寸是多少?
- 是否使用 letterbox?
- 标签坐标是否和图片尺寸对应?
- 可视化标签时,框是否真的贴住目标?
09 实操教程:从 0 检查一张图的输入张量
下面这套流程适合科研小白跟着做。第一次不要急着训练 YOLO26,先把一张图怎么变成张量跑通。
第一步:创建环境
conda create-nimage-tensorpython=3.10-yconda activate image-tensor pipinstallnumpy pillow opencv-python matplotlib torch如果暂时不想装 PyTorch,可以先跑 NumPy、PIL、OpenCV 部分。
第二步:手写 2 x 2 RGB 小图
新建01_make_2x2_rgb_tensor.py:
importnumpyasnp img_hwc=np.array([[[255,0,0],[0,255,0]],[[0,0,255],[255,255,255]],],dtype=np.uint8)img_chw=np.transpose(img_hwc,(2,0,1))img_bchw=np.expand_dims(img_chw,axis=0)img_norm=img_bchw.astype(np.float32)/255.0print("HWC shape:",img_hwc.shape)print(img_hwc)print("CHW shape:",img_chw.shape)print(img_chw)print("BCHW shape:",img_bchw.shape)print("normalized range:",img_norm.min(),img_norm.max())你要看懂这三行:
np.transpose(img_hwc,(2,0,1))np.expand_dims(img_chw,axis=0)img_bchw.astype(np.float32)/255.0它们分别对应:
- 把
HWC变成CHW。 - 加一个 batch 维度,变成
BCHW。 - 把 0-255 缩放到 0-1。
第三步:读取真实图片并打印 shape
准备一张test.jpg,新建02_inspect_image_shape.py:
frompathlibimportPathimportcv2importnumpyasnpfromPILimportImage image_path=Path("test.jpg")ifnotimage_path.exists():raiseFileNotFoundError("请先放一张 test.jpg 到当前目录")img_cv=cv2.imread(str(image_path))print("OpenCV:",type(img_cv),img_cv.shape,img_cv.dtype)print("OpenCV first pixel BGR:",img_cv[0,0].tolist())img_rgb=cv2.cvtColor(img_cv,cv2.COLOR_BGR2RGB)print("After BGR->RGB first pixel:",img_rgb[0,0].tolist())img_pil=Image.open(image_path).convert("RGB")img_np=np.array(img_pil)print("PIL to NumPy:",type(img_np),img_np.shape,img_np.dtype)print("PIL first pixel RGB:",img_np[0,0].tolist())img_chw=np.transpose(img_np,(2,0,1))img_bchw=np.expand_dims(img_chw,axis=0).astype(np.float32)/255.0print("CHW:",img_chw.shape)print("BCHW normalized:",img_bchw.shape,img_bchw.min(),img_bchw.max())如果你能读懂输出,就说明你已经掌握了图像数字化的核心。
第四步:做一次 letterbox 尺寸检查
新建03_letterbox_check.py:
importcv2importnumpyasnpdefletterbox(img,new_shape=(640,640),color=(114,114,114)):h,w=img.shape[:2]new_h,new_w=new_shape scale=min(new_w/w,new_h/h)resized_w,resized_h=int(round(w*scale)),int(round(h*scale))pad_w,pad_h=new_w-resized_w,new_h-resized_h left,right=pad_w//2,pad_w-pad_w//2top,bottom=pad_h//2,pad_h-pad_h//2resized=cv2.resize(img,(resized_w,resized_h))padded=cv2.copyMakeBorder(resized,top,bottom,left,right,cv2.BORDER_CONSTANT,value=color)returnpadded,scale,(left,top)img=cv2.imread("test.jpg")ifimgisNone:raiseFileNotFoundError("请先放一张 test.jpg 到当前目录")out,scale,pad=letterbox(img)print("original:",img.shape)print("letterbox:",out.shape)print("scale:",scale)print("pad left/top:",pad)cv2.imwrite("letterbox_preview.jpg",out)运行后会生成letterbox_preview.jpg。你要观察:图片有没有被强行拉伸,边缘是否补了灰边。
10 用这张表定位输入问题
| 异常现象 | 优先检查 | 常见原因 |
|---|---|---|
| 颜色明显不对 | RGB/BGR | OpenCV 读图后没转 RGB |
| shape 报错 | HWC/CHW/BCHW | 少了 batch 维或通道位置错 |
| 检测框乱飘 | resize/letterbox/标签 | 几何比例或标签坐标对应不上 |
| 结果置信度很低 | 归一化 | 训练和推理数值范围不一致 |
| 训练显存爆掉 | batch/imgsz | batch 太大或图像尺寸太大 |
| 小目标变差 | resize 策略 | 缩放后目标太小,信息丢失 |
排错顺序建议:
- 先打印原图 shape。
- 再打印预处理后 shape。
- 再检查通道顺序。
- 再检查数值范围。
- 最后可视化图片和标签。
这比盲目改模型、改学习率、改 epoch 更靠谱。
11 学完本讲,你要能回答这 5 个问题
- 一个 RGB 像素为什么有三个数?
8 x 3 x 640 x 640每一维分别代表什么?- 为什么 OpenCV 读取的图片经常要从 BGR 转 RGB?
- 为什么归一化会影响训练和推理稳定性?
- resize 和 letterbox 对检测任务有什么区别?
如果你能把这 5 个问题讲给别人听,第 02 讲就真正学进去了。
12 最后总结
本讲最重要的一句话:
模型看到的不是照片,而是按规则排列的数字张量。
一张图片进入模型前,通常要经过:
读取图片 → 解码成像素 → 拆分通道 → 调整尺寸 → 改变维度顺序 → 数值归一化 → 加 batch 维度 → 进入网络
后面学 YOLO26 时,只要遇到输入问题,就回到这条链路检查。
如果这篇文章帮你把RGB、HWC、CHW、BCHW想清楚了,建议先关注、收藏,也可以转给正在被 shape 折磨的同学。
评论区留言:张量。下一篇继续整理第 03 讲:有了输入张量之后,标签和 Loss 如何告诉模型哪里做错。
参考资料
- B 站视频:《第02讲〈图像数字化:像素、通道与张量〉》
https://www.bilibili.com/video/BV1GaJ56jEeM/ - PyTorch Tensor 文档
https://docs.pytorch.org/docs/stable/tensors.html - Pillow Image 文档
https://pillow.readthedocs.io/en/stable/reference/Image.html - OpenCV 图像读写文档
https://docs.opencv.org/4.x/d4/da8/group__imgcodecs.html - Ultralytics YOLO Predict 文档
https://docs.ultralytics.com/modes/predict/
