Files
Video-Processing-Scripts/video-processing-scripts/Create-Turnaround.md
T

270 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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).