使用颜色矩阵转换单色

Windows GDI+ 提供用于存储和操作图像 的图像位图 类。 图像位图 对象将每个像素的颜色存储为 32 位数字:红色、绿色、蓝色和 alpha 各 8 位。 四个分量中的每一个都是一个从 0 到 255 的数字,其中 0 表示无强度,而 255 表示全强度。 Alpha 分量指定颜色的透明度:0 表示完全透明,255 表示完全不透明。

颜色向量是一个 4 元组的形式(红、绿、蓝、Alpha)。 例如,颜色矢量 (0, 255, 0, 255) 表示不透明的颜色,它没有红色或蓝色,但在全强度下具有绿色。

表示颜色的另一个约定使用数字 1 表示最大强度,数字 0 表示最小强度。 使用该约定,上一段中描述的颜色将由向量 (0, 1, 0, 1) 表示。 GDI+ 在执行颜色转换时使用 1 作为全强度的约定。

可以通过将 4 ×4 矩阵相乘,将线性变换 (旋转、缩放等) 应用于颜色向量。 但是,不能使用 4 ×4 矩阵来执行 (非线性) 的转换。 例如,如果添加虚拟的第五个坐标 (数字 1) 每个颜色向量,则可以使用 5 ×5 矩阵来应用线性转换和平移的任意组合。 由线性转换和平移组成的转换称为仿射变换。 表示仿射变换的 5 ×5 矩阵称为 4 空间转换的同质矩阵。 5 ×5 同质矩阵的第五行和第五列中的元素必须为 1,并且第五列中的所有其他条目必须为 0。

例如,假设想从颜色 (0.2, 0.0, 0.4, 1.0) 开始并应用以下转换:

  1. 将红色分量翻倍
  2. 向红色、绿色和蓝色分量增加 0.2

以下矩阵乘法将按列出的顺序执行这对转换。

此图显示了一个由数字乘以 5x5 矩阵以创建新的 5x1 矩阵的 5x1 矩阵

颜色矩阵的元素先按行索引,然后按列索引(从零开始)。 例如,矩阵 M 的第五行第三列中的条目由 M[4][2] 表示。

下图中所示的 5 ×5 标识矩阵 () 对角线有 1 个值,其他位置有 0 个。 如果将颜色向量乘以单位矩阵,则颜色向量不会改变。 形成颜色转换矩阵的一种简便方法是从单位矩阵入手,进行小的更改以生成所需的转换。

显示 5x5 标识矩阵的插图;从左上到右下对角线为 1,其他位置为 0

有关矩阵和转换的更详细讨论,请参阅坐标系和坐标转换

以下示例采用全为一种颜色 (0.2, 0.0, 0.4, 1.0) 的图像并应用前面段落中描述的转换。

Image            image(L"InputColor.bmp");
ImageAttributes  imageAttributes;
UINT             width = image.GetWidth();
UINT             height = image.GetHeight();

ColorMatrix colorMatrix = {
   2.0f, 0.0f, 0.0f, 0.0f, 0.0f,
   0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
   0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
   0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
   0.2f, 0.2f, 0.2f, 0.0f, 1.0f};
   
imageAttributes.SetColorMatrix(
   &colorMatrix, 
   ColorMatrixFlagsDefault,
   ColorAdjustTypeBitmap);
   
graphics.DrawImage(&image, 10, 10);

graphics.DrawImage(
   &image, 
   Rect(120, 10, width, height),  // destination rectangle 
   0, 0,        // upper-left corner of source rectangle 
   width,       // width of source rectangle
   height,      // height of source rectangle
   UnitPixel,
   &imageAttributes);

下图左侧显示原始图像,右侧显示转换后的图像。

插图显示由深纯色填充的矩形,然后由较浅的纯色填充的矩形

上述示例中的代码使用以下步骤执行重新着色:

  1. 初始化 ColorMatrix 结构。
  2. 创建 ImageAttributes 对象,并将 ColorMatrix 结构的地址传递给 ImageAttributes 对象的 ImageAttributes::SetColorMatrix 方法。
  3. ImageAttributes 对象的地址传递给 Graphics 对象的 DrawImage 方法方法。