Author: Roy Okello, Stelar Labs
IML is a toolkit for image, video, and diffusion model workflows. It includes:
Install Python 3.10+.
Install CUDA 12.3 for GPU-accelerated tools.
Clone the repo.
git clone <repo-url>cd imlCreate a virtual environment.
python -m venv venvpython3 -m venv .venvActivate the virtual environment.
venv\Scripts\activatesource .venv/bin/activateInstall Python dependencies.
pip install -r requirements.txtRun the root directory creator.
python -c "from utils.startup import prepare_directories; prepare_directories('/models', '/tools')"/models/flux/models/gemma4/tools/ffmpeg/tools/llama.cppInstall FFmpeg.
ffmpeg and ffprobe are on PATH, or place them under the configured tools root.Install llama.cpp.
Download FLUX.2 weights.
Download Gemma 4 weights.
mmproj.bf16 from ggml-org: ggml-org/gemma-4-E2B-it-GGUFmmproj.fp16 from Unsloth: unsloth/gemma-4-E4B-it-GGUFNotes:
video.loop and YOLO inference/training.video.group and video.loop.set PYTORCH_NO_CUDA_MEMORY_CACHING=1.image.groupwidth, height, longest, or shortest side into size thresholds_<size> folders by default; add --orientation-split to nest horizontal / vertical folders for longest and shortest.txt captions alongside imagespython -m image.group --input "images" --orientation shortest --sizes 255 383 511 767 --dry-runpython -m image.group --input "images" --orientation shortest --orientation-split --sizes 256 384 512 768 --dry-runimage.collect.txt captions alongside imagespython -m image.collect --input_dir "images" --output_dir "images_flat" --mode fileimage.dataset.txt caption pairs without repeating within a run<output>/<number>.<ext> and the selected reference caption to <output>/<number>.txt<output>/<number>/ and copies the selected reference image into that subfolderpython -m image.dataset --inputs "images" --refs "refs" --output "dataset"--filename-width (default 6), --seed, --dry-runimage.shape--inplace
side mode sets the chosen side (width, height, or longest) and scales the other side to preserve aspect ratioratio mode picks the closest ratio by aspect, computes the target size, then applies a best-fit center crop; --min sets the shortest side exactly, --max sets the longest side exactly, and --mid caps square outputs at that side while keeping square inputs at or above --min aligned to --length-multiple--min, --max, and --mid must be multiples of --length-multiplepython -m image.shape --input "images" --output "out" --mode side --side width --size 1024python -m image.shape --input "images" --output "out" --mode ratio --ratios 1x1 3x4 4x3 1x2 --length-multiple 64python -m image.shape --input "images" --inplace --mode side --side height --size 768python -m image.shape --input "images" --inplace --mode side --side longest --size 768python -m image.shape --input "images" --inplace --mode ratio --ratios 1x1 2x3 3x4 1x2 2x1 4x3 3x2 --length-multiple 64 --min 512python -m image.shape --input "images" --inplace --mode ratio --ratios 1x1 2x3 3x4 1x2 2x1 4x3 3x2 --length-multiple 64 --max 768python -m image.shape --input "images" --inplace --mode ratio --ratios 1x1 2x3 3x4 1x2 2x1 4x3 3x2 --length-multiple 64 --min 512 --mid 640
--mid will not upscale square inputs below 640x640; square inputs at or above --min are snapped to the nearest --length-multiplepython -m image.shape --inplace --mode ratio --ratios 1x1 2x3 1x2 2x1 3x2 --length-multiple 64 --min 512 --mid 512 --input "images"image.tag.mainwd14_model by default).txt files beside each image (same basename)python -m image.tag.main --input "images" --thresh 0.35 --max-tags 50--thresh, --general-thresh, --character-thresh, --max-tags (0 for none), --model, --prefiximage.caption.txt captions beside each image--max-res (768 by default).txt captionpython -m image.caption --input "images" --trigger "trxxr" --prompt-file "prompt_woman.txt"python -m image.caption --input "images" --trigger "trxxr" --prompt-file "prompt_man.txt"python -m image.caption --input "images" --trigger "objxx" --prompt-file "prompt_object.txt"python -m image.caption --input "images" --trigger "mystyle" --prompt-file "prompt_style.txt"--trigger, --prompt-file, --max-res, --max-tokens, --server-url, --model, --print-response, --resetimage.tag.extract.txt captions beside each image<input>/<tag1>__<tag2>python -m image.tag.extract --input "images" --tags "1girl, blonde_hair"image.tag.add.txt caption in the input directorypython -m image.tag.add --input "images" --tags "1girl, blonde_hair"--position (start or end)image.tag.replace.txt caption under a dataset directorypython -m image.tag.replace --dataset "images" --old "old tag" --new "new tag"image.tag.group.txt captions beside each image<input>/<caption_count>_<image_count_in_bucket>0_12, 3_57, 8_4python -m image.tag.group --input "images"image.dedup--first-set / --second-set limits comparisons to matching height, width, longest, shortest, or ratio--target-set balanced|weighted (default: weighted).txt captionspython -m image.dedup.main --input "images" --dhash-threshold 8python -m image.dedup.main --input "images" --first-set ratio --second-set longest --dhash-threshold 8python -m image.dedup.main --input "images" --target 500python -m image.dedup.main --input "images" --target 500 --first-set ratio --target-set balancedpython -m image.dedup.main --input "images" --target 500 --output "images_dedup"python -m image.dedup.main --input "images" --output "images_dedup" --sharpness-weight 1.0 --exposure-weight 0.4 --noise-weight 0.3 --artifact-weight 0.55--input, -o/--output--dhash-size, --dhash-threshold--target--target-set (default weighted; only relevant with --target and sets)--first-set, --second-set (--second-set requires --first-set)--min-group-size--sharpness-weight (default 1.0)--exposure-weight (default 0.4)--noise-weight (default 0.3)--artifact-weight (default 0.55)image.generate — Web GUIA Flask-based web interface for generating images, supporting Flux 2 (4b/9b, distilled/base) and Ideogram models.
static/config/python -m image.generate --root /path/to/data then open http://localhost:5053image.similar — Web GUIA Flask-based web interface for finding and exporting near-duplicate images using dHash and Union-Find clustering.
.txt captions) to a user-specified output folder{root}/image/similar/{timestamp}/thumbs/python -m image.similar.main --root /path/to/data --port 5051image.cull--mode copy|link|inplace for kept-image output handling; logs and metrics savedpython -m image.cull.label.main --project "/proj" --stage 1 then open http://localhost:5050stage_<N>_cull_labels.csv under the project root (resumes if present)python -m image.cull.train --project "/proj" --stage 1 --batch-size 32python -m image.cull.sample --project "/proj" --stage 1 --count 1000 (runs the stage cull model on a random sample and hard-links predicted keeps to stage_<N>_cull_samples)python -m image.cull.main --project "/proj" --stage 1python -m image.cull.main --project "/proj" --stage 1 --mode linkpython -m image.cull.main --project "/proj" --stage 1 --mode inplacepython -m image.cull.main --project "/proj" --resume 14487--mode defaults to copy--resume compares the numeric filename stem (e.g., 014486.png -> 14486) and handles discontinuities--resume and no --stage, source stage is inferred as latest - 1 (resume into the latest stage)stage_<N> with images and stage_<N>_cull_labels.csv for training; writes stage_<N>_cull_model.pthimage.rankstage_<N>_rank_labels.csv under the project root (resumes if present)--stage omitted, uses the latest stage_<N> foundpython -m image.rank.label.main --project "/proj" --stage 1 then open http://localhost:5000image.crop000001.png via --filename-width)python -m image.crop.label.main --project "/proj" --stage 1 then open http://localhost:5051python -m image.crop.label.main --project "/proj" --stage 1 --port 5001stage_<N>_crop_labels.csv under the project root (resumes if present)python -m image.crop.prepare.main --model yolo --project "/proj" --stage 1 --val_split 0.2--balance only if you explicitly want to downsample every class to the smallest class countpython -m image.crop.train --project "/proj" --stage 1 --variant yolo11n --epochs 100 --batch 16python -m image.crop.main --project "/proj" --stage 1 --classes 0 1 2 3 4python -m image.crop.main --project "/proj" --stage 1 --resolution 768 --classes 0 1 2 3 4video.extract--all--all overrides --frames, --time, and --random--buffer compares the marked frame with nearby frames and saves the best-scoring result--buffer 0 saves the exact marked frame; --all ignores --buffer--filename-width (default 000001.png style)--resolution to keep original frame size)python -m video.extract.select --root "/tools" for previewing interval marks, optionally limiting work to time spans like 1:00 - 1:10, 32:40 - 40:00, or 00:15:30 - 00:18:00, and saving best nearby native-res frames<root>/ffmpeg/bin/ffmpeg.exe<output>/preview, and that folder is cleared before each new preview buildpython -m video.extract --input "videos" --output "frames" --frames 2 --time second --resolution 768python -m video.extract --input "videos" --output "frames_best" --frames 2 --time second --buffer 3 --resolution 768python -m video.extract --input "videos" --output "frames_all" --frames 50 --random --collate --resolution 512python -m video.extract --input "videos" --output "frames_native" --frames 2 --time secondpython -m video.extract --input "videos" --output "all_frames" --all --collatepython -m video.extract.select --root "/tools" --port 5052video.groupffprobe to read width/height_<orientation>/_<short-side> buckets and prefixes non-group path to filenamepython -m video.group --root "videos" --ffprobe "/tools/ffprobe" --sizes 256 384 512 768 1024 --dry-runvideo.loopscale_cuda), SSIM-based loop detection, batched exports.mkv loops at target --fps and --out-respython -m video.loop --input "videos" --output "loops" --lengths 33 49 --fps 30 --in-res 64 --out-res 768 --ffmpeg "/tools/ffmpeg"video.grid.generateinterval mode samples frames at a fixed seconds interval and emits as many full grids as possiblesegment_midpoints mode divides the full video into exactly rows * cols segments and picks the middle frame from each segment to build one grid<dir>/000001.png ... + config.jsonpython -m video.grid.generate -i "video.mp4" -o "grids" --ffmpeg "/tools/ffmpeg" --rows 3 --cols 3 --selection-mode interval --frame-interval 30video.grid.animatepython -m video.grid.animate -i "grids/000001.png" -o "animation.gif" --ffmpeg "/tools/ffmpeg" --length 5 --fps 12 --format gifThe unified flux2 package provides the version-aware entrypoints for generation and quantization.
flux2.gen4b and 9b--version to select <root>/flux2/4b or <root>/flux2/9bmodel directory:
tokenizertext_encoderschedulervaetransformer/distilltransformer/base4 steps and guidance scale 1.0--base switches to base-model defaults: 50 steps and CFG guidance scale 4.0--images "img1.png, img2.png" by encoding reference images into latent tokens--ref-size, then cropped to the nearest valid VAE multiple before encoding--text-quant-method and --denoiser-quant-method to choose none, sym-high, sym-med, aff-high, aff-med, or aff-low--text-quant-method none or --denoiser-quant-method none keeps the original fp16 checkpoint weights<root>/<model>/model/text_encoder/symmetric_high_quant.safetensors, symmetric_med_quant.safetensors, affine_high_quant.safetensors, affine_med_quant.safetensors, or affine_low_quant.safetensors<root>/<model>/model/transformer/<variant>/symmetric_high_quant.safetensors, symmetric_med_quant.safetensors, affine_high_quant.safetensors, affine_med_quant.safetensors, or affine_low_quant.safetensors--loras "path1:1.0,path2:0.7"python -m flux2.gen --root "/models/flux" --version 4b --prompt "cinematic portrait, 85mm photo, rim lighting"python -m flux2.gen --root "/models/flux" --version 9b --base --prompt "fashion editorial, full body, studio backdrop" --width 768 --height 1024python -m flux2.gen --root "/models/flux" --version 4b --images "/data/ref1.png, /data/ref2.png" --prompt "same outfit, new pose, soft daylight"python -m flux2.gen --root "/models/flux" --version 9b --prompt "stylized character art" --loras "/models/lora/style.safetensors:0.8,/models/lora/character.safetensors:1.0"python -m flux2.gen --root "/models/flux" --version 4b --prompt "product render on white seamless" --denoiser-quant-method none--root--version--prompt--images--width, --height--steps--seed--base--ref-size--guidance-scale--text-quant-method--denoiser-quant-method--loras--max-lengthflux2.train.main--version to select <root>/flux2_4b/model or <root>/flux2_9b/modelprompt_embeds and text_idsmodel/transformer/base directory--project as the dataset directory and training run directory<project>/models/<step>.safetensors<project>/models/steps.csv--root--version--project--steps (default: 1024)--resume--model (LoRA init checkpoint file, optional)--text-quant (default: aff-high-mini)--denoiser-quant (default: aff-high-mini)--trigger--cache-text (pre-encode captions to CPU)--cache-images (pre-encode image latents to disk)--rank (default: 32)--alpha (default: 32)--target_res (default: 384)--ref_res (default: 384)--lr (default: 1e-4)python -m flux2.train.main --root "/models/flux" --version 4b --project "/data/flux_dataset" --trigger "TOK"flux2.sampleflux2.train--project as the training project directory<project>/models for epoch_<n>.safetensors and writes images to <project>/samples--start to skip earlier epochs and only render checkpoints at or after the selected epoch number--project--prompt--trigger, which is encoded once and reused for all samples--samples spreads selections evenly across the dataset from image 1 to the last image--sample-indices overrides --samples with explicit 1-based dataset indices such as "1,25,100"--force-size is dataset-only and rescales each selected sample so its longest side matches the requested size while preserving aspect ratio--width and --height--sample-seed + sample_indexflux2.gen--text-quant-method and --denoiser-quant-method to choose none, sym-high, sym-med, aff-high, aff-med, or aff-lowmodel directory:
tokenizertext_encoderschedulervaetransformer/distill<project>/samples/<step>_<width>x<height>.png for single samples, or <step>_<index>_<width>x<height>.png for multi-samples000012_768x768.png is rendered from step checkpoint models/12.safetensors at 768x768 resolution000012_3_768x768.png is sample 3 from the same checkpointpython -m flux2.sample --root "/models/flux" --version 4b --project "/data/flux_dataset"python -m flux2.sample --root "/models/flux" --version 4b --project "/data/flux_dataset" --samples 4 --start 10python -m flux2.sample --root "/models/flux" --version 9b --project "/data/flux_dataset" --sample-indices "1,25,100"python -m flux2.sample --root "/models/flux" --version 4b --project "/data/flux_dataset" --sample-indices "5,9" --trigger "TOK" --force-size 768python -m flux2.sample --root "/models/flux" --version 9b --project "/data/flux_dataset" --prompt "fashion portrait, soft key light, editorial pose" --width 768 --height 1024--root--version--project--prompt--samples--sample-indices--start--sample-seed--trigger--force-size--width, --height--target-res, --ref-res--steps, --distill--text-quant-method--denoiser-quant-methodflux2.text_encoder.quant4b or 9bmodel-00001-of-00002.safetensors for 4bmodel-00001-of-00004.safetensors through model-00004-of-00004.safetensors for 9b--input must point directly at the source text_encoder model directory<iml_root>/<model>/model/text_encoder/<method>_quant.safetensors, creating that output directory if neededpython -m flux2.text_encoder.quant --root "/iml_root" --version 4b --input "/models/flux2_4b/text_encoder" --method sym-high-mini--root--input--version--methodflux2.denoiser.quant4b or 9bdiffusion_pytorch_model.safetensors for 4bdiffusion_pytorch_model-00001-of-00002.safetensors and diffusion_pytorch_model-00002-of-00002.safetensors for 9b--input is required; point directly at a source transformer checkpoint directory5 double blocks and 20 single blocks for 4b8 double blocks and 24 single blocks for 9b<root>/<model>/model/transformer/<variant>/<method>_quant.safetensors, creating that output directory if neededpython -m flux2.denoiser.quant --root "/iml_root" --version 4b --variant base --method sym-med-mini--root--version--variant--method8e-5 to 1e-41500-25001500-3000512px, then move higher latertimestep_type: "weighted"timestep_type: "shift"--project is the training run folder<project>/images.png, .jpg, .jpeg, .webp.txt caption with the same basename.txt caption and --trigger "<text>" is requiredimages/000001.pngimages/000001.txtimages/000002.jpgimages/000002.txtimages/000001.pngimages/000002.jpgmodels/logs/20-40 images10-15 imagesideogram.genpatch_size * ae_scale_factor (default 16)python -m ideogram.gen --root "/models" --prompt "A cat holding a sign" --output "cat.png"python -m ideogram.gen --root "/models" --prompt "cinematic portrait" --width 768 --height 768 --steps 64 --guidance-scale 5.0python -m ideogram.gen --root "/models" --prompt "futuristic city" --text-quant-method sym-high --denoiser-quant-method sym-med--root, --prompt, --output--width, --height, --steps, --guidance-scale, --seed--text-quant-method, --denoiser-quant-method--offloadideogram.text_encoder.quant<input>/model.safetensorslanguage_model. prefix in the saved output<root>/ideogram/text_encoder/<method>_quant.safetensorspython -m ideogram.text_encoder.quant --root "/models" --input "/path/to/f8/text_encoder" --method sym-high-nano--root, --input, --methodideogram.denoiser.quant<input>/transformer/diffusion_pytorch_model.safetensors<input>/unconditional_transformer/diffusion_pytorch_model.safetensors<root>/ideogram/transformer/<variant>/<method>_quant.safetensorspython -m ideogram.denoiser.quant --root "/models" --input "/path/to/f8/root" --variant cond --method sym-med-mini--root, --input, --method, --variant (cond or uncond)utils.quant.estimator.main.safetensors output size without running CUDA quantization.pth source checkpoints for PyTorch-backed model componentsflux2_4b_text_encoderflux2_9b_text_encoderflux2_4b_denoiser, flux2_9b_denoisergemma4_2bwan22_5b_text_encoder, wan22_5b_denoiserpython -m utils.quant.estimator.main --model flux2_4b_text_encoder --source "/models/flux/flux_2_klein_4b/model/text_encoder"python -m utils.quant.estimator.main --model flux2_9b_denoiser --source "/models/flux/flux2_9b/model/transformer/base"python -m utils.quant.estimator.main --model gemma4_2b --source "/models/gemma4"python -m utils.quant.estimator.main --model wan22_5b_text_encoder --source "/models/wan/wan22/model/text_encoder"--model--sourceutils.quant.evalfp16 only for the Flux 2 Klein 4B denoiser linear tensor shapesutils.quant dequantizers:
symmetric_high_dequantsymmetric_med_dequantaffine_high_dequantaffine_med_dequantaffine_low_dequantquantize_to_symmetricquantize_to_affinefp16 weight tensor for each casemax_abs_diffmean_abs_diffutils/quant/cudapython -m utils.quant.eval106 commits
Python
83.1%
JavaScript
4.5%
HTML
3.6%
Cuda
3.3%
CSS
3.1%
C++
2.5%
Author: Roy Okello, Stelar Labs
IML is a toolkit for image, video, and diffusion model workflows. It includes:
Install Python 3.10+.
Install CUDA 12.3 for GPU-accelerated tools.
Clone the repo.
git clone <repo-url>cd imlCreate a virtual environment.
python -m venv venvpython3 -m venv .venvActivate the virtual environment.
venv\Scripts\activatesource .venv/bin/activateInstall Python dependencies.
pip install -r requirements.txtRun the root directory creator.
python -c "from utils.startup import prepare_directories; prepare_directories('/models', '/tools')"/models/flux/models/gemma4/tools/ffmpeg/tools/llama.cppInstall FFmpeg.
ffmpeg and ffprobe are on PATH, or place them under the configured tools root.Install llama.cpp.
Download FLUX.2 weights.
Download Gemma 4 weights.
mmproj.bf16 from ggml-org: ggml-org/gemma-4-E2B-it-GGUFmmproj.fp16 from Unsloth: unsloth/gemma-4-E4B-it-GGUFNotes:
video.loop and YOLO inference/training.video.group and video.loop.set PYTORCH_NO_CUDA_MEMORY_CACHING=1.image.groupwidth, height, longest, or shortest side into size thresholds_<size> folders by default; add --orientation-split to nest horizontal / vertical folders for longest and shortest.txt captions alongside imagespython -m image.group --input "images" --orientation shortest --sizes 255 383 511 767 --dry-runpython -m image.group --input "images" --orientation shortest --orientation-split --sizes 256 384 512 768 --dry-runimage.collect.txt captions alongside imagespython -m image.collect --input_dir "images" --output_dir "images_flat" --mode fileimage.dataset.txt caption pairs without repeating within a run<output>/<number>.<ext> and the selected reference caption to <output>/<number>.txt<output>/<number>/ and copies the selected reference image into that subfolderpython -m image.dataset --inputs "images" --refs "refs" --output "dataset"--filename-width (default 6), --seed, --dry-runimage.shape--inplace
side mode sets the chosen side (width, height, or longest) and scales the other side to preserve aspect ratioratio mode picks the closest ratio by aspect, computes the target size, then applies a best-fit center crop; --min sets the shortest side exactly, --max sets the longest side exactly, and --mid caps square outputs at that side while keeping square inputs at or above --min aligned to --length-multiple--min, --max, and --mid must be multiples of --length-multiplepython -m image.shape --input "images" --output "out" --mode side --side width --size 1024python -m image.shape --input "images" --output "out" --mode ratio --ratios 1x1 3x4 4x3 1x2 --length-multiple 64python -m image.shape --input "images" --inplace --mode side --side height --size 768python -m image.shape --input "images" --inplace --mode side --side longest --size 768python -m image.shape --input "images" --inplace --mode ratio --ratios 1x1 2x3 3x4 1x2 2x1 4x3 3x2 --length-multiple 64 --min 512python -m image.shape --input "images" --inplace --mode ratio --ratios 1x1 2x3 3x4 1x2 2x1 4x3 3x2 --length-multiple 64 --max 768python -m image.shape --input "images" --inplace --mode ratio --ratios 1x1 2x3 3x4 1x2 2x1 4x3 3x2 --length-multiple 64 --min 512 --mid 640
--mid will not upscale square inputs below 640x640; square inputs at or above --min are snapped to the nearest --length-multiplepython -m image.shape --inplace --mode ratio --ratios 1x1 2x3 1x2 2x1 3x2 --length-multiple 64 --min 512 --mid 512 --input "images"image.tag.mainwd14_model by default).txt files beside each image (same basename)python -m image.tag.main --input "images" --thresh 0.35 --max-tags 50--thresh, --general-thresh, --character-thresh, --max-tags (0 for none), --model, --prefiximage.caption.txt captions beside each image--max-res (768 by default).txt captionpython -m image.caption --input "images" --trigger "trxxr" --prompt-file "prompt_woman.txt"python -m image.caption --input "images" --trigger "trxxr" --prompt-file "prompt_man.txt"python -m image.caption --input "images" --trigger "objxx" --prompt-file "prompt_object.txt"python -m image.caption --input "images" --trigger "mystyle" --prompt-file "prompt_style.txt"--trigger, --prompt-file, --max-res, --max-tokens, --server-url, --model, --print-response, --resetimage.tag.extract.txt captions beside each image<input>/<tag1>__<tag2>python -m image.tag.extract --input "images" --tags "1girl, blonde_hair"image.tag.add.txt caption in the input directorypython -m image.tag.add --input "images" --tags "1girl, blonde_hair"--position (start or end)image.tag.replace.txt caption under a dataset directorypython -m image.tag.replace --dataset "images" --old "old tag" --new "new tag"image.tag.group.txt captions beside each image<input>/<caption_count>_<image_count_in_bucket>0_12, 3_57, 8_4python -m image.tag.group --input "images"image.dedup--first-set / --second-set limits comparisons to matching height, width, longest, shortest, or ratio--target-set balanced|weighted (default: weighted).txt captionspython -m image.dedup.main --input "images" --dhash-threshold 8python -m image.dedup.main --input "images" --first-set ratio --second-set longest --dhash-threshold 8python -m image.dedup.main --input "images" --target 500python -m image.dedup.main --input "images" --target 500 --first-set ratio --target-set balancedpython -m image.dedup.main --input "images" --target 500 --output "images_dedup"python -m image.dedup.main --input "images" --output "images_dedup" --sharpness-weight 1.0 --exposure-weight 0.4 --noise-weight 0.3 --artifact-weight 0.55--input, -o/--output--dhash-size, --dhash-threshold--target--target-set (default weighted; only relevant with --target and sets)--first-set, --second-set (--second-set requires --first-set)--min-group-size--sharpness-weight (default 1.0)--exposure-weight (default 0.4)--noise-weight (default 0.3)--artifact-weight (default 0.55)image.generate — Web GUIA Flask-based web interface for generating images, supporting Flux 2 (4b/9b, distilled/base) and Ideogram models.
static/config/python -m image.generate --root /path/to/data then open http://localhost:5053image.similar — Web GUIA Flask-based web interface for finding and exporting near-duplicate images using dHash and Union-Find clustering.
.txt captions) to a user-specified output folder{root}/image/similar/{timestamp}/thumbs/python -m image.similar.main --root /path/to/data --port 5051image.cull--mode copy|link|inplace for kept-image output handling; logs and metrics savedpython -m image.cull.label.main --project "/proj" --stage 1 then open http://localhost:5050stage_<N>_cull_labels.csv under the project root (resumes if present)python -m image.cull.train --project "/proj" --stage 1 --batch-size 32python -m image.cull.sample --project "/proj" --stage 1 --count 1000 (runs the stage cull model on a random sample and hard-links predicted keeps to stage_<N>_cull_samples)python -m image.cull.main --project "/proj" --stage 1python -m image.cull.main --project "/proj" --stage 1 --mode linkpython -m image.cull.main --project "/proj" --stage 1 --mode inplacepython -m image.cull.main --project "/proj" --resume 14487--mode defaults to copy--resume compares the numeric filename stem (e.g., 014486.png -> 14486) and handles discontinuities--resume and no --stage, source stage is inferred as latest - 1 (resume into the latest stage)stage_<N> with images and stage_<N>_cull_labels.csv for training; writes stage_<N>_cull_model.pthimage.rankstage_<N>_rank_labels.csv under the project root (resumes if present)--stage omitted, uses the latest stage_<N> foundpython -m image.rank.label.main --project "/proj" --stage 1 then open http://localhost:5000image.crop000001.png via --filename-width)python -m image.crop.label.main --project "/proj" --stage 1 then open http://localhost:5051python -m image.crop.label.main --project "/proj" --stage 1 --port 5001stage_<N>_crop_labels.csv under the project root (resumes if present)python -m image.crop.prepare.main --model yolo --project "/proj" --stage 1 --val_split 0.2--balance only if you explicitly want to downsample every class to the smallest class countpython -m image.crop.train --project "/proj" --stage 1 --variant yolo11n --epochs 100 --batch 16python -m image.crop.main --project "/proj" --stage 1 --classes 0 1 2 3 4python -m image.crop.main --project "/proj" --stage 1 --resolution 768 --classes 0 1 2 3 4video.extract--all--all overrides --frames, --time, and --random--buffer compares the marked frame with nearby frames and saves the best-scoring result--buffer 0 saves the exact marked frame; --all ignores --buffer--filename-width (default 000001.png style)--resolution to keep original frame size)python -m video.extract.select --root "/tools" for previewing interval marks, optionally limiting work to time spans like 1:00 - 1:10, 32:40 - 40:00, or 00:15:30 - 00:18:00, and saving best nearby native-res frames<root>/ffmpeg/bin/ffmpeg.exe<output>/preview, and that folder is cleared before each new preview buildpython -m video.extract --input "videos" --output "frames" --frames 2 --time second --resolution 768python -m video.extract --input "videos" --output "frames_best" --frames 2 --time second --buffer 3 --resolution 768python -m video.extract --input "videos" --output "frames_all" --frames 50 --random --collate --resolution 512python -m video.extract --input "videos" --output "frames_native" --frames 2 --time secondpython -m video.extract --input "videos" --output "all_frames" --all --collatepython -m video.extract.select --root "/tools" --port 5052video.groupffprobe to read width/height_<orientation>/_<short-side> buckets and prefixes non-group path to filenamepython -m video.group --root "videos" --ffprobe "/tools/ffprobe" --sizes 256 384 512 768 1024 --dry-runvideo.loopscale_cuda), SSIM-based loop detection, batched exports.mkv loops at target --fps and --out-respython -m video.loop --input "videos" --output "loops" --lengths 33 49 --fps 30 --in-res 64 --out-res 768 --ffmpeg "/tools/ffmpeg"video.grid.generateinterval mode samples frames at a fixed seconds interval and emits as many full grids as possiblesegment_midpoints mode divides the full video into exactly rows * cols segments and picks the middle frame from each segment to build one grid<dir>/000001.png ... + config.jsonpython -m video.grid.generate -i "video.mp4" -o "grids" --ffmpeg "/tools/ffmpeg" --rows 3 --cols 3 --selection-mode interval --frame-interval 30video.grid.animatepython -m video.grid.animate -i "grids/000001.png" -o "animation.gif" --ffmpeg "/tools/ffmpeg" --length 5 --fps 12 --format gifThe unified flux2 package provides the version-aware entrypoints for generation and quantization.
flux2.gen4b and 9b--version to select <root>/flux2/4b or <root>/flux2/9bmodel directory:
tokenizertext_encoderschedulervaetransformer/distilltransformer/base4 steps and guidance scale 1.0--base switches to base-model defaults: 50 steps and CFG guidance scale 4.0--images "img1.png, img2.png" by encoding reference images into latent tokens--ref-size, then cropped to the nearest valid VAE multiple before encoding--text-quant-method and --denoiser-quant-method to choose none, sym-high, sym-med, aff-high, aff-med, or aff-low--text-quant-method none or --denoiser-quant-method none keeps the original fp16 checkpoint weights<root>/<model>/model/text_encoder/symmetric_high_quant.safetensors, symmetric_med_quant.safetensors, affine_high_quant.safetensors, affine_med_quant.safetensors, or affine_low_quant.safetensors<root>/<model>/model/transformer/<variant>/symmetric_high_quant.safetensors, symmetric_med_quant.safetensors, affine_high_quant.safetensors, affine_med_quant.safetensors, or affine_low_quant.safetensors--loras "path1:1.0,path2:0.7"python -m flux2.gen --root "/models/flux" --version 4b --prompt "cinematic portrait, 85mm photo, rim lighting"python -m flux2.gen --root "/models/flux" --version 9b --base --prompt "fashion editorial, full body, studio backdrop" --width 768 --height 1024python -m flux2.gen --root "/models/flux" --version 4b --images "/data/ref1.png, /data/ref2.png" --prompt "same outfit, new pose, soft daylight"python -m flux2.gen --root "/models/flux" --version 9b --prompt "stylized character art" --loras "/models/lora/style.safetensors:0.8,/models/lora/character.safetensors:1.0"python -m flux2.gen --root "/models/flux" --version 4b --prompt "product render on white seamless" --denoiser-quant-method none--root--version--prompt--images--width, --height--steps--seed--base--ref-size--guidance-scale--text-quant-method--denoiser-quant-method--loras--max-lengthflux2.train.main--version to select <root>/flux2_4b/model or <root>/flux2_9b/modelprompt_embeds and text_idsmodel/transformer/base directory--project as the dataset directory and training run directory<project>/models/<step>.safetensors<project>/models/steps.csv--root--version--project--steps (default: 1024)--resume--model (LoRA init checkpoint file, optional)--text-quant (default: aff-high-mini)--denoiser-quant (default: aff-high-mini)--trigger--cache-text (pre-encode captions to CPU)--cache-images (pre-encode image latents to disk)--rank (default: 32)--alpha (default: 32)--target_res (default: 384)--ref_res (default: 384)--lr (default: 1e-4)python -m flux2.train.main --root "/models/flux" --version 4b --project "/data/flux_dataset" --trigger "TOK"flux2.sampleflux2.train--project as the training project directory<project>/models for epoch_<n>.safetensors and writes images to <project>/samples--start to skip earlier epochs and only render checkpoints at or after the selected epoch number--project--prompt--trigger, which is encoded once and reused for all samples--samples spreads selections evenly across the dataset from image 1 to the last image--sample-indices overrides --samples with explicit 1-based dataset indices such as "1,25,100"--force-size is dataset-only and rescales each selected sample so its longest side matches the requested size while preserving aspect ratio--width and --height--sample-seed + sample_indexflux2.gen--text-quant-method and --denoiser-quant-method to choose none, sym-high, sym-med, aff-high, aff-med, or aff-lowmodel directory:
tokenizertext_encoderschedulervaetransformer/distill<project>/samples/<step>_<width>x<height>.png for single samples, or <step>_<index>_<width>x<height>.png for multi-samples000012_768x768.png is rendered from step checkpoint models/12.safetensors at 768x768 resolution000012_3_768x768.png is sample 3 from the same checkpointpython -m flux2.sample --root "/models/flux" --version 4b --project "/data/flux_dataset"python -m flux2.sample --root "/models/flux" --version 4b --project "/data/flux_dataset" --samples 4 --start 10python -m flux2.sample --root "/models/flux" --version 9b --project "/data/flux_dataset" --sample-indices "1,25,100"python -m flux2.sample --root "/models/flux" --version 4b --project "/data/flux_dataset" --sample-indices "5,9" --trigger "TOK" --force-size 768python -m flux2.sample --root "/models/flux" --version 9b --project "/data/flux_dataset" --prompt "fashion portrait, soft key light, editorial pose" --width 768 --height 1024--root--version--project--prompt--samples--sample-indices--start--sample-seed--trigger--force-size--width, --height--target-res, --ref-res--steps, --distill--text-quant-method--denoiser-quant-methodflux2.text_encoder.quant4b or 9bmodel-00001-of-00002.safetensors for 4bmodel-00001-of-00004.safetensors through model-00004-of-00004.safetensors for 9b--input must point directly at the source text_encoder model directory<iml_root>/<model>/model/text_encoder/<method>_quant.safetensors, creating that output directory if neededpython -m flux2.text_encoder.quant --root "/iml_root" --version 4b --input "/models/flux2_4b/text_encoder" --method sym-high-mini--root--input--version--methodflux2.denoiser.quant4b or 9bdiffusion_pytorch_model.safetensors for 4bdiffusion_pytorch_model-00001-of-00002.safetensors and diffusion_pytorch_model-00002-of-00002.safetensors for 9b--input is required; point directly at a source transformer checkpoint directory5 double blocks and 20 single blocks for 4b8 double blocks and 24 single blocks for 9b<root>/<model>/model/transformer/<variant>/<method>_quant.safetensors, creating that output directory if neededpython -m flux2.denoiser.quant --root "/iml_root" --version 4b --variant base --method sym-med-mini--root--version--variant--method8e-5 to 1e-41500-25001500-3000512px, then move higher latertimestep_type: "weighted"timestep_type: "shift"--project is the training run folder<project>/images.png, .jpg, .jpeg, .webp.txt caption with the same basename.txt caption and --trigger "<text>" is requiredimages/000001.pngimages/000001.txtimages/000002.jpgimages/000002.txtimages/000001.pngimages/000002.jpgmodels/logs/20-40 images10-15 imagesideogram.genpatch_size * ae_scale_factor (default 16)python -m ideogram.gen --root "/models" --prompt "A cat holding a sign" --output "cat.png"python -m ideogram.gen --root "/models" --prompt "cinematic portrait" --width 768 --height 768 --steps 64 --guidance-scale 5.0python -m ideogram.gen --root "/models" --prompt "futuristic city" --text-quant-method sym-high --denoiser-quant-method sym-med--root, --prompt, --output--width, --height, --steps, --guidance-scale, --seed--text-quant-method, --denoiser-quant-method--offloadideogram.text_encoder.quant<input>/model.safetensorslanguage_model. prefix in the saved output<root>/ideogram/text_encoder/<method>_quant.safetensorspython -m ideogram.text_encoder.quant --root "/models" --input "/path/to/f8/text_encoder" --method sym-high-nano--root, --input, --methodideogram.denoiser.quant<input>/transformer/diffusion_pytorch_model.safetensors<input>/unconditional_transformer/diffusion_pytorch_model.safetensors<root>/ideogram/transformer/<variant>/<method>_quant.safetensorspython -m ideogram.denoiser.quant --root "/models" --input "/path/to/f8/root" --variant cond --method sym-med-mini--root, --input, --method, --variant (cond or uncond)utils.quant.estimator.main.safetensors output size without running CUDA quantization.pth source checkpoints for PyTorch-backed model componentsflux2_4b_text_encoderflux2_9b_text_encoderflux2_4b_denoiser, flux2_9b_denoisergemma4_2bwan22_5b_text_encoder, wan22_5b_denoiserpython -m utils.quant.estimator.main --model flux2_4b_text_encoder --source "/models/flux/flux_2_klein_4b/model/text_encoder"python -m utils.quant.estimator.main --model flux2_9b_denoiser --source "/models/flux/flux2_9b/model/transformer/base"python -m utils.quant.estimator.main --model gemma4_2b --source "/models/gemma4"python -m utils.quant.estimator.main --model wan22_5b_text_encoder --source "/models/wan/wan22/model/text_encoder"--model--sourceutils.quant.evalfp16 only for the Flux 2 Klein 4B denoiser linear tensor shapesutils.quant dequantizers:
symmetric_high_dequantsymmetric_med_dequantaffine_high_dequantaffine_med_dequantaffine_low_dequantquantize_to_symmetricquantize_to_affinefp16 weight tensor for each casemax_abs_diffmean_abs_diffutils/quant/cudapython -m utils.quant.eval106 commits
Python
83.1%
JavaScript
4.5%
HTML
3.6%
Cuda
3.3%
CSS
3.1%
C++
2.5%