Instructions to use d1ngdongji/MCSkin with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Diffusers
How to use d1ngdongji/MCSkin with Diffusers:
pip install -U diffusers transformers accelerate
import torch from diffusers import DiffusionPipeline # switch to "mps" for apple devices pipe = DiffusionPipeline.from_pretrained("black-forest-labs/FLUX.2-klein-9B", dtype=torch.bfloat16, device_map="cuda") pipe.load_lora_weights("d1ngdongji/MCSkin") prompt = "Generate a 64x64 pixel texture maps of this Minecraft character" image = pipe(prompt).images[0] - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- Draw Things
- DiffusionBee
| import os | |
| import asyncio | |
| from pathlib import Path | |
| from PIL import Image | |
| import random | |
| from minepi import Skin | |
| from minepi.skin_render import Render | |
| # 创建输出文件夹 | |
| RENDER_OUTPUT_DIR = "rendered_skins" | |
| TEXTURE_OUTPUT_DIR = "scaled_textures" | |
| os.makedirs(RENDER_OUTPUT_DIR, exist_ok=True) | |
| os.makedirs(TEXTURE_OUTPUT_DIR, exist_ok=True) | |
| # 获取所有皮肤文件 | |
| TRAIN_DIR = "train" | |
| skin_files = [] | |
| for file in os.listdir(TRAIN_DIR): | |
| if file.endswith('.png'): | |
| skin_files.append(os.path.join(TRAIN_DIR, file)) | |
| print(f"找到 {len(skin_files)} 个皮肤文件") | |
| async def render_skin(skin_path, output_name): | |
| """渲染单个皮肤为3D全身视图(正面+背面并排)""" | |
| try: | |
| # 生成随机深色背景 (RGB值在0-80之间) | |
| bg_color = ( | |
| random.randint(0, 80), | |
| random.randint(0, 80), | |
| random.randint(0, 80) | |
| ) | |
| # 读取皮肤文件为PIL Image | |
| skin_img = Image.open(skin_path) | |
| # 创建Skin对象 | |
| skin = Skin(raw_skin=skin_img) | |
| # 为腿脚摆动添加±10度的随机偏移 | |
| leg_offset = random.randint(-10, 10) | |
| arm_offset = random.randint(-10, 10) | |
| vr_offset = random.randint(-6, 6) | |
| hr_offset = random.randint(-20,10) | |
| ratio_offset = random.randint(-2,0) | |
| # 计算带随机偏移的角度 | |
| #vrll_front = 15 + leg_offset # 左腿向前 | |
| vrll_front = 15 | |
| #vrrl_front = -15 - leg_offset # 右腿向后 | |
| vrrl_front = -15 | |
| #vrla_front = -15 - arm_offset # 左臂向后 | |
| vrla_front = -15 | |
| #vrra_front = 15 + arm_offset # 右臂向前 | |
| vrra_front = 15 | |
| # vrrl = -15 + vr_offset | |
| vrrl = -15 | |
| # hrrl = 35 + hr_offset | |
| hrrl = 35 | |
| # ratiorl = 30 + ratio_offset | |
| ratiorl = 30 | |
| # 渲染正面视图 - 3D行走姿势 | |
| render_front = Render( | |
| player=skin, | |
| vr=vrrl, # 垂直旋转,稍微向下看 | |
| hr=hrrl, # 水平旋转35度 | |
| hrh=0, # 头部水平旋转 | |
| vrll=vrll_front, # 左腿向前(带随机偏移) | |
| vrrl=vrrl_front, # 右腿向后(带随机偏移) | |
| vrla=vrla_front, # 左臂向后(带随机偏移) | |
| vrra=vrra_front, # 右臂向前(带随机偏移) | |
| ratio=ratiorl, # 缩放比例 | |
| aa=True, # 抗锯齿 | |
| display_hair=True, | |
| display_layers=True | |
| ) | |
| # 渲染背面视图 - 旋转180度(使用相反的角度) | |
| render_back = Render( | |
| player=skin, | |
| vr=vrrl, # 垂直旋转,稍微向下看 | |
| hr=hrrl + 180, # 水平旋转215度 (35+180) | |
| hrh=0, # 头部水平旋转 | |
| vrll=-vrrl_front, # 左腿向后(与正面右腿相反) | |
| vrrl=-vrll_front, # 右腿向前(与正面左腿相反) | |
| vrla=-vrra_front, # 左臂向前(与正面右臂相反) | |
| vrra=-vrla_front, # 右臂向后(与正面左臂相反) | |
| ratio=ratiorl, # 缩放比例 | |
| aa=True, # 抗锯齿 | |
| display_hair=True, | |
| display_layers=True | |
| ) | |
| # 获取渲染结果 | |
| front_img = await render_front.get_render() | |
| back_img = await render_back.get_render() | |
| # 创建1024x1024的画布,使用随机深色背景 | |
| canvas = Image.new('RGB', (1024, 1024), bg_color) | |
| # 计算每个图像的位置(并排放置) | |
| front_width, front_height = front_img.size | |
| back_width, back_height = back_img.size | |
| # 左侧放置正面视图 | |
| front_x = (512 - front_width) // 2 | |
| front_y = (1024 - front_height) // 2 | |
| # 右侧放置背面视图 | |
| back_x = 512 + (512 - back_width) // 2 | |
| back_y = (1024 - back_height) // 2 | |
| # 粘贴正面视图 | |
| if front_img.mode == 'RGBA': | |
| canvas.paste(front_img, (front_x, front_y), front_img) | |
| else: | |
| canvas.paste(front_img, (front_x, front_y)) | |
| # 粘贴背面视图 | |
| if back_img.mode == 'RGBA': | |
| canvas.paste(back_img, (back_x, back_y), back_img) | |
| else: | |
| canvas.paste(back_img, (back_x, back_y)) | |
| # 保存渲染图 | |
| render_output_path = os.path.join(RENDER_OUTPUT_DIR, output_name) | |
| canvas.save(render_output_path) | |
| return True | |
| except Exception as e: | |
| print(f"渲染失败 {skin_path}: {str(e)}") | |
| return False | |
| def scale_texture(skin_path, output_name): | |
| """缩放原始材质到1024x1024,灰色背景""" | |
| try: | |
| # 打开原始皮肤 | |
| img = Image.open(skin_path) | |
| # 处理所有可能有透明度的模式 | |
| # P模式(调色板)、LA模式、RGBA模式都可能有透明度 | |
| if img.mode in ('RGBA', 'LA', 'P'): | |
| # 先转换为RGBA以保留透明度信息 | |
| if img.mode != 'RGBA': | |
| img = img.convert('RGBA') | |
| # 创建灰色背景 | |
| background = Image.new('RGB', img.size, (128, 128, 128)) | |
| # 使用alpha通道合成 | |
| background.paste(img, (0, 0), img) | |
| img = background | |
| elif img.mode == 'RGB' and 'transparency' in img.info: | |
| # RGB模式但有透明色信息(通常是某个颜色被指定为透明) | |
| # 需要转换为RGBA来处理透明度 | |
| # 获取透明色 | |
| trans_color = img.info['transparency'] | |
| # 转换为RGBA | |
| img = img.convert('RGBA') | |
| # 获取图片数据 | |
| datas = img.getdata() | |
| # 将透明色替换为完全透明 | |
| newData = [] | |
| for item in datas: | |
| # 检查是否是透明色(RGB模式的transparency可能是元组或单个值) | |
| if isinstance(trans_color, tuple): | |
| if item[:3] == trans_color: | |
| newData.append((128, 128, 128, 0)) # 完全透明 | |
| else: | |
| newData.append(item) | |
| else: | |
| # 单通道透明度(灰度图) | |
| if item[0] == trans_color: | |
| newData.append((128, 128, 128, 0)) | |
| else: | |
| newData.append(item) | |
| img.putdata(newData) | |
| # 创建灰色背景并合成 | |
| background = Image.new('RGB', img.size, (128, 128, 128)) | |
| background.paste(img, (0, 0), img) | |
| img = background | |
| elif img.mode != 'RGB': | |
| # 转换其他模式为RGB | |
| img = img.convert('RGB') | |
| # 使用最近邻插值缩放皮肤材质(保持像素风格) | |
| # 通常Minecraft皮肤是64x64或64x32 | |
| img_resized = img.resize((1024, 1024), Image.NEAREST) | |
| # 确保保存为RGB模式(没有透明通道) | |
| if img_resized.mode != 'RGB': | |
| img_resized = img_resized.convert('RGB') | |
| # 保存缩放后的材质 | |
| texture_output_path = os.path.join(TEXTURE_OUTPUT_DIR, output_name) | |
| img_resized.save(texture_output_path, 'PNG') | |
| return True | |
| except Exception as e: | |
| print(f"缩放失败 {skin_path}: {str(e)}") | |
| return False | |
| async def process_all_skins(): | |
| """处理所有皮肤""" | |
| total = len(skin_files) | |
| success_render = 0 | |
| success_texture = 0 | |
| for idx, skin_path in enumerate(skin_files, 1): | |
| # 获取文件名(不含扩展名) | |
| filename = os.path.basename(skin_path) | |
| output_name = filename | |
| print(f"处理 [{idx}/{total}]: {filename}") | |
| # 渲染皮肤 | |
| if await render_skin(skin_path, output_name): | |
| success_render += 1 | |
| # 缩放材质 | |
| if scale_texture(skin_path, output_name): | |
| success_texture += 1 | |
| # 每处理10个文件显示进度 | |
| if idx % 10 == 0: | |
| print(f"进度: {idx}/{total} - 渲染成功: {success_render}, 材质成功: {success_texture}") | |
| print(f"\n完成!") | |
| print(f"总计: {total}") | |
| print(f"渲染成功: {success_render}") | |
| print(f"材质缩放成功: {success_texture}") | |
| print(f"渲染图保存在: {RENDER_OUTPUT_DIR}/") | |
| print(f"缩放材质保存在: {TEXTURE_OUTPUT_DIR}/") | |
| if __name__ == "__main__": | |
| asyncio.run(process_all_skins()) | |