MIQUEL CASTRO NAVARRO
MIQUEL CASTRO NAVARRO

Reputation: 43

ffmpeg: zoom image to positions X Y (zoompan)

I'm using ffmpeg to zoom an image to a specific point (x, y).

I want the point to be the final position of the zoom. The command I use now is:

ffmpeg -y -i zoomInimg.jpg -vf "scale=6198:2350:-1,zoompan=z='min(max(zoom,pzoom)+0.06,10)':d=2400:x=4000 :y=1000 :s=6198x2350" -t 5 zoomIn.mp4

I want to have a video of zooming an image to the point (x=4000, y=1000).

Upvotes: 2

Views: 3086

Answers (2)

Felipe
Felipe

Reputation: 17181

I made some small improvements in the answer of Eugene Khyst (with the help of ChatGPT 4).

The following script let you choose the output name and randomize a coordinate x,y. The size of input now is irrelevant too.

#!/bin/bash
# Based on: https://stackoverflow.com/a/72329573/450148
# Example: <cmd> image.JPG output.mp4 3 6
# max zoom (times). Default 2.5.
# duration (seconds). Default 5.

INPUT="$1"
OUTPUT="$2"
MAX_ZOOM="${3:-2.5}"

DURATION_SECONDS="${4:-5}"
FPS="30"
DURATION=$(bc <<< "${DURATION_SECONDS}*${FPS}")
ZOOM_SPEED=$(bc <<< "scale=6;(${MAX_ZOOM}-1)/${DURATION}")

SIZE=$(ffprobe -v error -select_streams v -show_entries stream=width,height -of csv=p=0:s=x "$INPUT")
OW=$(echo $SIZE | cut -d'x' -f1)
OH=$(echo $SIZE | cut -d'x' -f2)

# Randomly select X and Y coordinates
XP=$(shuf -i 0-$OW -n 1)
YP=$(shuf -i 0-$OH -n 1)

# Use the original image size
W=$OW
H=$OH

# Ensure width and height are divisible by 2
W=$((W % 2 == 0 ? W : W - 1))
H=$((H % 2 == 0 ? H : H - 1))

ffmpeg -y -loop 1 -i "$INPUT" -vf "scale=iw*6:ih*6,zoompan=z='zoom+${ZOOM_SPEED}':x='$XP':y='$YP':d=$DURATION:s='${W}x${H}'" -c:v libx264 -pix_fmt yuv420p -t $DURATION_SECONDS -shortest "$OUTPUT"

Upvotes: 0

Eugene Khyst
Eugene Khyst

Reputation: 10315

The ffmpeg command to use zoom in to a custom point of an image:

ffmpeg -i "$INPUT" \
   -vf "zoompan=z='min(zoom+${ZOOM_SPEED},${MAX_ZOOM})':x='iw/2-iw*(1/2-${XP}/100)*on/${DURATION}-iw/zoom/2':y='ih/2-ih*(1/2-${YP}/100)*on/${DURATION}-ih/zoom/2':d=${DURATION}:fps=${FPS}:s=${W}x${H}" \
   -c:v libx264 "$OUTPUT" -y

ffprobe can be used to get the original image dimension.

I created a convenient script image_to_video_zoom.sh to create a video of zooming to a custom point of an image and controlling a zoom level and duration of the video:

#!/bin/bash

INPUT="$1"
OUTPUT="${INPUT%%.*}_zoom.MP4"
MAX_ZOOM="${2:-2.5}"

DURATION_SECONDS="${3:-5}"
FPS="30"
DURATION=$(bc <<< "${DURATION_SECONDS}*${FPS}")
ZOOM_SPEED=$(bc <<< "scale=4;(${MAX_ZOOM}-1)/${DURATION}")

SIZE=$(ffprobe -v error -select_streams v -show_entries stream=width,height -of csv=p=0:s=x "$INPUT")
OW=$(echo $SIZE | cut -d'x' -f1)
OH=$(echo $SIZE | cut -d'x' -f2)

if (( $(bc -l <<< "scale=4;${OW}/${OH} >= 16/9") ))
then
  R=$(bc <<< "scale=4;${OH}/1080")
  W=$(bc <<< "${OW}/${R}")
  H=$(bc <<< "${OH}/${R}")
else
  R=$(bc <<< "scale=4;${OW}/1920")
  W=$(bc <<< "${OW}/${R}")
  H=$(bc <<< "${OH}/${R}")
  if (( $(bc <<< "${H}%2") ))
  then
    H=$(bc <<< "${H}-1")
  fi
fi

if [[ -z "$4" || -z "$5" ]]
then
  XP="50"
  YP="50"
else
  XP=$(bc <<< "100*${4}/${OW}")
  YP=$(bc <<< "100*${5}/${OH}")
fi

ffmpeg -i "$INPUT" \
       -vf "zoompan=z='min(zoom+${ZOOM_SPEED},${MAX_ZOOM})':x='iw/2-iw*(1/2-${XP}/100)*on/${DURATION}-iw/zoom/2':y='ih/2-ih*(1/2-${YP}/100)*on/${DURATION}-ih/zoom/2':d=${DURATION}:fps=${FPS}:s=${W}x${H}" \
       -c:v libx264 "$OUTPUT" -y

Example:

./image_to_video_zoom.sh image.JPG 3 6 1200 1800
  1. 3 - max zoom (times). Default 2.5.
  2. 6 - duration (seconds). Default 5.
  3. 1200 - X coordinate (pixels) to zoom in.
  4. 1800 - Y coordinate (pixels) to zoom in.

The result:

ffmpeg video of zooming in to a custom point

The script allows controlling the zoom level, duration of the video and point to zoom in. If a point to zoom in is not specified, the script will create a video of zooming to the center of the image.

The resulting video is proportionally scaled to have height 1080 or width 1920 depending on the aspect ratio (16:9).

Video with width 1920 and height greater than 1080 can be cropped to 1920x1080 using crop filter:

ffmpeg -i "$INPUT" -filter:v "crop=1920:1080" "$OUTPUT"

Upvotes: 5

Related Questions