第二章节 图像基础:从像素到Python库
2.1 像素——构成图像的最小单元
先问个问题:你在电脑上看到的一张照片,本质上是什么?
答案是——一堆有颜色的小方块排成矩阵。每个小方块就是一个像素 (Pixel)。像素是图像的基本单元,就像砖头是墙的基本单元一样。我在早期做图像算法时,经常要手动处理像素级数据,那时候才真正理解“图像就是矩阵”这句话的分量。
每个像素存储颜色信息,这个信息所占的二进制位数叫位深度 (Bit Depth):
- 1位:只能表示黑白(0或1),用在二值图像里。
- 8位:能表示 256 种颜色,通常是灰度图。
- 24位:RGB 各占 8 位,总共 1600 多万种颜色——这就是我们常见的真彩色。
- 32位:在 24 位基础上加一个 Alpha 通道(透明度)。
2.2 RGB色彩空间与灰度图
你想想看,显示器是怎么混合出五颜六色的?
靠的是光的三原色:红 (Red)、绿 (Green)、蓝 (Blue)。每个像素用三个数值 (R, G, B) 表示,范围 0~255。 (0,0,0) 是纯黑,(255,255,255) 是纯白。
我刚开始接触图像时,常遇到一个需求:把彩色图转成灰度图。其实公式很简单:
Gray = 0.299 × R + 0.587 × G + 0.114 × B
为什么权重不同?因为人眼对绿色最敏感,对蓝色最不敏感。这是心理学和生理学实验得出的经验系数。我曾在一次图像预处理项目里,直接用平均值公式 (R+G+B)/3,结果灰度图看起来暗部细节丢了——换成加权公式就正常了。
| 特性 | RGB彩色图 | 灰度图 |
|---|---|---|
| 通道数 | 3(R、G、B) | 1(亮度) |
| 数据量 | 大(每个像素3字节) | 小(每个像素1字节) |
| 适用场景 | 需要颜色信息的处理 | 边缘检测、模板匹配等算法 |
2.3 常见图像存储格式:BMP、JPEG、PNG
同样是图片,为什么有的几兆、有的几百 KB?差别就在编码方式。我用表格把三种主流格式的核心差异列出来:
| 格式 | 压缩方式 | 是否无损 | 支持透明 | 典型场景 |
|---|---|---|---|---|
| BMP | 无压缩 / RLE | 是(RLE无损) | 否 | Windows 原生位图,适合做原始数据测试 |
| JPEG | 离散余弦变换 + 量化 | 否(有损) | 否 | 照片、互联网传输,压缩率高 |
| PNG | DEFLATE(LZ77 + Huffman) | 是 | 是(Alpha通道) | 需要无损和透明的场景,比如Logo、UI元素 |
有个避坑指南:我做盲水印时,不要用 JPEG 作为嵌入载体。因为 JPEG 的有损压缩会破坏你嵌入的像素数据,造成水印提取失败。我曾经在这个坑里栽过跟头——测试时用 BMP 效果完美,换 JPEG 后水印全碎了。后来学乖了,要么用 PNG 输出,要么强制设定 JPEG 最高质量(但依然不保险)。
2.4 Python图像处理库:PIL/Pillow 与 OpenCV
实际项目中,我用的最多的是两个库:Pillow(PIL 的活跃分支)和 OpenCV(Open Source Computer Vision Library)。
Pillow 适合快速读写、格式转换、简单滤镜。安装很简单:
pip install Pillow
看个例子——读取一张图片并转为灰度:
from PIL import Image
img = Image.open('test.bmp')
print(img.size) # (width, height)
gray = img.convert('L') # 转灰度
gray.save('test_gray.png')
OpenCV 更强大,擅长矩阵运算、特征检测、视频处理。安装:pip install opencv-python。但有个坑——OpenCV 默认用 BGR 顺序,不是 RGB!我第一次用 OpenCV 显示图片时,颜色全变成蓝紫调……
import cv2
img = cv2.imread('test.bmp')
print(img.shape) # (height, width, channels)
# 转 RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# 转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
本章知识体系
下面这张 SVG 图梳理了本章的核心逻辑,从像素定义,到色彩空间,再到存储格式和 Python 工具。