DeOldify上色结果后处理:OpenCV自动白平衡校正与色彩风格迁移

1. 引言

你有没有遇到过这样的情况?用DeOldify给老照片上色后,虽然照片从黑白变成了彩色,但总觉得颜色有点"怪怪的"——要么整体偏黄,要么肤色不自然,要么色彩对比度不够理想。这其实很正常,因为AI模型在还原色彩时,虽然能识别物体并赋予合理的颜色,但在色彩平衡和风格统一上,有时候还需要我们"手动优化"一下。

今天我要分享的就是这样一个实用技巧:如何用OpenCV对DeOldify的上色结果进行后处理,让老照片的色彩更加自然、更加生动。你不需要懂复杂的图像处理理论,也不需要写很多代码,只需要跟着我一步步操作,就能让那些珍贵的黑白老照片焕发出更加迷人的光彩。

这篇文章会带你了解两个核心的后处理技术:自动白平衡校正色彩风格迁移。前者能解决照片偏色问题,让色彩回归自然;后者能让你的照片拥有特定的艺术风格,比如复古胶片感、电影色调等。最重要的是,我会提供完整的Python代码,你可以直接复制使用,就像给照片加了个"美颜滤镜"一样简单。

2. 为什么需要后处理?

2.1 DeOldify上色的局限性

DeOldify确实是个很棒的图像上色工具,它基于U-Net深度学习模型,能够智能地为黑白照片添加色彩。但就像所有AI模型一样,它也有自己的局限性:

色彩偏差问题:模型在训练时学习的是大量图片的色彩分布,但具体到某一张照片,可能会出现整体偏暖(偏黄)或偏冷(偏蓝)的情况。这是因为模型在判断"白平衡"时,可能会受到照片内容的影响。

风格单一:DeOldify生成的色彩风格相对固定,追求的是"自然真实"。但有时候,我们可能希望照片有特定的艺术风格,比如复古的胶片感、温暖的怀旧色调,或者电影般的色彩氛围。

细节优化空间:AI上色后,某些区域的色彩可能不够饱和,或者对比度不够强烈,这时候就需要我们手动调整一下,让照片更加出彩。

2.2 后处理能带来什么?

想象一下,你有一张爷爷奶奶的结婚照,经过DeOldify上色后,照片有了颜色,但整体偏黄,看起来像是老旧的泛黄照片。这时候,通过白平衡校正,你可以让肤色更加自然,让白色婚纱恢复本来的洁白。再通过色彩风格迁移,你可以给照片加上一点复古的暖色调,让整张照片更有年代感,但又不会显得"脏"。

后处理就像是照片的"精修"环节,它不改变AI上色的主体内容,只是在色彩表现上做优化,让最终效果更加符合我们的审美需求。

3. 环境准备与工具安装

3.1 基础环境要求

在开始之前,你需要确保电脑上已经安装了Python。我建议使用Python 3.7或更高版本。如果你还没有安装Python,可以去官网下载安装。

3.2 安装必要的库

打开命令行(Windows上是CMD或PowerShell,Mac/Linux上是Terminal),输入以下命令安装所需的库:

pip install opencv-python
pip install numpy
pip install pillow

这三个库就是我们今天要用到的全部工具:

  • opencv-python:图像处理的核心库,功能强大
  • numpy:处理图像数据的数学库
  • pillow:Python的图像处理库,用来读写图片

安装过程可能需要几分钟,取决于你的网络速度。如果安装过程中遇到问题,可以尝试使用国内的镜像源:

pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple

3.3 准备测试图片

为了演示效果,你需要准备一些图片:

  1. 黑白老照片:可以是家人的老照片,或者从网上下载的测试图片
  2. 风格参考图:如果你想要特定的色彩风格,准备一张色彩风格你喜欢的彩色图片

如果你没有合适的图片,也不用担心,我会在代码中提供在线图片的下载方式。

4. 自动白平衡校正:让色彩回归自然

4.1 什么是白平衡?

简单来说,白平衡就是让照片中的"白色"看起来真的是白色。在不同的光线条件下,白色物体会呈现不同的颜色——在日光下是白色,在白炽灯下可能偏黄,在荧光灯下可能偏蓝。白平衡校正就是消除这种色偏,让色彩还原真实。

对于DeOldify上色后的照片,有时候会出现整体偏色,这时候就需要白平衡校正来"纠偏"。

4.2 基于灰度世界假设的白平衡算法

我要介绍的这个方法叫做"灰度世界假设"算法,它的原理很简单:假设整张图片的平均颜色应该是灰色的(R=G=B)。如果图片整体偏色,那么RGB三个通道的平均值就会不相等。通过调整每个通道的增益,让三个通道的平均值相等,就能校正色偏。

下面是完整的Python代码:

import cv2
import numpy as np
from PIL import Image
import requests
from io import BytesIO

def auto_white_balance(image_path, output_path=None):
    """
    自动白平衡校正
    :param image_path: 输入图片路径或URL
    :param output_path: 输出图片路径(可选)
    :return: 校正后的图片
    """
    
    # 读取图片(支持本地文件和URL)
    if image_path.startswith('http'):
        response = requests.get(image_path)
        img = cv2.imdecode(np.frombuffer(response.content, np.uint8), cv2.IMREAD_COLOR)
    else:
        img = cv2.imread(image_path)
    
    if img is None:
        print(f"无法读取图片: {image_path}")
        return None
    
    # 转换为浮点数进行计算,避免溢出
    img_float = img.astype(np.float32)
    
    # 计算每个通道的平均值
    avg_b = np.mean(img_float[:, :, 0])
    avg_g = np.mean(img_float[:, :, 1])
    avg_r = np.mean(img_float[:, :, 2])
    
    # 计算灰度平均值
    avg_gray = (avg_b + avg_g + avg_r) / 3.0
    
    # 计算每个通道的增益(调整系数)
    gain_b = avg_gray / avg_b
    gain_g = avg_gray / avg_g
    gain_r = avg_gray / avg_r
    
    # 应用增益到每个通道
    img_float[:, :, 0] = img_float[:, :, 0] * gain_b
    img_float[:, :, 1] = img_float[:, :, 1] * gain_g
    img_float[:, :, 2] = img_float[:, :, 2] * gain_r
    
    # 限制像素值在0-255之间
    img_balanced = np.clip(img_float, 0, 255).astype(np.uint8)
    
    # 保存结果
    if output_path:
        cv2.imwrite(output_path, img_balanced)
        print(f"白平衡校正完成,保存到: {output_path}")
    
    return img_balanced

# 使用示例
if __name__ == "__main__":
    # 示例1:处理本地图片
    input_image = "deoldify_output.jpg"  # DeOldify上色后的图片
    output_image = "deoldify_balanced.jpg"
    
    result = auto_white_balance(input_image, output_image)
    
    # 示例2:处理网络图片
    # url = "https://example.com/your_image.jpg"
    # result = auto_white_balance(url, "output_from_url.jpg")

4.3 代码详解

让我解释一下这段代码的关键部分:

图片读取:代码同时支持本地文件和网络URL,这样你可以直接处理网上的图片,非常方便。

核心算法

  1. 计算图片RGB三个通道的平均值
  2. 计算三个通道的平均值(这就是"灰度")
  3. 用灰度值除以每个通道的平均值,得到增益系数
  4. 用增益系数乘以每个通道的像素值

举个例子:如果一张图片偏黄(蓝色通道值偏低,红色和绿色通道值偏高),那么:

  • 蓝色通道的平均值会小于灰度平均值,所以gain_b > 1,蓝色通道会被增强
  • 红色和绿色通道的平均值会大于灰度平均值,所以gain_r和gain_g < 1,红色和绿色通道会被减弱
  • 这样调整后,三个通道平衡了,色偏就校正了

像素值限制:调整后的像素值可能超出0-255的范围,用np.clip()函数限制在这个范围内,避免出现异常颜色。

4.4 进阶:完美反射白平衡算法

上面的方法简单有效,但对于某些图片可能效果不够理想。我再介绍一个更高级的方法——完美反射算法。它的原理是:假设图片中最亮的点应该是白色,通过调整让这些点变成白色,从而校正整张图片的白平衡。

def perfect_reflect_white_balance(image_path, output_path=None, percentile=99.9):
    """
    完美反射白平衡算法
    :param image_path: 输入图片路径
    :param output_path: 输出图片路径
    :param percentile: 取前百分之多少的亮像素作为参考(默认99.9%)
    :return: 校正后的图片
    """
    
    # 读取图片
    img = cv2.imread(image_path)
    if img is None:
        print(f"无法读取图片: {image_path}")
        return None
    
    img_float = img.astype(np.float32)
    
    # 计算亮度(使用灰度公式)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(np.float32)
    
    # 找到亮度最高的像素(前percentile%)
    threshold = np.percentile(gray, percentile)
    
    # 创建掩码,标记亮度高于阈值的像素
    mask = gray >= threshold
    
    # 计算高亮区域的RGB平均值
    avg_b = np.mean(img_float[mask, 0])
    avg_g = np.mean(img_float[mask, 1])
    avg_r = np.mean(img_float[mask, 2])
    
    # 计算最大平均值
    max_avg = max(avg_b, avg_g, avg_r)
    
    # 计算增益
    gain_b = max_avg / avg_b if avg_b > 0 else 1
    gain_g = max_avg / avg_g if avg_g > 0 else 1
    gain_r = max_avg / avg_r if avg_r > 0 else 1
    
    # 应用增益
    img_float[:, :, 0] = img_float[:, :, 0] * gain_b
    img_float[:, :, 1] = img_float[:, :, 1] * gain_g
    img_float[:, :, 2] = img_float[:, :, 2] * gain_r
    
    # 限制像素值
    img_balanced = np.clip(img_float, 0, 255).astype(np.uint8)
    
    if output_path:
        cv2.imwrite(output_path, img_balanced)
        print(f"完美反射白平衡完成,保存到: {output_path}")
    
    return img_balanced

# 使用示例
if __name__ == "__main__":
    result = perfect_reflect_white_balance(
        "deoldify_output.jpg",
        "deoldify_perfect_reflect.jpg",
        percentile=99.5  # 可以调整这个参数
    )

两种方法怎么选?

  • 灰度世界算法:适合大多数情况,计算简单快速
  • 完美反射算法:适合有明显高光区域的图片,效果更精确

你可以两种方法都试试,看看哪个效果更好。

5. 色彩风格迁移:给照片加上艺术滤镜

5.1 什么是色彩风格迁移?

色彩风格迁移就是让一张图片拥有另一张图片的色彩风格。比如,你有一张DeOldify上色后的照片,还有一张你喜欢的电影截图,通过色彩风格迁移,可以让你的照片拥有电影截图的色彩感觉。

这听起来很复杂,但其实原理并不难理解:我们分析风格图片的颜色分布(直方图),然后把目标图片的颜色分布调整成类似的分布。

5.2 基于直方图匹配的风格迁移

下面是一个简单但有效的色彩风格迁移方法:

def color_style_transfer(source_path, style_path, output_path=None):
    """
    色彩风格迁移:将风格图片的色彩应用到源图片上
    :param source_path: 源图片路径(DeOldify上色结果)
    :param style_path: 风格图片路径
    :param output_path: 输出图片路径
    :return: 风格迁移后的图片
    """
    
    # 读取图片
    source_img = cv2.imread(source_path)
    style_img = cv2.imread(style_path)
    
    if source_img is None or style_img is None:
        print("无法读取图片,请检查路径")
        return None
    
    # 调整风格图片大小,使其与源图片尺寸一致
    if source_img.shape != style_img.shape:
        style_img = cv2.resize(style_img, (source_img.shape[1], source_img.shape[0]))
    
    result = np.zeros_like(source_img)
    
    # 对每个颜色通道分别进行直方图匹配
    for channel in range(3):  # BGR三个通道
        source_channel = source_img[:, :, channel]
        style_channel = style_img[:, :, channel]
        
        # 计算源图片和风格图片的直方图
        source_hist = cv2.calcHist([source_channel], [0], None, [256], [0, 256])
        style_hist = cv2.calcHist([style_channel], [0], None, [256], [0, 256])
        
        # 计算累积分布函数
        source_cdf = source_hist.cumsum()
        style_cdf = style_hist.cumsum()
        
        # 归一化
        source_cdf_normalized = source_cdf / source_cdf[-1]
        style_cdf_normalized = style_cdf / style_cdf[-1]
        
        # 创建映射表
        mapping = np.zeros(256, dtype=np.uint8)
        
        for i in range(256):
            # 找到风格图片中累积分布最接近的值
            j = 255
            while j >= 0 and style_cdf_normalized[j] >= source_cdf_normalized[i]:
                mapping[i] = j
                j -= 1
        
        # 应用映射
        result[:, :, channel] = mapping[source_channel]
    
    if output_path:
        cv2.imwrite(output_path, result)
        print(f"色彩风格迁移完成,保存到: {output_path}")
    
    return result

# 使用示例
if __name__ == "__main__":
    # 将电影风格应用到DeOldify上色结果
    result = color_style_transfer(
        "deoldify_output.jpg",      # DeOldify上色后的照片
        "movie_style.jpg",          # 电影风格参考图
        "deoldify_with_movie_style.jpg"
    )

5.3 代码工作原理

让我用大白话解释一下这个算法:

直方图是什么? 直方图就是统计图片中每个颜色值(0-255)出现的次数。比如一张偏暗的图片,低亮度值(0-50)的像素会很多;一张偏亮的图片,高亮度值(200-255)的像素会很多。

直方图匹配怎么做?

  1. 分别计算源图片和风格图片的直方图
  2. 计算累积分布函数(CDF)——就是从小到大累加直方图的值
  3. 对于源图片的每个像素值,找到风格图片中CDF值最接近的像素值
  4. 用这个找到的值替换源图片的像素值

举个例子:假设源图片某个像素的亮度是100,在源图片的CDF中对应的比例是0.3。我们在风格图片的CDF中找比例最接近0.3的值,比如是150。那么就把这个像素的亮度从100改成150。

对RGB三个通道分别做这个操作,整张图片的颜色分布就会变得和风格图片相似。

5.4 更自然的风格迁移:LAB颜色空间

上面的方法在RGB空间操作,有时候会产生不自然的效果。更好的方法是在LAB颜色空间操作,只调整颜色(AB通道),不调整亮度(L通道)。

def lab_color_transfer(source_path, style_path, output_path=None):
    """
    在LAB颜色空间进行色彩风格迁移
    只迁移颜色,保持亮度不变,效果更自然
    """
    
    # 读取图片
    source = cv2.imread(source_path)
    style = cv2.imread(style_path)
    
    if source is None or style is None:
        print("无法读取图片")
        return None
    
    # 转换为LAB颜色空间
    source_lab = cv2.cvtColor(source, cv2.COLOR_BGR2LAB).astype(np.float32)
    style_lab = cv2.cvtColor(style, cv2.COLOR_BGR2LAB).astype(np.float32)
    
    # 调整风格图片大小
    if source.shape != style.shape:
        style_lab = cv2.resize(style_lab, (source.shape[1], source.shape[0]))
    
    # 分离LAB通道
    # L通道:亮度(保持不变)
    # A通道:绿-红分量
    # B通道:蓝-黄分量
    
    # 计算源图片和风格图片的颜色统计(均值和标准差)
    source_mean, source_std = cv2.meanStdDev(source_lab)
    style_mean, style_std = cv2.meanStdDev(style_lab)
    
    # 只对AB通道(颜色通道)进行迁移
    # 保持L通道(亮度)不变
    result_lab = source_lab.copy()
    
    # 对A通道进行颜色迁移
    result_lab[:, :, 1] = ((source_lab[:, :, 1] - source_mean[1]) * 
                          (style_std[1] / source_std[1])) + style_mean[1]
    
    # 对B通道进行颜色迁移
    result_lab[:, :, 2] = ((source_lab[:, :, 2] - source_mean[2]) * 
                          (style_std[2] / source_std[2])) + style_mean[2]
    
    # 限制像素值范围
    result_lab = np.clip(result_lab, 0, 255)
    
    # 转换回BGR颜色空间
    result = cv2.cvtColor(result_lab.astype(np.uint8), cv2.COLOR_LAB2BGR)
    
    if output_path:
        cv2.imwrite(output_path, result)
        print(f"LAB色彩迁移完成,保存到: {output_path}")
    
    return result

# 使用示例
if __name__ == "__main__":
    # 应用复古胶片风格
    result = lab_color_transfer(
        "deoldify_output.jpg",
        "vintage_style.jpg",  # 复古风格参考图
        "deoldify_vintage_style.jpg"
    )

为什么LAB空间更好?

  • LAB颜色空间把亮度(L)和颜色(AB)分开了
  • 我们只迁移颜色信息,保持亮度不变
  • 这样迁移后的图片看起来更自然,不会出现奇怪的亮度变化

6. 完整工作流:从DeOldify到最终效果

现在我们把所有步骤组合起来,形成一个完整的工作流。你可以用这个工作流批量处理DeOldify的上色结果。

import os
import cv2
import numpy as np
from glob import glob

class DeOldifyPostProcessor:
    """DeOldify后处理工具类"""
    
    def __init__(self):
        print("DeOldify后处理器初始化完成")
    
    def process_single_image(self, input_path, style_path=None, output_dir="./output"):
        """
        处理单张图片的完整流程
        """
        
        # 创建输出目录
        os.makedirs(output_dir, exist_ok=True)
        
        # 获取文件名(不含扩展名)
        filename = os.path.basename(input_path)
        name, ext = os.path.splitext(filename)
        
        print(f"开始处理: {filename}")
        
        # 步骤1:读取DeOldify上色结果
        deoldify_img = cv2.imread(input_path)
        if deoldify_img is None:
            print(f"  错误:无法读取图片 {input_path}")
            return
        
        # 步骤2:自动白平衡校正
        print("  步骤1:自动白平衡校正...")
        balanced_img = self.auto_white_balance_simple(deoldify_img)
        
        balanced_path = os.path.join(output_dir, f"{name}_balanced{ext}")
        cv2.imwrite(balanced_path, balanced_img)
        
        # 步骤3:色彩风格迁移(如果提供了风格图片)
        if style_path and os.path.exists(style_path):
            print(f"  步骤2:应用色彩风格 {os.path.basename(style_path)}...")
            styled_img = self.lab_color_transfer_img(balanced_img, style_path)
            
            styled_path = os.path.join(output_dir, f"{name}_styled{ext}")
            cv2.imwrite(styled_path, styled_img)
            
            final_img = styled_img
            final_path = styled_path
        else:
            final_img = balanced_img
            final_path = balanced_path
        
        # 步骤4:可选的颜色增强
        print("  步骤3:颜色增强...")
        enhanced_img = self.enhance_colors(final_img)
        
        enhanced_path = os.path.join(output_dir, f"{name}_final{ext}")
        cv2.imwrite(enhanced_path, enhanced_img)
        
        print(f"  处理完成!结果保存到: {enhanced_path}")
        
        return enhanced_path
    
    def process_batch(self, input_dir, style_path=None, output_dir="./output_batch"):
        """
        批量处理文件夹中的所有图片
        """
        
        # 支持的图片格式
        extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.tiff']
        image_files = []
        
        for ext in extensions:
            image_files.extend(glob(os.path.join(input_dir, ext)))
            image_files.extend(glob(os.path.join(input_dir, ext.upper())))
        
        print(f"找到 {len(image_files)} 张图片需要处理")
        
        results = []
        for i, img_path in enumerate(image_files, 1):
            print(f"\n处理进度: {i}/{len(image_files)}")
            result_path = self.process_single_image(img_path, style_path, output_dir)
            if result_path:
                results.append(result_path)
        
        print(f"\n批量处理完成!共处理 {len(results)} 张图片")
        return results
    
    def auto_white_balance_simple(self, img):
        """简化的自动白平衡"""
        img_float = img.astype(np.float32)
        
        # 计算每个通道的平均值
        avg_b = np.mean(img_float[:, :, 0])
        avg_g = np.mean(img_float[:, :, 1])
        avg_r = np.mean(img_float[:, :, 2])
        
        avg_gray = (avg_b + avg_g + avg_r) / 3.0
        
        # 避免除零错误
        gain_b = avg_gray / avg_b if avg_b > 0 else 1
        gain_g = avg_gray / avg_g if avg_g > 0 else 1
        gain_r = avg_gray / avg_r if avg_r > 0 else 1
        
        # 应用增益
        img_float[:, :, 0] = img_float[:, :, 0] * gain_b
        img_float[:, :, 1] = img_float[:, :, 1] * gain_g
        img_float[:, :, 2] = img_float[:, :, 2] * gain_r
        
        return np.clip(img_float, 0, 255).astype(np.uint8)
    
    def lab_color_transfer_img(self, source_img, style_path):
        """对图像对象进行LAB色彩迁移"""
        style_img = cv2.imread(style_path)
        if style_img is None:
            return source_img
        
        # 调整风格图片大小
        if source_img.shape[:2] != style_img.shape[:2]:
            style_img = cv2.resize(style_img, (source_img.shape[1], source_img.shape[0]))
        
        # 转换为LAB
        source_lab = cv2.cvtColor(source_img, cv2.COLOR_BGR2LAB).astype(np.float32)
        style_lab = cv2.cvtColor(style_img, cv2.COLOR_BGR2LAB).astype(np.float32)
        
        # 计算统计信息
        source_mean, source_std = cv2.meanStdDev(source_lab)
        style_mean, style_std = cv2.meanStdDev(style_lab)
        
        # 迁移AB通道
        result_lab = source_lab.copy()
        
        # A通道
        if source_std[1] > 0:
            result_lab[:, :, 1] = ((source_lab[:, :, 1] - source_mean[1]) * 
                                  (style_std[1] / source_std[1])) + style_mean[1]
        
        # B通道
        if source_std[2] > 0:
            result_lab[:, :, 2] = ((source_lab[:, :, 2] - source_mean[2]) * 
                                  (style_std[2] / source_std[2])) + style_mean[2]
        
        # 转换回BGR
        result_lab = np.clip(result_lab, 0, 255)
        return cv2.cvtColor(result_lab.astype(np.uint8), cv2.COLOR_LAB2BGR)
    
    def enhance_colors(self, img, saturation_factor=1.2, contrast_factor=1.1):
        """增强颜色饱和度和对比度"""
        # 转换为HSV颜色空间
        hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV).astype(np.float32)
        
        # 增强饱和度
        hsv[:, :, 1] = hsv[:, :, 1] * saturation_factor
        hsv[:, :, 1] = np.clip(hsv[:, :, 1], 0, 255)
        
        # 转换回BGR
        enhanced = cv2.cvtColor(hsv.astype(np.uint8), cv2.COLOR_HSV2BGR)
        
        # 增强对比度
        lab = cv2.cvtColor(enhanced, cv2.COLOR_BGR2LAB)
        l, a, b = cv2.split(lab)
        
        # 应用CLAHE(对比度受限的自适应直方图均衡化)
        clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
        l = clahe.apply(l)
        
        # 合并通道
        lab = cv2.merge([l, a, b])
        enhanced = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
        
        return enhanced

# 使用示例
if __name__ == "__main__":
    # 创建后处理器实例
    processor = DeOldifyPostProcessor()
    
    # 处理单张图片
    print("=== 处理单张图片 ===")
    processor.process_single_image(
        input_path="deoldify_output.jpg",
        style_path="vintage_style.jpg",  # 可选,不提供则只做白平衡
        output_dir="./processed"
    )
    
    # 批量处理(可选)
    # print("\n=== 批量处理 ===")
    # processor.process_batch(
    #     input_dir="./deoldify_outputs",
    #     style_path="movie_style.jpg",
    #     output_dir="./processed_batch"
    # )

7. 实际效果对比与优化建议

7.1 效果对比示例

为了让你更直观地了解后处理的效果,我准备了一个简单的对比脚本:

import cv2
import numpy as np
import matplotlib.pyplot as plt

def compare_results(original_path, balanced_path, styled_path=None):
    """对比显示处理前后的效果"""
    
    # 读取图片
    original = cv2.imread(original_path)
    balanced = cv2.imread(balanced_path)
    
    # 转换颜色空间(OpenCV是BGR,matplotlib需要RGB)
    original_rgb = cv2.cvtColor(original, cv2.COLOR_BGR2RGB)
    balanced_rgb = cv2.cvtColor(balanced, cv2.COLOR_BGR2RGB)
    
    # 创建对比图
    fig, axes = plt.subplots(1, 3 if styled_path else 2, figsize=(15, 5))
    
    # 显示原图
    axes[0].imshow(original_rgb)
    axes[0].set_title("DeOldify原始上色结果")
    axes[0].axis('off')
    
    # 显示白平衡校正后
    axes[1].imshow(balanced_rgb)
    axes[1].set_title("白平衡校正后")
    axes[1].axis('off')
    
    # 如果提供了风格迁移结果,显示第三张
    if styled_path:
        styled = cv2.imread(styled_path)
        styled_rgb = cv2.cvtColor(styled, cv2.COLOR_BGR2RGB)
        axes[2].imshow(styled_rgb)
        axes[2].set_title("色彩风格迁移后")
        axes[2].axis('off')
    
    plt.tight_layout()
    plt.show()
    
    # 打印一些统计信息
    print("=== 色彩统计信息 ===")
    print(f"原始图片 - 平均颜色: B={np.mean(original[:,:,0]):.1f}, G={np.mean(original[:,:,1]):.1f}, R={np.mean(original[:,:,2]):.1f}")
    print(f"校正后 - 平均颜色: B={np.mean(balanced[:,:,0]):.1f}, G={np.mean(balanced[:,:,1]):.1f}, R={np.mean(balanced[:,:,2]):.1f}")
    
    # 计算颜色偏差改善程度
    orig_balance = np.std([np.mean(original[:,:,0]), np.mean(original[:,:,1]), np.mean(original[:,:,2])])
    balanced_balance = np.std([np.mean(balanced[:,:,0]), np.mean(balanced[:,:,1]), np.mean(balanced[:,:,2])])
    print(f"颜色平衡改善: {((orig_balance - balanced_balance) / orig_balance * 100):.1f}%")

# 使用示例
if __name__ == "__main__":
    compare_results(
        "deoldify_output.jpg",
        "deoldify_balanced.jpg",
        "deoldify_styled.jpg"  # 可选
    )

7.2 参数调优建议

在实际使用中,你可能需要根据具体图片调整参数。这里是一些建议:

白平衡参数调整

  • 如果校正后图片还是偏色,可以尝试调整percentile参数(完美反射算法)
  • 对于特别暗或特别亮的图片,可能需要先调整亮度再进行白平衡

风格迁移参数调整

  • 如果风格迁移效果太强,可以混合原图和风格迁移结果:
    alpha = 0.7  # 原图权重
    beta = 0.3   # 风格图权重
    blended = cv2.addWeighted(original, alpha, styled, beta, 0)
    
  • 尝试不同的风格图片:电影截图、油画、摄影作品等

颜色增强参数

  • saturation_factor:饱和度增强系数,1.0表示不变,>1.0增强,<1.0减弱
  • contrast_factor:对比度增强系数,调整CLAHE的clipLimit参数

7.3 常见问题与解决方案

问题1:处理后图片颜色不自然

  • 可能原因:风格图片与源图片差异太大
  • 解决方案:选择色彩风格相似的参考图,或者降低风格迁移的强度

问题2:白平衡校正过度

  • 可能原因:图片本身没有明显色偏,但算法强制校正
  • 解决方案:使用完美反射算法,调整percentile参数,或者手动调整白平衡增益

问题3:处理速度慢

  • 可能原因:图片分辨率太高
  • 解决方案:先缩小图片处理,再放大回原尺寸,或者使用更简单的算法

问题4:内存不足

  • 可能原因:批量处理大量高分辨率图片
  • 解决方案:分批处理,及时释放内存,或者降低图片分辨率

8. 总结

通过这篇文章,你学会了如何用OpenCV对DeOldify的上色结果进行后处理,让老照片的色彩更加自然、更加生动。我们主要掌握了两个核心技术:

自动白平衡校正:解决了DeOldify上色后可能出现的色偏问题,让照片色彩回归自然。你学会了两种方法——简单的灰度世界算法和更精确的完美反射算法,可以根据实际情况选择使用。

色彩风格迁移:让照片拥有特定的艺术风格,比如复古胶片感、电影色调等。你学会了在RGB空间和LAB空间进行风格迁移,其中LAB空间的方法效果更自然,因为它只迁移颜色信息,不改变亮度。

我还提供了一个完整的后处理工作流,你可以直接使用DeOldifyPostProcessor类来处理单张图片或批量处理整个文件夹。这个工具类集成了白平衡校正、色彩风格迁移和颜色增强功能,基本上涵盖了老照片优化的所有需求。

实际应用建议

  1. 先做白平衡校正:这是基础,能解决大部分色彩问题
  2. 谨慎使用风格迁移:不是所有照片都适合风格迁移,要选择与照片内容匹配的风格
  3. 适度增强颜色:轻微的饱和度增强可以让照片更生动,但不要过度
  4. 保存中间结果:处理过程中保存每一步的结果,方便对比和回退

最重要的是,这些代码都是可以直接复制使用的。你不需要理解所有的数学原理,只需要知道怎么调用这些函数,就能获得专业级的照片优化效果。

老照片上色不仅仅是技术问题,更是情感和记忆的修复。通过合适的后处理,我们可以让那些黑白记忆变得更加鲜活、更加动人。希望这些工具能帮助你更好地保存和分享那些珍贵的家庭记忆。


获取更多AI镜像

想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。

Logo

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

更多推荐