Files
Video-Processing-Scripts/skill/Compare-Videos.md
T

20 KiB
Raw Blame History

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 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.

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 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 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 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):

[0:v][1:v]hstack=inputs=2[outv]

The console reports it as:

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.

[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.

[S1][S2]
[  S3  ]

4 videos: Output is 1920x1080. Four 960x540 cells in a 2x2 grid.

[S1][S2]
[S3][S4]

4K UHD (3840x2160) - With -In4K

2 videos: Output is 3840x2160. Each video occupies a 1920x2160 cell side-by-side.

[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.

[S1][S2]
[  S3  ]

4 videos: Output is 3840x2160. Four 1920x1080 cells in a 2x2 grid.

[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).

[  ][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).

[ ][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).

[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).

[  ][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).

[ ][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).

[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.

[S1][S2]

3 videos: Each cell is 640x1080. Video content scales to 640x360, padded vertically. Total: 1920x1080. With -Clip: total 1920x360.

[S1][S2][S3]

4 videos: Each cell is 480x1080. Video content scales to 480x270, padded vertically. Total: 1920x1080. With -Clip: total 1920x270.

[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):

.\Compare-Videos.ps1 -S1 wide_1280x1080.mp4 -S2 narrow_640x1080.mp4 -Output exact.mp4

Exact fit at 4K (2560x2160 + 1280x2160 → 3840x2160):

.\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):

.\Compare-Videos.ps1 -S1 portrait1.mp4 -S2 portrait2.mp4 -Output portrait_clip.mp4 -Clip

Same pair at 4K (2432x2160):

.\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):

.\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):

.\Compare-Videos.ps1 -S1 p1.mp4 -S2 p2.mp4 -S3 p3.mp4 -S4 p4.mp4 -Output portrait4_clip.mp4 -Clip

Compare two videos:

.\Compare-Videos.ps1 -S1 original.mp4 -S2 enhanced.mp4 -Output comparison.mp4

Compare three videos:

.\Compare-Videos.ps1 -S1 version1.mp4 -S2 version2.mp4 -S3 version3.mp4 -Output comparison.mp4

Compare four videos in grid:

.\Compare-Videos.ps1 -S1 a.mp4 -S2 b.mp4 -S3 c.mp4 -S4 d.mp4 -Output grid.mp4

Without audio:

.\Compare-Videos.ps1 -S1 video1.mp4 -S2 video2.mp4 -Output comparison.mp4 -NoAudio

Using absolute paths:

.\Compare-Videos.ps1 -S1 "C:\Videos\original.mp4" -S2 "D:\Processed\enhanced.mp4" -Output "E:\Output\comparison.mp4"

Generate 4K UHD comparison:

.\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):

.\Compare-Videos.ps1 -S1 a_720p.mp4 -S2 b_720p.mp4 -Output side-by-side.mp4 -Clip

Same pair at 4K (3840x1080):

.\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):

.\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):

.\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:

.\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):

.\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):

.\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):

.\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)

Existing Files

  • An existing -Output video is replaced without a prompt. ffmpeg runs with -y, so the old file is lost. Use -WhatIf first, or write to a new name.
  • The folder that contains -Output must already exist. The script stops with an error instead of creating it.
  • The source videos are only read and are never modified.

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
  • 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