2、图像基础回顾:数字图像表示(RGB、灰度)、图像坐标系、图像读取与显示(OpenCV基础)
好,咱们正式开始图像融合之前,我得先带你把地基打牢。你想想看,如果连图像在计算机里长什么样都不清楚,那后面的全景拼接、多曝光融合,基本就是空中楼阁。
这一节,咱们就聊聊数字图像最核心的几个概念。我保证,不会讲得太枯燥。咱们边聊边动手,把OpenCV这个趁手的工具也一并熟悉了。
2.1 数字图像的本质:一个数字矩阵
说白了,一张数字图像在计算机眼里,就是一个巨大的数字表格——矩阵。每个小格子就是一个像素(Pixel)。
我个人习惯把图像想象成一张坐标纸。每个格子都有自己的位置(坐标),也有自己的颜色值(数值)。
核心公式:
图像 = f(x, y)
其中 (x, y) 是像素坐标,f(x, y) 是该坐标处的亮度或颜色值。
嗯,这里要注意:这个公式虽然简单,但它是一切图像处理算法的起点。你后面学到的滤波、变换、融合,本质上都是在操作这个 f(x, y)。
2.2 RGB 彩色图像:三原色的世界
我们平时看到的彩色照片,在计算机里是怎么存的?答案是:三个通道。
每个像素由三个数值组成:R(红)、G(绿)、B(蓝)。每个数值的范围是 0~255,0 代表最暗,255 代表最亮。
举个例子:
- 纯红色:R=255, G=0, B=0
- 纯绿色:R=0, G=255, B=0
- 纯蓝色:R=0, G=0, B=255
- 白色:R=255, G=255, B=255
- 黑色:R=0, G=0, B=0
我在项目中遇到过一个问题:用手机拍的全景图,颜色总是偏紫。排查了半天,发现是白平衡算法在拼接时把 RGB 三个通道的增益算错了。你看,理解 RGB 通道,能帮你快速定位这种 bug。
小技巧: 在 OpenCV 中,图像的通道顺序是 BGR,不是 RGB!这是历史遗留问题。很多新手在这里栽过跟头。如果你用 matplotlib 显示 OpenCV 读取的图像,颜色会完全错乱。记得用 cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 转换一下。
2.3 灰度图像:一个通道就够了
有时候我们不需要颜色信息,比如做边缘检测、特征点匹配。这时候用灰度图就够了。
灰度图只有一个通道,每个像素值也是 0~255。0 是纯黑,255 是纯白,中间是各种深浅的灰色。
彩色转灰度,常用的公式是:
Gray = 0.299 * R + 0.587 * G + 0.114 * B
为什么权重不一样?因为人眼对绿色最敏感,对蓝色最不敏感。这个公式是国际标准,你直接用就行。
避坑指南: 我曾经在做一个图像融合项目时,直接对 RGB 三个通道取了平均值来转灰度,结果图像细节丢失了一大半。后来改用标准加权公式,效果才正常。记住:不要偷懒用平均法。
2.4 图像坐标系:原点在哪里?
这个问题看似简单,但很容易搞混。图像坐标系和数学坐标系不一样。
- 原点 (0, 0):在图像的左上角
- x 轴:水平向右(列方向)
- y 轴:垂直向下(行方向)
也就是说,像素 (x, y) 表示第 y 行、第 x 列的那个点。注意顺序:先列后行。
你想想看,为什么这么设计?因为计算机内存是一维的,图像数据按行存储。从左上角开始,一行一行往下读,最符合内存访问习惯。
下面我用一张 SVG 图来展示这个坐标系,帮你建立直观印象:
2.5 OpenCV 基础:读取与显示
好了,理论讲完了,咱们来点实际的。OpenCV 是图像处理的瑞士军刀,我几乎每天都在用。
先看最基础的操作:读取一张图片并显示出来。
import cv2
# 读取图像
img = cv2.imread('panorama.jpg')
# 检查是否读取成功
if img is None:
print("图片没找到,检查路径!")
else:
print(f"图像尺寸:{img.shape}") # (高度, 宽度, 通道数)
# 显示图像
cv2.imshow('My Image', img)
# 等待按键,0表示无限等待
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
这段代码虽然简单,但有几个细节我得提醒你:
- cv2.imread() 默认以 BGR 彩色模式读取。如果你想读成灰度图,加第二个参数:
cv2.imread('img.jpg', cv2.IMREAD_GRAYSCALE) - img.shape 返回的是一个元组。对彩色图是 (h, w, 3),对灰度图是 (h, w)。
- cv2.waitKey(0) 这行不能省。没有它,窗口一闪就没了。
我的习惯: 在调试时,我经常用 cv2.imwrite('output.jpg', img) 把中间结果保存下来,而不是每次都弹窗显示。这样方便对比不同处理步骤的效果。
2.6 像素操作:动手试试
读取图像之后,我们就能直接操作像素了。比如把某个像素改成红色:
import cv2
import numpy as np
# 创建一个 300x300 的黑色图像
img = np.zeros((300, 300, 3), dtype=np.uint8)
# 把 (100, 100) 位置的像素设为红色(注意是 BGR)
img[100, 100] = (0, 0, 255) # B=0, G=0, R=255
# 把一块区域设为绿色
img[50:150, 50:150] = (0, 255, 0)
cv2.imshow('Pixel Demo', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
你看,操作像素就是这么直接。img[y, x] 就是访问第 y 行、第 x 列的像素。记住:先行后列,和坐标系里的 (x, y) 顺序是反的。
为什么会这样?因为 numpy 数组的索引是 (row, column),而 row 对应 y,column 对应 x。这个反直觉的设计,我刚开始也经常搞混。
2.7 本章知识体系总览
最后,我用一张 SVG 图把这一节的核心知识串起来,方便你复习:
好了,这一节的内容就到这里。你掌握了数字图像的表示方法、坐标系,以及 OpenCV 的读写操作。这些都是后面做图像融合的必备基础。
记住:多动手写代码,多看看像素值的变化。图像处理这东西,光看是学不会的。