2. 图像类型与色彩空间转换

在 MATLAB 图像处理中,图像并不是“图片”这个模糊概念,而是具有明确维度和数值范围的矩阵。同一幅画面,用不同的图像类型存储,其数据结构、取值范围和显示方式都不同。理解灰度、二值、索引和 RGB 四类图像的数据组织方式,是后续滤波、分割、特征提取能够正确进行的前提。本节围绕这四类图像的数据结构展开,并给出 rgb2gray、im2double、ind2rgb 等转换函数的实际用法,最后实现彩色图像到灰度图像的批量处理。

四类图像的数据结构

灰度图像是二维矩阵,尺寸为 M×N,每个元素代表一个像素的亮度。常见存储类型为 uint8,取值范围 0~255,0 表示黑,255 表示白;若为 double 类型,则取值范围约定为 0~1。灰度图像只有一个通道,因此矩阵的行列直接对应像素的空间位置。

二值图像是逻辑型(logical)二维矩阵,元素只有 0 和 1 两个值,分别代表背景与前景。它常用于分割结果、掩膜(mask)和形态学操作的输入输出。需要注意,二值图像在内存中每个元素占 1 字节,但语义上只有两种状态,不能当作普通灰度图参与算术运算而不加转换。

索引图像由两个数组组成:数据矩阵 X(M×N,元素为整数索引)和颜色映射表 map(K×3 的 double 矩阵,每行是一个 RGB 三元组,取值 0~1)。像素颜色不直接存在 X 中,而是通过 X 的值去查 map 的第几行。这种结构在早期 GIF、科学伪彩图和类别标注图中很常见,优点是颜色表可统一替换,从而快速改变整幅图的配色。

RGB 图像是三维矩阵,尺寸为 M×N×3,第三维依次为红、绿、蓝三个通道。uint8 类型下每个通道取值 0~255,double 类型下为 0~1。三个通道叠加决定最终颜色,因此 RGB 图像的数据量是同等尺寸灰度图的三倍。

四类图像的数据结构与转换关系 灰度图像 M×N 二维矩阵 uint8: 0~255 double: 0~1 单通道亮度 二值图像 M×N logical 仅 0 / 1 前景与背景 掩膜、分割结果 索引图像 X: M×N 索引 map: K×3 double 颜色查表获得 伪彩、类别标注 RGB 图像 M×N×3 三维 R / G / B 通道 uint8: 0~255 彩色显示 rgb2gray imbinarize ind2rgb im2double:uint8 → double,数值除以 255 归一化到 0~1 灰度 / RGB / 索引数据矩阵均可转换,便于统一参与浮点运算 判断依据:size(A) 看维度,class(A) 看类型,islogical(A) 判二值 索引图还需检查 map 是否存在,避免误当灰度图处理

转换函数的关键行为

rgb2gray 把 M×N×3 的彩色矩阵压缩为 M×N 的灰度矩阵。它并非简单取三通道平均,而是按亮度加权:Y = 0.2989·R + 0.5870·G + 0.1140·B。绿色权重最高,符合人眼对绿光更敏感的特性。输入为 uint8 时输出仍为 uint8,输入为 double 时输出为 double,数值范围随输入类型保持一致。

im2double 负责类型与量纲的统一。对 uint8 输入,它把每个元素除以 255,映射到 0~1 的 double;对已经是 double 的输入,若数值本就在 0~1 则基本不变。很多算法(如 imfilter、边缘检测、SIFT 类特征)在 double 域计算更稳定,因此读入图像后先 im2double 是常见做法。与之相对的 im2uint8 则把 0~1 的 double 还原为 0~255 的 uint8,便于保存和显示。

ind2rgb 把索引图像还原为 RGB 图像,调用形式为 RGB = ind2rgb(X, map)。它按 X 中的索引逐像素查 map 得到三通道值,输出为 double 类型、范围 0~1。若只想把索引图当灰度图使用,应改用 ind2gray,它会把颜色表按亮度加权折算成灰度,而不是简单取索引值。

此外,gray2ind 与 rgb2ind 用于生成索引图像,mat2gray 用于把任意范围的 double 矩阵拉伸到 0~1,这些函数在需要统一显示或压缩存储时很有用。

批量彩色转灰度处理

实际项目中往往有成百上千张彩色图需要统一转为灰度。下面给出一个可直接运行的批量处理脚本,输入为某个文件夹下的所有 JPG/PNG 彩色图,输出为同名灰度图,并统一保存为 PNG 以避免二次压缩损失。

srcDir = 'C:\data\color';      % 彩色图输入目录
dstDir = 'C:\data\gray';       % 灰度图输出目录
if ~exist(dstDir, 'dir')
    mkdir(dstDir);
end

files = [dir(fullfile(srcDir, '*.jpg'));
         dir(fullfile(srcDir, '*.png'))];

for k = 1:numel(files)
    inName  = fullfile(srcDir, files(k).name);
    I = imread(inName);

    % 统一转为 double 灰度,便于后续算法处理
    if size(I, 3) == 3
        G = rgb2gray(I);        % 彩色 -> 灰度
    else
        G = I;                  % 已是灰度则直接使用
    end
    G = im2double(G);           % 归一化到 0~1

    % 保存时转回 uint8,避免显示发白或全黑
    [~, base, ~] = fileparts(files(k).name);
    outName = fullfile(dstDir, [base '.png']);
    imwrite(im2uint8(G), outName);
end
disp(['处理完成,共 ' num2str(numel(files)) ' 张']);

脚本中有三个关键点值得注意。第一,用 size(I,3)==3 判断是否为彩色图,避免对已经是灰度的图重复调用 rgb2gray 而报错。第二,im2double 放在 rgb2gray 之后,使灰度值落在 0~1,后续若做阈值分割或滤波,参数含义更直观。第三,保存前用 im2uint8 还原,因为 imwrite 对 double 图像默认按 0~1 解释,若直接写 0~255 的 double 会被截断成接近全白。

若图像数量很大,可先用 imageDatastore 建立数据源,再配合 readimage 与循环处理,便于后续接入并行计算或深度学习流程。

结果检查与常见问题

处理完成后应做基本校验。可用 imfinfo 查看输出文件的位深与颜色类型,确认已变为灰度;也可随机抽取几张用 imshow 对比原图与灰度图,观察亮度层次是否合理。若发现输出整体偏白,通常是 double 未归一化就交给 imwrite;若整体偏黑,则可能是把 0~255 的 uint8 误当 0~1 的 double 处理。索引图若被误当灰度图,会出现颜色错乱或索引值被当作亮度,此时应检查是否遗漏了 ind2rgb 或 ind2gray。

掌握这四类图像的结构差异与转换函数的行为边界,就能在读取、处理、保存的每个环节选择正确的类型,避免因量纲或通道数不匹配导致的隐蔽错误,为后续的滤波、分割与特征提取打下可靠基础。