一、选题背景与研究意义

1.1 研究背景

黑白图像作为一种重要的历史影像和艺术形式,在新闻档案、老照片修复、电影修复以及艺术创作等领域有着广泛的存在。然而,由于缺乏色彩信息,黑白图像在视觉表达力和信息传递方面存在一定局限。传统的图像上色方法主要依赖于人工手工上色,这种方法不仅耗时耗力,而且高度依赖操作者的艺术素养和经验,主观性强,难以实现大规模的批量处理。

随着计算机视觉和深度学习技术的迅速发展,自动图像着色技术逐渐成为研究热点。卷积神经网络(CNN)、生成对抗网络(GAN)、自注意力机制等深度学习方法已经在图像风格迁移、图像修复等领域取得了显著成果。近年来,研究人员发现,通过构建端到端的神经网络模型,可以直接学习从黑白图像到彩色图像的映射关系,从而实现自动化、智能化的图像着色。

本课题拟采用深度学习中的条件生成对抗网络(Conditional GAN, cGAN),结合U-Net生成器结构PatchGAN判别器结构,利用LAB色彩空间的分离特性,将亮度信息(L通道)作为网络输入,预测色彩信息(a、b通道),最终合成为彩色图像,从而在保持原有结构细节的同时,实现色彩还原。

1.2 研究意义

本研究具有以下几个方面的重要意义:

  1. 技术意义
  2. 采用深度学习方法,避免了传统手工上色方法中大量的人工干预和先验知识依赖,实现了端到端的自动化处理。相比于传统算法,本研究的模型能够更好地学习黑白图像的语义信息与色彩映射关系,具备较强的泛化能力。
  3. 应用意义
  • 历史档案修复:可以为老照片、历史纪录片、黑白电影提供高效的着色方案。
  • 艺术创作辅助:为插画、漫画等创作者提供自动化着色工具,提升生产效率。
  • 计算机视觉研究:作为图像到图像转换(Image-to-Image Translation)的一个重要分支,本研究可为风格迁移、图像增强等领域提供参考。
  1. 学术意义
  2. 本研究能够验证生成对抗网络在图像着色任务中的有效性,同时探索LAB色彩空间在网络训练中的优势,为后续研究提供经验与参考。

二、国内外研究现状

2.1 国外研究现状

国外在黑白图像自动着色方面起步较早,研究成果较为丰富。

  • 传统方法阶段:早期的研究多基于图像处理与手工特征设计。例如 Levin 等提出基于颜色传播(Color Propagation)的半自动上色方法,用户需提供少量颜色标记点,算法再通过图像梯度与颜色相似性传播颜色信息。
  • 深度学习阶段
  • Iizuka 等(2016)提出端到端的卷积神经网络结构,结合全局与局部特征,实现自动化着色。
  • Zhang 等(2016)在论文《Colorful Image Colorization》中首次提出利用分类任务的特征进行着色预测,并将预测问题转化为分类问题,效果显著。
  • Isola 等(2017)提出的Pix2Pix框架将条件GAN应用于图像到图像的转换任务,在着色任务中表现优异。

2.2 国内研究现状

国内学者也在黑白图像着色领域开展了大量研究工作:

  • 部分研究聚焦于传统的图像检索与颜色迁移方法,通过找到与黑白图像结构相似的彩色参考图,将色彩信息迁移到目标图像。
  • 近年来,随着深度学习的普及,国内研究团队更多采用生成对抗网络、残差网络等结构,并引入注意力机制来提升色彩的准确性与局部细节的还原效果。例如,清华大学的研究团队在GAN结构中引入自注意力模块,显著提升了复杂场景中的着色表现。

2.3 存在问题与研究方向

尽管已有研究取得了可观的成果,但仍存在以下问题:

  1. 色彩偏差:对于缺乏明显语义特征的区域,模型可能产生不真实或与常识不符的颜色。
  2. 细节丢失:在低分辨率或高压缩图像中,细节容易在着色过程中被忽略。
  3. 泛化不足:在与训练数据差异较大的图像上,模型的表现可能明显下降。

本课题将通过改进生成器结构、引入多任务损失函数以及采用数据增强手段来缓解上述问题。

三、研究目标与内容

3.1 研究目标

本课题旨在设计并实现一个基于深度学习的黑白图像自动着色系统,主要目标包括:

  1. 算法设计:基于条件生成对抗网络(cGAN)构建适用于着色任务的网络结构,采用U-Net生成器与PatchGAN判别器的组合,确保细节保留与整体色彩的自然性。
  2. 数据预处理:利用LAB色彩空间,将着色问题转化为亮度通道到色彩通道的映射学习,提高模型训练的稳定性与效果。
  3. 损失函数优化:设计结合L1重建损失与对抗损失的多任务损失函数,平衡图像结构保真度与色彩自然度。
  4. 系统实现:完成数据集准备、模型训练、推理接口与可视化界面的开发,实现端到端的自动化黑白图像着色。
  5. 效果评估:通过峰值信噪比(PSNR)、结构相似性(SSIM)、色彩丰富度等指标进行客观评价,并通过主观用户调查评估视觉效果。

3.2 研究内容

  1. 数据集构建与处理
  • 选用大规模自然图像数据集(如COCO、Places365)作为训练数据。
  • 将彩色图像转换为LAB色彩空间,提取L通道作为输入,ab通道作为预测目标。
  • 使用数据增强(随机翻转、缩放、裁剪)提升模型的泛化能力。
  1. 网络模型设计
  • 生成器:采用U-Net结构,利用编码器-解码器与跳跃连接(skip connections)保留细节信息。
  • 判别器:采用PatchGAN结构,判别器对局部图像块进行真伪判断,从而提升纹理细节的真实性。
  1. 损失函数与优化策略
  • L1重建损失:确保生成图像的色彩接近真实。
  • 对抗损失:引导生成器生成更逼真的色彩分布。
  • 优化器:使用Adam优化器,设置适当的学习率衰减策略。
  1. 系统开发与实现
  • 完成模型训练脚本(支持GPU加速)。
  • 实现推理模块,将训练好的模型应用于任意黑白图像的着色。
  • 开发用户交互界面,支持图像上传、着色结果显示与保存。
  1. 实验与评估
  • 在测试集上进行量化评估(PSNR、SSIM等)。
  • 对比现有公开模型(如Zhang等人的模型)进行效果对比。
  • 进行用户主观评价测试。

四、研究方法与技术路线

4.1 研究方法

  • 深度学习方法:采用条件生成对抗网络(cGAN),并在生成器中嵌入跳跃连接,提升细节还原效果。
  • 图像处理方法:利用LAB色彩空间分离亮度与色彩信息,减少网络学习难度。
  • 多任务损失优化:结合结构相似性、L1距离与对抗损失,使生成结果在客观指标与主观视觉上均具有较高质量。

4.2 技术路线

  1. 数据准备:采集并预处理彩色图像 → 转换为LAB空间 → 提取L通道与ab通道。
  2. 模型设计:搭建U-Net生成器与PatchGAN判别器 → 定义损失函数与优化策略。
  3. 模型训练:利用训练集进行迭代训练 → 保存最佳模型参数。
  4. 推理与测试:输入黑白图像 → 输出彩色结果 → 保存并显示。
  5. 评估与优化:通过客观指标与主观评价评估效果 → 调整网络结构或超参数优化模型。

流程示意:

数据集准备 → 网络结构设计 → 模型训练 → 模型评估 → 系统实现

五、预期成果

  1. 完成一个可运行的深度学习图像着色系统,支持黑白图像的自动着色。
  2. 输出训练好的模型文件,可供后续研究或应用直接使用。
  3. 撰写毕业论文,总结算法设计、实验结果与研究意义。
  4. 提供用户测试界面,方便展示项目成果。

六、参考文献

  1. Zhang R, Isola P, Efros A A. Colorful image colorization[C]//ECCV. 2016.
  2. Iizuka S, Simo-Serra E, Ishikawa H. Let there be color!: Joint end-to-end learning of global and local image priors for automatic image colorization with simultaneous classification[C]//ACM TOG. 2016.
  3. Isola P, Zhu J Y, Zhou T, Efros A A. Image-to-image translation with conditional adversarial networks[C]//CVPR. 2017.
  4. Levin A, Lischinski D, Weiss Y. Colorization using optimization[J]. ACM TOG, 2004.

核心设计部分(仅供学习参考)

1. 系统概述

本毕设项目实现了一个基于深度学习的黑白图像自动着色系统,采用卷积神经网络(CNN)与生成对抗网络(GAN)相结合的架构,能够将输入的黑白图像转换为逼真的彩色图像。系统包括完整的训练流水线和推理接口,可实现端到端的图像着色功能。

2. 算法设计

2.1 网络架构设计

采用Conditional GAN (cGAN)作为基础框架,生成器采用U-Net结构,判别器采用PatchGAN结构。

生成器 (U-Net)
import torch.nn as nn
import torch.nn.functional as F

class UNetGenerator(nn.Module):
    def __init__(self, input_channels=1, output_channels=2):
        super(UNetGenerator, self).__init__()
        
        # 下采样 (Encoder)
        self.down1 = nn.Sequential(
            nn.Conv2d(input_channels, 64, 4, 2, 1),
            nn.LeakyReLU(0.2, inplace=True)
        )
        self.down2 = nn.Sequential(
            nn.Conv2d(64, 128, 4, 2, 1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2, inplace=True)
        )
        self.down3 = nn.Sequential(
            nn.Conv2d(128, 256, 4, 2, 1),
            nn.BatchNorm2d(256),
            nn.LeakyReLU(0.2, inplace=True)
        )
        self.down4 = nn.Sequential(
            nn.Conv2d(256, 512, 4, 2, 1),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2, inplace=True)
        )
        self.down5 = nn.Sequential(
            nn.Conv2d(512, 512, 4, 2, 1),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2, inplace=True)
        )
        
        # 上采样 (Decoder)
        self.up1 = nn.Sequential(
            nn.ConvTranspose2d(512, 512, 4, 2, 1),
            nn.BatchNorm2d(512),
            nn.ReLU(inplace=True)
        )
        self.up2 = nn.Sequential(
            nn.ConvTranspose2d(1024, 256, 4, 2, 1),
            nn.BatchNorm2d(256),
            nn.ReLU(inplace=True)
        )
        self.up3 = nn.Sequential(
            nn.ConvTranspose2d(512, 128, 4, 2, 1),
            nn.BatchNorm2d(128),
            nn.ReLU(inplace=True)
        )
        self.up4 = nn.Sequential(
            nn.ConvTranspose2d(256, 64, 4, 2, 1),
            nn.BatchNorm2d(64),
            nn.ReLU(inplace=True)
        )
        self.up5 = nn.Sequential(
            nn.ConvTranspose2d(128, output_channels, 4, 2, 1),
            nn.Tanh()
        )
    
    def forward(self, x):
        # 下采样
        d1 = self.down1(x)        # 64x128x128
        d2 = self.down2(d1)       # 128x64x64
        d3 = self.down3(d2)       # 256x32x32
        d4 = self.down4(d3)       # 512x16x16
        d5 = self.down5(d4)       # 512x8x8
        
        # 上采样 + skip connections
        u1 = self.up1(d5)         # 512x16x16
        u1 = torch.cat([u1, d4], dim=1)  # 1024x16x16
        
        u2 = self.up2(u1)         # 256x32x32
        u2 = torch.cat([u2, d3], dim=1)  # 512x32x32
        
        u3 = self.up3(u2)         # 128x64x64
        u3 = torch.cat([u3, d2], dim=1)  # 256x64x64
        
        u4 = self.up4(u3)         # 64x128x128
        u4 = torch.cat([u4, d1], dim=1)  # 128x128x128
        
        u5 = self.up5(u4)         # 2x256x256
        
        return u5
判别器 (PatchGAN)
class Discriminator(nn.Module):
    def __init__(self, input_channels=3):
        super(Discriminator, self).__init__()
        
        self.model = nn.Sequential(
            nn.Conv2d(input_channels, 64, 4, 2, 1),
            nn.LeakyReLU(0.2, inplace=True),
            
            nn.Conv2d(64, 128, 4, 2, 1),
            nn.BatchNorm2d(128),
            nn.LeakyReLU(0.2, inplace=True),
            
            nn.Conv2d(128, 256, 4, 2, 1),
            nn.BatchNorm2d(256),
            nn.LeakyReLU(0.2, inplace=True),
            
            nn.Conv2d(256, 512, 4, 2, 1),
            nn.BatchNorm2d(512),
            nn.LeakyReLU(0.2, inplace=True),
            
            nn.Conv2d(512, 1, 4, 1, 1),
            nn.Sigmoid()
        )
    
    def forward(self, x):
        return self.model(x)

2.2 数据处理与准备

使用LAB色彩空间进行计算,L通道(亮度)作为输入,ab通道(色彩)作为输出目标。

import cv2
import numpy as np
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
from PIL import Image

class ColorizationDataset(Dataset):
    def __init__(self, image_paths, size=(256, 256)):
        self.image_paths = image_paths
        self.size = size
        self.transform = transforms.Compose([
            transforms.Resize(size),
            transforms.RandomHorizontalFlip(),
            transforms.ToTensor()
        ])
    
    def __len__(self):
        return len(self.image_paths)
    
    def __getitem__(self, idx):
        img = Image.open(self.image_paths[idx]).convert('RGB')
        img = self.transform(img)
        
        # 转换为LAB色彩空间
        img_lab = transforms.ToPILImage()(img).convert('LAB')
        img_lab = np.array(img_lab)
        
        # 归一化
        img_lab = img_lab.astype('float32')
        img_lab = transforms.ToTensor()(img_lab)
        
        L = img_lab[[0], ...] / 100.0  # L通道归一化到[0,1]
        ab = img_lab[1:, ...] / 128.0  # ab通道归一化到[-1,1]
        
        return L, ab

2.3 损失函数设计

结合多个损失函数以获得最佳着色效果:

import torch
from torch import nn

class ColorizationLoss(nn.Module):
    def __init__(self):
        super(ColorizationLoss, self).__init__()
        self.l1_loss = nn.L1Loss()
        self.mse_loss = nn.MSELoss()
        self.bce_loss = nn.BCELoss()
        
    def forward(self, pred_ab, target_ab, pred_real, target_real):
        # 重建损失(L1)
        l1_l = 100 * self.l1_loss(pred_ab, target_ab)
        
        # GAN损失
        gan_loss = self.bce_loss(pred_real, target_real)
        
        # 特征匹配损失(从判别器多层特征)
        fm_loss = 0.0
        
        total_loss = l1_l + gan_loss + fm_loss
        return total_loss, l1_l, gan_loss, fm_loss

2.4 训练过程

完整训练流程实现:

from torch.optim import Adam
from torchvision.utils import save_image

def train(dataloader, generator, discriminator, device, epochs=100):
    # 初始化模型
    G = generator().to(device)
    D = discriminator().to(device)
    
    # 优化器
    g_optim = Adam(G.parameters(), lr=0.0002, betas=(0.5, 0.999))
    d_optim = Adam(D.parameters(), lr=0.0002, betas=(0.5, 0.999))
    
    # 损失函数
    criterion = ColorizationLoss()
    
    for epoch in range(epochs):
        for i, (L, ab) in enumerate(dataloader):
            L = L.to(device)
            ab = ab.to(device)
            real = torch.ones(L.size(0), 1, 1, 1).to(device)
            fake = torch.zeros(L.size(0), 1, 1, 1).to(device)
            
            # 生成伪彩色图像
            fake_ab = G(L)
            
            # 训练判别器
            d_optim.zero_grad()
            
            # 真实图像判别
            real_images = torch.cat([L, ab], dim=1)
            pred_real = D(real_images.detach())
            d_real_loss = criterion(None, None, pred_real, real)[2]
            
            # 生成图像判别
            fake_images = torch.cat([L, fake_ab.detach()], dim=1)
            pred_fake = D(fake_images)
            d_fake_loss = criterion(None, None, pred_fake, fake)[2]
            
            d_loss = (d_real_loss + d_fake_loss) * 0.5
            d_loss.backward()
            d_optim.step()
            
            # 训练生成器
            g_optim.zero_grad()
            
            # 对抗训练
            fake_images = torch.cat([L, fake_ab], dim=1)
            pred_real = D(fake_images)
            g_loss, l1_loss, gan_loss, fm_loss = criterion(fake_ab, ab, pred_real, real)
            
            g_loss.backward()
            g_optim.step()
            
            # 打印训练信息
            if i % 50 == 0:
                print(f"[Epoch {epoch}/{epochs}] [Batch {i}/{len(dataloader)}] "
                      f"[D loss: {d_loss.item():.4f}] [G loss: {g_loss.item():.4f} "
                      f"(L1: {l1_loss.item():.4f}, GAN: {gan_loss.item():.4f})]")
                
                # 保存示例图像
                with torch.no_grad():
                    fake_rgb = lab_to_rgb(L[0], fake_ab[0])
                    save_image(fake_rgb, f"samples/sample_{epoch}_{i}.png", normalize=True)
    
    return G

2.5 色彩空间转换辅助函数

def lab_to_rgb(L, ab):
    """
    将Lab张量转换为RGB图像
    L: 1xHxW 张量, [0,1]
    ab: 2xHxW 张量, [-1,1]
    返回: 3xHxW 张量, [0,1]
    """
    # 反归一化
    L = L * 100.0
    ab = ab * 128.0
    
    # 合并通道
    Lab = torch.cat([L, ab], dim=0)
    Lab = Lab.permute(1, 2, 0).cpu().numpy()
    
    # Lab->RGB转换
    rgb = cv2.cvtColor(Lab, cv2.COLOR_LAB2RGB)
    rgb = transforms.ToTensor()(rgb)
    
    return rgb

2.6 推理接口实现

训练完成后,实现简易推理接口:

def colorize_image(generator, image_path, device, size=(256, 256)):
    # 加载图像
    img = Image.open(image_path).convert('RGB')
    img = img.resize(size)
    
    # 转换为LAB并提取L通道
    img_lab = np.array(img.convert('LAB')).astype('float32')
    L = img_lab[..., 0] / 100.0
    
    # 转换为张量
    L = torch.FloatTensor(L).unsqueeze(0).unsqueeze(0).to(device)
    
    # 生成ab通道
    with torch.no_grad():
        fake_ab = generator(L)
    
    # 转换为RGB
    fake_rgb = lab_to_rgb(L[0], fake_ab[0])
    
    return fake_rgb

# 使用示例
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
G = UNetGenerator().to(device)
G.load_state_dict(torch.load("weights/generator.pth"))
G.eval()

colorized = colorize_image(G, "test_images/old_photo.jpg", device)
save_image(colorized, "result.jpg", normalize=True)

3. 系统实现

3.1 环境配置

Python 3.8+
PyTorch 1.10+
torchvision
numpy
opencv-python
Pillow

3.2 训练流程

  1. 准备数据集(建议使用COCO或Places365数据集)
  2. 运行预处理脚本进行数据准备
  3. 执行训练脚本
python train.py --dataset path/to/dataset --epochs 100 --batch_size 16
  1. 训练过程中会自动保存模型权重和示例图像

3.3 推理测试

  1. 训练完成后,使用测试脚本进行图像着色
python test.py --model weights/generator.pth --input test.jpg --output result.jpg
  1. 也可以使用交互式界面进行实时测试

4. 评估指标

采用以下几种指标评估着色质量:

4.1 PSNR (峰值信噪比)

def calculate_psnr(target, output, max_val=1.0):
    mse = torch.mean((target - output) ** 2)
    if mse == 0:
        return float('inf')
    return 20 * torch.log10(max_val / torch.sqrt(mse))

4.2 SSIM (结构相似性)

from pytorch_msssim import ssim

def calculate_ssim(target, output):
    return ssim(target, output, data_range=1.0, size_average=True)

4.3 Colorfulness指标

def calculate_colorfulness(output):
    # 转换为RGB
    rgb = output.permute(1, 2, 0).cpu().numpy()
    
    # 分离通道
    R = rgb[:, :, 0]
    G = rgb[:, :, 1]
    B = rgb[:, :, 2]
    
    # 计算各向异性
    rg = np.abs(R - G)
    yb = np.abs(0.5 * (R + G) - B)
    
    # 计算均值与标准差
    rg_mean, rg_std = np.mean(rg), np.std(rg)
    yb_mean, yb_std = np.mean(yb), np.std(yb)
    
    # 计算色彩丰富度
    colorfulness = rg_std + yb_std + 0.3 * (rg_mean + yb_mean)
    return colorfulness

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐