简介:这是一套基于深度学习的老照片修复项目工程包,面向具备Python基础、希望入门或进阶计算机视觉的开发者。整体共81个文件,包含54个Python脚本,覆盖数据预处理、模型训练、测试及人脸检测对齐等完整链路;17张图片用于新旧对比与流程示意;4份PDF文档和1份演示视频可帮助快速理解项目方案与效果,压缩包约50.71MB。目录按data、models、options、util等模块划分,结构清晰,便于按需提取训练脚本与工具函数。目前已有350人学习下载。读者可通过源码和文档了解CNN、GAN等模型在划痕、污损老照片修复中的应用,也可借助演示视频与README快速复现推理流程,适合课程设计、毕业课题或AI图像修复实践。
1. 老照片修复项目:先搞清楚你下载的这个 zip 里到底装了什么
你手里应该是一个几百 MB 到几个 GB 不等的 zip 压缩包,标题写着「老照片修复项目是通过深度学习的方法修复严重退化的老照片」。解压之前先想清楚一件事——你要处理的不是普通去噪,而是修复「严重退化」。划痕、霉斑、大块噪点、褪色、细节丢失这些退化叠在一起,传统 OpenCV 那套「去噪 + 对比度拉伸」基本没用。深度学习模型能做这件事,靠的是从大量配对数据里学会「干净照片 → 退化照片」的逆向映射。
这个标题真正指向的思路是:用 Python 搭一个基于生成对抗网络(GAN)或扩散模型的老照片修复管线,输入一张布满折痕和噪点的扫描件,输出一张细节可信、颜色自然的数字照片。适合两类人——手里有大量真实老照片需要批量修复的从业者,以及刚学完 PyTorch 想找一个完整实战项目练手的深度学习新人。前者关心能不能出活,后者关心管线的每一步怎么拆。本文就按这个思路,从解压 zip 到跑通推理、调参数、避坑、微调,一条线讲完。
2. 从 zip 到能跑的 Python 环境:解压、目录检查和依赖安装
老照片修复项目这类深度学习代码包,拿到手之后最怕的不是代码看不懂,而是环境装不上。先别急着双击运行什么 train.py,按顺序做三件事:解压、看目录结构、装依赖。这三步走稳,后面所有问题都好排查。
2.1 解压 zip 的三种方式与目录结构识别
不管你是 Windows 还是 Linux,解压一个深度学习项目压缩包都有讲究。Windows 上右键「全部解压缩」最省事,但如果你习惯用命令行,Linux 下的 unzip 命令要记住几个参数:
unzip old_photo_repair.zip -d old_photo_repair cd old_photo_repair && ls -la-d指定解压目标目录,避免把所有文件直接洒在当前文件夹里。解压完成后先别急,逐个看目录。老照片修复项目沿用的是学术界比较统一的组织方式:
old_photo_repair/ ├── checkpoints/ # 预训练权重存放处,有些项目用 weights/ 或 pretrained/ ├── data/ # 训练数据或测试图片 ├── scripts/ # 训练和推理脚本 ├── models/ # 网络结构定义 ├── requirements.txt # Python 依赖清单 └── README.md # 项目说明,一定要读我见过不少翻车现场,都是因为跳过了 README 就直接跑。这个文件里通常会写明权重文件的下载地址、Python 版本要求、CUDA 版本要求。如果 zip 里没有 README,那你至少得确认 requirements.txt 里的依赖列表能不能在当前 Python 版本下安装。
2.2 用 conda 建独立环境:别污染你的主环境
老照片修复项目依赖的 PyTorch、torchvision、opencv-python 版本不是随意定的。直接用 pip 装到系统 Python 里,大概率会跟其他项目冲突。我一般习惯用 conda 建一个独立环境,这也是深度学习环境配置里最稳妥的做法:
conda create -n photo_repair python=3.8 conda activate photo_repair pip install torch==1.13.1 torchvision==0.14.1 --index-url https://download.pytorch.org/whl/cu117Python 版本不要一上来就选最新的,很多老项目是在 Python 3.7/3.8 下写的,用的第三方库在 3.10 以上可能已经不做兼容。PyTorch 的安装要特别注意 CUDA 版本,上面命令里的cu117表示 CUDA 11.7。先运行nvidia-smi查看你机器驱动支持的 CUDA 版本,再选对应的 PyTorch 轮子包。如果你没有 NVIDIA 显卡,把--index-url去掉装 CPU 版也能跑推理,只是速度慢很多。
依赖安装用 requirements.txt 批量装:
pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple这里用的是清华镜像源加速下载,实测比默认源快好几倍。装完后先验证关键包能不能正常导入,发现缺了就补,而不是等到跑脚本时才报错。
2.3 快速验证环境:一张纯色图就跑通模型加载
环境装没装好,不要用 python 交互式一行一行敲,直接写个小脚本验证 GPU 可用性和模型能否加载:
# verify_env.py import torch import torchvision print("PyTorch 版本:", torch.__version__) print("CUDA 可用:", torch.cuda.is_available()) print("GPU 名称:", torch.cuda.get_device_name(0) if torch.cuda.is_available() else "CPU") from models import get_repair_model model = get_repair_model() # 按项目实际 API 调整 print("模型参数量: {:.2f}M".format(sum(p.numel() for p in model.parameters()) / 1e6))CUDA 可用这一项如果输出 False,后面一切推理都会慢到怀疑人生。参数量输出能帮你判断模型有没有正确加载,一般在 50M 到 200M 之间,太小的说明权重没加载进去。到这里环境就绪,可以进入下一步了。
3. 先跑通最小推理:预训练权重、测试图像和第一张修复结果
环境装好只是万里长征第一步。老照片修复项目能不能出图,关键在预训练权重。这一步的坑最多,也最容易被忽略。
3.1 预训练权重:zip 里通常没有,你要单独下载
大多数深度学习项目仓库不会把训练好的权重直接塞进 zip,因为动辄几百 MB。zip 里一般只有一个 checkpoints 目录和一个包含下载链接或 md5 校验值的说明文件。你先检查checkpoints/目录下有没有.pth或.pt后缀的文件,如果没有,就去 README 里找下载地址。
权重文件下载完先校验 md5 再做放置:
md5sum checkpoints/old_photo_repair.pth mkdir -p checkpoints mv ~/Downloads/old_photo_repair.pth checkpoints/我习惯的做法是从不跳过 md5 校验,尤其是用网盘或第三方镜像下载的权重,损坏概率比想象中高。放错位置的话,训练脚本会报「找不到权重文件」,推理脚本则有可能静默用随机初始化权重运行——出来的图当然全是噪声,你还以为是模型不行。
3.2 最小推理命令与脚本解析
跑通推理的完整命令通常长这样:
python test.py \ --checkpoint ./checkpoints/old_photo_repair.pth \ --input ./data/test/old_photo.jpg \ --output ./results/ \ --device cuda:0--checkpoint指定权重路径,--input是待修复图片路径,--output是结果保存目录,--device决定用 GPU 还是 CPU。有些项目还支持--with_scratch参数,表示是否启用划痕检测模块——严重退化的老照片建议保持开启,后面我会专门讲这个参数。
第一次跑通别用太复杂的图。找一张只有轻度噪点和褪色的照片试水,把模型推理全流程先走完。输出一张结果后,用肉眼对比原图,看看有没有以下现象:整体变干净了,还是出现了不该有的纹理。如果输出一片黑或一片白,多半是输入图像预处理出了问题,八成是图像归一化或数据类型转换的 bug,往这个方向排查。
3.3 推理脚本内部到底做了什么
如果你打算把老照片修复项目接入自己的业务,就不能只当一个黑匣子调用。看一遍推理脚本的主流程,你会发现它无非在做这几件事:
# test.py 核心流程摘要 import cv2 import torch from torchvision import transforms # 1. 读取图片并做尺寸对齐 img = cv2.imread(args.input) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) H, W = img.shape[:2] img = cv2.resize(img, (W // 16 * 16, H // 16 * 16)) # 对齐到16的倍数 # 2. 转 Tensor 并归一化到 [-1, 1] img_tensor = transforms.ToTensor()(img).unsqueeze(0) * 2.0 - 1.0 # 3. 推理 with torch.no_grad(): result = model(img_tensor.to(device))[0] # 4. 转回图像并保存 result = (result.squeeze(0) + 1.0) / 2.0 result = transforms.ToPILImage()(result.clamp(0, 1)) result.save(args.output + "/" + args.input.split("/")[-1])注意第三步里的torch.no_grad(),推理阶段不需要计算梯度,加上能省大量显存。尺寸对齐到 16 的倍数是因为网络里有多层下采样,输入尺寸不能被整除的话,特征图尺寸会错位,报错或者输出边缘异常。这里如果报size mismatch,先检查是不是输入图片尺寸没有对齐,而不是先去怀疑网络结构。
4. 参数调节与效果优化:退化程度、人脸区域和色彩偏差
跑通不代表能交付。真实的老照片退化情况千差万别,有划痕密集的,有整体发黄的,有人脸完全模糊的。这一章讲三个最影响出图质量的调节点,每一项都是老照片修复项目实际使用中反复要动的参数。
4.1 划痕检测阈值与形态学处理参数
老照片最常见的退化就是划痕。这类项目普遍的做法是先检测出划痕区域,生成一个掩码(mask),再用网络专门修复掩码覆盖的区域。掩码质量直接决定修复效果,而掩码由两个参数控制:
python test.py --with_scratch --scratch_threshold 0.3 --scratch_dilation 3--scratch_threshold是划痕检测的灵敏度阈值,值越小检测出的划痕越多,但误检也越严重。--scratch_dilation是掩码膨胀的像素数,划痕检测出来通常是一条细线,直接交给修复网络容易留下不连续的痕迹,膨胀几像素让修复区域覆盖到划痕边缘的完整上下文。
我踩过最深的坑是阈值设太高,细划痕全部漏检。一张 1960 年代的合照,扫描后上面全是头发丝一样的细纹,用默认阈值 0.5 跑了二十分钟,输出图里细纹一条没少。把阈值降到 0.2,膨胀调到 5,重跑一次就干净了。
经验参数如下:
| 参数名 | 默认值 | 建议范围 | 适用场景 |
|---|---|---|---|
| scratch_threshold | 0.5 | 0.15~0.4 | 划痕浅且细,调低;划痕深且宽,调高 |
| scratch_dilation | 1 | 2~5 | 划痕细密时调大,覆盖完整上下文 |
| mask_blur | 0 | 3~7 | 掩码边缘软化,修复结果过渡更自然 |
4.2 人脸区域增强比例:让五官不再「糊成一团」
严重退化的老照片里,人脸往往是最难修复的部分。通用修复网络对纹理恢复效果好,但人脸这种有强先验结构的区域,直接修复的结果经常是「像一张脸但不像照片里的人」。这就需要用专门的人脸增强模型做二次修复,而控制这个过程的参数是修复区域的扩展比例:
python test.py --face_enhance --face_scale_ratio 0.8--face_scale_ratio表示在检测到人脸框的基础上向外扩展的比例,0.8 意味着面部区域向外扩 80% 的空间都交给增强网络处理。这个参数很有讲究,调太大,头发、衣领会被误改成不真实的样子;调太小,人脸边缘出现明显接缝。我用 0.6 到 0.9 之间的效果最稳,先跑一张看接缝在哪,再调整。
另外一个相关参数是--face_denoise_strength,控制人脸增强时的降噪强度。数值越大,人脸越平滑,但容易变成「塑料脸」。老照片修复的优先级永远是「真实感优先于精致感」,宁可留一点噪点,也不要磨皮过度。
4.3 色彩偏差修复:从 RGB 到 Lab 空间的校正
严重退化的照片普遍偏色,常见的有整体发黄、偏青、暗部发蓝。模型训练时见过的退化模式有限,输出结果常常带着原图的色彩偏向,需要后处理兜底。常见做法是先把结果转到 Lab 色彩空间,只对亮度通道做增强,色度通道保持不动:
# color_correction.py import cv2 import numpy as np def auto_white_balance(img, percent=5): """简单的白平衡校正:按百分比截断高低光""" lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB) L, A, B = cv2.split(lab) # 对 a、b 通道做直方图截断 for channel in [A, B]: lo = np.percentile(channel, percent) hi = np.percentile(channel, 100 - percent) np.clip(channel, lo, hi, out=channel) cv2.normalize(channel, channel, 0, 255, cv2.NORM_MINMAX) corrected = cv2.merge([L, A, B]) return cv2.cvtColor(corrected, cv2.COLOR_LAB2BGR) result = auto_white_balance(result, percent=2)percent控制色彩校正的强度,2% 表示把 a、b 通道最亮和最暗的 2% 像素截断后重新拉伸。为什么只在 Lab 空间处理色度通道而不动 L 通道?因为过度调整亮度会让修复区域暴露新的人工痕迹。这套方法不能替代模型的颜色修复能力,但能大幅度提升交付时的观感。
5. 老照片修复项目部署中的 5 个高频翻车点
写到这一章,你应该已经跑通了最小推理,也试过调参。接下来这段时间,你会在这里翻车无数次——我替你把最常踩的坑先填了。
5.1 现象:CUDA error: no kernel image is available for execution on the device
原因:显卡驱动支持的 CUDA 版本和 PyTorch 编译时的 CUDA 版本不匹配。老照片修复项目通常要求 CUDA 10.2 或 11.x,但你的驱动可能只支持更新的版本。
解决:先看驱动支持的最高 CUDA 版本:nvidia-smi右上角有个 CUDA Version,那个是驱动支持的最高版本。然后按这个版本重装 PyTorch,pip install torch torchvision --index-url https://download.pytorch.org/whl/cu121。不要盲目上最新版,兼容性第一。
5.2 现象:权重文件秒加载完,但输出全黑或全灰
原因:权重文件和模型结构不匹配,或者预处理/后处理的归一化方向反了。很多项目训练时数据归一化到 [-1, 1],推理脚本里却写成了 [0, 1],结果张量经过 Sigmoid 后直接饱和。
解决:先检查推理脚本里有没有 Sigmoid 或 Tanh,再确认图像读入后除以了 255 还是乘以 2 减 1。一个快排查方法:用 Python 打开输出张量,看数值分布是否在 [0, 1] 或 [-1, 1] 的合理区间内。如果分布正常但仍然全黑,那就是权重和模型结构对不上——重新下载正确的权重文件。
5.3 现象:zip 解压时报「不可预料的压缩文件末端」
原因:压缩包没有下载完整,或者网盘客户端限速导致文件被截断。这个 zip 一般会分成多个分卷,少一个都不行,但很多人只下了第一个就解压出几M然后放弃。
解决:核对文件大小是否和下载页面标注一致。用 7-Zip 打开压缩包时,如果能看到目录列表但解压中途报错,说明文件损坏,需要重新下载。用命令行unzip -t测试完整性:
unzip -t old_photo_repair.zip | tail -5显示 No errors detected 再继续使用。平时下载这些重量级 zip 我都是先放机械硬盘,用哈希校验工具看一遍,避免坏档读不出,再决定要不要动工。
5.4 现象:修复后的人脸出现「两张脸」的重影或诡异五官
原因:人脸检测模块把背景中的相似纹理误判为人脸,或者人脸增强网络在图块拼接处产生了伪影。这类翻车在多人合影照片上尤其明显。
解决:先关闭人脸增强,只看通用修复结果——如果正常,说明问题出在人脸检测/增强模块。调低--face_scale_ratio,缩小人脸区域范围。仍然不行就在人脸检测阶段加大置信度阈值,减少误检。这种合成伪影不细看很难发现,但交付给客户时一定会被放大看,所以在保存结果前要放大检查眼睛和嘴周区域。
5.5 现象:显存占用飙到接近 OOM,大批量修复时进程被杀
原因:一张 3000x2000 的扫描老照片直接送进模型,特征图尺寸极大,显存自然吃满。部分项目没有对大图做分块处理。
解决:看项目是否支持--tile_size参数,把长边分块处理,修复后再拼回去。多数老照片修复项目没做这个优化,你就得自己把图缩小——但缩小意味着清晰度下降,这是个两难。我常用的折中方案:长边缩到 1200px 修复,再用传统超分(比如 Real-ESRGAN 的思路)把结果放大回原始分辨率。效果比直接硬跑大图稳定得多,显存占用降低一半以上。
6. 进阶技巧:用合成退化数据微调,把模型「掰」向你的老照片风格
通用预训练模型对大多数退化照片有效,但它见过的是合成退化数据,和你手里这批五六十年历史的老照片的划痕分布、胶片颗粒特征完全不同。想要交付质量上一个台阶,一定绕不开微调这一步。
做微调不需要收集真实老照片——真实的老照片没有对应的「干净原图」,无法构成训练对。常见做法是合成退化:找一批当前的高清图片,人为叠加划痕、噪点、颜色偏移、模糊,模拟老照片的退化过程:
# synthesize_degradation.py import cv2 import numpy as np def degrade(img, scratch_density=0.3, noise_level=15): """模拟老照片退化:划痕 + 高斯噪声 + 褪色""" degraded = img.copy() h, w = degraded.shape[:2] # 随机划痕 num_scratch = int(h * w * scratch_density / 10000) for _ in range(num_scratch): x1, y1 = np.random.randint(0, w), np.random.randint(0, h) x2, y2 = np.random.randint(0, w), np.random.randint(0, h) cv2.line(degraded, (x1, y1), (x2, y2), (255, 255, 255), np.random.randint(1, 3)) # 高斯噪声 noise = np.random.normal(0, noise_level, degraded.shape) degraded = np.clip(degraded + noise, 0, 255).astype(np.uint8) # 整体褪色 degraded = cv2.addWeighted(degraded, 0.8, img, 0.2, 0) return degraded用三四百张高清人像和风景照,生成对应的退化版本,组成微调数据对。学习率要比预训练时小两个数量级,1e-5到5e-5之间,防止把模型原有的修复能力抹掉。20 到 40 个 epoch 就好,步数太多模型会过拟合到你合成的退化模式上,真实老照片的表现反而下降。
验证模型效果,不要只看眼睛。用 PSNR 和 SSIM 看像素级恢复质量,同时参考 LPIPS 评估感知相似度——后者更接近人眼对「像不像原图」的判断。具体到老照片修复场景,还得保留一定数量的真实老照片作为「验收集」:跑完一轮微调,挑几张没参与训练的图做肉眼对比,重点看不规则划痕和胶片颗粒的自然程度。这种判断没有客观指标,但它决定了客户验收时一句「这图修过吗」是惊喜还是质疑。
我每次接到批量老照片修复任务都会做一轮这样的微调,再把权重固定下来。之后批量跑图全程黑盒运行,除了随机种子引发的小幅波动,结果稳定可复现。等哪一天你发现模型的修复结果越来越不稳定,先回想是不是微调时学习率设太大把模型权重搞乱了。这个项目玩到现在,我最深的体会就是:老照片修复不是让模型「画」一部分内容骗过眼睛,而是让模型「知道」干净的旧照片长什么样,剩下的事都是参数和耐心的磨合。希望帮到你。
本文还有配套的精品资源,点击获取