# Compare-Videos.ps1 Combines 2 to 4 video files into a single comparison video using ffmpeg. *Left side: Original video, Right side: Outpainted video using LTX-2.3 and Wan2GP.* *See [Create-PillarBox.ps1](Create-PillarBox.md) script for an example with 4 videos.* ## Synopsis Creates side-by-side or grid comparison videos with automatic scaling and padding to preserve aspect ratios. By default, creates 1920x1080 (1K HD) output. Use `-In4K` for 3840x2160 (4K UHD) output. The script detects the orientation of the first video (S1) and applies appropriate scaling for either landscape or portrait mode. Each source video is scaled to fit its cell without stretching, with black letterbox/pillarbox padding applied as needed. When exactly two sources already tile the output frame perfectly, they are passed through untouched — see [Exact Fit](#exact-fit). ## Parameters - **`-S1`** (required): Path to source video 1 - **`-S2`** (required): Path to source video 2 - **`-S3`** (optional): Path to source video 3 - **`-S4`** (optional): Path to source video 4 - **`-Output`** (required): Path to the output video file - **`-NoAudio`**: Exclude audio from output (default: copies audio from S1) - **`-In4K`**: Generate comparison video in 4K UHD (3840x2160) resolution instead of 1K HD (1920x1080) - **`-Clip`**: Removes black padding from the output. Ignored when the two sources are an [exact fit](#exact-fit) for the output frame, since there is no padding to remove. For landscape, only applies when exactly **2** source videos are provided; ignored otherwise. For portrait, applies when **2–4** source videos are provided (all sources must be portrait); for 2–3 videos removes the outer side (left/right) padding, for 4 videos removes the outer top/bottom padding. With one landscape source and one portrait source (2-video only), switches to a **mixed-orientation** layout: the landscape video keeps its native pixel size (or 2× in `-In4K`) and the portrait video is centered in the remaining width with black pillar boxes. See [Clip Mode](#clip-mode) for details. - **`-Sideways`**: Arranges all videos in a single horizontal row. For landscape mode, replaces the default 2x2 grid layout (used for 3 and 4 videos) with a single row. For portrait mode, the layout is the same as the default (already side-by-side). Can be combined with `-Clip` to remove black padding. See [Sideways Mode](#sideways-mode) for details. ## Exact Fit With exactly **2** source videos, when both are already as tall as the output frame and their widths add up to the output width, placing them side by side *is* the target frame. In this case the script applies **no scaling and no padding at all** — the two streams go straight into `hstack`, so neither source is resampled and no quality is lost. The condition is checked against the selected output resolution: | | Required height (both sources) | Required width (S1 + S2) | | --- | --- | --- | | Default (1K HD) | `1080` | `1920` | | With `-In4K` | `2160` | `3840` | The widths do not have to be equal — any split that sums to the output width qualifies. ### Examples | S1 | S2 | Output | Exact fit? | | --- | --- | --- | --- | | `1280x1080` | `640x1080` | `1920x1080` | Yes — 1280 + 640 = 1920, both 1080 tall | | `960x1080` | `960x1080` | `1920x1080` | Yes — equal halves | | `1440x1080` | `480x1080` | `1920x1080` | Yes — uneven split still sums to 1920 | | `2560x2160` + `-In4K` | `1280x2160` | `3840x2160` | Yes — 2560 + 1280 = 3840, both 2160 tall | | `2560x2160` | `1280x2160` | `1920x1080` | No — heights are not 1080 (add `-In4K`) | | `1280x720` | `640x1080` | `1920x1080` | No — S1 is not 1080 tall | | `1280x1080` | `680x1080` | `1920x1080` | No — widths sum to 1960, not 1920 | Resulting filter graph (note the absence of `scale` and `pad`): ```text [0:v][1:v]hstack=inputs=2[outv] ``` The console reports it as: ```text Exact fit: 1280x1080 + 640x1080 already tile 1920x1080; sources used unscaled. ``` ### Interaction with other switches - **`-Clip`** is ignored: an exact-fit output contains no padding to remove. A notice is printed. - **`-Sideways`** is ignored: two sources side by side are already a single row. A notice is printed. - Applies to 2 sources only. With 3 or 4 videos the normal grid/row layouts are used. - Orientation detection still runs, but is irrelevant — the exact-fit check takes precedence over both the landscape and portrait layouts. (A pair such as two `960x1080` sources is detected as "portrait", yet is now passed through instead of being squeezed into 608-wide cells.) ## Orientation Detection The script automatically detects the orientation of S1 by comparing its width and height: - **Landscape mode:** Width > Height (e.g., 1920x1080) - **Portrait mode:** Height > Width (e.g., 1080x1920) All videos are then scaled according to the detected orientation. ## Layout Rules Each source video is scaled to fit its cell without stretching. Aspect ratio is always preserved using `force_original_aspect_ratio=decrease` combined with black padding to fill the cell exactly. ### Landscape Mode (width > height) #### 1K HD (1920x1080) - Default **2 videos:** Output is 1920x1080. Each video occupies a 960x1080 cell side-by-side. ```text [S1][S2] ``` **3 videos:** Output is 1920x1080. Top row has two 960x540 cells (S1 left, S2 right). Bottom row has S3 in a 960x540 cell, horizontally centered with 480px black padding on each side. ```text [S1][S2] [ S3 ] ``` **4 videos:** Output is 1920x1080. Four 960x540 cells in a 2x2 grid. ```text [S1][S2] [S3][S4] ``` #### 4K UHD (3840x2160) - With -In4K **2 videos:** Output is 3840x2160. Each video occupies a 1920x2160 cell side-by-side. ```text [S1][S2] ``` **3 videos:** Output is 3840x2160. Top row has two 1920x1080 cells (S1 left, S2 right). Bottom row has S3 in a 1920x1080 cell, horizontally centered with 960px black padding on each side. ```text [S1][S2] [ S3 ] ``` **4 videos:** Output is 3840x2160. Four 1920x1080 cells in a 2x2 grid. ```text [S1][S2] [S3][S4] ``` ### Portrait Mode (height > width) #### 1K HD (1920x1080) - Default All videos are displayed horizontally side-by-side, scaled and centered with black padding. **2 videos:** Each video is scaled to 608x1080. Combined width is 1216x1080, centered horizontally in 1920x1080 output with 352px padding on each side. With `-Clip`: 1216x1080 (no side padding). ```text [ ][S1][S2][ ] ``` **3 videos:** Each video is scaled to 608x1080. Combined width is 1824x1080, centered horizontally in 1920x1080 output with 48px padding on each side. With `-Clip`: 1824x1080 (all sources must be portrait; otherwise falls back to padded layout). ```text [ ][S1][S2][S3][ ] ``` **4 videos:** Each video is scaled to 480x854. Combined width is 1920x854, centered vertically in 1920x1080 output with 113px padding on top and bottom. With `-Clip`: 1920x854 (all sources must be portrait; otherwise falls back to padded layout). ```text [S1][S2][S3][S4] ``` #### 4K UHD (3840x2160) - With -In4K **2 videos:** Each video is scaled to 1216x2160. Combined width is 2432x2160, centered horizontally in 3840x2160 output with 704px padding on each side. With `-Clip`: 2432x2160 (no side padding). ```text [ ][S1][S2][ ] ``` **3 videos:** Each video is scaled to 1216x2160. Combined width is 3648x2160, centered horizontally in 3840x2160 output with 96px padding on each side. With `-Clip`: 3648x2160 (all sources must be portrait; otherwise falls back to padded layout). ```text [ ][S1][S2][S3][ ] ``` **4 videos:** Each video is scaled to 960x1708. Combined width is 3840x1708, centered vertically in 3840x2160 output with 226px padding on top and bottom. With `-Clip`: 3840x1708 (all sources must be portrait; otherwise falls back to padded layout). ```text [S1][S2][S3][S4] ``` ## Sideways Mode With `-Sideways`, all videos are placed in a single horizontal row regardless of video count. For landscape sources this is the key difference from the default layout, which uses a 2x2 grid for 3 and 4 videos. Can be combined with `-Clip` to remove black padding: for landscape, the output height is reduced to the natural content height of the sources; for portrait, the outer centering pad is omitted and the output matches the raw hstack dimensions. ### Landscape Mode — Sideways #### 1K HD (1920x1080) **2 videos:** Each cell is 960x1080. Video content scales to 960x540, padded vertically. Total: 1920x1080. With `-Clip`: total 1920x540. ```text [S1][S2] ``` **3 videos:** Each cell is 640x1080. Video content scales to 640x360, padded vertically. Total: 1920x1080. With `-Clip`: total 1920x360. ```text [S1][S2][S3] ``` **4 videos:** Each cell is 480x1080. Video content scales to 480x270, padded vertically. Total: 1920x1080. With `-Clip`: total 1920x270. ```text [S1][S2][S3][S4] ``` #### 4K UHD (3840x2160) — With -In4K **2 videos:** Each cell is 1920x2160. Video content scales to 1920x1080, padded vertically. Total: 3840x2160. With `-Clip`: total 3840x1080. **3 videos:** Each cell is 1280x2160. Video content scales to 1280x720, padded vertically. Total: 3840x2160. With `-Clip`: total 3840x720. **4 videos:** Each cell is 960x2160. Video content scales to 960x540, padded vertically. Total: 3840x2160. With `-Clip`: total 3840x540. ### Portrait Mode — Sideways Portrait videos are already displayed side-by-side by default. The `-Sideways` switch produces the same layout as the standard portrait mode. With `-Clip`, the outer black padding is omitted and the output matches the raw hstack dimensions. #### 1K HD (1920x1080) **2 videos:** Each cell 608x1080. Output 1920x1080 (352px padding per side). With `-Clip`: 1216x1080. **3 videos:** Each cell 608x1080. Output 1920x1080 (48px padding per side). With `-Clip`: 1824x1080. **4 videos:** Each cell 480x854. Output 1920x1080 (~113px padding top/bottom). With `-Clip`: 1920x854. #### 4K UHD (3840x2160) — With -In4K **2 videos:** Each cell 1216x2160. Output 3840x2160 (704px padding per side). With `-Clip`: 2432x2160. **3 videos:** Each cell 1216x2160. Output 3840x2160 (96px padding per side). With `-Clip`: 3648x2160. **4 videos:** Each cell 960x1706. Output 3840x2160 (~227px padding top/bottom). With `-Clip`: 3840x1706. ## Clip Mode With only 2 source videos, the default behavior pads the output to the full target resolution (1920x1080 or 3840x2160). When the sources are shorter than 16:9 (e.g. two 1280x720 videos placed side-by-side at 1K HD), this leaves significant black bars on the top and bottom. The `-Clip` switch removes those bars. It works in two modes depending on the source orientations. ### Same-orientation mode (both landscape or both portrait) The final output height is reduced to the actual height each source naturally scales to inside its half-width cell, removing the top/bottom letterbox entirely. #### How it works 1. Both `S1` and `S2` are probed for their original `width x height`. 2. For each source, the script computes the height it would scale to when fitted by width into its cell (`outputWidth / 2` for landscape, `outputWidth * 0.6333 / 2` for portrait), preserving aspect ratio (`force_original_aspect_ratio=decrease`). 3. The larger of the two scaled heights is used as the new output height (rounded up to an even number for `libx264`/`yuv420p`), capped at the original output height. 4. All subsequent scaling/padding uses that reduced output height, so neither source is cropped and there is no top/bottom letterbox. #### Examples | Sources | Default output | With `-Clip` | With `-Clip -In4K` | | --- | --- | --- | --- | | Two `1280x720` (16:9) | `1920x1080` | `1920x540` | `3840x1080` | | Two `1920x1080` (16:9) | `1920x1080` | `1920x1080` (already full height) | `3840x2160` (already full height) | | One `1280x720` + one `1280x960` | `1920x1080` | `1920x720` (max scaled height) | `3840x1440` | ### Mixed-orientation mode (one landscape + one portrait) When one source is landscape and the other is portrait, `-Clip` switches to a layout designed to display the landscape video at its native pixel size while pillar-boxing the portrait into the remaining horizontal space. #### How it works 1. Both `S1` and `S2` are probed; the script detects that one is landscape (width > height) and the other is portrait. 2. A `scaleFactor` is derived from the target resolution: `1.0` at 1K HD, `2.0` at 4K UHD (`outputWidth / 1920`). 3. The landscape source is rendered at `landscapeNativeWidth * scaleFactor` × `landscapeNativeHeight * scaleFactor`. At 1K HD this is its native pixel size; at 4K UHD it's exactly 2× upscaled, matching the `-In4K` doubling used elsewhere. 4. The output height is set to the landscape's scaled height. 5. The portrait source is scaled to fit the remaining width (`outputWidth - landscapeScaledWidth`) × output height with `force_original_aspect_ratio=decrease`, then padded with black to fill that cell exactly (pillar-box bars). 6. `S1` is placed on the left and `S2` on the right (the script preserves the same S1/S2 ordering as the same-orientation layout, regardless of which one is landscape). 7. If the landscape source is already as wide as the target resolution (e.g. a 1080p source at `-In4K` would scale to 3840 wide, leaving no room for the portrait), the script falls back to the same-orientation logic and emits a console warning. #### Examples | Sources (S1, S2) | `-Clip` output | `-Clip -In4K` output | | --- | --- | --- | | `1280x720` landscape + `720x1280` portrait | `1920x720` (landscape `1280x720` on its side, portrait cell `640x720`) | `3840x1440` (landscape `2560x1440`, portrait cell `1280x1440`) | | `720x1280` portrait + `1280x720` landscape | `1920x720` (portrait cell `640x720` on left as S1, landscape `1280x720` on right as S2) | `3840x1440` (same with `-In4K` doubling) | | `854x480` landscape + `480x854` portrait | `1920x480` (landscape `854x480`, portrait cell `1066x480`) | `3840x960` | | `1920x1080` landscape + `1080x1920` portrait | falls back to same-orientation behaviour (landscape already at target width) | falls back to same-orientation behaviour | ### Portrait clip mode When all source videos are portrait, `-Clip` removes the outer black padding rather than adjusting height: - **2 or 3 videos:** The outer side (left/right) padding is omitted. Output width equals the raw hstack width (2× or 3× the cell width). - **4 videos:** The outer top/bottom padding is omitted. Output height equals the natural 9:16 cell height. For 3 and 4 videos, S2 (and S3/S4 as needed) are probed to verify all sources are portrait. If any source is not portrait, the full padded layout is used and a warning is emitted. | Sources | Default output | With `-Clip` | With `-Clip -In4K` | | --- | --- | --- | --- | | 2 portrait videos | 1920x1080 | 1216x1080 | 2432x2160 | | 3 portrait videos (all portrait) | 1920x1080 | 1824x1080 | 3648x2160 | | 4 portrait videos (all portrait) | 1920x1080 | 1920x854 | 3840x1708 | ### Restrictions - For landscape, only applies when exactly **2** source videos are provided. With 3 or 4 landscape videos, the switch is ignored and a warning is emitted. - For portrait, applies when **2–4** source videos are provided. For 3 and 4 videos, all sources must be portrait; if any is not, the padded layout is used automatically. - Mixed-orientation mode (one landscape + one portrait) requires the landscape source's scaled width to be strictly less than the target width and its scaled height to fit within the target height; otherwise the script falls back to the same-orientation behaviour automatically. - The audio handling, codec, and CRF are unchanged. ## Examples **Exact fit — two videos that already tile 1920x1080 (no scaling, no padding):** ```powershell .\Compare-Videos.ps1 -S1 wide_1280x1080.mp4 -S2 narrow_640x1080.mp4 -Output exact.mp4 ``` **Exact fit at 4K (2560x2160 + 1280x2160 → 3840x2160):** ```powershell .\Compare-Videos.ps1 -S1 wide_2560x2160.mp4 -S2 narrow_1280x2160.mp4 -Output exact_4k.mp4 -In4K ``` **Two portrait videos clipped to remove side padding (1216x1080):** ```powershell .\Compare-Videos.ps1 -S1 portrait1.mp4 -S2 portrait2.mp4 -Output portrait_clip.mp4 -Clip ``` **Same pair at 4K (2432x2160):** ```powershell .\Compare-Videos.ps1 -S1 portrait1.mp4 -S2 portrait2.mp4 -Output portrait_clip_4k.mp4 -Clip -In4K ``` **Three portrait videos clipped (1824x1080 — all sources must be portrait):** ```powershell .\Compare-Videos.ps1 -S1 p1.mp4 -S2 p2.mp4 -S3 p3.mp4 -Output portrait3_clip.mp4 -Clip ``` **Four portrait videos clipped to remove top/bottom padding (1920x854 — all sources must be portrait):** ```powershell .\Compare-Videos.ps1 -S1 p1.mp4 -S2 p2.mp4 -S3 p3.mp4 -S4 p4.mp4 -Output portrait4_clip.mp4 -Clip ``` **Compare two videos:** ```powershell .\Compare-Videos.ps1 -S1 original.mp4 -S2 enhanced.mp4 -Output comparison.mp4 ``` **Compare three videos:** ```powershell .\Compare-Videos.ps1 -S1 version1.mp4 -S2 version2.mp4 -S3 version3.mp4 -Output comparison.mp4 ``` **Compare four videos in grid:** ```powershell .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -S4 d.mp4 -Output grid.mp4 ``` **Without audio:** ```powershell .\Compare-Videos.ps1 -S1 video1.mp4 -S2 video2.mp4 -Output comparison.mp4 -NoAudio ``` **Using absolute paths:** ```powershell .\Compare-Videos.ps1 -S1 "C:\Videos\original.mp4" -S2 "D:\Processed\enhanced.mp4" -Output "E:\Output\comparison.mp4" ``` **Generate 4K UHD comparison:** ```powershell .\Compare-Videos.ps1 -S1 original.mp4 -S2 enhanced.mp4 -Output comparison_4k.mp4 -In4K ``` **Two 720p videos clipped to a true 16:9 strip (1920x540):** ```powershell .\Compare-Videos.ps1 -S1 a_720p.mp4 -S2 b_720p.mp4 -Output side-by-side.mp4 -Clip ``` **Same pair at 4K (3840x1080):** ```powershell .\Compare-Videos.ps1 -S1 a_720p.mp4 -S2 b_720p.mp4 -Output side-by-side_4k.mp4 -Clip -In4K ``` **Mixed orientation: 720p landscape + 720x1280 portrait (1920x720 output):** ```powershell .\Compare-Videos.ps1 -S1 landscape_720p.mp4 -S2 portrait_720x1280.mp4 -Output mixed.mp4 -Clip # Landscape occupies 1280x720 on the left, portrait fits in the right 640x720 cell with pillar boxes. ``` **Same mixed pair at 4K (3840x1440 output):** ```powershell .\Compare-Videos.ps1 -S1 landscape_720p.mp4 -S2 portrait_720x1280.mp4 -Output mixed_4k.mp4 -Clip -In4K # Landscape is upscaled to 2560x1440, portrait cell is 1280x1440. ``` **4K comparison with four videos:** ```powershell .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -S4 d.mp4 -Output grid_4k.mp4 -In4K ``` **Three landscape videos in a single row (640x1080 cells):** ```powershell .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -Output sideways.mp4 -Sideways ``` **Four landscape videos in a single row at 4K (960x2160 cells):** ```powershell .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -S4 d.mp4 -Output sideways_4k.mp4 -Sideways -In4K ``` **Three landscape videos in a single row, clipped to content height (1920x360 output):** ```powershell .\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -Output sideways_clip.mp4 -Sideways -Clip ``` ## Encoding Settings - **Codec:** libx264 - **Preset:** fast - **CRF:** 8 (high quality) - **Pixel Format:** yuv420p - **Audio:** Copied from S1 (unless `-NoAudio` specified) ## Notes - Requires `ffmpeg` and `ffprobe` in system PATH - All source video files and output directory must exist before execution - The script probes S1 to determine orientation (landscape vs. portrait) - All videos are scaled according to the detected orientation of S1 - The full ffmpeg command is printed to console before execution - Supports `-WhatIf` for dry-run testing without creating output - Videos with different aspect ratios are automatically letterboxed/pillarboxed with black padding - Source videos are never stretched or distorted - With 2 sources that exactly tile the output frame, no `scale`/`pad` filter is applied at all; see [Exact Fit](#exact-fit) - Portrait mode videos are always displayed side-by-side horizontally, not in a grid - Use `-Sideways` to force all videos into a single horizontal row; this replaces the default 2×2 grid for landscape 3 and 4 videos - `-Sideways` and `-Clip` can be combined: for landscape, the cell height is reduced to the natural content height; for portrait, the outer centering padding is omitted