Created
March 5, 2024 02:57
-
-
Save rileyjshaw/afe98fce6e6988bf3ce7a09b26e5e120 to your computer and use it in GitHub Desktop.
Create an ultra-wide photograph out of a slow dolly shot video. FFMPEG is amazing!
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# Default values | |
start_time=0 | |
rtl_flag=false | |
output_file="output.png" | |
downscaling_factor=1 | |
aspect_ratio="16:9" | |
# Parse arguments | |
while [[ "$#" -gt 0 ]]; do | |
case $1 in | |
-i|--input) input_file="$2"; shift ;; | |
-s|--start) start_time="$2"; shift ;; | |
--rtl) rtl_flag=true ;; | |
-o|--output) output_file="$2"; shift ;; | |
-d|--downscale) downscaling_factor="$2"; shift ;; | |
-a|--aspect-ratio) aspect_ratio="$2"; shift ;; | |
*) echo "Unknown parameter: $1"; exit 1 ;; | |
esac | |
shift | |
done | |
if [ -z "$input_file" ]; then | |
echo "Input file is required." | |
exit 1 | |
fi | |
# Parse the aspect ratio string | |
aspect_ratio_numerator=$(echo $aspect_ratio | cut -d: -f1) | |
aspect_ratio_denominator=$(echo $aspect_ratio | cut -d: -f2) | |
# Derive the duration and total number of frames in the video | |
duration=$(ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "$input_file") | |
total_frames=$(ffprobe -v error -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1:nokey=1 "$input_file") | |
if ! [[ "$total_frames" =~ ^[0-9]+$ ]]; then | |
echo "Error: Could not determine total frames in the video." | |
exit 1 | |
fi | |
total_frames=$(echo "$total_frames" | bc) | |
# Find the height of the video | |
height=$(ffprobe -v error -select_streams v:0 -show_entries stream=height -of csv=p=0 "$input_file" | awk -F, '{print $1}') | |
scaled_height=$(echo "$height / $downscaling_factor" | bc) | |
# Calculate desired width based on aspect ratio and round to nearest integer | |
width=$(echo "scale=2; $scaled_height * $aspect_ratio_numerator / $aspect_ratio_denominator + 0.5" | bc | cut -d. -f1) | |
# Each frame will be 1 pixel wide, so we need $width frames | |
frame_count=$width | |
if [ "$total_frames" -lt "$frame_count" ]; then | |
echo "Error: Total frames in the video ($total_frames) is less than the required frame count ($frame_count) for this aspect ratio ($aspect_ratio)." | |
exit 1 | |
fi | |
frame_interval=$(echo "$total_frames / $frame_count" | bc) | |
time_interval=$(echo "$duration / $frame_count" | bc -l) | |
# Create a temporary directory | |
temp_dir=$(mktemp -d) | |
# Extract frames as images | |
# Method 1: Take the center column of each frame | |
ffmpeg -ss "$start_time" -i "$input_file" -vf "fps=1/$time_interval,scale=$width:$scaled_height,format=yuvj444p,crop=1:ih:(iw/2):0:exact=1" -vframes "$frame_count" "$temp_dir/frame_%04d.png" | |
# Method 2: Move from the leftmost column (first frame) to the rightmost column (last frame) | |
# echo "Looping through each frame. This will take a while…" | |
# for ((frame_index=0; frame_index<frame_count; frame_index++)); do | |
# # Calculate the progress (i) from 0 to 1 | |
# progress=$(echo "scale=8; $frame_index / ($frame_count - 1)" | bc) | |
# | |
# # Calculate dynamic x coordinate for cropping | |
# crop_x=$(echo "$width * $progress" | bc) | |
# if $rtl_flag; then | |
# crop_x=$(echo "$width - $crop_x" | bc) | |
# fi | |
# | |
# frame_time=$(echo "$frame_index * $time_interval / $frame_count" | bc -l) | |
# formatted_frame_time=$(printf "%08f" $frame_time) | |
# | |
# # Extract frame with dynamic crop | |
# ffmpeg -loglevel error -ss $formatted_frame_time -i "$input_file" -frames:v 1 -vf "scale=$width:$scaled_height,format=yuvj444p,crop=1:ih:$crop_x:0:exact=1" "$temp_dir/frame_$(printf "%04d" $frame_index).png" | |
# done | |
# Concatenate frames | |
ffmpeg -i "$temp_dir/frame_%04d.png" -filter_complex "tile=${width}x1" "$output_file" | |
# Apply RTL flag if set | |
if $rtl_flag; then | |
ffmpeg -i "$output_file" -vf "hflip" "$temp_dir/output_flipped.png" | |
mv "$temp_dir/output_flipped.png" "$output_file" | |
fi | |
# Cleanup and open | |
rm -rf "$temp_dir" | |
open "$output_file" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment