图像基础与OpenCV入门:图像读取/显示/保存、像素操作、色彩空间、图像属性

说实话,很多初学者一上来就追着深度学习跑,连图像在计算机里长什么样都没搞清楚。我个人觉得,这是个大坑。你想想看,连像素怎么存、颜色怎么表示都不懂,后面调模型参数的时候,出了问题你根本不知道从哪下手。

这一章,咱们就把地基打牢。我会带着你,从OpenCV最基础的操作开始,一步步把图像的本质摸清楚。

1. 图像读取、显示与保存

OpenCV读取图像,用的是 cv2.imread()。这个函数很直接,给它一个路径,它就给你返回一个数组。但这里有个坑,我当年刚入行时就踩过。

注意: OpenCV 默认读取的通道顺序是 BGR,不是 RGB!这一点非常重要。很多人在显示图像时发现颜色不对,就是因为这个。

举个例子:

import cv2

# 读取图像
img = cv2.imread('cat.jpg')

# 显示图像
cv2.imshow('My Cat', img)
cv2.waitKey(0)  # 等待按键
cv2.destroyAllWindows()

# 保存图像
cv2.imwrite('cat_copy.jpg', img)

cv2.waitKey(0) 这行代码,说白了就是让窗口一直等着,直到你按任意键才关闭。我曾经在项目里忘了加这行,结果窗口一闪而过,我还以为是代码写错了,排查了半天。

小技巧: 如果你想让窗口自动关闭,可以设置等待时间,比如 cv2.waitKey(1000) 表示等待1秒。

2. 像素操作

图像在计算机里,就是一个多维数组。对于一张彩色图像,它的形状是 (高度, 宽度, 通道数)。每个像素点,就是一个包含三个数值的列表,分别代表 B、G、R 的强度。

直接操作像素,是理解图像最直观的方式。我习惯用 NumPy 来操作,因为 OpenCV 的图像数据本身就是 NumPy 数组。

import cv2
import numpy as np

# 创建一个 300x300 的黑色图像
img = np.zeros((300, 300, 3), dtype=np.uint8)

# 修改某个像素为蓝色
img[100, 100] = [255, 0, 0]  # BGR 格式

# 修改一块区域为绿色
img[50:150, 50:150] = [0, 255, 0]

cv2.imshow('Pixel Operation', img)
cv2.waitKey(0)
cv2.destroyAllWindows()

这里要注意,dtype=np.uint8 表示每个通道的值范围是 0-255。你想想看,如果超出这个范围,OpenCV 会自动截断,不会报错。嗯,这一点很容易被忽略。

核心要点: 像素操作的本质就是数组索引和赋值。掌握了 NumPy,你就掌握了图像处理的一半。

3. 色彩空间

色彩空间,说白了就是颜色的表示方式。OpenCV 里最常用的有 BGR、RGB、HSV、灰度。我刚开始学的时候,总觉得 BGR 和 RGB 差不多,直到有一次做颜色识别,才发现区别大了去了。

3.1 BGR 与 RGB

BGR 是 OpenCV 的默认格式,而 RGB 是大多数其他库(比如 Matplotlib)的默认格式。转换很简单:

# BGR 转 RGB
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# RGB 转 BGR
img_bgr = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2BGR)

我在项目中遇到过一个问题:用 Matplotlib 显示 OpenCV 读取的图像,结果猫变成了蓝色。其实就是因为没做 BGR 到 RGB 的转换。

3.2 灰度图

灰度图只有一个通道,每个像素值代表亮度。转换方法:

gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

灰度图在很多任务中都能用,比如边缘检测、人脸识别。它去掉了颜色信息,只保留亮度,计算量小很多。

3.3 HSV 色彩空间

HSV 把颜色分解成色调(H)、饱和度(S)、明度(V)。这个空间特别适合做颜色分割,因为颜色信息集中在 H 通道上。

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

举个例子,你想提取图像中的红色区域。在 BGR 空间里,红色可能是 (0, 0, 255),但受光照影响,实际值会变化。而在 HSV 空间里,红色的 H 值范围是固定的,比如 0-10 和 170-180。这样分割起来就稳定多了。

避坑指南: 我曾经在 HSV 分割时,只设了一个范围,结果红色区域总是漏掉一部分。后来才发现,红色在 HSV 中跨越了 0 和 180 两个区间,需要两个范围才能完整覆盖。

4. 图像属性

图像属性包括形状、数据类型、像素数量等。这些信息在调试时非常有用。

import cv2

img = cv2.imread('cat.jpg')

# 获取图像属性
print('图像形状:', img.shape)      # (高度, 宽度, 通道数)
print('数据类型:', img.dtype)      # uint8
print('像素总数:', img.size)       # 高度 * 宽度 * 通道数

# 单独获取高度、宽度、通道数
height, width, channels = img.shape
print(f'高度: {height}, 宽度: {width}, 通道数: {channels}')

img.shape 返回的是一个元组。如果是灰度图,它只有两个值(高度和宽度)。这一点要注意,否则在解包时可能会出错。

总结一下: 图像属性是理解图像结构的基础。每次处理图像前,先看看它的形状和数据类型,能避免很多低级错误。

5. 综合示例:图像处理流水线

最后,咱们把今天学的知识串起来,做一个简单的图像处理流水线:读取图像、转为灰度、保存结果。

import cv2

# 1. 读取图像
img = cv2.imread('cat.jpg')

# 2. 转为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 3. 显示原图和灰度图
cv2.imshow('Original', img)
cv2.imshow('Gray', gray)

# 4. 等待按键
cv2.waitKey(0)

# 5. 保存灰度图
cv2.imwrite('cat_gray.jpg', gray)

# 6. 释放窗口
cv2.destroyAllWindows()

这个例子虽然简单,但涵盖了图像处理的基本流程。你想想看,后面不管多复杂的项目,本质上都是在这个流程上做扩展。

个人建议: 刚开始学的时候,多动手写代码,多看看图像的 shape 和 dtype。这些基础操作熟练了,后面学高级内容会轻松很多。

好了,这一章的内容就到这里。下一章,咱们会深入图像的基本变换,比如缩放、旋转、裁剪。这些操作在实际项目中几乎天天用,到时候我会分享一些实战中的小技巧。