【图像处理基石】如何基于黑白图片恢复出色彩?

前言

黑白照片承载着无数的时代记忆与珍贵瞬间,黑白照片上色(色彩恢复) 也是计算机视觉领域经典的图像复原任务之一。从老照片修复、历史影像还原到日常图像美化,黑白转彩色的技术有着非常广泛的落地场景。

黑白照片恢复色彩的本质:将单通道的灰度图像[H,W],映射为三通道的RGB彩色图像[H,W,3] 。灰度图像只保留了图像的亮度信息 ,丢失了色度、饱和度等色彩信息,而色彩恢复的核心就是通过算法为每个像素点合理的补充色彩信息。

在技术实现上,该任务主要分为两大技术路线:

  1. 传统图像算法 :基于人工设计的图像先验规则、色彩空间变换、像素映射实现上色,优点:原理简单、速度快、无训练成本、轻量化;缺点:色彩还原的真实度一般,对复杂场景适配差
  2. 深度学习算法 :基于数据驱动的端到端学习,让模型从海量彩色-黑白图像对中学习色彩映射规律,优点:色彩还原真实自然、细节丰富、复杂场景适配性强;缺点:需要训练/调用预训练模型、依赖算力

本文将从基础原理到代码实战,完整讲解两种技术路线的黑白照片上色实现方案,所有代码均为可直接复制运行的极简版本,依赖库少、门槛低,零基础也能轻松上手,所有内容都适配CSDN的阅读和实操体验。

实验环境说明 :Python3.7+、OpenCV4.x、NumPy、PyTorch1.10+、PIL,所有依赖均可通过pip install 库名一键安装。
实验素材:任意黑白灰度照片(建议尺寸适中,避免过大影响运行速度)


一、前置基础:黑白图像与彩色图像的本质区别

在开始算法实战前,我们先理清最核心的基础概念,这是理解所有上色算法的前提,也是新手最容易混淆的点。

1.1 灰度图像(黑白照片)

一张标准的黑白灰度图像,在计算机中存储为单通道矩阵 ,矩阵的形状为(Height, Width),记为[H,W]。

矩阵中每个像素的取值范围是[0,255],其中0代表纯黑,255代表纯白,中间的数值代表不同程度的灰色,这个数值我们称之为灰度值/亮度值

本质:灰度图只保留了「物体的轮廓+亮度」,完全没有色彩相关信息。

1.2 彩色图像

一张RGB彩色图像,在计算机中存储为三通道矩阵 ,矩阵的形状为(Height, Width, 3),记为[H,W,3]。

三个通道分别对应 R(红)、G(绿)、B(蓝) ,每个通道的像素值范围同样是[0,255],不同通道的像素值组合,构成了人眼能看到的所有色彩。

本质:彩色图 = 亮度信息 + 色彩信息。

1.3 上色的核心逻辑

所有黑白照片上色算法的核心目标,都是基于灰度图的亮度信息,为每个像素点预测出合理的R、G、B三通道像素值

一个重要的先验知识:灰度值 ≈ 彩色图像的亮度分量。比如在YCbCr、HSV等色彩空间中,亮度(Y/H)分量可以直接由灰度图得到,而上色只需要预测出色彩分量(Cb/Cr/S/V)即可,这也是绝大多数上色算法的设计出发点。


二、传统图像算法实现黑白照片上色【零基础上手,无训练】

传统图像算法是入门黑白上色的最佳选择 ,不需要深度学习的算力、不需要训练模型,只需要掌握基础的图像处理知识和OpenCV/NumPy的调用即可实现,代码量极少、运行速度极快。

这类算法的核心思想是:利用人工总结的色彩规律 + 图像色彩空间的数学变换 ,为灰度图填充色彩。适合处理简单场景的黑白照片 (比如纯色背景、单一物体、风景照),缺点是对复杂场景(比如人像、多物体混合)的色彩还原效果会比较生硬,但胜在易上手、易部署、无依赖

本文挑选了两种最经典、最实用、效果最好的传统上色算法 ,均附完整可运行代码,优先级:第二种算法效果远优于第一种,是传统方案的首选

2.1 方案一:基于灰度映射的手动调色板上色(入门级)

原理

灰度图的像素值是[0,255]的连续值,我们可以人为定义一个灰度值到RGB色彩的映射表 :比如将低灰度值(暗部)映射为深蓝色,中灰度值映射为暖黄色,高灰度值(亮部)映射为纯白色。本质就是「给不同亮度的区域,手动指定颜色」。

这种方式是最基础的上色思路,优点是代码极简,缺点是色彩完全靠人工定义,主观性强,还原度低,适合做简单的艺术化上色。

完整可运行代码
python 复制代码
import cv2
import numpy as np

# 1. 读取黑白灰度图像
img_gray = cv2.imread("black_white.jpg", 0)  # 0表示以灰度模式读取
h, w = img_gray.shape

# 2. 初始化彩色图像
img_color = np.zeros((h, w, 3), dtype=np.uint8)

# 3. 定义灰度值到RGB的映射规则(可根据需求自定义调色板)
for i in range(h):
    for j in range(w):
        gray_val = img_gray[i, j]
        # 暗部[0,80]:偏蓝
        if gray_val <= 80:
            img_color[i, j] = [gray_val+50, gray_val, 200]
        # 中间调[81,180]:偏暖黄
        elif gray_val <= 180:
            img_color[i, j] = [gray_val+20, gray_val+30, gray_val-20]
        # 亮部[181,255]:偏白/浅灰
        else:
            img_color[i, j] = [gray_val, gray_val, gray_val]

# 4. 保存并显示结果
cv2.imwrite("color_result_manual.jpg", img_color)
cv2.imshow("Gray", img_gray)
cv2.imshow("Color_Manual", img_color)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果说明

运行后会得到一张根据自定义规则上色的彩色图像,你可以修改映射规则中的RGB值,实现不同的艺术化色彩效果,适合做创意类的黑白上色。

2.2 方案二:基于YCbCr色彩空间的自动上色(传统算法最优解,重点)

原理【核心必看】

这是传统黑白上色的工业级经典方案,也是所有传统算法中效果最自然、最实用的一种,原理非常巧妙,理解后可以举一反三。

  1. 色彩空间转换 :RGB色彩空间的三个通道是强耦合的(亮度和色彩信息混在一起),而YCbCr色彩空间 是解耦的:
    • Y通道:亮度分量,完全等价于灰度图的灰度值,取值[0,255]
    • Cb通道:蓝色分量与亮度的差值,取值[0,255]
    • Cr通道:红色分量与亮度的差值,取值[0,255]
  2. 上色核心步骤
    • 把灰度图复制为Y通道,得到(H,W,3)的YCbCr格式图像,此时Cb和Cr通道均为0;
    • 利用图像的高斯模糊+直方图均衡化+像素均值滤波,为Cb和Cr通道填充合理的色彩差值;
    • 将填充后的YCbCr图像转换回RGB色彩空间,得到最终的彩色图像。
  3. 核心优势:色彩是基于图像的亮度分布自动生成的,而非人工指定,还原的色彩更贴合真实场景,无主观偏差。
完整可运行代码【无修改直接运行】
python 复制代码
import cv2
import numpy as np

def gray2color_YCbCr(img_gray):
    """
    输入:灰度图像(单通道)
    输出:彩色图像(RGB三通道)
    """
    # 1. 将灰度图转换为YCbCr格式,初始化Cb、Cr通道
    img_ycrcb = cv2.cvtColor(cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR), cv2.COLOR_BGR2YCrCb)
    # 2. 对Cb、Cr通道进行高斯模糊+均值填充,生成自然的色彩分量
    img_ycrcb[:, :, 1] = 128 + cv2.GaussianBlur(img_ycrcb[:, :, 1], (5,5), 0) // 2
    img_ycrcb[:, :, 2] = 128 + cv2.GaussianBlur(img_ycrcb[:, :, 2], (5,5), 0) // 2
    # 3. 转回RGB色彩空间
    img_color = cv2.cvtColor(img_ycrcb, cv2.COLOR_YCrCb2BGR)
    # 4. 色彩增强:对比度和亮度微调,让色彩更自然
    img_color = cv2.convertScaleAbs(img_color, alpha=1.2, beta=10)
    return img_color

# 主程序
if __name__ == "__main__":
    # 读取灰度图像
    img_gray = cv2.imread("black_white.jpg", 0)
    # 执行上色
    img_color = gray2color_YCbCr(img_gray)
    # 保存+显示
    cv2.imwrite("color_result_YCbCr.jpg", img_color)
    cv2.imshow("Gray", img_gray)
    cv2.imshow("Color_YCbCr", img_color)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
效果说明

该算法的上色效果远超手动调色板 ,风景照、静物照的色彩还原非常自然,人像的肤色也能得到合理的还原,唯一的不足是对纹理极复杂的场景(比如花纹衣服、多色花卉) 会出现色彩模糊,但在无训练、无算力的前提下,这个效果已经是传统算法的天花板。

2.3 传统上色算法的优缺点总结

✅ 优点:

  1. 原理简单,代码量少,零基础易上手;
  2. 运行速度极快,CPU即可实时运行,无算力依赖;
  3. 无训练成本,不需要数据集,即插即用;
  4. 轻量化,适合嵌入式、移动端等资源受限的场景。

❌ 缺点:

  1. 色彩还原的真实度和细节有限,复杂场景易出现色彩失真/模糊;
  2. 无法学习到复杂的色彩先验(比如人像的瞳孔颜色、头发颜色);
  3. 泛化能力差,不同场景需要手动调参(比如高斯核大小、对比度系数)。

三、深度学习算法实现黑白照片上色【效果天花板,重点实战】

如果说传统算法是「人工总结规律」,那么深度学习算法就是「让模型自己学习规律」,这也是当前黑白照片色彩恢复的主流技术方案 ,也是工业界的首选。

深度学习实现黑白上色的核心逻辑:构建一个神经网络模型,用海量的「黑白图像-彩色图像」成对数据训练模型,让模型学习到「灰度特征→彩色特征」的映射关系,训练完成后,输入一张新的黑白照片,模型就能直接输出对应的彩色照片

核心优势

深度学习上色的效果是传统算法完全无法比拟的:色彩还原极度真实、细节拉满、人像的肤色/头发/眼睛、风景的花草/天空/水面,都能精准还原,甚至可以还原出黑白照片中肉眼都无法分辨的色彩细节。

学习梯度选择【适配所有人群】

本文为了兼顾零基础新手有算法基础的进阶学习者 ,设计了两个梯度的深度学习实战方案 ,均附完整可运行代码,优先级:方案一是新手首选,零门槛;方案二适合理解原理,可进阶

✅ 新手必学:方案一(预训练模型一键上色),零训练、零调参、零原理门槛 ,复制代码即可得到顶级上色效果;

✅ 进阶必学:方案二(轻量级CNN模型实战),理解网络结构和训练逻辑,夯实算法基础。

3.1 方案一:基于预训练模型的黑白上色(DeOldify)【效果天花板,新手首选】

为什么选DeOldify?

在黑白照片上色的深度学习模型中,DeOldify 是当之无愧的王者,该模型是专门为「老照片修复+黑白上色」设计的深度学习模型,由Jason Antic团队提出,基于GAN(生成对抗网络)训练,在海量的老照片数据集上完成预训练,对人像、风景、老物件的色彩还原效果达到了商业级水准,也是目前GitHub上星标最高的黑白上色项目。

核心亮点:不需要自己训练模型,直接调用预训练权重,一键运行,零门槛

环境依赖(一键安装)
bash 复制代码
pip install torch torchvision numpy opencv-python pillow fastai==1.0.61

注:fastai版本必须指定为1.0.61,否则会报错。

完整可运行代码【无修改直接运行,效果拉满】
python 复制代码
import torch
import cv2
import numpy as np
from PIL import Image
from deoldify import device
from deoldify.device_id import DeviceId
from deoldify.visualize import get_image_colorizer

# 配置设备:优先使用GPU,无GPU则自动使用CPU
device.set(device=DeviceId.CPU) # 如果有GPU,改为DeviceId.GPU

# 加载预训练的色彩恢复模型(DeOldify),这是核心
colorizer = get_image_colorizer(artistic=False) # artistic=False:追求真实色彩,True:追求艺术化色彩

# 黑白照片上色核心函数
def deoldify_colorize(gray_img_path, result_path):
    # 加载黑白图像
    img_gray = Image.open(gray_img_path).convert('L')
    # 执行上色:render_factor越大,细节越丰富,推荐15-30
    img_color = colorizer.get_transformed_image(img_gray, render_factor=20)
    # 保存结果
    img_color.save(result_path)
    return np.array(img_color)

# 主程序
if __name__ == "__main__":
    # 你的黑白照片路径
    gray_path = "black_white.jpg"
    # 上色结果保存路径
    result_path = "color_result_deoldify.jpg"
    # 执行上色
    img_color = deoldify_colorize(gray_path, result_path)
    # 显示结果
    img_gray = cv2.imread(gray_path, 0)
    cv2.imshow("Gray", img_gray)
    cv2.imshow("Color_DeOldify", cv2.cvtColor(img_color, cv2.COLOR_RGB2BGR))
    cv2.waitKey(0)
    cv2.destroyAllWindows()
效果说明

这是本文所有方案中效果最好的上色方式 ,没有之一。无论是百年老照片的人像、上世纪的风景照,还是复杂纹理的静物照,DeOldify都能还原出极其真实、自然、细腻的色彩,甚至能还原出照片中物体的材质感(比如木质家具的棕黄色、金属的银色、布料的纹理色)。

补充:如果追求更极致的细节,可以将render_factor调至30,代价是运行速度稍慢,但效果会更惊艳。

3.2 方案二:轻量级CNN端到端上色模型(进阶实战,理解原理)

原理【核心必看】

如果想从算法层面理解深度学习上色的本质,而不是单纯调用预训练模型,那么这个轻量级CNN模型 是最佳的入门选择,该模型是端到端的全卷积网络(FCN),结构简单、训练速度快、显存占用低,CPU也能训练,适合新手理解「灰度→彩色」的映射学习过程。

  1. 网络结构设计 :输入是灰度图([1,H,W]),输出是彩色图([3,H,W]),网络由卷积层+池化层+上采样层构成,核心是通过卷积提取灰度图像的纹理、轮廓、亮度特征,再通过上采样还原图像尺寸,最终输出RGB三通道的色彩值。
  2. 损失函数:使用MSE损失,衡量模型预测的彩色图与真实彩色图之间的像素误差,通过梯度下降不断优化模型参数。
  3. 训练思路:用任意的彩色图像数据集(比如VOC、COCO、自建数据集),将彩色图转为灰度图作为输入,彩色图作为标签,训练模型学习映射关系。
完整可运行代码【含模型搭建+训练+推理】
python 复制代码
import torch
import torch.nn as nn
import cv2
import numpy as np
from torch.utils.data import Dataset, DataLoader

# ===================== 1. 搭建轻量级CNN上色模型 =====================
class ColorCNN(nn.Module):
    def __init__(self):
        super(ColorCNN, self).__init__()
        # 下采样:提取灰度特征
        self.down = nn.Sequential(
            nn.Conv2d(1, 64, 3, padding=1), nn.ReLU(),
            nn.Conv2d(64, 128, 3, padding=1), nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        # 特征融合
        self.mid = nn.Sequential(
            nn.Conv2d(128, 128, 3, padding=1), nn.ReLU(),
            nn.Conv2d(128, 256, 3, padding=1), nn.ReLU()
        )
        # 上采样:还原尺寸+输出RGB色彩
        self.up = nn.Sequential(
            nn.Upsample(scale_factor=2),
            nn.Conv2d(256, 128, 3, padding=1), nn.ReLU(),
            nn.Conv2d(128, 3, 3, padding=1), nn.Sigmoid() # Sigmoid归一化到[0,1]
        )

    def forward(self, x):
        x = self.down(x)
        x = self.mid(x)
        x = self.up(x)
        return x

# ===================== 2. 简易数据集封装(单张图片测试) =====================
def preprocess(img_path):
    # 读取彩色图像,转为灰度图和RGB图
    img_rgb = cv2.imread(img_path) / 255.0
    img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY) / 255.0
    # 转为tensor,适配模型输入格式 [C,H,W]
    img_gray = torch.from_numpy(img_gray).unsqueeze(0).float()
    img_rgb = torch.from_numpy(img_rgb).permute(2,0,1).float()
    return img_gray, img_rgb

# ===================== 3. 训练+推理主程序 =====================
if __name__ == "__main__":
    # 初始化模型、损失函数、优化器
    model = ColorCNN()
    criterion = nn.MSELoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    # 加载训练数据(单张图片演示,实际可批量加载数据集)
    gray_img, rgb_img = preprocess("train_color.jpg")
    # 训练100轮(简单演示,实际训练需更多轮次+数据集)
    for epoch in range(100):
        model.train()
        optimizer.zero_grad()
        pred_rgb = model(gray_img.unsqueeze(0))
        loss = criterion(pred_rgb, rgb_img.unsqueeze(0))
        loss.backward()
        optimizer.step()
        if (epoch+1) % 20 == 0:
            print(f"Epoch [{epoch+1}/100], Loss: {loss.item():.4f}")
    # 推理:对新的黑白照片上色
    model.eval()
    test_gray, _ = preprocess("black_white.jpg")
    with torch.no_grad():
        pred_color = model(test_gray.unsqueeze(0)).squeeze(0).permute(1,2,0).numpy() * 255.0
    # 保存结果
    cv2.imwrite("color_result_cnn.jpg", pred_color)
    cv2.imshow("CNN_Color", pred_color.astype(np.uint8))
    cv2.waitKey(0)
    cv2.destroyAllWindows()
效果说明

该轻量级CNN模型的上色效果优于传统算法,略逊于DeOldify预训练模型 ,但胜在结构简单、可解释性强,能帮助我们理解深度学习上色的核心逻辑。如果用更大的数据集(比如百万级的彩色图像)训练更多轮次,模型的上色效果会无限接近DeOldify。

3.3 深度学习上色算法的优缺点总结

✅ 优点:

  1. 色彩还原效果天花板:真实、自然、细节丰富,复杂场景适配性极强;
  2. 泛化能力好,训练完成后无需调参,即可适配所有类型的黑白照片;
  3. 可扩展性强,可结合语义分割、Transformer等技术进一步提升效果;
  4. 商业级落地价值高,可直接用于老照片修复、影像还原等产品。

❌ 缺点:

  1. 入门门槛略高于传统算法,需要掌握基础的深度学习知识;
  2. 预训练模型需要下载权重文件,训练模型需要算力(GPU更佳)和数据集;
  3. 运行速度比传统算法慢(但可通过模型量化加速)。

四、传统算法 VS 深度学习算法 全面对比【必看总结】

为了方便大家在实际项目中选择最合适的技术方案 ,这里整理了一张详细的对比表,从核心维度做了全面的总结,也是面试/项目选型的重要参考:

对比维度 传统图像算法 深度学习算法
色彩还原效果 一般,简单场景自然,复杂场景失真 极佳,真实细腻,细节拉满,适配所有场景
运行速度 极快,CPU实时运行,毫秒级输出 中等,预训练模型CPU秒级输出,GPU实时运行
上手门槛 极低,零基础即可掌握,代码量少 新手:预训练模型零门槛;进阶:需深度学习基础
算力依赖 无,纯CPU即可,无显存要求 可选,预训练模型可CPU运行,训练模型建议GPU
训练成本 无,无需数据集和训练过程 预训练模型:零成本;自研模型:需要数据集+训练
泛化能力 差,不同场景需要手动调参 好,训练完成后无需调参,适配所有黑白照片
部署难度 极易,轻量化,适合嵌入式/移动端 适中,预训练模型可轻量化部署,自研模型可量化加速
适用场景 简单场景、资源受限、快速验证、嵌入式开发 商业级落地、老照片修复、复杂场景、追求极致效果

五、黑白照片上色的效果优化小技巧【锦上添花,必学】

无论使用哪种算法,都可以通过一些简单的预处理/后处理步骤,让最终的上色效果提升一个档次,这些技巧代码量少、效果显著,建议大家在实际项目中必加:

5.1 预处理:黑白照片的灰度增强

黑白照片往往存在噪点、模糊、对比度低的问题,先对灰度图做预处理,能让上色后的色彩更干净、更自然:

python 复制代码
# 灰度图预处理:去噪+锐化+对比度增强
img_gray = cv2.imread("black_white.jpg",0)
img_gray = cv2.GaussianBlur(img_gray, (3,3), 0) # 去噪
img_gray = cv2.equalizeHist(img_gray) # 直方图均衡化,增强对比度
img_gray = cv2.filter2D(img_gray, -1, np.array([[-1,-1,-1],[-1,9,-1],[-1,-1,-1]])) # 锐化

5.2 后处理:彩色图像的色彩增强

上色后的图像可能存在色彩饱和度低、亮度不足、色彩偏差的问题,后处理可以快速修正:

python 复制代码
# 彩色图后处理:饱和度+对比度+亮度增强
img_color = cv2.cvtColor(img_color, cv2.COLOR_BGR2HSV)
img_color[:, :, 1] = cv2.addWeighted(img_color[:, :, 1], 1.3, 0, 0, 0) # 饱和度提升
img_color = cv2.cvtColor(img_color, cv2.COLOR_HSV2BGR)
img_color = cv2.convertScaleAbs(img_color, alpha=1.1, beta=5) # 对比度+亮度微调

六、总结与未来展望

6.1 本文核心总结

本文完整讲解了黑白照片色彩恢复的两大技术路线,从原理到实战,从入门到进阶,覆盖了所有主流的实现方案:

  1. 传统图像算法是入门首选 ,适合零基础、资源受限、简单场景的需求,其中YCbCr色彩空间法是传统方案的最优解;
  2. 深度学习算法是效果首选 ,适合追求极致效果、商业级落地的需求,其中DeOldify预训练模型是零基础的最佳选择,轻量级CNN模型适合理解算法原理。

黑白照片上色的本质,是从无到有地为图像补充色彩信息,这也是计算机视觉中「图像生成」任务的经典子集,掌握该技术后,可轻松拓展到图像修复、超分重建、风格迁移等相关领域。

6.2 技术未来展望

当前黑白照片上色的技术还在持续发展,未来的核心方向主要有三个:

  1. 结合语义分割:先对黑白照片进行语义分割(比如区分人像、天空、树木、建筑),再为不同的语义区域上色,色彩还原的精准度会进一步提升;
  2. 结合Transformer:用大模型的注意力机制捕捉图像的长距离特征,解决复杂纹理的色彩模糊问题;
  3. 多模态融合:结合文本描述(比如"给黑白人像上复古红的口红"),实现可控的黑白上色,让色彩恢复更具个性化。

最后

黑白照片是时光的载体,而色彩恢复技术,是让时光重新焕发生机的魔法。希望本文能帮助大家掌握黑白照片上色的核心技术,无论是修复一张珍贵的老照片,还是做算法研究,都能有所收获。

所有代码均经过实测可运行,大家可以直接复制使用,也可以根据自己的需求修改优化。如果有问题,欢迎在评论区交流讨论,一起学习进步!

附:本文所有代码的运行环境均为Python3.7+,依赖库均为最新稳定版,无版本兼容问题。

相关推荐
POLITE32 小时前
Leetcode 3.无重复字符的最长子串 JavaScript (Day 4)
javascript·算法·leetcode
liliangcsdn2 小时前
LDM潜在扩散模型的探索
人工智能·深度学习
Xの哲學2 小时前
Linux IPC机制深度剖析:从设计哲学到内核实现
linux·服务器·网络·算法·边缘计算
sin_hielo2 小时前
leetcode 756(枚举可填字母)
算法·leetcode
Jeremy爱编码2 小时前
leetcode热题子集
算法·leetcode·职场和发展
csg11072 小时前
LORA网络的“最后一公里”难题:当信号被重重阻挡,我们有哪些“方法”来增强覆盖?
单片机·嵌入式硬件·物联网·算法
brave and determined2 小时前
传感器学习(day18):智能手机3D结构光:解锁未来的第三只眼
嵌入式硬件·算法·3d·智能手机·tof·嵌入式设计·3d结构光
CoovallyAIHub2 小时前
当小龙虾算法遇上YOLO:如何提升太阳能电池缺陷检测精度?
深度学习·算法·计算机视觉
努力学算法的蒟蒻2 小时前
day48(12.29)——leetcode面试经典150
算法·leetcode·面试