【图像处理基石】如何从动漫参考图中提取色彩风格?

引言

动漫的色彩风格是其视觉表达的核心------《你的名字》的清新透亮、《鬼灭之刃》的高饱和对比、《进击的巨人》的暗沉压抑,不同作品的配色体系直接决定了观众的情感共鸣。对于创作者(同人画师、游戏UI设计师、动画从业者)而言,手动提取动漫参考图的色彩风格(如主色调、配色比例、明暗规律)不仅耗时,还难以精准复现其和谐性。

本文将拆解动漫色彩风格的核心要素,基于K-Means聚类色彩空间分析等算法,用Python实现一套完整的动漫色彩风格提取流程,包括主色调提取、配色比例计算、色彩分布统计、色彩和谐性分析四大核心功能,代码可直接运行,帮你快速"扒取"心仪动漫的色彩密码!

一、动漫色彩风格的核心要素

在动手实现前,先明确我们需要提取的"色彩风格"到底包含什么------这是算法设计的前提:

  1. 主色调:画面中最具代表性的3-8种核心颜色(动漫配色通常简洁集中,不会过于复杂);
  2. 配色比例:各主色调在画面中的像素占比(比如《你的名字》中蓝色占比30%、粉色占比25%);
  3. 色彩分布规律:亮度(明暗)、饱和度(鲜艳度)的统计分布(比如清新风动漫多集中在高亮度、中高饱和度);
  4. 色彩和谐性:主色调之间的色相关系(如互补色、邻近色),这是动漫配色"好看"的关键。

二、技术选型与环境准备

1. 依赖库说明

  • Pillow:图片加载、预处理、色彩空间转换;
  • numpy:像素矩阵运算、数据处理;
  • scipy.cluster.vq:K-Means聚类(提取主色调核心算法);
  • matplotlib:色彩分布可视化、结果展示;
  • scipy.stats:统计分析(亮度/饱和度分布)。

2. 环境安装

bash 复制代码
pip install pillow numpy scipy matplotlib

三、算法原理与分步实现

核心流程

动漫参考图 → 预处理(去噪、缩放)→ 色彩空间转换(RGB→HSV)→ 像素数据提取 → K-Means聚类提取主色调 → 配色比例计算 → 亮度/饱和度分布分析 → 色彩和谐性判断 → 结果可视化与导出

完整代码(带详细注释)

python 复制代码
from PIL import Image, ImageFilter
import numpy as np
import matplotlib.pyplot as plt
from scipy.cluster.vq import kmeans, vq
from scipy.stats import gaussian_kde
import json

# -------------------------- 1. 图片预处理函数 --------------------------
def preprocess_image(image_path, target_size=(800, 600)):
    """
    图片预处理:加载、缩放、去噪(减少干扰,提高聚类效率)
    :param image_path: 动漫参考图路径
    :param target_size: 目标尺寸(宽, 高),缩放后加速运算
    :return: RGB格式图片、HSV格式图片
    """
    # 加载图片并转换为RGB(排除Alpha通道干扰)
    img_rgb = Image.open(image_path).convert("RGB")
    # 缩放(保持比例,避免拉伸)
    img_rgb.thumbnail(target_size, Image.Resampling.LANCZOS)
    # 轻微高斯模糊去噪(动漫图本身边缘清晰,模糊半径不宜过大)
    img_rgb = img_rgb.filter(ImageFilter.GaussianBlur(radius=0.5))
    # 转换为HSV色彩空间(更符合人眼对色彩的感知,便于分离色相/饱和度/明度)
    img_hsv = img_rgb.convert("HSV")
    return img_rgb, img_hsv

# -------------------------- 2. K-Means聚类提取主色调 --------------------------
def extract_main_colors(img_rgb, num_colors=5, threshold=0.02):
    """
    基于K-Means聚类提取主色调(核心算法)
    :param img_rgb: RGB格式图片
    :param num_colors: 期望提取的主色调数量(动漫图推荐3-8)
    :param threshold: 最小占比阈值(过滤占比低于此值的次要颜色)
    :return: 主色调列表(RGB格式)、各颜色占比列表
    """
    # 1. 提取像素数据:将图片转为像素矩阵(shape: [像素数, 3])
    pixels = np.array(img_rgb).reshape(-1, 3).astype(np.float32)
    # 归一化到[0,1](K-Means对数值范围敏感,归一化提升稳定性)
    pixels_normalized = pixels / 255.0

    # 2. K-Means聚类:自动分组相似像素
    # kmeans函数返回:聚类中心(主色调)、方差(聚类质量,越小越好)
    centroids, _ = kmeans(pixels_normalized, num_colors, iter=20)
    # 3. 为每个像素分配到最近的聚类中心(确定每个像素属于哪个主色调)
    labels, _ = vq(pixels_normalized, centroids)

    # 4. 计算各主色调的占比
    total_pixels = len(labels)
    color_counts = np.bincount(labels)  # 统计每个聚类的像素数
    color_ratios = color_counts / total_pixels  # 转换为占比

    # 5. 过滤占比过低的次要颜色,按占比降序排序
    valid_mask = color_ratios >= threshold
    valid_centroids = centroids[valid_mask]
    valid_ratios = color_ratios[valid_mask]

    # 按占比降序排序(方便后续展示核心颜色)
    sorted_indices = np.argsort(valid_ratios)[::-1]
    sorted_colors = (valid_centroids[sorted_indices] * 255).astype(np.uint8)  # 转回[0,255]
    sorted_ratios = valid_ratios[sorted_indices]

    # 转换为列表格式(便于后续处理)
    main_colors = [tuple(color) for color in sorted_colors]
    color_ratios = [float(ratio) for ratio in sorted_ratios]

    return main_colors, color_ratios

# -------------------------- 3. 色彩分布分析(亮度/饱和度) --------------------------
def analyze_color_distribution(img_hsv):
    """
    分析动漫图的亮度(V通道)和饱和度(S通道)分布
    :param img_hsv: HSV格式图片
    :return: 亮度数据、饱和度数据
    """
    # 分离HSV通道(Pillow的HSV通道范围:H[0,255], S[0,255], V[0,255])
    h_channel, s_channel, v_channel = img_hsv.split()
    # 转为numpy数组便于统计
    s_data = np.array(s_channel).flatten() / 255.0  # 饱和度(0=灰度,1=全饱和)
    v_data = np.array(v_channel).flatten() / 255.0  # 亮度(0=纯黑,1=纯白)
    return s_data, v_data

# -------------------------- 4. 色彩和谐性分析 --------------------------
def analyze_color_harmony(main_colors):
    """
    分析主色调的和谐关系(基于色相角)
    和谐类型:邻近色(色相差0-30°)、类似色(30-60°)、互补色(150-180°)、对比色(90-150°)
    :param main_colors: 主色调列表(RGB格式)
    :return: 和谐性分析结果(字典)
    """
    # 1. 将RGB主色调转为HSV,提取色相角(H通道)
    hsv_colors = [Image.new("RGB", (1,1), color).convert("HSV").getpixel((0,0)) for color in main_colors]
    hues = [h / 255.0 * 360.0 for h, _, _ in hsv_colors]  # 转为[0,360°]色相角

    # 2. 计算所有主色调对的色相差(取最小夹角,0-180°)
    color_pairs = []
    num_colors = len(hues)
    for i in range(num_colors):
        for j in range(i+1, num_colors):
            hue_diff = abs(hues[i] - hues[j])
            hue_diff = min(hue_diff, 360 - hue_diff)  # 最小夹角(避免190°等价于170°)
            
            # 判断和谐类型
            if hue_diff <= 30:
                harmony_type = "邻近色"
            elif hue_diff <= 60:
                harmony_type = "类似色"
            elif hue_diff <= 150:
                harmony_type = "对比色"
            else:
                harmony_type = "互补色"
            
            color_pairs.append({
                "color1": main_colors[i],
                "color2": main_colors[j],
                "hue_diff": round(hue_diff, 1),
                "harmony_type": harmony_type
            })

    return {
        "hues": [round(h, 1) for h in hues],
        "color_pairs": color_pairs
    }

# -------------------------- 5. 结果可视化与导出 --------------------------
def visualize_and_export(main_colors, color_ratios, s_data, v_data, harmony_result, output_dir="color_style_result"):
    """
    可视化色彩风格结果(主色调、占比、分布直方图)并导出数据
    :param main_colors: 主色调列表
    :param color_ratios: 颜色占比列表
    :param s_data: 饱和度数据
    :param v_data: 亮度数据
    :param harmony_result: 和谐性分析结果
    :param output_dir: 输出目录
    """
    import os
    os.makedirs(output_dir, exist_ok=True)

    # 1. 可视化:主色调+占比饼图
    fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, figsize=(15, 12))
    
    # 主色调色卡
    color_blocks = np.zeros((50, len(main_colors)*100, 3), dtype=np.uint8)
    for i, color in enumerate(main_colors):
        color_blocks[:, i*100:(i+1)*100, :] = color
    ax1.imshow(color_blocks)
    ax1.axis("off")
    ax1.set_title("主色调色卡", fontsize=14)

    # 配色占比饼图
    colors_hex = [f"#{r:02x}{g:02x}{b:02x}" for r, g, b in main_colors]
    ax2.pie(color_ratios, labels=[f"颜色{i+1}\n{ratio:.1%}" for i, ratio in enumerate(color_ratios)],
            colors=colors_hex, autopct="%1.1f%%", startangle=90)
    ax2.set_title("配色占比", fontsize=14)

    # 饱和度分布直方图(带密度曲线)
    ax3.hist(s_data, bins=50, alpha=0.6, color="#ff7f0e", density=True)
    # 高斯核密度估计(更平滑的分布曲线)
    s_kde = gaussian_kde(s_data)
    s_range = np.linspace(0, 1, 100)
    ax3.plot(s_range, s_kde(s_range), color="#1f77b4", linewidth=2)
    ax3.set_xlabel("饱和度(0=灰度,1=全饱和)")
    ax3.set_ylabel("密度")
    ax3.set_title("饱和度分布", fontsize=14)
    ax3.grid(alpha=0.3)

    # 亮度分布直方图(带密度曲线)
    ax4.hist(v_data, bins=50, alpha=0.6, color="#2ca02c", density=True)
    v_kde = gaussian_kde(v_data)
    v_range = np.linspace(0, 1, 100)
    ax4.plot(v_range, v_kde(v_range), color="#d62728", linewidth=2)
    ax4.set_xlabel("亮度(0=纯黑,1=纯白)")
    ax4.set_ylabel("密度")
    ax4.set_title("亮度分布", fontsize=14)
    ax4.grid(alpha=0.3)

    # 保存可视化图
    plt.tight_layout()
    plt.savefig(os.path.join(output_dir, "color_style_analysis.png"), dpi=300, bbox_inches="tight")
    plt.show()

    # 2. 导出色彩风格数据(JSON格式,便于后续应用)
    style_data = {
        "main_colors": [list(color) for color in main_colors],  # RGB值
        "color_ratios": color_ratios,  # 占比
        "color_hex": colors_hex,  # 十六进制颜色码(便于设计工具使用)
        "harmony_analysis": harmony_result,
        "saturation_stats": {
            "mean": float(np.mean(s_data)),  # 平均饱和度
            "std": float(np.std(s_data))    # 饱和度标准差(越小越集中)
        },
        "value_stats": {
            "mean": float(np.mean(v_data)),  # 平均亮度
            "std": float(np.std(v_data))    # 亮度标准差
        }
    }

    with open(os.path.join(output_dir, "color_style_data.json"), "w", encoding="utf-8") as f:
        json.dump(style_data, f, ensure_ascii=False, indent=4)

    print(f"色彩风格分析结果已保存至:{output_dir}")
    print(f"提取主色调数:{len(main_colors)}")
    print(f"主色调(RGB/十六进制):")
    for i, (color, hex_code, ratio) in enumerate(zip(main_colors, colors_hex, color_ratios)):
        print(f"  颜色{i+1}:{color} / {hex_code}(占比:{ratio:.1%})")

# -------------------------- 6. 整合流程:一键提取动漫色彩风格 --------------------------
def extract_anime_color_style(image_path, num_colors=5, threshold=0.02):
    """
    一键提取动漫参考图的色彩风格
    :param image_path: 动漫参考图路径(支持JPG/PNG)
    :param num_colors: 期望提取的主色调数量
    :param threshold: 最小占比阈值(过滤次要颜色)
    """
    # 步骤1:图片预处理
    img_rgb, img_hsv = preprocess_image(image_path)
    print(f"图片预处理完成,尺寸:{img_rgb.size}")

    # 步骤2:提取主色调与配色比例
    main_colors, color_ratios = extract_main_colors(img_rgb, num_colors, threshold)
    print("主色调与配色比例提取完成")

    # 步骤3:分析亮度/饱和度分布
    s_data, v_data = analyze_color_distribution(img_hsv)
    print("色彩分布分析完成")

    # 步骤4:色彩和谐性分析
    harmony_result = analyze_color_harmony(main_colors)
    print("色彩和谐性分析完成")

    # 步骤5:可视化与导出结果
    visualize_and_export(main_colors, color_ratios, s_data, v_data, harmony_result)

# -------------------------- 7. 调用示例 --------------------------
if __name__ == "__main__":
    # 替换为你的动漫参考图路径(建议选择画面主体清晰、无过多杂物的图)
    ANIME_IMAGE_PATH = "anime_reference.jpg"
    # 执行色彩风格提取(可调整参数)
    extract_anime_color_style(
        image_path=ANIME_IMAGE_PATH,
        num_colors=6,        # 期望提取的主色调数(3-8为宜)
        threshold=0.03       # 过滤占比低于3%的次要颜色
    )

四、算法核心原理解析

1. 主色调提取:为什么用K-Means?

动漫图的色彩特征是离散化、高对比度(比如角色头发、服装、背景的颜色区分明显),K-Means聚类能自动将像素按RGB相似度分组,聚类中心就是"最具代表性的主色调"。相比传统的"直方图统计+阈值筛选",K-Means能:

  • 自动适应不同动漫的色彩复杂度(无需手动调整阈值);
  • 合并相似颜色(比如不同明暗的蓝色会被归为一类);
  • 按占比排序,优先保留核心配色。

2. 色彩空间选择:为什么用HSV?

RGB空间适合计算,但难以分离"色彩种类"(色相)、"鲜艳度"(饱和度)、"明暗"(亮度)。HSV空间直接拆分这三个维度,让我们能:

  • 单独分析饱和度分布(比如清新风动漫的饱和度集中在0.6-0.9);
  • 单独分析亮度分布(比如暗黑系动漫的亮度集中在0.2-0.5);
  • 基于色相角判断色彩和谐性(互补色、邻近色等)。

3. 色彩和谐性判断依据

基于色彩理论的色相角差规则(行业通用标准):

色相角差 和谐类型 特点(动漫中常见场景)
0-30° 邻近色 柔和统一(如《玉子爱情故事》的暖色调)
30-60° 类似色 协调有层次(如《夏目友人帐》的自然配色)
90-150° 对比色 鲜明有张力(如《鬼灭之刃》的红+绿)
150-180° 互补色 强烈冲击(如《EVA》的红+蓝)

五、效果验证与参数调优

1. 关键参数调优技巧

参数 作用 推荐范围 调优建议
num_colors 期望主色调数 3-8 简洁动漫图(如单角色)用3-5种;复杂场景(如全景)用6-8种
threshold 最小占比阈值 0.02-0.05 过滤杂色:背景复杂时设0.03-0.05;需要保留细节时设0.02
target_size 预处理缩放尺寸 (600,400)-(1000,800) 尺寸越小运算越快,但过小会丢失色彩信息;建议≥600px

2. 避坑指南

  • 选择参考图时,优先选无水印、无黑边、主体清晰的图(避免杂物干扰聚类);
  • 若提取的主色调偏暗/偏亮,可检查图片是否过曝/欠曝,或调整threshold过滤背景色;
  • K-Means聚类结果可能有微小差异(随机初始化),若不满意可多次运行,或增大kmeansiter参数(如30)。

六、扩展应用场景

提取的色彩风格数据可直接用于以下场景:

1. 辅助创作

  • color_style_data.json中的十六进制颜色码导入PS、SAI等设计工具,直接使用参考图的配色;
  • 基于亮度/饱和度分布,调整自己的画作(比如参考《你的名字》的高饱和,提高作品饱和度至0.7-0.9)。

2. 自动配色迁移

基于提取的主色调,用色彩替换算法将其他图片的配色替换为参考图风格(扩展代码示例):

python 复制代码
def transfer_color_style(source_img_path, target_style_data, output_path):
    """
    将目标图的配色迁移为参考图的色彩风格
    :param source_img_path: 目标图路径
    :param target_style_data: 参考图色彩风格数据(JSON加载)
    :param output_path: 输出路径
    """
    source_img = Image.open(source_img_path).convert("RGB")
    source_pixels = np.array(source_img).reshape(-1, 3).astype(np.float32) / 255.0

    # 参考图主色调(归一化)
    target_colors = np.array(target_style_data["main_colors"]) / 255.0

    # 为目标图每个像素分配到参考图最近的主色调
    labels, _ = vq(source_pixels, target_colors)
    # 替换为参考图主色调
    transferred_pixels = target_colors[labels] * 255.0
    transferred_img = Image.fromarray(transferred_pixels.reshape(source_img.size[1], source_img.size[0], 3).astype(np.uint8))
    transferred_img.save(output_path)
    print(f"配色迁移完成,保存至:{output_path}")

# 调用示例
with open("color_style_result/color_style_data.json", "r") as f:
    style_data = json.load(f)
transfer_color_style("my_drawing.jpg", style_data, "transferred_drawing.jpg")

3. 批量分析与风格分类

对多个动漫作品的色彩风格进行批量提取,统计主色调、亮度、饱和度特征,实现动漫风格分类(如"清新风""暗黑风""高对比风")。

七、总结与优化方向

本文实现了一套轻量、高效的动漫色彩风格提取算法,核心优势是:

  • 基于K-Means聚类,自动适配不同动漫的色彩特征;
  • 兼顾"主色调+配色比例+分布规律+和谐性",提取信息全面;
  • 代码模块化,易于扩展和二次开发。

后续可优化的方向:

  1. 加入背景色自动过滤(基于语义分割,如用Mask R-CNN剔除纯黑/纯白背景);
  2. 结合深度学习(如CNN)提取更高级的色彩风格特征(如局部配色规律);
  3. 支持批量处理,并生成可直接导入设计工具的色卡文件(如ASE格式);
  4. 增加配色冲突检测(避免提取的配色出现不和谐组合)。

如果在使用过程中遇到问题,或有更好的优化思路,欢迎在评论区交流~

相关推荐
阿里云大数据AI技术1 小时前
PAI Physical AI Notebook详解3:基于仿真的导航模型训练
人工智能
2501_941145852 小时前
深度学习与计算机视觉在工业质检与智能检测系统中的创新应用研究
人工智能·深度学习·计算机视觉
Maynor9962 小时前
突发!Grok 4.1 刚刚发布,情商拉满,国内免费使用!
人工智能
q***38512 小时前
Spring Boot + Spring AI快速体验
人工智能·spring boot·spring
羊羊小栈2 小时前
基于知识图谱(Neo4j)和大语言模型(LLM)的图检索增强(GraphRAG)的医疗健康知识问诊系统(vue+flask+AI算法)
人工智能·语言模型·毕业设计·知识图谱·neo4j·大作业
Mintopia2 小时前
Trae Coding - 「Excel 秒变海报」—— 上传 CSV,一句话生成可打印信息图。
前端·人工智能·trae
大千AI助手2 小时前
多叉树:核心概念、算法实现与全领域应用
人工智能·算法·决策树·机器学习··多叉树·大千ai助手
无妄无望2 小时前
ragflow代码学习切片方式(1)docling_parser.py
人工智能·python·学习
努力的光头强2 小时前
《智能体设计模式》从零基础入门到精通,看这一篇就够了!
大数据·人工智能·深度学习·microsoft·机器学习·设计模式·ai