
很多人学完深度学习后都会处在一个很尴尬的位置课程能听懂Notebook 能跑通Ultralytics 示例也能顺利框出几个目标但一旦脱离练习环境让自己独立搭一套能处理真实图片的 CV 系统就不知道该从哪里下手。YOLOv11 古籍上色项目正好用来补上这个鸿沟。它不是一个跑通 demo 就结束的小练习而是把目标检测、图像生成、数据标注、结果评估和模型部署放在同一条链路上。这套系统的核心思路是YOLOv11 负责从古籍扫描图中找出需要上色的插画、印章、文字等区域另一个上色模型负责生成颜色再通过后处理把彩色结果拼回原图。最终你得到的是一套可复现、可排查、可扩展的完整 CV 系统而不是一个只能对固定图片产生输出的单点函数。1. 为什么用 YOLOv11 做古籍上色先把系统架构想清楚1.1 YOLOv11 不会上色它负责的是版面元素检测看到“YOLOv11 古籍上色项目”这个标题很多人会误以为 YOLOv11 本身能把黑白古籍变成彩色古籍。这是一个需要立刻纠正的预期。YOLOv11 是一个目标检测模型它的输出是“图像里有哪些目标、每个目标在哪个位置、属于哪个类别”。例如它可以在一张古籍扫描图中标出插画区域坐标框x1,y1,x2,y2类别illustration文字区域坐标框类别text_region印章区域坐标框类别seal破损区域坐标框类别damage而上色是图像生成任务输入是一张灰度图或线稿图输出是一张彩色图。它属于像素级别的预测和“框出目标”完全不是同一类问题。所以在这套系统里YOLOv11 承担的是“版面理解”的作用。它先把复杂的古籍页面拆成不同区域再决定哪些区域需要送去上色模型哪些区域应该保持原样。没有这一步直接对整页图做上色文字会被染上奇怪颜色印章颜色会被覆盖破损区域也可能被错误加重。1.2 完整管线检测、抠图、上色、融合整套系统的主流程可以表达成下面这个链路古籍扫描图 | v 版面预处理转灰度、去噪、纠偏、切分大图 | v YOLOv11 元素检测 | ---- text_region / seal / damage 保留原样或进入修复分支 | ---- illustration / line_art 裁剪成小图 | v 上色模型生成彩色通道 | v Alpha 融合回原图 | v 输出彩色页面 可视化检测框把检测和上色分开而不是直接用一个端到端模型把整页图变成彩色图有三个实际好处。第一是错误隔离。如果整页直接上色文字和印章被污染时你很难判断是模型问题还是数据问题。拆分以后检测模型负责位置上色模型负责颜色哪个环节出错就排查哪个环节。第二是数据效率。目标检测模型只需要框的标注上色模型只需要成对的灰度图和彩色图。两类数据可以分别生产和迭代不需要做像素级的全图上色标注。第三是部署灵活。检测模型可以用 ONNX 或 TensorRT 导出上色模型也可以单独优化。两个模型之间用缓存、消息队列或者文件接口连接升级其中一个不会影响另一个。1.3 项目验收标准不是训练跑完而是整套流程能稳定输出玩具式学习的典型标志是“模型能训练完”就算完成。真实 CV 系统的验收标准要严格得多。这套古籍上色项目至少需要满足以下条件输入一张任意分辨率的古籍扫描图程序能自动完成检测、上色、拼接。输出结果包含两个文件彩色古籍页和带检测框的可视化图。检测模型有明确的指标记录例如 mAP50、mAP50-95。上色结果不会破坏文字区域也不会把印章染成蓝色或绿色。训练好的模型能导出成 ONNX 或 TensorRT 等部署格式。在 CPU 或 GPU 上都能跑通推理且预处理和后处理逻辑与训练时保持一致。只有把这些都串起来才算真正从“调包跑 demo”走向“搭建完整 CV 系统”。2. 环境准备与工程目录先让项目可复现再谈训练2.1 推荐环境这种多模型项目最怕两件事一是 PyTorch 和 CUDA 版本不匹配二是依赖版本在换机器后发生变化。所以第一步不是急着写训练代码而是把环境固定下来。推荐环境如下表所示项目推荐配置说明操作系统Windows 10/11、Ubuntu 20.04训练服务器优先 LinuxPython3.10对 PyTorch 和 ultralytics 兼容较好PyTorch2.x需要按本机 CUDA 版本安装GPU8GB 显存起步4GB 可以调通代码不适合完整训练ultralytics最新稳定版提供 YOLOv11 训练和导出接口ONNX Runtime最新稳定版用于导出后的推理验证创建环境时建议先使用 conda 或 venv 隔离项目依赖conda create -n book-cv python3.10 -y conda activate book-cv pip install ultralytics如果不想使用 conda也可以用 Python 自带的 venvpython -m venv book-cv source book-cv/bin/activate # Windows 下是 book-cv\Scripts\activate pip install ultralytics安装 PyTorch 时不要直接用默认命令覆盖已有环境。先检查本机 GPU 驱动和 CUDA 情况nvidia-smi python -c import torch; print(torch.__version__, torch.cuda.is_available())如果 CUDA 不可用说明 PyTorch 装成了 CPU 版本或者驱动版本太低。训练虽然可以用 CPU但速度会非常慢调试代码阶段可以接受正式训练不建议。2.2 项目目录结构不要把所有脚本堆在一个文件里。下面这个目录结构适合“检测 上色”的复合项目cv_ancient_book/ ├── configs/ │ ├── data.yaml │ ├── train_det.yaml │ └── deploy.yaml ├── data/ │ ├── images/ │ │ ├── train/ │ │ └── val/ │ ├── labels/ │ │ ├── train/ │ │ └── val/ │ └── color_pairs/ │ ├── train/ │ └── val/ ├── scripts/ │ ├── prepare_data.py │ ├── train_det.py │ ├── train_color.py │ └── run_pipeline.py ├── models/ │ ├── detection/ │ └── colorizer/ ├── runs/ └── app/runs目录保存训练日志和权重models目录保存最终导出的部署文件configs目录集中管理数据路径和超参数。这样换机器时只需要复制项目目录重新安装依赖再调整配置里的路径就能恢复训练环境。2.3 数据配置data.yamlYOLOv11 训练时依赖一个配置文件告诉框架“图片在哪里、标签在哪里、类别叫什么”。在configs/data.yaml中写入path: ../data train: images/train val: images/val names: 0: illustration 1: line_art 2: text_region 3: seal 4: damage这里path是相对configs目录的路径所以实际数据目录是../data。names的顺序必须和标注文件里的类别 ID 一一对应。如果后面新增类别一定要重新检查和修改所有标签否则训练时类别错乱会出现很难排查的指标异常。3. 数据准备与标注YOLOv11 的输入决定训练上限3.1 类别设计先少后多古籍扫描图非常复杂一页上可能有插画、手写批注、印刷文字、印章、虫蛀破损、折痕、噪点。如果一开始就把类别设计得很细标注成本会急剧上升模型也容易混淆。建议第一版只保留四个类别类别含义在系统中的角色illustration插画区域可能已经有部分颜色上色模型的重点候选line_art线稿区域黑白线条上色模型的重点候选text_region文字区块不送上色模型避免文字被染色seal印章区域保持红色特征不强行修改破损区域可以放在第二个版本再加入。第一版先把“哪些区域要上色”和“哪些区域不能动”分开系统就能跑起来。3.2 标注格式YOLO 的 txt 标签使用 LabelImg、CVAT 或 X-AnyLabeling 标注后导出为 YOLO 格式。每张图片对应一个同名 txt 文件每一行表示一个目标0 0.498 0.512 0.216 0.364 2 0.125 0.080 0.150 0.040这一行数据的含义是类别 ID、目标中心点的 x 坐标、目标中心点的 y 坐标、目标宽度、目标高度。所有数值都归一化到 0 到 1 之间除以图像宽高。标注完成后要检查标签文件是否和图片同名。坐标是否都在 0 到 1 范围内。是否存在空标签文件。同一张图是否有重复标注。一个常见错误是使用绝对坐标导出YOLO 训练时坐标超出边界导致 loss 异常。标注工具一般会提供输出格式选择导出时务必选 YOLO。3.3 上色模型的成对数据准备上色模型不能用“灰度图 - 彩色图”这种简单映射来训练还要考虑颜色空间的表示。经典做法是使用 Lab 颜色空间L 通道表示亮度作为模型输入。ab 通道表示颜色信息作为模型预测目标。准备数据时把一张彩色古籍图拆成两个文件import cv2 img cv2.imread(data/color_pairs/raw/001_color.jpg) lab cv2.cvtColor(img, cv2.COLOR_BGR2LAB) l_channel lab[:, :, 0] ab_channels lab[:, :, 1:] cv2.imwrite(data/color_pairs/train/001_gray.jpg, l_channel) cv2.imwrite(data/color_pairs/train/001_ab.npy, ab_channels)注意001_gray.jpg是亮度图不是把原图用cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)转出来的普通灰度图。两者虽然看起来差不多但它们和 ab 通道的对应关系不同会影响训练损失的计算。3.4 数据划分按页面拆分不按随机裁剪拆分检测数据和上色数据都要划分训练集、验证集。最稳妥的方式是按“页面”划分而不是按“随机裁剪块”划分。如果同一页的大图既出现在训练集又出现在验证集模型很容易通过背景和纹理“记住”图片导致验证指标虚高。真实项目里建议用脚本统计所有页面然后按页面名划分python scripts/prepare_data.py --data data --val-ratio 0.2 --seed 42划分完成后打印训练集和验证集的类别分布。如果某个类别只出现在验证集没有出现在训练集这类目标基本不可能被检测出来。4. 训练 YOLOv11 元素检测模型用参数理解替换黑盒运行4.1 先用最小数据集跑通再全量训练很多人在正式训练前没有验证“代码能跑”就直接投入全量数据。一旦报错排查成本非常高。建议先用少量图片和少量轮次跑通整个流程cd cv_ancient_book yolo detect train \ modelyolo11n.pt \ dataconfigs/data.yaml \ epochs3 \ imgsz640 \ batch4 \ device0这个命令会下载 YOLOv11 的预训练权重并基于自定义数据集微调。如果数据集只有几十张图也会很快跑完。跑通后再进入正式训练yolo detect train \ modelyolo11n.pt \ dataconfigs/data.yaml \ epochs100 \ imgsz640 \ batch16 \ device0训练结束后最优权重通常保存在runs/detect/train/weights/best.pt验证模型时运行yolo detect val \ modelruns/detect/train/weights/best.pt \ dataconfigs/data.yaml这里要注意modelyolo11n.pt是官方预训练模型类别是 COCO 的 80 类。加载后会在自定义数据集上微调最终输出类别数由data.yaml决定不需要手工改模型结构。4.2 关键训练参数YOLO 系列训练参数很多但新手不需要全部理解先掌握下面几个就够参数常见值影响epochs100训练总轮数。不是越大越好要观察验证集是否过拟合imgsz640训练输入尺寸。越大越能检测小目标但显存占用也越高batch8/16/32批大小。越大梯度越平滑但显存压力越大lr00.01初始学习率。过高容易发散过低收敛慢patience50验证集指标连续多少轮不提升就提前停止device0GPU 编号。CPU 推理用cpu训练不建议用 CPUseed42固定随机种子让实验可复现如果显存溢出优先降低batch其次降低imgsz。如果降低后精度下降明显再考虑使用自动混合精度 AMP。Ultralytics 默认会启用 AMP因此大部分情况下不需要额外设置。4.3 训练结果怎么看训练结束后打开runs/detect/train/目录重点看这几个文件results.png包含 loss 曲线和 mAP 曲线。confusion_matrix.png每类目标的预测混淆情况。val_batch_pred.jpg验证集图片上的预测框可视化。weights/best.pt验证集指标最好的权重。判断训练是否正常不要只看 loss 是否下降。还要看训练 loss 下降验证 loss 回升说明过拟合。mAP50 很高但 mAP50-95 很低说明框的位置可能不够准确。某个类别的 Recall 很低说明漏检严重。val_batch_pred.jpg中检测框明显偏移说明标签或后处理有误。在古籍扫描图上大插画一般容易检测小印章和细线稿容易漏检。因此不能只看整体 mAP要单独看每个类别的精确率和召回率。4.4 小目标和漏检怎么优化古籍扫描图分辨率经常是几千乘几千而训练时为了节省显存常常被压缩到 640 乘 640。原本很小的印章和线稿经过缩放后可能只有几个像素模型自然检测不到。可以从下面几个方向优化提高imgsz例如imgsz1280。把大图切成 640 或 1024 的 patch分别送入模型再把框坐标映射回原图。使用 SAHISlicing Aided Hyper Inference这类切片推理库。降低推理时的conf阈值从 0.25 降到 0.1 或 0.05观察漏检目标是否出现。检查标签是否漏标。小目标如果本身没有标注模型不可能学会。最有效的方法通常是“切图 提高输入尺寸”。但切图会带来重叠区域的重复检测需要做 NMS 合并不能直接把结果叠加回原图。5. 把检测结果接到上色模型组成一套可运行管线5.1 检测推理与结果保存训练完成 YOLOv11 后先写一个独立的检测脚本确认输出格式。下面这段代码读取一张古籍扫描图解析所有检测框并保存标框图from ultralytics import YOLO import cv2 model YOLO(runs/detect/train/weights/best.pt) image_path data/samples/page_001.jpg image cv2.imread(image_path) results model.predict( sourceimage, conf0.25, iou0.45, imgsz640, devicecpu ) annotated image.copy() for r in results: for i, box in enumerate(r.boxes): cls_id int(box.cls[0]) conf float(box.conf[0]) x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) label model.names[cls_id] print(label, round(conf, 3), x1, y1, x2, y2) if label in [illustration, line_art]: color (0, 255, 0) else: color (0, 0, 255) cv2.rectangle(annotated, (x1, y1), (x2, y2), color, 2) cv2.putText( annotated, f{label} {conf:.2f}, (x1, max(0, y1 - 6)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2 ) cv2.imwrite(output/detection.jpg, annotated)这里有一个容易被忽略的点YOLO 的默认坐标是浮点数坐标值可能超出原图边界。绘制前必须用int转换并且用max(0, y1 - 6)防止文字画在图片外面。复杂项目里还要对框做边界裁剪。5.2 上色模型的接口设计上色模型不一定要和 YOLOv11 写在同一个类里。更好的做法是定义统一的Colorizer接口内部再决定使用 ONNX、PyTorch 还是 TensorRT。下面是一个基于 ONNX Runtime 的最小接口import cv2 import numpy as np import onnxruntime as ort class Colorizer: def __init__(self, onnx_path): providers [CUDAExecutionProvider, CPUExecutionProvider] self.session ort.InferenceSession(onnx_path, providersproviders) def preprocess(self, bgr_crop): gray cv2.cvtColor(bgr_crop, cv2.COLOR_BGR2GRAY) gray cv2.resize(gray, (256, 256)) gray gray.astype(np.float32) / 255.0 return np.expand_dims(gray, axis(0, 1)) def postprocess(self, output, target_size): ab output[0] # shape [2, 256, 256] ab np.transpose(ab, (1, 2, 0)) ab cv2.resize(ab, target_size) return ab def colorize(self, bgr_crop): input_tensor self.preprocess(bgr_crop) output self.session.run(None, {input: input_tensor})[0] target_size