commit 4c2972e7a290d1ca1f76b44eeb6d4cf5ab8ead07 Author: Johannes Date: Fri Mar 13 22:12:04 2026 +0100 initial setup: ComfyUI + kohya_ss scripts, LoRA config, workflows diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6badae3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,31 @@ +# models - too large for git +models/ +*.safetensors +*.ckpt +*.pt +*.pth +*.bin + +# generated outputs +output/ +outputs/ + +# venvs / installs +comfyui/venv/ +comfyui/__pycache__/ +kohya_ss/venv/ +kohya_ss/__pycache__/ +kohya_ss/logs/ + +# training data (images) +training_data/ + +# ComfyUI user data / cache +comfyui/user/ +comfyui/temp/ +comfyui/custom_nodes/**/node_modules/ + +# misc +*.log +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..c231686 --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# animepics + +Anime image generation + LoRA training setup. + +## Stack + +- **ComfyUI** — image generation UI +- **NoobAI-XL** — base anime model (SDXL-based, SFW+NSFW) +- **kohya_ss** — LoRA training + +## Requirements + +- Python 3.10+ installed and in PATH +- Git installed +- NVIDIA GPU with CUDA (4070 recommended) +- ~20GB free disk space for models + +## Setup + +Run once to install everything: + +```powershell +.\setup.ps1 +``` + +This will: +1. Clone ComfyUI and install dependencies +2. Clone kohya_ss and install dependencies +3. Install ComfyUI custom nodes (AnimateDiff, ControlNet, etc.) +4. Download the NoobAI-XL base model + VAE + +## Launch + +```powershell +# Start image generation UI (opens browser at localhost:8188) +.\launch_comfyui.ps1 + +# Start LoRA training UI (opens browser at localhost:7860) +.\launch_kohya.ps1 +``` + +## LoRA Training + +1. Put your training images in `training_data//img/10_/` +2. Copy `training/example_lora_config.toml` and edit it +3. Launch kohya and use the GUI, or run `.\train_lora.ps1 ` + +## Directory Structure + +``` +animepics/ +├── comfyui/ # ComfyUI install (gitignored venv) +├── kohya_ss/ # kohya_ss install (gitignored venv) +├── models/ # shared model storage (gitignored) +│ ├── checkpoints/ # base models (.safetensors) +│ ├── loras/ # trained LoRAs +│ ├── vae/ # VAE models +│ ├── embeddings/ # textual inversions +│ └── controlnet/ # ControlNet models +├── training_data/ # LoRA training images (gitignored) +├── output/ # generated images (gitignored) +├── training/ # LoRA training configs +└── workflows/ # ComfyUI workflow JSON files +``` + +## Model Downloads + +Base model (NoobAI-XL Vpred): +- https://civitai.com/models/833294 + +Good NSFW VAE: +- Already baked into NoobAI, but sdxl_vae.safetensors from stabilityai works too diff --git a/config/extra_model_paths.yaml b/config/extra_model_paths.yaml new file mode 100644 index 0000000..700f553 --- /dev/null +++ b/config/extra_model_paths.yaml @@ -0,0 +1,11 @@ +# ComfyUI extra model paths — points to shared models/ directory +# so both ComfyUI and kohya_ss use the same model storage + +animepics: + base_path: E:/animepics/models + checkpoints: checkpoints + loras: loras + vae: vae + embeddings: embeddings + controlnet: controlnet + upscale_models: upscale_models diff --git a/launch_comfyui.ps1 b/launch_comfyui.ps1 new file mode 100644 index 0000000..39a3381 --- /dev/null +++ b/launch_comfyui.ps1 @@ -0,0 +1,12 @@ +#!/usr/bin/env pwsh +# launch_comfyui.ps1 — start ComfyUI + +$root = $PSScriptRoot +Set-Location "$root/comfyui" + +.\venv\Scripts\Activate.ps1 +python main.py ` + --listen 127.0.0.1 ` + --port 8188 ` + --extra-model-paths-config "$root/comfyui/extra_model_paths.yaml" ` + --preview-method auto diff --git a/launch_kohya.ps1 b/launch_kohya.ps1 new file mode 100644 index 0000000..5047214 --- /dev/null +++ b/launch_kohya.ps1 @@ -0,0 +1,8 @@ +#!/usr/bin/env pwsh +# launch_kohya.ps1 — start kohya_ss GUI + +$root = $PSScriptRoot +Set-Location "$root/kohya_ss" + +.\venv\Scripts\Activate.ps1 +python kohya_gui.py --listen 127.0.0.1 --server_port 7860 diff --git a/setup.ps1 b/setup.ps1 new file mode 100644 index 0000000..e4b18a2 --- /dev/null +++ b/setup.ps1 @@ -0,0 +1,88 @@ +#!/usr/bin/env pwsh +# setup.ps1 — one-time install of ComfyUI + kohya_ss + +$root = $PSScriptRoot +Set-Location $root + +Write-Host "==> setting up animepics environment" -ForegroundColor Cyan + +# create model dirs +$model_dirs = @( + "models/checkpoints", + "models/loras", + "models/vae", + "models/embeddings", + "models/controlnet", + "models/upscale_models", + "training_data", + "output" +) +foreach ($dir in $model_dirs) { + New-Item -ItemType Directory -Force -Path "$root/$dir" | Out-Null +} +Write-Host " created model directories" -ForegroundColor Green + +# --- ComfyUI --- +Write-Host "`n==> installing ComfyUI" -ForegroundColor Cyan +if (-not (Test-Path "$root/comfyui")) { + git clone https://github.com/comfyanonymous/ComfyUI.git "$root/comfyui" +} else { + Write-Host " comfyui already cloned, pulling latest" -ForegroundColor Yellow + git -C "$root/comfyui" pull +} + +Push-Location "$root/comfyui" +python -m venv venv +.\venv\Scripts\Activate.ps1 +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 +pip install -r requirements.txt +deactivate +Pop-Location +Write-Host " ComfyUI installed" -ForegroundColor Green + +# ComfyUI custom nodes +Write-Host "`n==> installing ComfyUI custom nodes" -ForegroundColor Cyan +$custom_nodes = @( + @{ name="ComfyUI-Manager"; url="https://github.com/ltdrdata/ComfyUI-Manager.git" }, + @{ name="ComfyUI_IPAdapter_plus"; url="https://github.com/cubiq/ComfyUI_IPAdapter_plus.git" }, + @{ name="ComfyUI-Advanced-ControlNet"; url="https://github.com/Kosinkadink/ComfyUI-Advanced-ControlNet.git" }, + @{ name="was-node-suite-comfyui"; url="https://github.com/WASasquatch/was-node-suite-comfyui.git" } +) +New-Item -ItemType Directory -Force -Path "$root/comfyui/custom_nodes" | Out-Null +foreach ($node in $custom_nodes) { + $node_path = "$root/comfyui/custom_nodes/$($node.name)" + if (-not (Test-Path $node_path)) { + git clone $node.url $node_path + Write-Host " cloned $($node.name)" -ForegroundColor Green + } else { + git -C $node_path pull + Write-Host " updated $($node.name)" -ForegroundColor Yellow + } +} + +# copy extra_model_paths config so ComfyUI finds our shared models dir +Copy-Item -Force "$root/config/extra_model_paths.yaml" "$root/comfyui/extra_model_paths.yaml" +Write-Host " copied extra_model_paths.yaml" -ForegroundColor Green + +# --- kohya_ss --- +Write-Host "`n==> installing kohya_ss" -ForegroundColor Cyan +if (-not (Test-Path "$root/kohya_ss")) { + git clone https://github.com/bmaltais/kohya_ss.git "$root/kohya_ss" +} else { + Write-Host " kohya_ss already cloned, pulling latest" -ForegroundColor Yellow + git -C "$root/kohya_ss" pull +} + +Push-Location "$root/kohya_ss" +python -m venv venv +.\venv\Scripts\Activate.ps1 +pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124 +pip install -r requirements.txt +deactivate +Pop-Location +Write-Host " kohya_ss installed" -ForegroundColor Green + +Write-Host "`n==> setup complete!" -ForegroundColor Cyan +Write-Host " next: download your base model into models/checkpoints/" -ForegroundColor White +Write-Host " recommended: NoobAI-XL from https://civitai.com/models/833294" -ForegroundColor White +Write-Host "`n then run: .\launch_comfyui.ps1" -ForegroundColor White diff --git a/train_lora.ps1 b/train_lora.ps1 new file mode 100644 index 0000000..e8b635f --- /dev/null +++ b/train_lora.ps1 @@ -0,0 +1,23 @@ +#!/usr/bin/env pwsh +# train_lora.ps1 — run LoRA training from a config file +# usage: .\train_lora.ps1 training/my_lora.toml + +param( + [Parameter(Mandatory=$true)] + [string]$config_file +) + +$root = $PSScriptRoot + +if (-not (Test-Path "$root/$config_file")) { + Write-Host "config file not found: $config_file" -ForegroundColor Red + exit 1 +} + +Set-Location "$root/kohya_ss" +.\venv\Scripts\Activate.ps1 + +accelerate launch ` + --num_cpu_threads_per_process 2 ` + train_network.py ` + --config_file "$root/$config_file" diff --git a/training/example_lora_config.toml b/training/example_lora_config.toml new file mode 100644 index 0000000..0c7eed9 --- /dev/null +++ b/training/example_lora_config.toml @@ -0,0 +1,54 @@ +# example LoRA training config for kohya_ss (SDXL) +# copy this, rename it, and edit the paths/settings for your lora +# then train with: .\train_lora.ps1 training/my_lora.toml + +[model_arguments] +pretrained_model_name_or_path = "E:/animepics/models/checkpoints/noobai-xl.safetensors" +# set to true for vpred models (NoobAI-XL uses vpred) +v_parameterization = true +zero_terminal_snr = true + +[saving_arguments] +save_every_n_epochs = 1 +save_model_as = "safetensors" +output_dir = "E:/animepics/models/loras" +output_name = "my_lora_v1" + +[dataset_arguments] +# dataset dir structure: training_data//img/_/ +train_data_dir = "E:/animepics/training_data/my_lora/img" +resolution = "1024,1024" +enable_bucket = true +min_bucket_reso = 512 +max_bucket_reso = 2048 +bucket_reso_steps = 64 +caption_extension = ".txt" +shuffle_caption = true +keep_tokens = 1 + +[training_arguments] +output_dir = "E:/animepics/models/loras" +logging_dir = "E:/animepics/kohya_ss/logs" +max_train_epochs = 10 +train_batch_size = 1 +gradient_accumulation_steps = 1 +gradient_checkpointing = true +mixed_precision = "bf16" +save_precision = "bf16" +seed = 42 +max_token_length = 225 +xformers = true +# learning rates — good defaults for NoobAI-XL +learning_rate = 0.0001 +unet_lr = 0.0001 +text_encoder_lr = 0.00005 +lr_scheduler = "cosine_with_restarts" +lr_warmup_steps = 100 +optimizer_type = "AdamW8bit" + +[network_arguments] +network_module = "networks.lora" +network_dim = 32 # rank — higher = more capacity, 16-64 is typical +network_alpha = 16 # usually half of dim +# optional: train only specific layers +# network_args = ["conv_dim=16", "conv_alpha=8"] diff --git a/workflows/anime_txt2img.json b/workflows/anime_txt2img.json new file mode 100644 index 0000000..682037a --- /dev/null +++ b/workflows/anime_txt2img.json @@ -0,0 +1,120 @@ +{ + "last_node_id": 9, + "last_link_id": 9, + "nodes": [ + { + "id": 4, + "type": "CheckpointLoaderSimple", + "pos": [26, 474], + "size": [315, 98], + "flags": {}, + "order": 0, + "mode": 0, + "outputs": [ + {"name": "MODEL", "type": "MODEL", "links": [1], "slot_index": 0}, + {"name": "CLIP", "type": "CLIP", "links": [3, 5], "slot_index": 1}, + {"name": "VAE", "type": "VAE", "links": [8], "slot_index": 2} + ], + "properties": {"Node name for S&R": "CheckpointLoaderSimple"}, + "widgets_values": ["noobai-xl.safetensors"] + }, + { + "id": 6, + "type": "CLIPTextEncode", + "pos": [415, 186], + "size": [422, 164], + "flags": {}, + "order": 2, + "mode": 0, + "inputs": [{"name": "clip", "type": "CLIP", "link": 3}], + "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [4], "slot_index": 0}], + "properties": {"Node name for S&R": "CLIPTextEncode"}, + "widgets_values": ["1girl, anime, masterpiece, best quality, highly detailed, beautiful face, solo"] + }, + { + "id": 7, + "type": "CLIPTextEncode", + "pos": [415, 400], + "size": [422, 164], + "flags": {}, + "order": 3, + "mode": 0, + "inputs": [{"name": "clip", "type": "CLIP", "link": 5}], + "outputs": [{"name": "CONDITIONING", "type": "CONDITIONING", "links": [6], "slot_index": 0}], + "properties": {"Node name for S&R": "CLIPTextEncode"}, + "widgets_values": ["worst quality, low quality, bad anatomy, extra limbs, poorly drawn face, ugly, watermark, text"] + }, + { + "id": 5, + "type": "EmptyLatentImage", + "pos": [473, 609], + "size": [315, 106], + "flags": {}, + "order": 1, + "mode": 0, + "outputs": [{"name": "LATENT", "type": "LATENT", "links": [2], "slot_index": 0}], + "properties": {"Node name for S&R": "EmptyLatentImage"}, + "widgets_values": [832, 1216, 1] + }, + { + "id": 3, + "type": "KSampler", + "pos": [863, 186], + "size": [315, 474], + "flags": {}, + "order": 4, + "mode": 0, + "inputs": [ + {"name": "model", "type": "MODEL", "link": 1}, + {"name": "positive", "type": "CONDITIONING", "link": 4}, + {"name": "negative", "type": "CONDITIONING", "link": 6}, + {"name": "latent_image", "type": "LATENT", "link": 2} + ], + "outputs": [{"name": "LATENT", "type": "LATENT", "links": [7], "slot_index": 0}], + "properties": {"Node name for S&R": "KSampler"}, + "widgets_values": [42, "fixed", 28, 7, "dpmpp_2m", "karras", 1] + }, + { + "id": 8, + "type": "VAEDecode", + "pos": [1209, 186], + "size": [210, 46], + "flags": {}, + "order": 5, + "mode": 0, + "inputs": [ + {"name": "samples", "type": "LATENT", "link": 7}, + {"name": "vae", "type": "VAE", "link": 8} + ], + "outputs": [{"name": "IMAGE", "type": "IMAGE", "links": [9], "slot_index": 0}], + "properties": {"Node name for S&R": "VAEDecode"} + }, + { + "id": 9, + "type": "SaveImage", + "pos": [1451, 186], + "size": [210, 58], + "flags": {}, + "order": 6, + "mode": 0, + "inputs": [{"name": "images", "type": "IMAGE", "link": 9}], + "properties": {"Node name for S&R": "SaveImage"}, + "widgets_values": ["anime"] + } + ], + "links": [ + [1, 4, 0, 3, 0, "MODEL"], + [2, 5, 0, 3, 3, "LATENT"], + [3, 4, 1, 6, 0, "CLIP"], + [4, 6, 0, 3, 1, "CONDITIONING"], + [5, 4, 1, 7, 0, "CLIP"], + [6, 7, 0, 3, 2, "CONDITIONING"], + [7, 3, 0, 8, 0, "LATENT"], + [8, 4, 2, 8, 1, "VAE"], + [9, 8, 0, 9, 0, "IMAGE"] + ], + "groups": [], + "config": {}, + "extra": {"ds": {"scale": 0.8, "offset": [0, 0]}}, + "version": 0.4 +}