跳到主要内容

图像在计算机里是什么

这一章讲三件事: 一张图对程序来说到底是什么; 为什么同一张图用两个不同的库打开,红色和蓝色会颠倒; 以及有一条分界线 —— 哪些操作只挪像素的位置、哪些只改像素的值 —— 这条线到第 13 章会决定一整类做法能不能用。 它在全书链条里的位置是地基: 第 10 到第 14 章全部建在本章第 2 节那一句话上。 遇到的生词都在当场解释。

1. 顶层全景

摄像头拍下的那张图(640×480 彩色)

│ ① 它其实是三张表格叠在一起:红一张、绿一张、蓝一张
│ 每张表格 480 行 × 640 列,每格一个 0—255 的数

┌─────────┬─────────┬─────────┐
│ R 表格 │ G 表格 │ B 表格 │
└─────────┴─────────┴─────────┘

│ ② 从这里开始,所有花样都是在这些数上动手:

├── 只挪位置,不改值 → 翻转、旋转、平移、缩放
├── 只改值,不挪位置 → 变亮、加对比度、模糊、二值化
├── 换一套坐标来描述同一种颜色 → 转成灰度、转成 HSV
└── 拿一个小方块在上面滑一遍 → 卷积(第 10 章)

图说:这一章讲前三行,第 10 章讲第四行。
它们的共同前提只有一句话:图 = 一堆数。

本章主走查: 同一张 640×480 的车前道路图,五次变身 —— 每一步都盯住同一个像素点,写出它此刻具体是哪几个数。 走查从第 3 节开始,到第 7 节走完。

2. 一张图,是一张写满 0 到 255 的表格

这一节只有一句话,但它是后面十二章的地基。

先看现象

你拍一张照片,看到的是路面、车道线、天空。计算机没有「看到」任何东西 —— 它拿到的只是一堆数。那么问题是:这堆数是怎么排的?

做法:把图切成格子,每格记一个亮度

书讲得很清楚:常见的图片是模拟图像,不能直接处理,要先数字化 —— 把图像分割成一个个像素,每个像素用一个灰度值表示; 这样一张图像就变成了一个二维数组,数组里每个元素都是一个 0 到 255 的数1

两个词在这里就地解释:

意思
像素把图切成网格之后,最小的那一个格子。640×480 的图有 307 200 个
灰度值那个格子有多亮。0 是纯黑,255 是纯白,中间是各种深浅的灰
一张 4×4 的小灰度图,长这样:

列0 列1 列2 列3
行0 │ 30 │ 32 │ 200│ 205│ ← 左边暗(路面),右边亮(车道线)
行1 │ 28 │ 35 │ 198│ 210│
行2 │ 31 │ 33 │ 201│ 208│
行3 │ 29 │ 30 │ 199│ 206│

图说:「看到一条白线」这件事,对程序来说就是「右边两列的数明显比左边大」。
这几个数是为演示编的,不是书里的数。

彩色图:三张表格叠起来

灰度图一张表格就够了。彩色图要三张。

书的说法是:彩色图可以分解为红、绿、蓝三个通道, 也就是拆成 3 个同样像素大小的灰度图;把这三张按顺序组合起来就是一张彩色图2

「通道」这个词在这里就地解释:一张彩色图里,负责同一种基色的那一整张表格,叫一个通道。

彩色图 (480, 640, 3)
├── 通道 0:红,一张 480×640 的表,每格记「这里的红有多强」
├── 通道 1:绿,同尺寸
└── 通道 2:蓝,同尺寸

某一个像素点 = 从三张表的同一个位置各取一个数 = (R, G, B)
例如 (200, 30, 40) 就是一块偏红的地方

图说:所谓「调色」「变亮」「提取车道线」,全都是在改这些数。

这一节要记住的就一句话:图 = 一堆按格子码好的数。 第 10 章的卷积是在这堆数上滑一个小方块;第 12 章挑车道线是在这堆数上定一个范围; 第 14 章逐格判类别是给这堆数里的每一格贴一个标签。没有例外。

3. 装这堆数的容器,不是列表

第 03 章讲过列表。处理图像不用它 —— 这一节讲用什么、为什么。

现象先行:列表算起来太啰嗦

假设你要把一张图整体调暗一半。用列表就得这样:

for row in range(480):
for col in range(640):
img[row][col] = img[row][col] // 2 # 三十万次循环,在 Python 这一层跑

三十万次循环全在 Python 这一层跑 —— 而第 02 章说过,慢的正是这一层。

做法:换成 numpy 的数组

书给了这个库的两个基本对象3:

对象是什么
ndarray存单一数据类型的多维数组。名字里的 N 是维数:一维、二维、三维都行
ufunc能对整个数组一次性动手的函数,不用你自己写循环

于是上面那件事变成一行:

import numpy as np
img = img // 2 # 三十万个数一起除,循环在 C 那一层跑

两条差别要记牢:

列表numpy 数组
里面装什么什么都行,可以混着装必须同一种类型
怎么算一个一个来,循环写在 Python 里整块一起算,循环在底层的 C 里

「必须同一种类型」听起来是限制,其实正是它快的原因(补充,不在书里,来自通用知识): 类型统一,这些数才能在内存里一个挨一个连续摆放,底层才能一次搬一大块。

三组容易混的方法,书讲得很清楚

这个库有几组名字很像、行为不同的方法,书专门做了对照4:

一组差别在哪
x.resize() / np.resize() / reshape()第一个就地改、没有返回值;第二个允许改前改后元素个数不同,会自动截断或拼接;第三个有返回值、元素个数必须一样,而且可以用 -1 让它自己算某一维
ravel() / flatten()都是「把多维压成一维」。前者给的是同一份数据的另一个视角(改它会影响原数组),后者是复制一份
np.repeat() / np.tile()前者复制每一个元素,后者复制整个数组

第二组和第 03 章那个 lt = ls 的坑是同一件事: 有的操作给你一份新数据, 有的只是给同一份数据换个名字。分不清这一点,就会出现「我明明没改它,它怎么变了」。

这个库为什么要在这一章讲(而不是等到讲数据分析的时候): 因为「图像是一个数组」这句话里的那个「数组」,指的就是它。 第 12 章会看到「蒙版就是一个 numpy 数组」——到那时这句话才接得上。

4. 主走查开始:两个库读同一张图,结果不一样

这是实战最容易翻车的一处,而且错了不报错。

先看现象

书里的示例图是智能车前置摄像头拍的车前道路图,640×4805 —— 正是第 01 章那条链的第一环。

现在用两个常用的库分别打开它,各取同一个像素点:

用 PIL 打开: img.getpixel((320, 400)) → (200, 30, 40)
用 OpenCV 打开: img[400, 320] → ( 40, 30, 200)

↑ 同一个点,三个数一模一样,顺序反了。
这两个三元组是我们为演示编的,书没有给具体像素值。

如果你不知道这件事,会发生什么: 你以为那是一块偏红的路标(R=200), 其实程序告诉你的是「蓝 200」—— 你会把红的当成蓝的,而程序不会报任何错。

做法:两个库的通道顺序本来就不同

书说得很明确:PIL 打开的图是 RGB 顺序, 而 OpenCV 打开的图是 BGR 顺序 —— 蓝在最前面6

安装时叫代码里叫通道顺序
PILpillowfrom PIL import ImageR、G、B
OpenCVopencv-pythonimport cv2B、G、R

注意安装名和导入名不一样,这两个库都是。 书特意点了这件事7

互相转换书也给了8:

# PIL 的图 → OpenCV 的图
cv_img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)

# OpenCV 的图 → PIL 的图
pil_img = Image.fromarray(cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB))

判断(我们的,不是书里的): 这个坑之所以害人,不是因为它复杂, 而是因为它不报错。凡是「两套约定同时存在、而且都合法」的地方, 都会长出这类静默错误。处理图像时,养成在读进来的第一行就写清顺序的习惯。 如果错,会错在: 如果你的整条流水线从头到尾只用一个库,那这个坑根本不存在, 这条提醒就是多余的开销。判据是:项目里有没有同时出现这两个库的 import。

走查记账

主走查 ① 原图,PIL 读: (320, 400) 这个点 = (R=200, G=30, B=40)
主走查 ② 原图,cv2 读: 同一个点 = (B= 40, G=30, R=200)

↑ 这两组数是为演示编的,不是书里的数。

5. 存的时候也有坑:通道数对不上

这一节讲一个具体的报错,以及它背后的道理。

先看现象

你有一张 .png 图,想存成 .jpg。直接一句 save(),报错:

OSError: cannot write mode RGBA as JPEG

为什么

书给的解释很干净:png 是四通道的 RGBA 模式 —— 红、绿、蓝之外还有一个 alpha 通道(记录「这一格有多透明」);而 jpg 只有三通道 RGB, 它根本没有地方放透明度那一张表9

治法是先转模式再存:

img.convert("RGB").save("out.jpg")

另一个会让人愣一下的数字

书还给了一个缩略图的例子:对一张 640×480 的图要一个 150×100 的缩略图, 得到的却是 133×10010

原因是它按原图比例等比缩放: 640÷480 = 4:3, 高压到 100,宽就只能是 100 × 4/3 ≈ 133 —— 再宽就变形了。

你要的: 150 × 100 (比例 3:2)
原图: 640 × 480 (比例 4:3)
它给的: 133 × 100 (比例 4:3,保住了)

图说:它把你给的尺寸当成「不许超过的上限」,而不是「必须精确达到的目标」。

要精确控制尺寸得用另一个方法 —— 那个方法不保比例,图会被拉扁, 但第 13 章训练时恰恰用的就是它(所有图必须一样大)。

6. 一条分界线:改位置的,和改值的

这一节是本章第二个承重点。它本身很简单,但第 13 章有一整节建在它上面。

两类操作

书把图像的几何变换定义得很干脆:几何变换不改变图像像素的值, 仅改变像素的空间排布位置11

反过来,另一类操作只改值不挪位置:调亮度、调对比度、模糊、二值化。

原图的一小块 左右翻转(只改位置) 调亮(只改值)
┌────┬────┐ ┌────┬────┐ ┌────┬────┐
│ 30 │200 │ │200 │ 30 │ │ 60 │230 │
├────┼────┤ ───▶ ├────┼────┤ ───▶ ├────┼────┤
│ 31 │198 │ │198 │ 31 │ │ 62 │228 │
└────┴────┘ └────┴────┘ └────┴────┘
数一个没变,只是换了格子 格子没动,数全变了

图说:这两类操作在代码里长得差不多,但对「标签还对不对」的影响天差地别。
这些数是为演示编的。
类别具体操作对像素做了什么
几何变换翻转、旋转、平移、缩放、裁剪只挪位置,值不变
滤波与增强调对比度、调亮度、调饱和度、模糊、锐化只改值,位置不变

为什么这条线要专门画出来

因为第 13 章会拿它做一次很漂亮的判断。

到那里你会看到:通用的说法是「训练数据不够,就把图片翻转、平移、缩放,凑出更多样本」。 而这本书当场指出:在「预测转角」这个任务上,几何变换会直接毁掉数据 —— 图翻过来了,画面里该往左打了,标签却还写着往右。

而只改值的那一类完全没有这个问题 —— 调亮一点,该往左还是往左。

这条线现在先记住,第 13 章兑现。 它是这本书全书最值得带走的一处推理。

7. 同一种颜色,三套坐标

这一节是本章第二个承重词,而第 12 章调参数时用的正是它。

先看现象:为什么不直接用 RGB

你要从图里挑出车道线。车道线是白的、周围是绿的,看起来用 RGB 定个范围就行。

试一次就知道不行: 太阳一偏、灯一暗,同一条白线的 RGB 三个数全都变了。 你调好的范围立刻失效。

做法:三套坐标,各为不同的目的而设

书给了三个色彩空间。「色彩空间」这个词在这里就地解释: 它就是一套用几个数来描述一种颜色的约定 —— 同一种颜色,换套约定就换一组数。

空间用几个数能表示多少种为谁设计的
GRAY1 个256 级,0 纯黑、255 纯白12只关心明暗的时候
RGB / BGR3 个256³ = 16 777 216 种,书说这「远高于人眼所能分辨的颜色数量」13为硬件设计的 —— 显示器就是拿三种光叠出颜色的
HSV3 个同上量级为视觉感知设计的,1978 年由 A.R.Smith 提出14

HSV 那三个数分别是什么,书也给了:

字母中文管什么
Hue色调是什么颜色 —— 红的、绿的、蓝的
Saturation饱和度颜色有多浓 —— 大红还是粉红
Value亮度有多亮 —— 人眼感受到的明暗

为什么这个差别很要紧

同一条白色车道线,光照变暗之后:

在 RGB 里: (240, 238, 235) → (150, 148, 146)
三个数全变了,原来定的范围全部失效

在 HSV 里: (H=30, S=5, V=240) → (H=30, S=5, V=150)
色调没变、饱和度没变,只有亮度掉了
⇒ 只要范围定在「色调 + 饱和度」上,它就还认得出来

图说:这就是为什么第 12 章调车道线阈值调的是 HSV,不是 RGB。
这两组数是为演示编的,书没有给具体数值。

书还提了另外三套坐标(XYZ、YCrCb、HLS),各有各擅长的领域, 不同空间之间可以互相转换,用一个方法就行15

走查记账

主走查 ③ 转成灰度:同一个点 (200, 30, 40) 三个数被合成一个数
结果:大约 82(**这是我们按常用加权公式算的,不是书里的数**)
整张图从 (480, 640, 3) 变成 (480, 640)

主走查 ④ 缩到 128×128: 307 200 个像素点变成 16 384 个,只剩十九分之一
原来在第 320 列、第 400 行的那个点,挪到了大约第 64 列、第 107 行

主走查 ⑤ 转成 HSV:同一个点 = (H≈178, S≈217, V≈200)
↑ 这一组是我们按标准转换公式从 (200,30,40) 算出来的,不是书里的数

8. 最朴素的两下,就能把车道线抠出来

这一节是本章的落点:第 12 章那条「一个模型都不训」的路线,就是从这里长出来的。

书在讲图像增强的时候给了三个具体用法16:

做法干什么用在哪
convert()二值化把每个像素只留 0 或 255 两种值 —— 亮过某条线的算白,其余算黑直接把车道线从路面上抠出来
ImageFilter.CONTOUR提轮廓看物体的边在哪
ImageEnhance.Contrast()拉对比度让明暗差别更明显,好定阈值

「二值化」在这里就地解释: 给一个门槛(叫阈值), 比它亮的一律改成 255,不比它亮的一律改成 0。整张图只剩黑白两种像素。

灰度图的一小块 二值化(阈值定在 128)
┌────┬────┐ ┌────┬────┐
│ 30 │200 │ │ 0 │255 │ ← 200 > 128,变成 255
├────┼────┤ ───▶ ├────┼────┤
│ 31 │198 │ │ 0 │255 │ ← 30 < 128,变成 0
└────┴────┘ └────┴────┘

图说:一步之后,「哪里是车道线」就变成了「哪里是 255」。
剩下的事情——算偏差、算转角——都在这张只有两种值的图上做。
这些数是为演示编的。

这就是第 12 章那条路线的全部技术含量。 它不训练任何模型, 靠的就是「定一个范围,把落在范围里的像素挑出来」。 代价也在这里:那个范围是人调出来的,换个光照就得重调。

9. 作者的判断与证据

说法是哪一类
图 = 二维数组、彩色图 = 三张同尺寸灰度图有证据: 数字图像的定义,全书最要紧的一句事实
PIL 给 RGB、OpenCV 给 BGR有证据: 库的行为事实,可复现
png 是 RGBA、jpg 是 RGB,直接存会报错有证据: 书给了确切的报错信息
缩略图 (150,100) 给出 133×100有证据: 等比缩放的算术结果,我们核过
几何变换只改位置、不改值有证据: 定义。它到第 13 章会变成一条硬约束
RGB 的 1677 万色「远高于人眼所能分辨的颜色数量」作者的说法,没给依据。 方向没错(人眼可分辨的颜色数常被估在百万量级),但这是通用知识,不是书的论证
HSV 由 A.R.Smith 于 1978 年提出有证据(书直接给出),但书没有列出这篇文献;它不在书末参考文献里
「RGB 面向硬件、HSV 面向视觉感知」有证据,而且是这一节唯一真正承重的一句 —— 第 12 章的做法直接建在它上面

10. 边界与局限

  • 这一章对应原书第 6 章的后半(两个图像库),外加从第 7 章前移过来的数组那一小块。 我们把它单独拿出来成章,因为它是第 10—14 章的地基,压在「文件」那一章里会被淹没。
  • 原书的示例图和处理结果全部是图片,清洗文本里没有像素值。 所以本章走查里的每一组三元组都是我们为演示编的,已在当场标明。
  • 两个库的方法清单(几十个)整体不采用 —— 原书里就是图片,而且属于查文档就有。
  • 书没有讲图像压缩。 jpg 为什么会有块状痕迹、压到 10KB 会损失什么,一个字没提 (只在习题里要求「把图压到 10KB 以内」)。
  • 书没有讲摄像头本身。 曝光、白平衡、卷帘快门这些会直接影响成像的东西, 要到第 16 章才以「拖影」的形式出现一次。
  • 色彩空间只讲了三套的用途,没讲转换公式。 我们也不补 —— 用得着的时候调一个方法就行。

11. 可带走的

全章主走查一行写完:

同一张 640×480 车前道路图上的同一个点 —— PIL 读到 (R=200, G=30, B=40) → OpenCV 读到 (B=40, G=30, R=200)(顺序反了)→ 转灰度,三个数合成一个数,约 82 → 缩到 128×128,像素点从 30 万变成 1.6 万 → 转 HSV,得到 (H≈178, S≈217, V≈200)

这些像素值全部是为演示编的,不是书里的数。

  1. 一张图 = 一张写满 0—255 的表格;彩色图 = 三张同尺寸的表格叠起来 —— 这是第 10 到 14 章全部内容的地基;
  2. 0 是纯黑,255 是纯白;一张 640×480 的图有 307 200 个像素点;
  3. 处理图像用的不是列表,是 numpy 的数组 —— 里面的数必须同一种类型, 换来的好处是整块一起算,循环跑在底层的 C 里;
  4. PIL 给 RGB,OpenCV 给 BGR —— 不转换就会红蓝颠倒,而且不报错;
  5. png 有四个通道(多一个透明度),jpg 只有三个 —— 直接存会报错,要先转模式;
  6. 缩略图那个方法是等比缩放,你给的尺寸是上限不是目标;
  7. 有一条分界线:几何变换只挪位置、滤波与增强只改值 —— 第 13 章会拿它判掉一整类通用做法;
  8. 同一种颜色有三套坐标: 灰度(1 个数)、RGB(为硬件设计)、HSV(为视觉感知设计);
  9. 光照一变,RGB 三个数全变,而 HSV 里通常只有亮度那一个数变 —— 这就是第 12 章调阈值调 HSV 的全部理由;
  10. 二值化就是「定个门槛,亮的全变 255、暗的全变 0」 —— 一步之后,「哪里是车道线」就变成了「哪里是 255」。

12. 原文地图

主题原书章原文位置
数字图像的本质:像素、灰度值、二维数组、三通道第6章 文件和数据格式化text/07-ch06.txt:487(搜「转换为一个二维数组」)
PIL 库的定位与安装第6章 文件和数据格式化text/07-ch06.txt:365(搜「最常用的图像处理库」) · text/07-ch06.txt:367(搜「pip install pillow」)
Image 对象;示例图是车前道路图 640×480第6章 文件和数据格式化text/07-ch06.txt:375(搜「Image类是最常用」) · text/07-ch06.txt:379(搜「车辆前方道路图像」)
png→jpg 报错与 convert 的用法第6章 文件和数据格式化text/07-ch06.txt:397(搜「cannot write mode RGBA as JPEG」)
resize 与 thumbnail;133×100 那个例子第6章 文件和数据格式化text/07-ch06.txt:407(搜「原图片大小为640×480」) · text/07-ch06.txt:415(搜「133, 100」)
通道分离与合并第6章 文件和数据格式化text/07-ch06.txt:423(搜「分离颜色通道」) · text/07-ch06.txt:435(搜「模式、大小必须一致」)
几何变换只改位置不改值第6章 文件和数据格式化text/07-ch06.txt:449(搜「不改变图像像素的值」)
二值化提车道线、提轮廓、拉对比度第6章 文件和数据格式化text/07-ch06.txt:469(搜「二值化处理提取图中车道线」)
OpenCV 安装名与导入名不同第6章 文件和数据格式化text/07-ch06.txt:481(搜「opencv-python」)
PIL 是 RGB、cv2 是 BGR,以及两边互转第6章 文件和数据格式化text/07-ch06.txt:497(搜「图像通道顺序是B、G、R」)
GRAY 色彩空间 256 级第6章 文件和数据格式化text/07-ch06.txt:509(搜「256个灰度级」)
RGB 1677 万色、远高于人眼分辨第6章 文件和数据格式化text/07-ch06.txt:513(搜「远高于人眼所能分辨」)
HSV:1978 年提出、面向视觉感知、三要素第6章 文件和数据格式化text/07-ch06.txt:517(搜「面向视觉感知模型」)
其他色彩空间与转换方法第6章 文件和数据格式化text/07-ch06.txt:519(搜「XYZ色彩空间」)
numpy 的两个基本对象 ndarray 与 ufunc第7章 Python计算生态及机器学习概述text/08-ch07-7-python.txt:111(搜「ndarray是存储单一数据类型」)
resize / reshape、ravel / flatten、repeat / tile 的差别第7章 Python计算生态及机器学习概述text/08-ch07-7-python.txt:153(搜「无返回值」) · text/08-ch07-7-python.txt:163(搜「ravel()是引用操作」) · text/08-ch07-7-python.txt:167(搜「复制的是多维数组本身」)

Footnotes

  1. 出处:「第6章 文件和数据格式化」第 487 段(text/07-ch06.txt:487,搜「转换为一个二维数组」)。原文:需要先将图像数字化,「将图像分割成一个个像素,每个像素都使用一个灰度值来表示,这样一张图像就可以转换为一个二维数组,数组中的每一个元素都是一个 0~255 的灰度值」。本节 4×4 表格里的数是我们为演示编的。

  2. 出处:「第6章 文件和数据格式化」第 487 段(text/07-ch06.txt:487,搜「拆分成3个同样像素大小的灰度图」)。原文:彩色图可以分解为 R、G、B 三个通道,拆成 3 个同样像素大小的灰度图来表示,按 BGR 的顺序组合起来就是一张彩色图。

  3. 出处:「第7章 Python计算生态及机器学习概述」第 111 段(text/08-ch07-7-python.txt:111,搜「ndarray是存储单一数据类型」)。原文:numpy 提供两种基本对象,ndarray 是存储单一数据类型的多维数组,ufunc 是能够对数组进行处理的函数。这一小块原书排在第 7 章,我们前移到本章,因为「图像是一个数组」这句话里的数组指的就是它。「类型统一才能连续存放、所以更快」是我们补的通用知识。

  4. 出处:「第7章 Python计算生态及机器学习概述」第 153—157 段(text/08-ch07-7-python.txt:153,搜「无返回值」)、第 163 段(text/08-ch07-7-python.txt:163,搜「ravel()是引用操作」)、第 167 段(text/08-ch07-7-python.txt:167,搜「复制的是多维数组本身」)。

  5. 出处:「第6章 文件和数据格式化」第 379 段(text/07-ch06.txt:379,搜「车辆前方道路图像」)与第 407 段(text/07-ch06.txt:407,搜「原图片大小为640×480」)。原文说明这张示例图就是本赛项巡航任务里前置摄像头采集到的车前道路图像。

  6. 出处:「第6章 文件和数据格式化」第 497 段(text/07-ch06.txt:497,搜「图像通道顺序是B、G、R」)。原文对照说明:Image.open() 打开的是图片格式、RGB 色彩空间、通道顺序 R G B;cv2.imread() 打开的是像素格式、通道顺序 B G R。

  7. 出处:「第6章 文件和数据格式化」第 481 段(text/07-ch06.txt:481,搜「opencv-python」)。原文还提醒 OpenCV 依赖其他库(例如 numpy),使用前要先装好。PIL 的安装名与导入名之差见第 367 段(text/07-ch06.txt:367,搜「pip install pillow」)。

  8. 出处:「第6章 文件和数据格式化」第 497 段(text/07-ch06.txt:497,搜「cv2.cvtColor」)。两个方向的转换写法书都给了。

  9. 出处:「第6章 文件和数据格式化」第 397 段(text/07-ch06.txt:397,搜「cannot write mode RGBA as JPEG」)。原文:png 是四通道 RGBA 模式(红、绿、蓝、alpha 透明色),jpg 是三通道 RGB 模式,所以不能直接另存,要先用 convert() 转成 RGB。

  10. 出处:「第6章 文件和数据格式化」第 415 段(text/07-ch06.txt:415,搜「133, 100」)。原文解释:PIL 会对原图像的长、宽等比例缩小,如果指定尺寸不符合原图的比例,给出的缩略图就不等于你要的尺寸;指定尺寸超出原图尺寸时还会直接失败。640:480 = 4:3 这个比例换算是我们补的。

  11. 出处:「第6章 文件和数据格式化」第 449 段(text/07-ch06.txt:449,搜「不改变图像像素的值」)。原文:「几何变换就是将一张图像上的坐标位置重新映射到另一坐标位置形成一张新的图像。几何变换不改变图像像素的值,仅改变像素的空间排布位置。」与之相对的滤波与增强见第 461 段(text/07-ch06.txt:461,搜「ImageFilter类提供了图像过滤」)。

  12. 出处:「第6章 文件和数据格式化」第 509 段(text/07-ch06.txt:509,搜「256个灰度级」)。

  13. 出处:「第6章 文件和数据格式化」第 513 段(text/07-ch06.txt:513,搜「远高于人眼所能分辨」)。原文给的数是 256³ = 16 777 216。「人眼可分辨的颜色数在百万量级」是通用知识,书没有给依据。

  14. 出处:「第6章 文件和数据格式化」第 517 段(text/07-ch06.txt:517,搜「面向视觉感知模型」)。原文:HSV 由 A.R.Smith 在 1978 年创建,「RGB 颜色空间是从硬件的角度提出的……而 HSV 颜色空间是面向视觉感知模型提出的」,三要素是色调、饱和度、亮度。这篇文献不在书末的参考文献表里。

  15. 出处:「第6章 文件和数据格式化」第 519 段(text/07-ch06.txt:519,搜「XYZ色彩空间」)。

  16. 出处:「第6章 文件和数据格式化」第 469 段(text/07-ch06.txt:469,搜「二值化处理提取图中车道线」)。原文明确说明这三个用法各自的效果:二值化提取车道线、CONTOUR 获得轮廓信息、Contrast 增加对比度使特征对比更明显。