Sprite Sheet(精灵图/图集)自动切割技术笔记

一、 常见自动切割方案概览

自动切割 Sprite Sheet 主要分为两种场景:网格对齐分割(固定宽高/行列)与 Alpha 通道连通域识别(不规则/非标准尺寸)。

1. 常见工具分类

  • 网页在线工具:EZGIF Sprite Sheet Cutter(适合规则网格)、TexturePacker Unpacker。
  • 桌面客户端:Shoebox(Sprite Sheet Depacker 自动边缘剥离)、Aseprite(像素风格自动切片)。
  • 游戏引擎内置
  • Unity:导入图片 $\rightarrow$ Sprite Mode 设为 Multiple $\rightarrow$ Sprite Editor $\rightarrow$ Slice(支持 Automatic / Grid)。
  • GodotSprite2D 节点 $\rightarrow$ 开启 Region $\rightarrow$ Texture Region 面板选择 Auto Slice
  • Cocos Creator:支持内置 Trim 自动裁切与坐标补偿。
  • 开发者自动化:Python (OpenCV) 智能连通域识别,ImageMagick 命令行工具。

二、 基于 Python OpenCV 的智能切割原理

对于非标准尺寸、宽高不一、斜向或乱序摆放的小图,只要在透明通道(Alpha Channel)上无物理粘连,即可通过连通域识别实现自动提取。

核心算法流程

  1. 提取 Alpha 通道:分析 PNG 图片的透明度信息。
  2. 二值化处理:将不透明区域转化为白色(255),透明区域转化为黑色(0)。
  3. 连通域/轮廓检测:识别不透明像素组成的“像素孤岛”。
  4. 计算最小外接矩形:获取每个孤岛的坐标与宽高 $(x, y, w, h)$。
  5. 切割并保存:根据矩形区域裁剪素材,导出独立 PNG。

三、 Windows 11 虚拟环境配置(CMD 交互)

为避免污染全局 Python 环境及依赖冲突,建议使用 venv 创建独立的虚拟环境。

1. 查看与管理环境

:: 退出当前虚拟环境
deactivate

:: 查看当前环境下的依赖包
pip list

:: 在全局环境中升级 pip
python -m pip install --upgrade pip

2. 创建与激活自定义虚拟环境

:: 1. 创建指定名称的虚拟环境(例如命名为 png)
python -m venv G:\ProgramFiles\pythonvenv\png

:: 2. 激活虚拟环境
G:\ProgramFiles\pythonvenv\png\Scripts\activate

:: 3. 在虚拟环境中安装依赖(注意包名必须为 opencv-python)
pip install opencv-python numpy

四、 自动化切割脚本代码 (cutter.py)

该脚本支持命令行参数传入,并解决了 OpenCV 在 Windows 环境下读取/写入中文路径抛出乱码及读取失败(cv::findDecoder)的问题。

import os
import argparse
import cv2
import numpy as np

def auto_crop_spritesheet(input_path, output_dir, padding=0, min_area=100, merge_dist=0):
    # 1. 检查输入文件是否存在
    if not os.path.exists(input_path):
        print(f"错误:找不到输入文件 -> {input_path}")
        return

    # 2. 自动创建输出文件夹
    os.makedirs(output_dir, exist_ok=True)

    # 3. 读取图片(使用 np.fromfile 绕过 OpenCV 中文路径 Bug)
    try:
        img_data = np.fromfile(input_path, dtype=np.uint8)
        img = cv2.imdecode(img_data, cv2.IMREAD_UNCHANGED)
    except Exception as e:
        print(f"读取图片失败: {e}")
        return

    if img is None or len(img.shape) < 3 or img.shape[2] != 4:
        print("错误:无法读取图片,或图片不包含 Alpha 透明通道(非 32 位透明 PNG)!")
        return

    # 4. 提取 Alpha 通道并二值化
    alpha = img[:, :, 3]
    _, binary = cv2.threshold(alpha, 1, 255, cv2.THRESH_BINARY)

    # 5. 膨胀处理(可选:用于合并微小断裂的图案部件)
    if merge_dist > 0:
        kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (merge_dist, merge_dist))
        binary_processed = cv2.dilate(binary, kernel)
    else:
        binary_processed = binary

    # 6. 寻找轮廓
    contours, _ = cv2.findContours(binary_processed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    count = 0
    base_name = os.path.splitext(os.path.basename(input_path))[0]

    for cnt in contours:
        x, y, w, h = cv2.boundingRect(cnt)
        
        # 过滤面积过小的孤立像素/杂质
        if w * h < min_area:
            continue

        # 计算带 Padding 的坐标区间
        x_start = max(0, x - padding)
        y_start = max(0, y - padding)
        x_end = min(img.shape[1], x + w + padding)
        y_end = min(img.shape[0], y + h + padding)

        # 裁剪图片
        sprite = img[y_start:y_end, x_start:x_end]
        
        # 保存图片(使用 cv2.imencode 支持中文输出路径)
        save_path = os.path.join(output_dir, f"{base_name}_{count:03d}.png")
        is_success, buffer = cv2.imencode(".png", sprite)
        if is_success:
            with open(save_path, "wb") as f:
                f.write(buffer)
            count += 1

    print(f"切割成功!共导出 {count} 张小图至: {output_dir}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Sprite Sheet 智能切割工具")
    
    parser.add_argument("-i", "--input", required=True, help="输入 PNG 图片的完整路径")
    parser.add_argument("-o", "--output", required=True, help="输出小图的保存目录")
    
    parser.add_argument("--padding", type=int, default=0, help="切片留白像素(默认 0)")
    parser.add_argument("--min_area", type=int, default=100, help="过滤最小像素面积(默认 100)")
    parser.add_argument("--merge_dist", type=int, default=0, help="膨胀合并间距(默认 0)")

    args = parser.parse_args()

    auto_crop_spritesheet(
        input_path=args.input,
        output_dir=args.output,
        padding=args.padding,
        min_area=args.min_area,
        merge_dist=args.merge_dist
    )

五、 命令行运行与批处理说明

无需手动激活虚拟环境,通过指定虚拟环境解释器的绝对路径即可直接调用:

"G:\ProgramFiles\pythonvenv\png\Scripts\python.exe" "G:\ProgramFiles\pythonvenv\png\cutter.py" -i "G:\file\图片\png\17831295365791.png" -o "G:\file\图片\png\out"

可选调节参数

  • -i / --input:输入 PNG 完整路径(必须添加标识)。
  • -o / --output:输出文件夹路径(自动创建不存在的目录)。
  • --padding:边框留白像素数(例如 --padding 2)。
  • --min_area:忽略噪点的最小像素面积(例如 --min_area 50)。
  • --merge_dist:多部分粘合距离,用于防止单个小图各组件断裂(例如 --merge_dist 3)。

标签: none

添加新评论