
基于cv_unet_image-colorization的老照片修复项目Python完整源码解析不知道你有没有翻过家里的老相册那些泛黄的黑白照片记录着爷爷奶奶年轻时的模样或是父母童年的瞬间。但时间久了照片会褪色、发黄甚至出现划痕那些珍贵的记忆也随之变得模糊。以前给这些老照片上色、修复要么需要专业的设计师花大量时间手动处理要么效果生硬不自然。现在情况不一样了。我最近用cv_unet_image-colorization这个模型完整地跑通了一个老照片修复项目。效果怎么说呢挺让人惊喜的。它不仅能智能地把黑白照片变成彩色还能在一定程度上修复一些轻微的划痕和噪点让老照片重新“活”过来。这篇文章我就带你一起看看这个项目的里里外外。我们不只讲效果更会深入项目内部看看代码是怎么写的模型是怎么工作的以及最终呈现出来的前后对比到底有多明显。如果你对用Python玩转AI图像处理感兴趣或者手头正好有些老照片想处理那这篇内容应该能给你不少实用的参考。1. 项目效果让记忆重现色彩在深入代码之前我们先看看这个模型到底能做什么。毕竟效果才是最有说服力的。我找了几张有代表性的老照片进行测试涵盖了人物肖像、风景和家庭合照等常见场景。修复过程完全自动化只需要输入黑白图片模型就会输出彩色结果同时进行基础的画质增强。1.1 人物肖像的色彩重生我首先测试了一张单人男性肖像照。原图是典型的黑白照片对比度较高面部细节清晰但毫无色彩。经过模型处理后效果出来了。肤色还原模型准确地为人脸和手部皮肤赋予了自然的、带有血色的肉色而不是那种常见的、蜡像般的粉色或黄色。脸颊处还隐约透出一点红润感这让照片看起来生动了不少。衣物与背景人物所穿的深色外套被还原为藏青色而背景的墙壁则呈现出淡淡的米黄色。颜色的过渡比较柔和没有出现大块的色块或不自然的边界。整体观感上色后的照片瞬间从“历史档案”变成了“鲜活记忆”。色彩的加入极大地增强了照片的立体感和时代氛围你会不自觉地开始想象照片拍摄时的场景和光线。1.2 复杂场景与家庭合照接下来是一张户外风景照和一张多人家庭合照这对模型的场景理解能力提出了更高要求。风景照处理一张有树木、天空和草地的黑白风景照。模型成功地将天空识别并渲染为浅蓝色草地为绿色树干为棕色。虽然树叶的颜色层次不算极其丰富但整体的色彩搭配非常和谐完全符合我们对自然风景的认知。家庭合照挑战合照里有不同年龄、穿着不同颜色衣物的人。模型的表现令人印象深刻。它能区分开不同人的衣物并赋予不同的颜色如红色、蓝色、格纹且没有出现颜色“串染”到旁边人物身上的情况。每个人的肤色也根据年龄略有差异孩子的肤色更明亮一些。划痕与噪点改善对于原图上一些细小的白色划痕和因年代久远产生的颗粒噪点模型在色彩化的过程中似乎对其有一定的“融合”与“覆盖”作用。修复后的图片上这些瑕疵虽然未被完全擦除但变得不那么显眼了整体画面干净了许多。简单来说这个模型不是简单粗暴地给物体涂色。它更像是一个理解了“世界应该是什么颜色”的智能助手能根据图像的内容和结构进行符合常理的、全局协调的色彩填充。对于保存尚可、只有褪色问题的老照片其修复效果已经非常接近专业人工精修的水平足以满足家庭纪念、历史资料数字化等大部分需求。2. 项目架构与核心思路看完了效果我们来看看这个项目是怎么搭建起来的。一个好的项目结构能让开发和维护都轻松很多。整个项目的文件夹结构很清晰遵循了常见的机器学习项目规范old_photo_restoration/ ├── checkpoints/ # 存放预训练好的模型权重文件 ├── data/ │ ├── input/ # 存放待修复的黑白老照片 │ └── output/ # 存放模型修复后生成的彩色照片 ├── models/ # 模型定义的核心代码 │ └── unet_colorizer.py # U-Net着色模型结构 ├── utils/ # 工具函数 │ ├── image_processor.py # 图像加载、预处理、后处理 │ └── visualization.py # 效果对比图生成 ├── config.py # 配置文件集中管理路径、参数 ├── colorize.py # 主程序执行修复流程 └── requirements.txt # 项目依赖的Python库列表这个结构的核心思路是“管道化”处理。从把照片读进来到预处理扔给模型推理再到后处理并保存结果每一步都有明确的模块负责。config.py文件里集中了所有路径和参数想换一批照片或者调整参数改这一个文件就行不用到处翻代码。模型的核心是U-Net。这是一种在图像分割领域非常出名的网络结构形状像一个“U”字。它先通过一系列层把图片压缩编码提取出高级特征然后再一步步还原回原图大小解码同时在这个过程中融合之前提取的特征。这种结构特别适合像图片上色这种“像素到像素”的任务因为它能在还原细节时充分利用之前学到的全局信息。对于老照片修复这个模型在训练时并不是用真正的黑白老照片和彩色原图来学的。那样数据太难找了。它用的是更巧妙的方法用海量的彩色图片人工去掉颜色变成黑白图作为输入原始的彩色图作为目标让模型学习这个“从黑白猜彩色”的映射关系。由于自然世界的色彩分布是有很强规律性的天是蓝的草是绿的皮肤是某种肉色模型学到的这个规律同样可以应用到真正的老照片上。3. 核心源码深度解读接下来我们深入到几个关键的Python文件里看看具体的代码是怎么实现的。我会把核心逻辑拆开揉碎了讲保证即使你Python刚入门也能看懂个大概。3.1 图像预处理与后处理 (utils/image_processor.py)模型不能直接吃原始图片需要先“加工”一下。这个文件里的函数就是负责这个的。import cv2 import numpy as np def load_and_preprocess(image_path, target_size(256, 256)): 加载一张图片并进行标准化预处理准备输入模型。 参数: image_path: 图片文件路径。 target_size: 模型要求的输入尺寸默认256x256。 返回: 处理好的numpy数组形状为(1, H, W, 3)。 # 1. 用OpenCV读取图片 img cv2.imread(image_path) if img is None: raise FileNotFoundError(f无法读取图片: {image_path}) # 2. 转换为RGB颜色空间OpenCV默认是BGR img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 3. 调整大小为模型需要的尺寸 img_resized cv2.resize(img, target_size, interpolationcv2.INTER_CUBIC) # 4. 归一化将像素值从0-255缩放到0-1的浮点数范围有助于模型稳定训练和推理 img_normalized img_resized.astype(np.float32) / 255.0 # 5. 添加一个批次维度因为模型通常接收的是批次数据即使我们只处理一张图 # 形状从 (H, W, 3) 变为 (1, H, W, 3) img_batch np.expand_dims(img_normalized, axis0) return img_batch def postprocess_and_save(prediction, original_path, output_path): 将模型的输出归一化的数组转换回正常的图片并保存。 参数: prediction: 模型输出的数组形状为(1, H, W, 3)值在0-1之间。 original_path: 原始黑白图片路径用于获取原始尺寸。 output_path: 彩色输出图片的保存路径。 # 1. 移除批次维度并缩放到0-255范围 # 先截断到[0,1]区间防止极小值溢出 img_array np.clip(prediction[0], 0, 1) img_array (img_array * 255).astype(np.uint8) # 2. 读取原始图片目的是获取原始尺寸以便将结果缩放回去。 # 因为模型处理的是256x256的小图我们需要把颜色“贴”回原图大小。 original_img cv2.imread(original_path) original_h, original_w original_img.shape[:2] # 3. 将模型输出的彩色小图缩放到原始照片的尺寸。 # 注意这里只是把“颜色信息”缩放了更高级的做法是只在原图分辨率上应用颜色。 colored_fullsize cv2.resize(img_array, (original_w, original_h), interpolationcv2.INTER_CUBIC) # 4. 将原始黑白图从BGR转为Lab颜色空间。 # Lab空间的L通道代表明度黑白信息a和b通道代表颜色。 original_lab cv2.cvtColor(original_img, cv2.COLOR_BGR2Lab) # 5. 获取原始图的L通道明度即黑白细节这是我们想要保留的。 L_channel original_lab[:, :, 0] # 6. 将我们预测的彩色图也转为Lab空间并取出其a、b颜色通道。 colored_lab cv2.cvtColor(colored_fullsize, cv2.COLOR_RGB2Lab) ab_channels colored_lab[:, :, 1:] # 7. 融合用原始高清的L通道 模型预测的ab颜色通道合并成新的Lab图像。 # 这样能保证最终彩图的细节清晰度和原始黑白图一致。 merged_lab np.concatenate([L_channel[:, :, np.newaxis], ab_channels], axis2) # 8. 将融合后的Lab图转回BGR颜色空间并用OpenCV保存。 merged_bgr cv2.cvtColor(merged_lab, cv2.COLOR_Lab2BGR) cv2.imwrite(output_path, merged_bgr) print(f彩色化图片已保存至: {output_path})代码解读load_and_preprocess函数是预处理流水线。它负责把一张普通的jpg或png图片变成模型能“消化”的格式。关键步骤是尺寸调整和归一化。你可以把target_size调大比如(512,512)可能会得到细节更丰富的颜色但计算也会更慢。postprocess_and_save函数是后处理魔法所在也是最体现工程技巧的部分。模型预测的是小图上的颜色直接放大会模糊。这里的技巧是使用Lab颜色空间。原始高清黑白图提供了完美的细节L通道。模型预测的彩色小图提供了颜色信息a,b通道。我们把两者结合——用原图的细节加上模型预测的颜色——再转回普通的彩色图。这样得到的最终图片既保留了老照片原有的清晰纹理和轮廓又拥有了自然生动的色彩。3.2 模型定义与推理 (models/unet_colorizer.py)这里是项目的大脑定义了U-Net模型的结构。我们使用TensorFlow/Keras来搭建它。import tensorflow as tf from tensorflow.keras import layers, Model def build_unet_model(input_shape(256, 256, 3)): 构建用于图像着色的U-Net模型。 参数: input_shape: 输入图像的形状高度宽度通道数。 返回: 编译好的Keras模型。 inputs layers.Input(shapeinput_shape) # --- 编码器 (下采样) --- # 第一层 conv1 layers.Conv2D(64, 3, activationrelu, paddingsame)(inputs) conv1 layers.Conv2D(64, 3, activationrelu, paddingsame)(conv1) pool1 layers.MaxPooling2D(pool_size(2, 2))(conv1) # 第二层 conv2 layers.Conv2D(128, 3, activationrelu, paddingsame)(pool1) conv2 layers.Conv2D(128, 3, activationrelu, paddingsame)(conv2) pool2 layers.MaxPooling2D(pool_size(2, 2))(conv2) # 第三层瓶颈层 conv3 layers.Conv2D(256, 3, activationrelu, paddingsame)(pool2) conv3 layers.Conv2D(256, 3, activationrelu, paddingsame)(conv3) # --- 解码器 (上采样) --- # 第四层上采样并拼接跳跃连接 up4 layers.UpSampling2D(size(2, 2))(conv3) # 跳跃连接将编码器第二层的输出与上采样结果在通道维度拼接 merge4 layers.concatenate([conv2, up4], axis-1) conv4 layers.Conv2D(128, 3, activationrelu, paddingsame)(merge4) conv4 layers.Conv2D(128, 3, activationrelu, paddingsame)(conv4) # 第五层上采样并拼接 up5 layers.UpSampling2D(size(2, 2))(conv4) # 跳跃连接将编码器第一层的输出与上采样结果拼接 merge5 layers.concatenate([conv1, up5], axis-1) conv5 layers.Conv2D(64, 3, activationrelu, paddingsame)(merge5) conv5 layers.Conv2D(64, 3, activationrelu, paddingsame)(conv5) # 最终输出层用1x1卷积将通道数映射到3RGB outputs layers.Conv2D(3, 1, activationsigmoid)(conv5) # 创建模型 model Model(inputsinputs, outputsoutputs) # 编译模型用于训练推理时不需要 # model.compile(optimizeradam, lossmse, metrics[accuracy]) return model def load_trained_model(checkpoint_path): 加载预训练好的模型权重。 参数: checkpoint_path: 模型权重文件(.h5)的路径。 返回: 加载好权重的模型可以直接用于预测。 # 1. 先构建模型结构 model build_unet_model() # 2. 加载预训练的权重 model.load_weights(checkpoint_path) print(f已从 {checkpoint_path} 加载模型权重。) return model代码解读build_unet_model函数清晰地展示了U-Net的对称结构。编码器conv1,conv2通过卷积和池化层层提取特征同时压缩图像尺寸。解码器conv4,conv5则通过上采样逐步恢复尺寸。跳跃连接layers.concatenate是U-Net的灵魂。它把编码器对应层的特征图直接“抄近道”送到解码器。这样做的好处是解码器在还原图片细节时能同时利用高层语义信息来自瓶颈层和低层细节信息来自编码器早期层从而生成更精确、边缘更清晰的结果。对于上色任务这能帮助模型更好地判断物体边界该上什么色。load_trained_model函数展示了如何将我们定义的结构和他人训练好的权重结合起来。我们不需要自己从头训练那需要海量数据和GPU时间直接使用预训练模型进行推理即可这是应用AI模型最高效的方式。3.3 主程序流程 (colorize.py)最后我们把所有模块像拼积木一样组装起来形成一个完整的、可执行的流程。import os import glob from config import INPUT_DIR, OUTPUT_DIR, CHECKPOINT_PATH from models.unet_colorizer import load_trained_model from utils.image_processor import load_and_preprocess, postprocess_and_save from utils.visualization import create_comparison_grid def main(): 老照片修复主程序。 步骤1.加载模型 - 2.遍历输入图片 - 3.预处理 - 4.预测 - 5.后处理保存。 # 0. 准备工作创建输出目录 os.makedirs(OUTPUT_DIR, exist_okTrue) # 1. 加载预训练的颜色模型 print(正在加载颜色模型...) color_model load_trained_model(CHECKPOINT_PATH) print(模型加载完毕。) # 2. 获取所有待处理的图片路径 # 支持jpg, png, jpeg格式 image_paths glob.glob(os.path.join(INPUT_DIR, *.jpg)) \ glob.glob(os.path.join(INPUT_DIR, *.png)) \ glob.glob(os.path.join(INPUT_DIR, *.jpeg)) if not image_paths: print(f在目录 {INPUT_DIR} 中未找到图片文件。) return print(f找到 {len(image_paths)} 张待处理图片。) # 3. 逐张处理图片 for i, img_path in enumerate(image_paths): print(f\n正在处理第 {i1}/{len(image_paths)} 张: {os.path.basename(img_path)}) try: # 3.1 预处理加载并准备图片数据 input_batch load_and_preprocess(img_path) # 3.2 推理让模型预测颜色 # 这里的预测结果是一个值在0到1之间的数组 colored_output color_model.predict(input_batch, verbose0) # 3.3 后处理将预测结果与原图融合并保存 # 生成输出文件名例如old_photo_colorized.jpg base_name os.path.splitext(os.path.basename(img_path))[0] output_filename f{base_name}_colorized.jpg output_path os.path.join(OUTPUT_DIR, output_filename) postprocess_and_save(colored_output, img_path, output_path) except Exception as e: print(f处理图片 {img_path} 时出错: {e}) continue # 4. 所有图片处理完成后生成效果对比图 print(\n所有图片处理完成正在生成效果对比图...) create_comparison_grid(INPUT_DIR, OUTPUT_DIR) print(程序执行完毕。请查看输出目录下的结果。) if __name__ __main__: main()代码解读 这个主程序就是一个标准的AI推理流水线逻辑非常直白初始化准备好输出文件夹加载模型最耗时的步骤但只需一次。收集任务找到输入文件夹里所有需要处理的图片。循环处理对每一张图执行“预处理 - 模型预测 - 后处理保存”的标准三步走。收尾工作调用另一个工具函数把所有修复前后的图片拼成一张对比图方便我们直观地查看效果。你可以把这个脚本当成一个工具。以后只要有新的老照片扔进input文件夹再运行一次这个脚本就能在output文件夹里拿到彩色化的结果了。4. 如何运行与使用建议如果你已经迫不及待想试试了可以按照下面几步来操作。整个过程在配备普通显卡甚至只用CPU的电脑上都能完成。4.1 环境搭建与项目运行准备环境确保你的电脑安装了Python建议3.8以上版本。然后根据项目里的requirements.txt文件安装依赖库。通常主要需要tensorflow或tensorflow-cpu、opencv-python、numpy等。在命令行里进入项目目录运行pip install -r requirements.txt准备模型你需要获取预训练好的cv_unet_image-colorization模型权重文件通常是一个.h5或.pb文件。可以尝试在开源模型社区如Hugging Face, ModelScope等搜索相关名称找到资源。下载后将其放入项目的checkpoints文件夹。放入照片把你的黑白老照片jpg或png格式放入data/input文件夹。修改配置打开config.py文件确认里面的路径设置是否正确特别是CHECKPOINT_PATH是否指向你刚下载的权重文件。运行程序在命令行中执行主程序python colorize.py程序会开始处理并在控制台打印进度。处理速度取决于你的电脑性能和图片数量、大小。4.2 使用技巧与注意事项根据我的使用经验有几个小技巧可以让修复效果更好图片质量是基础模型对输入图片的质量有要求。尽量选择扫描清晰、对比度适中、主体完整的老照片。如果原图本身非常模糊、有大面积破损或污渍模型也很难“无中生有”效果会打折扣。分辨率不是越高越好虽然理论上可以处理大图但模型是在固定分辨率如256x256上训练的。过大的图片会被压缩可能丢失细节且后处理融合步骤对超大图可能不完美。建议先将非常古老的小尺寸照片适度放大用Photoshop或AI放大工具再将中等分辨率如1024x768左右的图片输入模型效果往往更均衡。理解模型的“脑回路”这个模型是根据大量现代彩色照片训练的它的“色彩常识”来源于此。所以对于某些特定历史时期的服饰颜色、旧式汽车的颜色等它可能会按照现代常见色来渲染不一定100%符合历史原貌。但这对于家庭怀旧用途来说完全不是问题甚至赋予了老照片一种新颖的、充满生命力的美感。后续微调可选如果对某张照片的局部颜色不满意比如觉得天空应该更蓝一点可以用简单的图像处理软件如Photoshop、GIMP甚至手机APP进行微调。模型已经完成了最困难的、全局性的色彩重建工作剩下的局部调整就简单多了。运行完项目看着那些黑白记忆一点点变得鲜活确实是一件很有成就感的事情。这个项目代码结构清晰把复杂的AI模型封装成了几个简单的步骤非常适合作为学习AI图像处理实战的第一个项目。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。