基于PyTorch的灰度图像自动着色:ECCV16与SIGGRAPH17模型实战
2026/9/15 2:29:11 网站建设 项目流程

简介:一份以深度神经网络为核心的彩色图像自动着色项目代码包,基于Python实现,适合有一定深度学习基础、希望复现图像着色效果的开发者。项目内置ECCV16与SIGGRAPH17两种预训练着色模型,支持实时用户引导,通过转换至Lab色彩空间、统一调整为256×256输入后预测a/b通道,再与原始全分辨率L通道融合输出RGB结果。压缩包共23个文件,约4.47MB,其中6个Python脚本为主程序与模型加载逻辑,5个pyc为编译缓存便于直接调用,另有示例原图与效果图、依赖说明、README及License等文件,结构简洁便于上手。demo_release.py提供了命令行推理入口,colorizers目录封装模型定义与预处理/后处理工具,读者可快速运行并替换自己的灰度图测试。资源已有680人学习下载,适合作为图像处理、深度学习应用方向的项目参考与复现练习。

1. 一张黑白图到全彩:深度自动着色的这碗冷饭怎么炒

老照片修复场景里,用深度神经网络给黑白图像自动着色,一直是个看着简单、做起来玄学的问题。传统抠图加调色板的手工流程效率极低,而这个基于Python的彩色图像着色项目把这件事变成了一个离线可跑的流程:输入一张灰度图,预训练网络在Lab色彩空间里预测a和b通道,再与原始L通道合并回RGB。项目内置ECCV16和SIGGRAPH17两个模型,前者用于自动全图上色,后者支持用户引导;源码包里有完整的PyTorch推理实现,适合想快速验证深度先验效果、或者要批量处理老照片的开发者。如果你手头已经装了PyTorch环境,从拿到压缩包到跑出第一张彩色图,通常只需要一杯咖啡的时间。

2. 从Lab空间到CNN先验:ECCV16与SIGGRAPH17两个模型的原理与选型

2.1 为什么着色要绕开RGB,改用Lab空间

RGB三通道相关性太高,学习“灰度→RGB”的映射容易让网络输出偏灰或偏色的结果。Lab颜色空间把亮度分量L独立出来,a通道表示从绿色到品红,b通道表示从蓝色到黄色,这样神经网络只需要回归两个颜色对立通道,图像亮度直接复用输入,避免了端到端生成RGB时常见的细节丢失。项目在预处理阶段把输入图像转成Lab,再取L通道作为网络输入,这就是“学习深度先验”的核心含义:网络学习的是灰度结构与颜色分布之间的统计对应关系,而不是从零生成一张彩色图。

在推理时,项目固定将图像缩放到256×256,因为两个预训练模型都在这个分辨率上做过数据增强与归一化。这一步封装在preprocess_img内部,用户不需要自己插值。模型输出的是256×256的两个ab通道,后处理再把ab通道上采样回原始L通道的分辨率合并,最终转回RGB。整个过程在demo_release.py里是连贯的,但拆开看反而更能理解为什么后处理不能省略。

2.2 ECCV16:融合全局与局部语义的自动着色网络

ECCV16的名字来自ECCV 2016论文《Colorful Image Colorization》。网络以VGG16的前几层作为骨干,并设计了两个分支:一个全局分支通过多次下采样获得整幅图的语义类别,另一个局部分支保留边缘和纹理细节。两个分支的特征随后融合,再通过一系列上采样层输出ab通道。对一张完全不提供任何提示的灰度图,ECCV16能利用语义先验给出比较自然的天空蓝、草地绿和肤色,但它不允许用户指定某个区域的具体颜色,所以更适合无人值守的批量任务。

由于原版权重在Caffe框架上训练,这个项目里对应的是PyTorch移植版本。原作者在GitHub仓库里提供了caffe分支,但那个分支已经停止维护,依赖的旧版Caffe和Python 2环境很难在现在的系统上装起来。相比之下,PyTorch版本的eccv16.py模型定义写得直白,把卷积层、批归一化层和上采样模块顺序排好,权重文件通过torch.load即可加载。若你在新机器上跑旧Caffe代码出现编译错误,不要浪费时间排查,直接切换到当前项目即可。

2.3 SIGGRAPH17:支持用户笔刷的引导式着色网络

SIGGRAPH17是同一团队在SIGGRAPH 2017发表的后续工作,主要解决用户引导问题。网络更轻量,结构上增加了对输入提示点的融合:除了灰度图本身,用户可以在图像上画几笔颜色,网络会把提示区域对应的颜色信息编码进中间特征层。这里的“引导”不是简单的alpha叠加,而是让网络学习“用户指定颜色后,周围区域应如何扩散颜色”。因此,即使只给出一小块树叶的绿色,网络也可能把整棵树染成绿色。

对比两个模型时,可以从网络结构复杂度、是否支持输入提示、自动着色风格和适用场景四个维度看,整理如下表:

对比项eccv16siggraph17
网络骨干VGG16前段自建轻量全卷积网络
用户提示输入不支持支持笔刷或颜色点
自动模式表现全局色彩饱满色彩偏灰、更保守
模型文件大小较大较小
适用场景无人值守批量着色交互式修复或指定配色

需要注意的是,SIGGRAPH17在没有任何用户提示时仍然可以运行,但输出会比ECCV16寡淡,这是设计使然,不是模型损坏。我在实际项目里一般这样选型:如果只是把一批老照片变成彩色,直接跑eccv16;如果要在修图软件里交互式地上色,则用siggraph17并把鼠标位置写入提示张量。

2.4 用Python快速验证模型加载

拿到项目后先别急着处理图片,先确认两个预训练模型能否正确加载。colorizers包对外暴露了eccv16()siggraph17()工厂函数,它们返回的是torch.nn.Module子类。下面这段代码会实例化模型并统计参数数量,用最轻量的方式验证权重文件是否完整:

import torch import colorizers # 加载预训练模型,eval()必须调用,否则BatchNorm在推理时仍使用批统计量 colorizer_eccv16 = colorizers.eccv16().eval() colorizer_siggraph17 = colorizers.siggraph17().eval() # 打印可训练参数数量,判断是否加载成功 for name, model in [("eccv16", colorizer_eccv16), ("siggraph17", colorizer_siggraph17)]: params = sum(p.numel() for p in model.parameters()) print(f"{name} total parameters: {params}")

逻辑说明:numel()返回张量元素个数,eccv16的参数量大约在千万级,siggraph17会小一些,因为它的卷积通道数更少、网络更浅。eval()方法会把Dropout和BatchNorm切换到推理模式,如果不调用,同一张图每次输出都会有微小抖动,这在着色任务里会被放大成可见的颜色噪点。加载成功后再执行demo_release.py,脚本内部的预处理步骤才会被正确衔接。

3. 跑通demo_release.py:命令行参数、依赖安装与saved图像一致性验证

3.1 依赖与Python环境准备

项目依赖集中在requirements.txt中,主要包含PyTorch系列库、图像处理库和数值计算库。建议用Python 3.8以上的虚拟环境安装,避免系统包冲突。下面的命令会在当前目录创建虚拟环境并安装依赖:

python -m venv venv source venv/bin/activate # Windows下的venv\Scripts\activate pip install -r requirements.txt

逻辑说明:python -m venv是Python自带虚拟环境工具,source在Windows下对应activate脚本。安装依赖时,torch和torchvision的版本必须相互匹配,pip会自动解析多数组合。如果你只有CPU,安装CPU版torch即可,处理单张图通常在三秒以内;如果后续要跑批量,建议使用带CUDA的GPU版本。依赖表中我列出了容易出现问题的库及其用途:

依赖用途常见问题
torch模型定义、推理、自动求导CPU与GPU版本不匹配
numpy数组处理、Lab转换新版API变化不大
opencv-python图像读写、颜色转换、缩放读入图像默认BGR顺序
scipy部分插值兼容逻辑版本过新会触发插值模式警告
torchvision提供部分网络层实现与torch版本严格对应

如果pip install时torch下载太慢,可以设置国内镜像源,例如在pip命令后追加-i https://pypi.tuna.tsinghua.edu.cn/simple。安装完成后再执行import colorizers,这一步能提前暴露缺少依赖或Python路径配置错误。

3.2 命令行参数逐个拆解与两个模型的对比运行

demo_release.py入口参数不多,-i指定输入图像路径,--model_type选择模型类型。每次运行最好显式指定模型类型,因为不同版本可能在默认值上有差异。以下命令先跑eccv16再跑siggraph17:

python demo_release.py -i imgs/ansel_adams3.jpg --model_type eccv16 python demo_release.py -i imgs/ansel_adams3.jpg --model_type siggraph17

参数说明:-i接收BGR格式的图像路径,脚本内部会先用cv2.imread读取,再转RGB;--model_type只能取eccv16siggraph17,传其他值会在脚本报错。建议在项目根目录下执行命令,避免相对路径imgs/解析失败。如果你在Windows的CMD里运行,路径分隔符使用\,但在Git Bash或PowerShell中使用/也能识别。对比两个模型的输出时,重点关注同一张图中天空、皮肤和植物区域的颜色差异,通常差异最大的地方就是模型选型的决策点。

3.3 从demo_release.py里拆出的核心推理流程

实际上,demo_release.py的推理代码很短,核心是preprocess_imgpostprocess_tens两个函数。把这两个函数拆出来放到自己的脚本里,可以更灵活地控制输入输出,下面是完整推理片段:

import torch import numpy as np import cv2 from colorizers import eccv16, preprocess_img, postprocess_tens # 加载模型并切到评估模式 net = eccv16().eval() img_bgr = cv2.imread("imgs/ansel_adams3.jpg") # 预处理:BGR转RGB,再转Lab,缩放到256x256 tens_l_orig, tens_l_rs = preprocess_img(img_bgr, HW=(256, 256)) with torch.no_grad(): out = net(tens_l_rs).cpu() # 后处理:把预测的ab图上采样,与原始L通道合并,转回RGB img_out = postprocess_tens(tens_l_orig, out, torch.tensor([0])) cv2.imwrite("imgs_out/saved_mine.png", img_out)

逻辑说明:preprocess_img返回两个张量,tens_l_orig是原图分辨率下的L通道,tens_l_rs是缩放到256×256后的L通道。网络输入只取L通道,代码里不会出现ab通道,因为模型自己预测ab。postprocess_tens内部先对ab通道做双线性上采样到原图L的尺寸,再在通道维度上拼接,最后转换为RGB并还原成OpenCV可写的BGR格式。torch.tensor([0])表示没有用户提示,如果使用siggraph17,这里可以传入包含笔刷信息的张量。

参数说明:HW=(256, 256)是固定的网络输入分辨率,不要随意改成其他尺寸。虽然卷积网络可以接受任意输入,但预训练权重在固定尺寸上表现最好。对于超大原图,postprocess_tens的上采样内存开销较大,建议先把图像最长边限制在2000像素以内,或者在后处理前用cv2.resize降采样原图。如果你的图像是灰度单通道,也要先转成三通道BGR再送入预处理函数。

3.4 验证saved图像一致性

项目自带的imgs_out文件夹里有多张示例输出,README明确说结果应与这些图像匹配。这不是指文件大小一致,而是像素内容应基本一致。验证方法是在项目根目录执行上面的命令,然后把生成的saved_mine.png与项目提供的参考图对比。由于不同机器上浮点运算顺序可能有细微差异,允许几个像素级的误差:

import cv2 import numpy as np a = cv2.imread("imgs_out/saved_eccv16.png") b = cv2.imread("imgs_out/saved_mine.png") diff = np.mean(np.abs(a.astype(np.float32) - b.astype(np.float32))) print(f"mean pixel diff: {diff:.4f}")

逻辑说明:saved_eccv16.png是项目预置的参考结果,saved_mine.png是刚才重跑生成的。计算两张图的平均绝对差,差值小于1可以视为一致。如果差值达到几十甚至上百,说明模型输出有问题,优先检查权重文件是否被覆盖、是否用了不匹配的--model_type,以及opencv版本是否把图像读成了RGB顺序。另外注意项目里同时存在saved_eccv16.pngsaved_siggraph17.png,验证时要对应模型。

4. 批量着色流水线:用Python把单张推理改造成目录级处理

4.1 为什么需要自己的批处理脚本

demo_release.py一次只处理一张图,实际项目中往往要处理几十张扫描件或老照片。直接批量调用命令行的最大问题是模型每次启动都要重新加载,浪费大量时间。正确的做法是把模型加载一次,在一个Python进程内循环处理所有图片。项目本身没有提供批处理脚本,但其colorizers包接口清晰,拼装一个目录级流水线只需几十行代码。我在真实项目中通常还会加入重试和日志逻辑,但核心循环足够简单。

4.2 先写一个单线程批量版本

import os import glob import torch import cv2 from colorizers import eccv16, preprocess_img, postprocess_tens def batch_colorize(input_dir, output_dir): os.makedirs(output_dir, exist_ok=True) model = eccv16().eval() # 模型只加载一次 for img_path in glob.glob(os.path.join(input_dir, "*.jpg")): img_bgr = cv2.imread(img_path) if img_bgr is None: continue # 限制最大边长,避免后处理内存爆炸 h, w = img_bgr.shape[:2] if max(h, w) > 2000: scale = 2000 / max(h, w) img_bgr = cv2.resize(img_bgr, (int(w * scale), int(h * scale))) tens_l_orig, tens_l_rs = preprocess_img(img_bgr, HW=(256, 256)) with torch.no_grad(): out = model(tens_l_rs).cpu() img_out = postprocess_tens(tens_l_orig, out, torch.tensor([0])) name = os.path.splitext(os.path.basename(img_path))[0] out_path = os.path.join(output_dir, f"{name}_color.png") cv2.imwrite(out_path, img_out) print(f"processed: {name}") if __name__ == "__main__": batch_colorize("imgs_in", "imgs_out_batch")

逻辑说明:函数先创建输出目录,再遍历input_dir下的所有jpg文件。cv2.imread读入的是BGR三通道数组,灰度图也会被转成三通道,只是每个通道值相等。preprocess_img内部会完成BGR转RGB、RGB转Lab、缩放到256×256的全部步骤,因此用户不需要关心这些细节。torch.no_grad()禁用梯度,推理时不会保留中间变量的导数,内存占用更低。postprocess_tens输出的图像在OpenCV里是BGR顺序,直接写盘即可。

参数说明:HW=(256, 256)是模型的固定输入尺寸,不能修改。2000这个限制是经验值,对于超过2000像素的图片,后处理的双线性插值会生成一张全分辨率的大图,CPU内存会被大量占用。如果需要处理超长图,建议在预处理前就分块推理,最后再拼接;但分块会丢失全局语义,容易导致同一图像不同区域颜色不一致。如果你需要处理PNG或其他格式,把*.jpg改成对应的glob模式,或者使用*.*再通过后缀过滤。

4.3 多进程加速与中间张量保存

单线程循环在CPU上每张图约2到5秒,GPU上可以提速到几十毫秒。超过100张图时,多进程是更简单的加速手段。使用multiprocessing时要注意,torch模型最好不要在主进程中加载后直接传给子进程,常见做法是让每个子进程在初始化时重新加载自己的模型。下面是一个简单的进程池示例:

from concurrent.futures import ProcessPoolExecutor def worker(path): import colorizers model = colorizers.eccv16().eval() # ...省略与单线程版本相同的预处理和后处理... return os.path.basename(path) if __name__ == "__main__": paths = glob.glob("imgs_in/*.jpg") with ProcessPoolExecutor(max_workers=4) as pool: for result in pool.map(worker, paths): print(result)

逻辑说明:每个子进程在第一次调用worker时都会执行colorizers.eccv16(),这会让模型加载四次,但避免了模型状态共享时的锁开销。对于批量任务,这个并发粒度已经足够。调试时如果只需要观察中间结果,可以在后处理前把ab通道保存为npy文件:

np.save("ab_pred.npy", out[0].permute(1, 2, 0).numpy())

逻辑说明:out的形状是(1, 2, 256, 256)permute(1, 2, 0)把它变成(256, 256, 2),保存后用matplotlib的imshow结合L通道即可可视化网络预测的颜色分布。这样能快速定位是网络输出问题还是后处理颜色空间转换问题。

5. 出口对齐与色偏自查:全分辨率融合暗坑与灰度图先转Lab的验证技巧

5.1 全分辨率融合的暗坑:插值模式与通道顺序

当输入图像尺寸不是256的倍数时,后处理的上采样会引入插值误差,但这种误差通常小于1个像素值。真正的暗坑在于OpenCV与Python图像库之间的通道顺序。cv2.imread读入的是BGR,preprocess_img内部会转成RGB,而postprocess_tens输出的又是RGB顺序。如果你直接把这个输出交给plt.imshow,会出现红蓝互换。常见做法是用cv2.cvtColor(img_out, cv2.COLOR_BGR2RGB)在显示前再转一次。

另一个容易踩坑的是图像带Alpha通道。PNG文件如果带有透明通道,cv2.imread默认只读三通道,但如果使用cv2.imread(..., cv2.IMREAD_UNCHANGED)会返回四通道数组,preprocess_img可能无法正确处理。稳妥的办法是在读取后检查img_bgr.shape[2],如果是4就丢弃Alpha通道。下面是一个安全读取函数:

def safe_imread(path): img = cv2.imread(path, cv2.IMREAD_UNCHANGED) if img.ndim == 3 and img.shape[2] == 4: img = cv2.cvtColor(img, cv2.COLOR_BGRA2BGR) return img

逻辑说明:IMREAD_UNCHANGED保留原始通道数,检测到四通道后通过COLOR_BGRA2BGR丢弃Alpha。这样送到preprocess_img的图像保证是三通道BGR。

5.2 用彩色退化图做色偏回溯验证

要快速判断当前机器上的模型行为是否正常,可以找一张彩色图,先转灰度,再送入模型着色,最后与原始彩色图比较。因为模型不是为恢复同一张图设计的,这个测试不能用于评估模型绝对质量,但能暴露环境层面的错误。如果连“自己退化后”都出不了合理颜色,那问题大概率出在依赖或预处理步骤上。

import cv2 import torch from colorizers import eccv16, preprocess_img, postprocess_tens src = cv2.imread("color_reference.jpg") gray = cv2.cvtColor(cv2.cvtColor(src, cv2.COLOR_BGR2GRAY), cv2.COLOR_GRAY2BGR) model = eccv16().eval() tens_l_orig, tens_l_rs = preprocess_img(gray, HW=(256, 256)) with torch.no_grad(): out = model(tens_l_rs).cpu() recovered = postprocess_tens(tens_l_orig, out, torch.tensor([0])) print("original shape:", src.shape, "recovered shape:", recovered.shape) print("PSNR:", cv2.PSNR(src, recovered))

逻辑说明:第一行cv2.cvtColor(src, cv2.COLOR_BGR2GRAY)把彩色图变成灰度图,第二行cv2.COLOR_GRAY2BGR再转回三通道,这样满足preprocess_img的输入要求。cv2.PSNR计算两张图的峰值信噪比,如果低于18dB,说明模型输出的颜色分布异常,需要检查输入图像是否被意外反转成RGB顺序,或者模型权重文件是否损坏。如果PSNR在20到30之间,说明模型正常工作,因为深度着色得到的颜色本来就不可能和原始彩色图完全一致。

5.3 命令行一键对比与环境提醒

最后给一个可直接运行的命令,用于在项目根目录下验证当前环境输出是否稳定。假设你已经把批处理函数写在一个名为my_batch.py的文件里,可以这样调用:

python -c "from my_batch import batch_colorize; batch_colorize('imgs_in','imgs_out_check')"

这个命令适合在切换Python环境后快速重测。如果你在Windows的CMD里运行,路径分隔符使用\,但在Git Bash或PowerShell中使用/也能识别。着色任务本质上训练的是先验分布,所以输入图像中的水印、噪点或文字边框都会被网络当作结构特征染上颜色,这不是bug,而是预期行为。处理这类图像时,最有效的办法是先裁剪边缘,或者用去水印算法清理后再送入模型,而不是在后处理阶段强行抹色。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询