为单色注入生命:探索图像着色的深度学习

老照片的深褐色调和灰度渐变具有独特的魅力,捕捉了凝固在时间中的瞬间。然而,它们往往缺乏原始场景生动的即时性。想象一下,将生命的色彩重新注入这些珍贵的记忆中,将褪色的黑白肖像转变为一扇展现主体全彩世界的窗口。这个被称为图像着色的变革性过程,长期以来一直吸引着艺术家和历史学家。如今,在人工智能,特别是深度学习进步的推动下,自动化着色正取得曾经只存在于科幻小说中的成果。

为灰度图像上色提出了一个引人入胜的挑战。当图像以单色呈现时,大量信息——原始的色彩数据——固有地丢失了。算法如何仅凭亮度值就能知道花朵、连衣裙或天空的真实颜色?答案在于灰度图像本身蕴含的微妙线索:纹理、形状、上下文以及光影的相互作用。虽然精确确定确切的原始颜色可能是不可能的(那朵玫瑰真的是深红色,还是粉红色?),但目标转向了创造一种貌似可信且在美学上令人信服的着色。其目的是生成一张人类观察者会觉得可信,甚至可能与原始彩色照片无法区分的图像。

深度学习模型擅长在庞大的数据集中发现复杂的模式和统计关系。通过在数百万张图像上训练这些模型,比较灰度版本与其原始彩色对应物,算法学会了将特定的纹理和结构与可能的颜色联系起来。它们了解到草通常是绿色的,天空通常是蓝色的,某些纹理对应于木纹或织物。这类似于一种有根据的猜测,但这种猜测是基于一个巨大的视觉百科全书。算法并非以人类的方式’知道’真实的颜色,但它可以根据学到的相关性做出高度可能的预测。

色彩的语言:CIELab 和神经网络

为了在计算上处理着色问题,我们需要一种合适的方式来表示颜色。虽然 RGB(红、绿、蓝)常用于显示器,但它混合了亮度(brightness)和色度(color)信息。对于这项任务,一个更有利的系统是 CIELab 色彩空间。该模型巧妙地将颜色分离为三个不同的分量:

  • L (Lightness): 该通道表示灰度信息,范围从纯黑到纯白。它本质上是我们黑白图像中已有的输入数据。
  • a: 该通道编码从绿色(负值)到红色(正值)的光谱。
  • b: 该通道编码从蓝色(负值)到黄色(正值)的光谱。

CIELab 的美妙之处在于这种分离。我们的深度学习模型可以专注于仅根据输入的亮度(’L’)通道来预测两个色度通道(’a’ 和 ‘b’)。核心任务变成:给定灰度信息(L),每个像素最可能对应的 ‘a’ 和 ‘b’ 值是什么?

早期的尝试通常采用卷积神经网络(Convolutional Neural Networks, CNNs)——一种特别擅长处理像图像这样的网格状数据的深度学习架构。这些网络在大型图像数据集(如 ImageNet)上进行训练,直接预测每个像素的 ‘a’ 和 ‘b’ 值,将其视为一个回归问题(预测连续值)。然而,一个常见的陷阱出现了:由此产生的着色结果常常显得饱和度不足或色彩暗淡。为什么?考虑一个像苹果这样的物体。它可能合理地是红色、绿色,甚至黄色。如果网络在回归过程中试图平均这些可能性,它可能会选择一个沉闷的、偏棕色的折衷方案,而不是一个鲜艳、具体的颜色。这种跨越多种可能颜色的平均效应往往会冲淡结果。

范式转变:将着色视为分类

为了克服饱和度不足的问题并产生更鲜艳、更逼真的颜色,一种更复杂的方法重新定义了这个问题。它不再将颜色预测视为回归,而是将其视为一个分类任务

以下是概念上的转变:

  1. 量化色彩空间: 将可能的 ‘a’ 和 ‘b’ 值的连续谱离散化为一组预定义的代表性颜色’桶’(bins)或类别。可以将其视为将庞大的调色板缩减为 ‘a’-‘b’ 平面内一套易于管理但全面的不同颜色选项。
  2. 预测概率: 对于输入灰度图像中的每个像素,CNN 不预测单一的 ‘a’ 和 ‘b’ 值。相反,它输出一个跨越量化颜色桶的概率分布。它实质上是在说:’对于这个像素,有 70% 的可能性属于’鲜艳红色桶 #5’,有 20% 的可能性是’淡红色桶 #2’,有 5% 的可能性是’棕色桶 #12’’,依此类推。
  3. 处理模糊性: 这种概率方法固有地处理了颜色的模糊性。如果一个物体可能有多种颜色(比如苹果),网络可以为几个不同的颜色桶分配显著的概率,反映这种不确定性,而无需诉诸于平淡的平均值。
  4. 解码为鲜艳色彩: 最后一步涉及将此概率分布转换回每个像素的单一、特定颜色。一种简单的方法可能是简单地选择具有最高概率的颜色桶(众数)。然而,为了鼓励鲜艳度并避免饱和度不足的问题,使用了诸如计算分布的**退火均值(annealed mean)**等技术。这种方法给予不太可能但更鲜艳(更高饱和度)的预测更多的权重,有效地在偏向鲜艳度的同时’打破僵局’,同时仍然尊重整体预测的分布。

这种分类框架,结合专门为着色设计的损失函数(用于在训练期间评估模型性能的指标),使模型能够学习灰度特征与可能颜色分布之间的复杂关系。其结果是图像不仅颜色貌似可信,而且具有早期基于回归的方法通常缺乏的丰富性和视觉吸引力。

深入了解:实用的深度学习工作流程

虽然从头开始训练这样一个复杂的 CNN 是一项艰巨的任务,需要巨大的计算资源和庞大的数据集,但利用预训练模型使得这项技术变得触手可及。让我们逐步了解使用预训练的深度学习模型(特别是在原始示例中提到的使用 Caffe 框架构建的模型)进行图像着色的概念步骤,这些步骤使用 Python 和常用库实现。

1. 组装工具包:

基础通常涉及 Python,这是一种在数据科学和 AI 领域流行的多功能编程语言。关键库扮演着重要角色:

  • NumPy: 对于高效的数值运算至关重要,特别是处理表示图像的多维数组。
  • OpenCV (cv2): 一个用于计算机视觉任务的强大库。它提供了读取、写入、操作和显示图像的功能,并且关键的是,包含一个深度神经网络(Deep Neural Network, DNN)模块,能够加载和运行在各种框架(如 Caffe、TensorFlow 和 PyTorch)中训练的模型。
  • Argparse: 一个标准的 Python 库,用于创建用户友好的命令行界面,允许用户轻松指定输入参数,如图像文件路径。
  • OS: 用于基本的操作系统交互,例如以跨不同系统(Windows、macOS、Linux)工作的方式构建文件路径。

2. 获取预训练的智能:

我们不逐块构建神经网络,而是利用代表已为着色训练好的网络的文件。这些通常包括:

  • 模型架构文件(Caffe 为 .prototxt): 此文件定义了神经网络的结构——层、它们的类型、连接和参数。它是模型的蓝图。
  • 训练好的权重文件(Caffe 为 .caffemodel): 此文件包含网络在其广泛训练过程中学习到的数值权重。这些权重封装了模型获得的关于将灰度特征映射到颜色概率的’知识’。它是提炼出的智能。
  • 颜色量化数据(.npy 文件): 这个 NumPy 文件通常存储前面描述的分类方法中使用的量化颜色桶的中心点。它充当预测颜色概率的参考调色板。

这些文件代表了可能在强大硬件上进行数周或数月训练的成果。

3. 加载着色引擎:

有了必要的文件,OpenCV 的 DNN 模块提供了将预训练网络加载到内存中的机制。cv2.dnn.readNetFromCaffe 函数(或其他框架的等效函数)将架构和权重文件作为输入,并实例化网络,使其准备好进行推理(在新数据上进行预测的过程)。来自 .npy 文件的颜色量化点也通常使用 NumPy 加载。

4. 微调网络组件(如果需要):

有时,预训练网络中的特定层在推理之前需要进行微小的调整。在讨论的基于分类的着色模型的背景下:

  • 输出层调整: 负责输出 ‘a’ 和 ‘b’ 通道预测的最终层(例如,在参考模型中命名为 class8_ab)可能需要使用来自 .npy 文件的颜色桶中心显式加载。这确保了网络的输出概率正确映射到预定义的调色板。这些点通常在分配给层的’blobs’(Caffe 对数据容器的术语)之前被重塑并转换为适当的数据类型(例如,float32)。
  • 颜色再平衡: 另一个层(例如,conv8_313_rh)可能会被调整以影响输出中不同颜色之间的平衡,可能增强饱和度或纠正训练期间学到的偏差。这通常涉及将层的 blobs 设置为特定的学习值(如原始代码中提到的 2.606 值,可能是在实验中或训练期间得出的)。

这些步骤使用分类方法,针对着色任务的具体细微差别调整了通用的预训练模型。

5. 准备输入图像:

输入的灰度图像在送入神经网络之前需要经过几个预处理步骤:

  • 加载: 使用 cv2.imread 从指定的文件路径读取图像。即使是灰度图,OpenCV 默认情况下也可能将其加载为 3 通道 BGR 图像,将灰度值复制到所有通道。
  • 归一化: 像素值通常范围从 0 到 255,通过除以 255.0 将其缩放到一个较小的范围,通常是 0.0 到 1.0。这种归一化有助于稳定网络的学习和推理过程。
  • 色彩空间转换: 使用 cv2.cvtColor 将图像从默认的 BGR 色彩空间转换为 CIELab 色彩空间。这对于分离亮度(L)通道至关重要。
  • 调整大小: 大多数预训练的 CNN 期望输入图像具有固定的大小(例如,224x224 像素,这是受 ImageNet 等数据集影响的常见标准)。使用 cv2.resize 相应地调整 LAB 图像的大小。这种标准化确保了与网络架构的兼容性。
  • L 通道分离和中心化: 从调整大小后的 LAB 图像中提取亮度(L)通道。通常,其值(在 LAB 中通常为 0-100)然后通过减去一个平均值(例如 50)来使其围绕零中心化。这种中心化是另一种可以提高网络性能的常见做法。

这个经过精心预处理的 L 通道现在已准备好呈现给网络。

6. 推理步骤:预测颜色:

这就是魔法发生的地方:

  • 创建 Blob: 处理后的 L 通道(现在是一个 2D 数组)被转换为 DNN 模块期望的’blob’,一个 4 维数组格式(cv2.dnn.blobFromImage)。此格式通常包括批次大小、通道数、高度和宽度的维度。
  • 前向传播: 使用 net.setInput 将 blob 设置为加载网络的输入。然后,调用 net.forward() 方法。这会触发计算:输入数据流经网络的各层,根据学习到的权重进行转换,最终产生预测的输出。对于我们的着色模型,输出代表预测的 ‘a’ 和 ‘b’ 通道(或者更确切地说,是颜色桶上的概率分布)。
  • 输出重塑: 来自网络的原始输出需要被重塑并转置回与 ‘a’ 和 ‘b’ 通道相对应的 2D 空间格式。

网络现在已经根据输入的灰度图像生成了它对颜色信息的最佳猜测。

7. 重建彩色图像:

最后阶段涉及将预测的颜色信息与原始图像数据相结合:

  • 调整预测通道的大小: 预测的 ‘a’ 和 ‘b’ 通道(当前大小为 224x224,与网络输入匹配)需要使用 cv2.resize 调整回输入图像的原始尺寸。这确保了颜色信息与原始图像结构正确对齐。
  • 提取原始亮度: 至关重要的是,从原始、全尺寸的 LAB 图像(在预处理期间调整大小之前创建)中提取亮度(L)通道。使用原始 L 通道保留了图像的原始细节和亮度结构,如果使用调整大小后的 L 通道,这些细节将会退化。
  • 连接: 将原始 L 通道与调整大小后的、预测的 ‘a’ 和 ‘b’ 通道沿着颜色通道轴组合(连接)起来。这重新组装了一个完整的 LAB 图像,现在带有预测的颜色。
  • 转换回可显示格式: 使用 cv2.cvtColor 将生成的 LAB 图像转换回 BGR 色彩空间,因为这是大多数图像显示函数(如 cv2.imshow)期望的标准格式。
  • 裁剪和缩放: BGR 图像中的像素值,当前处于归一化范围(可能为 0.0 到 1.0),被裁剪以确保它们保持在此有效范围内(由于预测过程,值有时会略微超出边界)。然后,将它们缩放回标准 0-255 整数范围,以便显示或保存为标准图像文件。

8. 可视化:

最后,可以使用像 cv2.imshow 这样的函数来并排显示原始灰度图像及其新着色的对应物,以便进行即时视觉比较。

执行过程:

通常,实现这些步骤的脚本会从命令行运行。使用 argparse 设置,用户将提供输入灰度图像的路径作为参数(例如,python colorize_image.py --image my_photo.jpg)。然后脚本执行加载、预处理、推理和重建步骤,最终显示或保存着色结果。

这个工作流程利用了预训练模型和强大的库,将深度学习着色的复杂理论转化为一个实用的工具,能够为单色图像添加生动、貌似可信的色彩,有效地弥合了过去与现在之间的鸿沟。