# Create-Turnaround.ps1 Combines a sequence of images into a single **turnaround** (character reference sheet) image using ffmpeg. A turnaround image shows the same subject from several angles — typically front, 3/4, profile and back — side by side at a consistent scale, in one wide image. It is the usual input for keeping a character consistent across image and video generation models. ## Synopsis All images found at `-Source` are placed side by side, left to right, in the order given by a numeric-aware sort of their file names. `-Target` is optional: without it the sheet is saved as a JPEG named after the source directory, in the current directory. Every image keeps its original pixels: nothing is scaled, stretched or cropped. Images smaller than the largest one are centred in their cell and padded with `-Background` (white by default), so a set of slightly different sizes still produces a clean, aligned sheet. Supports PNG, JPEG, WEBP, BMP, AVIF and TIFF for both input and output. ## Parameters - **`-Source`** (required): Directory containing the images, a file pattern (e.g. `Img*.jpg`), or a single image file. Can be absolute or relative to the current directory. Non-recursive; only supported image extensions are considered. - **`-Target`** (optional): Path to the output turnaround image. Can be absolute or relative to the current directory. The extension selects the output format; `.png` is appended when the extension is missing or unrecognised. When omitted, the name is derived from the source — see [Default Target Name](#default-target-name). - **`-Rows`** (optional): Number of rows to arrange the images in. Defaults to `1` (a single horizontal row). When only `-Rows` is given, the column count is computed as `ceil(imageCount / Rows)`. - **`-Columns`** (optional): Number of columns to arrange the images in. When only `-Columns` is given, the row count is computed as `ceil(imageCount / Columns)`. When both are given, `Rows × Columns` must be at least the number of images. - **`-Background`** (optional): Colour used to pad images smaller than the largest one, and to fill the unused cells of an incomplete last row. Defaults to `white`. Accepts any ffmpeg colour: a name (`white`, `black`, `silver`), `0xRRGGBB`, or `name@alpha` (`white@0` for transparent). - **`-Quality`** (optional): Quality factor for the lossy output formats (jpeg, webp, avif). **Lower values mean better quality** in all three, see [Quality and Compression](#quality-and-compression). Ignored (with a warning) for png, bmp and tiff. - **`-Compression`** (optional): Compression level for png (`0`-`9`) and webp (`0`-`6`); higher means smaller files but slower encoding. Ignored (with a warning) for jpeg, avif, bmp and tiff. ## Default Target Name When `-Target` is omitted, the script writes a **JPEG named after the last directory member of the source**, into the **current directory**: | `-Source` | Name taken from | Result (run from `C:\work`) | | --- | --- | --- | | A directory, e.g. `C:\renders\CharacterA` | The last member of that directory's full path | `C:\work\CharacterA.jpg` | | A file pattern, e.g. `Img*.jpg` | The last member of the **current** directory's full path | `C:\work\work.jpg` | | A single image file | The last member of the current directory's full path | `C:\work\work.jpg` | Relative sources are resolved to a full path first, so `-Source .` run from `C:\renders\CharacterA` produces `CharacterA.jpg` in that same directory. Trailing separators are ignored (`C:\renders\CharacterA\` behaves identically). Since the derived name always ends in `.jpg`, the output uses the JPEG encoder and its default `-q:v 2`; add `-Quality` to change it, or pass `-Target` explicitly for a different format. ```powershell PS C:\work> .\Create-Turnaround.ps1 -Source C:\renders\CharacterA No -Target specified; using C:\work\CharacterA.jpg ``` ### Drive-root error A path with no directory member cannot name the file, so a drive root is rejected: ```powershell PS C:\work> .\Create-Turnaround.ps1 -Source "C:\" Cannot derive a target file name from 'C:\': the path is a drive root with no directory member. Specify -Target explicitly. ``` The same applies when `-Source` is a file pattern and the current directory is a drive root (e.g. running `Create-Turnaround.ps1 -Source *.png` from `C:\`). Supply `-Target` explicitly in that case. ## Image Order File names are sorted with a **numeric-aware (natural) sort**, so digit runs compare as numbers: ```text Img1.png, Img2.png, Img10.png (not Img1, Img10, Img2) Frame-00001.png ... Frame-00012.png ``` Zero-padded sequence names therefore sort identically to a plain alphabetical sort, while non-padded names (`Pose2`, `Pose10`) still come out in the intended order. ## Cell Size and Padding The cell is as large as the **largest** source image in both dimensions: - `cellWidth` = max width of all sources - `cellHeight` = max height of all sources - Output size = `cellWidth × columns` by `cellHeight × rows` Each image is centred in its cell. Images that already match the cell size are passed through untouched; smaller ones gain `-Background` margins. No source image is ever resized, so the sheet is lossless with respect to the originals (verified at PSNR = ∞ for PNG in/PNG out). When padding is applied, the console reports it: ```text Cell size: 600x768 Note: 2 image(s) smaller than the largest image; centred and padded with white. ``` ## Layout ### Single row (default) Four 512x768 images produce a 2048x768 sheet: ```text [1][2][3][4] ``` ### Grid `-Rows` and/or `-Columns` wrap the images into a grid. With 4 images and `-Columns 2` (→ 2 rows), the output is 1024x1536: ```text [1][2] [3][4] ``` An incomplete last row is **centred** inside the full output width and the remaining space filled with `-Background`. With 4 images and `-Columns 3` (→ 2 rows, 1536x1536): ```text [1][2][3] [4] ``` | Images | Switches | Rows x Columns | Output (512x768 cells) | | --- | --- | --- | --- | | 4 | *(none)* | 1 x 4 | 2048x768 | | 4 | `-Columns 2` | 2 x 2 | 1024x1536 | | 4 | `-Rows 2` | 2 x 2 | 1024x1536 | | 4 | `-Columns 3` | 2 x 3 | 1536x1536 (last row centred) | | 4 | `-Rows 1 -Columns 2` | — | Error: 2 cells is not enough for 4 images | ## Output Format The target extension selects the encoder, with the same mapping and defaults as [Clone-Image.ps1](Clone-Image.md): | Extension | Encoder | Default settings | `-Quality` | `-Compression` | | --- | --- | --- | --- | --- | | `.png` | `png` | Lossless | — | `-compression_level` (0-9) | | `.jpg` / `.jpeg` | `mjpeg` | `-q:v 2`, `yuvj420p` | `-q:v` (1-31) | — | | `.webp` | `libwebp` | `-quality 90` | `-quality` (inverted) | `-compression_level` (0-6) | | `.avif` | `libaom-av1` | `-still-picture 1`, `-cpu-used 4`, `-crf 25` | `-crf` (0-63) | — | | `.bmp` | `bmp` | Lossless | — | — | | `.tif` / `.tiff` | `tiff` | Lossless | — | — | PNG is recommended for a turnaround sheet that will be fed back into a generation model, since it is lossless and preserves transparency. ## Quality and Compression `-Quality` is always interpreted as **lower = better quality**, regardless of format. For webp — whose native `-quality` option means the opposite — the value is inverted internally (`-Quality 20` becomes `-quality 80`), so a single convention holds across jpeg, webp and avif. | Format | `-Quality` maps to | Range | Default when omitted | | --- | --- | --- | --- | | jpeg | `-q:v` | 1-31 (lower is better) | `2` | | webp | `-quality` = `100 - Quality` | 0-100 after inversion | `90` (i.e. `-Quality 10`) | | avif | `-crf` | 0-63 (lower is better) | `25` | `-Compression` trades encoding time for file size on the two formats that support it, and does not affect image quality (both are lossless/independent of it): | Format | `-Compression` maps to | Range | | --- | --- | --- | | png | `-compression_level` | 0-9 (higher = smaller, slower) | | webp | `-compression_level` | 0-6 (higher = smaller, slower) | Measured on a 3840x480 sheet: | Command | Output size | | --- | --- | | `-Target sheet.jpg` (default) | 73,744 B | | `-Target sheet.jpg -Quality 2` | 73,744 B (identical to the default) | | `-Target sheet.jpg -Quality 28` | 33,349 B | | `-Target sheet.webp -Quality 5` | 30,508 B | | `-Target sheet.webp -Quality 70` | 17,578 B | | `-Target sheet.png -Compression 0` | 7,395,829 B | | `-Target sheet.png -Compression 9` | 10,990 B | Passing `-Quality` to a lossless format (or `-Compression` to a format that ignores it) is not an error; the value is dropped and a warning is printed: ```text WARNING: -Quality is ignored for the lossless png format. ``` ## Examples **No `-Target` — JPEG named after the source directory, written to the current directory:** ```powershell .\Create-Turnaround.ps1 -Source C:\renders\CharacterA # Writes .\CharacterA.jpg ``` **No `-Target`, run from inside the frames directory:** ```powershell cd C:\renders\CharacterA .\Create-Turnaround.ps1 -Source "F*.png" # Writes C:\renders\CharacterA\CharacterA.jpg (name taken from the current directory) ``` **Four frames in a directory into a single-row sheet:** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.png ``` **Using a file pattern:** ```powershell .\Create-Turnaround.ps1 -Source ".\renders\Img*.jpg" -Target turnaround.png ``` **Extension-less target (`.png` is appended):** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target character-sheet # Writes character-sheet.png ``` **Eight angles as a 2x4 grid:** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.png -Rows 2 ``` **Three columns, incomplete last row centred:** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.png -Columns 3 ``` **Transparent background instead of white:** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.png -Background "white@0" ``` **Mid-gray background, JPEG output:** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.jpg -Background 0x808080 ``` **Smaller JPEG sheet (lower quality):** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.jpg -Quality 20 ``` **Maximum PNG compression (smallest lossless file, slowest encode):** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.png -Compression 9 ``` **Near-lossless webp with maximum compression effort:** ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.webp -Quality 1 -Compression 6 ``` ## Existing Files An existing `-Target` image is **replaced** without a prompt. ffmpeg runs with `-y`. - The folder that contains `-Target` must already exist. The script stops with an error instead of creating it. - Use `-WhatIf` first, or write to a new name. ## Safe Execution This script supports `-WhatIf` and `-Confirm` for safe execution: - **`-WhatIf`**: Prints the image list, the computed layout and the full ffmpeg command without writing the output file. - **`-Confirm`**: Prompts for confirmation before creating the turnaround image. ```powershell .\Create-Turnaround.ps1 -Source .\renders -Target turnaround.png -WhatIf ``` ## Notes - Requires `ffmpeg` and `ffprobe` in the Windows `PATH`. - The target directory must exist; the script errors out rather than creating it. - Every input is normalised to `rgba` before stacking, so sources may mix formats (e.g. PNG + JPEG + WEBP) and colour models freely. - Source images are never scaled, stretched or cropped — differing sizes are reconciled with padding only. - A single image is valid input and produces a one-cell output (useful to normalise/convert a frame). - Files that are not recognised images are ignored; if none remain, the script errors with the list of supported extensions. - The output file is excluded from its own input list, so writing `-Target` into the `-Source` directory stays idempotent across re-runs (the previous sheet is not stacked into the new one).