Last active
December 21, 2020 02:30
-
-
Save BETLOG/c10a578a5b4f382fbd350ce1b6381334 to your computer and use it in GitHub Desktop.
ffmpeg zoom directly at target (more useful/visually expected than the typical examples)
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 | |
# betlog - 2020-12-20--23-01-02 | |
# https://superuser.com/questions/1087685/ffmpeg-zoompan-image-to-center-position | |
# http://dragonquest64.blogspot.com/2019/02/ffmpeg-zoom.html <<<<------ THIS | |
# | |
# syntax: | |
# video-zoompanOnImage.sh [geometry(${sizeX}x${sizeY}+${posX}+${posY}] [zoom|zoompan] imageFilename | |
# omitting geometry or mode will query with kdialog | |
# eg: | |
# video-zoompanOnImage.sh image.png | |
# video-zoompanOnImage.sh 66x38+808+330 image.png | |
# video-zoompanOnImage.sh 66x38+808+330 zoom image.png # >10x so force-limited | |
# video-zoompanOnImage.sh 66x38+808+330 zoompan image.png # >10x so force-limited | |
# | |
# NOTE: assumes video res = screen res = image res | |
# | |
# TODO: padding might be useful for some zoompans | |
# TODO: trying to set a specific pos seems to always fail (animate quickly from 0,0) if there is a zoom involved | |
# TODO: only works on $screenRes (1920x1080) images | |
screenRes=$(xrandr|grep primary|grep -Eo '[0-9]{3,}x[0-9]{3,}') | |
enlargedScale=10 | |
zoomDuration=2 | |
framerate=60 | |
arg=$(grep -Eo '[0-9]{2,}x[0-9]{2,}\+[0-9]{1,}\+[0-9]{1,}'<<<"$1") | |
if [[ -n $arg ]];then | |
geom=$arg | |
# echo "geom: $geom" | |
sizeX=$(awk -F'x|+' '{print $1}'<<<$geom) | |
sizeY=$(awk -F'x|+' '{print $2}'<<<$geom) | |
posX=$(awk -F'x|+' '{print $3}'<<<$geom) | |
posY=$(awk -F'x|+' '{print $4}'<<<$geom) | |
# echo "sizeX:$sizeX, sizeY:$sizeY, posX:$posX, posY:$posY" | |
shift | |
if [[ $1 == zoompan ]];then | |
mode=zoompan | |
shift | |
elif [[ $1 == zoom ]];then | |
mode=zoom | |
shift | |
fi | |
# echo "remaining input: $@" | |
fi | |
# ------------------------- | |
for input in "$@"; do | |
input=$(printf "%b\n" "${input//%/\\x}"| sed 's/file:\/\///g') #also strip special characters in nfbc2 folder name | |
if [[ -f "$input" && $(mimetype -ab $(realpath "$input")) =~ image ]] ;then | |
# echo "input: $input" | |
baseName="${input%.*}" | |
baseExt="${input##*.}" | |
if [[ -z $geom ]];then | |
# geeqie --fullscreen "$input" & 2>&1 /dev/null | |
geeqie --fullscreen "$input" & | |
pid=$! | |
# echo "pid: $pid $(ps -p$pid -o cmd=)" | |
source /home/user/documents/scripts/video/f_getClickGeom | |
# outputs: | |
# $geom | |
# $sizeX | |
# $sizeY | |
# $posX | |
# $posY | |
# $resX | |
# $resY | |
# sleep 1s | |
kill $pid 2> /dev/null | |
else | |
# imgRes=$(file "$input"|grep -Eo '[0-9]{3,}( )?x( )?[0-9]{3,}'|sed 's/ //g') #actual image res, not screen res | |
# resX=$(awk -F'x' '{print $1}'<<<$imgRes) | |
# resY=$(awk -F'x' '{print $2}'<<<$imgRes) | |
resX=$(awk -F'x' '{print $1}'<<<$screenRes) | |
resY=$(awk -F'x' '{print $2}'<<<$screenRes) | |
fi | |
if [[ -z $mode ]];then | |
mode=$(kdialog --radiolist "Select mode:" zoom "direct zoom" on zoompan "zoom and pan" off) | |
[[ $? -ne 0 ]] && exit | |
fi | |
if [[ -z $mode ]];then | |
echo "ERROR: mode 'zoom' or 'zoompan' must be specified" | |
kdialog --title "ERROR:" --passivepopup "mode 'zoom' or 'zoompan' must be specified" 5 | |
exit | |
fi | |
exportName="${baseName}_${mode}${geom}.mp4" | |
echo -ne "=============== ENCODING ZOOMPAN - $mode - $geom ===============\\n" | |
echo -ne "input : ${input}\\noutput: ${exportName}\\n" | |
echo "imgRes : $imgRes" | |
echo "screenRes : $screenRes" | |
echo "enlargedScale: $enlargedScale" | |
echo "zoomDuration : $zoomDuration" | |
echo "framerate : $framerate" | |
echo "posX : $posX" | |
echo "posY : $posY" | |
echo "sizeX : $sizeX" | |
echo "sizeY : $sizeY" | |
echo "resX : $resX" | |
echo "resY : $resY" | |
zoomFactor=$( echo "scale=3;$resX/$sizeX"|bc) | |
if (( $(echo "$zoomFactor > 10" |bc -l) ));then | |
z=$zoomFactor | |
zoomFactor=10 | |
echo "zoomFactor :: $zoomFactor (WAS $z NOW FORCE LIMITED TO 10)" | |
else | |
echo "zoomFactor : $zoomFactor" | |
fi | |
centerX=$((posX+(sizeX/2))) | |
centerY=$((posY+(sizeY/2))) | |
echo "centerX :: $centerX" | |
echo "centerY :: $centerY" | |
posX=$((centerX-(resX/20))) | |
posY=$((centerY-(resY/20))) | |
echo "posX :: $posX" | |
echo "posY :: $posY" | |
zoomFrames=$(echo "scale=1;$zoomDuration*$framerate"|bc) | |
echo "zoomFrames : $zoomFrames" | |
zoomIncrement=$(echo "scale=3;($zoomFactor-1)/($zoomDuration*$framerate)"|bc) # CORRECT: starts at zoomFactor=1 | |
echo "zoomIncrement: $zoomIncrement" | |
enlargedX=$((enlargedScale * resX)) | |
enlargedY=$((enlargedScale * resY)) | |
echo "enlargedX: $enlargedX" | |
echo "enlargedY: $enlargedY" | |
enlargedPosX=$((enlargedScale * posX)) | |
enlargedPosY=$((enlargedScale * posY)) | |
echo "enlargedPosX: $enlargedPosX" | |
echo "enlargedPosY: $enlargedPosY" | |
enlargedCenterX=$((enlargedScale * centerX)) | |
enlargedCenterY=$((enlargedScale * centerY)) | |
echo "enlargedCenterX: $enlargedCenterX" | |
echo "enlargedCenterY: $enlargedCenterY" | |
enlargedPanIncrementX=$(echo "scale=3;$enlargedPosX/$zoomFrames"|bc) | |
enlargedPanIncrementY=$(echo "scale=3;$enlargedPosY/$zoomFrames"|bc) | |
echo "enlargedPanIncrementX: $enlargedPanIncrementX" | |
echo "enlargedPanIncrementY: $enlargedPanIncrementY" | |
# | |
# cmd="ffmpeg -hide_banner " | |
cmd="ffmpeg-bar -hide_banner " | |
cmd+="-i \"${input}\" " | |
cmd+="-filter_complex " | |
cmd+="\"[0:v]scale=${enlargedX}x${enlargedY}" | |
if [[ $mode == zoom ]];then | |
cmd+=",zoompan" | |
cmd+="=z='min(max(zoom,pzoom)+${zoomIncrement},${zoomFactor})'" | |
cmd+=":x='${enlargedCenterX}-(${enlargedCenterX}/zoom)'" | |
cmd+=":y='${enlargedCenterY}-(${enlargedCenterY}/zoom)'" | |
elif [[ $mode == zoompan ]];then | |
cmd+=",zoompan" | |
cmd+="=z='zoom+${zoomIncrement}'" | |
# cmd+="=z=zoom+${zoomIncrement},${zoomFactor}" | |
cmd+=":x='if(gte(zoom,$zoomFactor),x,x+${enlargedPanIncrementX})'" | |
cmd+=":y='if(gte(zoom,$zoomFactor),y,y+${enlargedPanIncrementY})'" | |
fi | |
cmd+=":d=${zoomFrames}" | |
cmd+=":s=${screenRes}" | |
cmd+=":fps=${framerate}" | |
cmd+="[v]" | |
cmd+="\" " | |
cmd+="-map [v] " | |
cmd+="-map 0:a? " | |
cmd+="-c:v libx264 " | |
cmd+="-pix_fmt yuv420p " | |
cmd+="-c:a copy " | |
cmd+="-y \"${exportName}\" </dev/null" | |
source "${0%/*}/f_echoCmd" | |
[[ -f "$exportName" ]] && rm "$exportName" | |
eval $cmd | |
[[ -f "$exportName" ]] && smplayer "$exportName" & | |
fi | |
done |
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 | |
# betlog - 2020-10-28--18-08-03 | |
# | |
# NOTE: get clicked rectangle, longest edge is used to match both sides to screen aspect ratio | |
# inputs: | |
# none (user clicks) | |
# outputs: | |
# $geom | |
# $sizeX | |
# $sizeY | |
# $posX | |
# $posY | |
# $resX | |
# $resY | |
# if [[ $(xprop -id $(xprop -root _NET_ACTIVE_WINDOW | cut -d ' ' -f 5)|grep '_NET_WM_STATE(ATOM)') =~ _NET_WM_STATE_FULLSCREEN ]];then | |
state=$(xprop -id $(xprop -root _NET_ACTIVE_WINDOW | cut -d ' ' -f 5)|grep '_NET_WM_STATE(ATOM)') | |
# echo -ne "\\n\\n===============\\n${state}\\n===============\\n\\n" | |
if [[ $state =~ _NET_WM_STATE_STAYS_ON_TOP || $state =~ _NET_WM_STATE_FULLSCREEN ]];then | |
sudo --background --user $USER kdialog --title "${0##*/}" --passivepopup "click TOP-LEFT and DRAG to set geometry\\n!NOTE!\\naspect will be forced along longest edge\\nto SCREEN (potentially XScreen) aspect" 5 2> /dev/null | |
else | |
kdialog --title "${0##*/}" --passivepopup "click TOP-LEFT and DRAG to set geometry\\n!NOTE!\\naspect will be forced along longest edge\\nto SCREEN (potentially XScreen) aspect" 5 2> /dev/null | |
fi | |
data=$(import -identify /dev/null 2>/dev/null) | |
[[ -z $data ]] && exit | |
# echo "data: $data" | |
size=$(awk '{print $3}'<<<"$data") | |
# echo "size: $size" | |
sizeX=$(awk -F'x|+' '{print $1}'<<<"$size") | |
sizeY=$(awk -F'x|+' '{print $2}'<<<"$size") | |
echo "size: ${sizeX}x${sizeY} (actual)" | |
# echo "sizeX: $sizeX" | |
# echo "sizeY: $sizeY" | |
xscreenSizeAndClickedPos=$(awk '{print $4}'<<<"$data") #unadjusted, as picked, no aspect ratio correction | |
# echo "xscreenSizeAndClickedPos (as clicked): $xscreenSizeAndClickedPos" | |
posX=$(awk -F'x|+' '{print $3}'<<<"$xscreenSizeAndClickedPos") | |
posY=$(awk -F'x|+' '{print $4}'<<<"$xscreenSizeAndClickedPos") | |
echo "pos: $posX,$posY" | |
# echo "posX: $posX" | |
# echo "posY: $posY" | |
xScreenResX=$(awk -F'x|+' '{print $1}'<<<"$xscreenSizeAndClickedPos") | |
xScreenResY=$(awk -F'x|+' '{print $2}'<<<"$xscreenSizeAndClickedPos") | |
# echo "xScreenRes: ${xScreenResX}x${xScreenResY}" | |
# echo "xScreenResX: $xScreenResX" | |
# echo "xScreenResY: $xScreenResY" | |
# echo | |
data=$(xrandr|grep -Eo '[0-9]{3,}x[0-9]{3,}\+[0-9]{1,}\+[0-9]{1,}'|sed -E 's/\ {1,}/ /g; s/\*//g') | |
# echo -ne "data:\\n$data\\n\\n" | |
declare -a displayGeomArray=( $(echo "$data"|grep -Eo '[0-9]{3,}x[0-9]{3,}\+[0-9]{1,}\+[0-9]{1,}'|tr '\n' ' ') ) | |
total=${#displayGeomArray[@]} | |
for ((i=0;i<$total;i++));do | |
region=${displayGeomArray[$i]} | |
# echo "i: $i" | |
# echo "region: $region" | |
minX=$(sed -E 's/[0-9]{1,}x[0-9]{1,}\+([0-9]{1,})\+[0-9]{1,}/\1/'<<<$region) | |
resX=$(sed -E 's/([0-9]{1,})x[0-9]{1,}\+[0-9]{1,}\+[0-9]{1,}/\1/'<<<$region) | |
maxX=$(( minX + resX )) | |
# echo "minX: $minX" | |
# echo "resY: $resY" | |
# echo "maxX: $maxX" | |
minY=$(sed -E 's/[0-9]{1,}x[0-9]{1,}\+[0-9]{1,}\+([0-9]{1,})/\1/'<<<$region) | |
resY=$(sed -E 's/[0-9]{1,}x([0-9]{1,})\+[0-9]{1,}\+[0-9]{1,}/\1/'<<<$region) | |
maxY=$(( minY + resY )) | |
# echo "minY: $minY" | |
# echo "resY: $resY" | |
# echo "maxY: $maxY" | |
if [[ $posX -gt $minX && $posX -lt $maxX && $posY -gt $minY && $posY -lt $maxY ]];then # first click point | |
clickedRegion=$i | |
break | |
fi | |
# echo | |
done | |
# echo "clickedRegion: $clickedRegion" | |
displayResArray=( $(xrandr|grep ' connected '|sed -E 's/^.* ([0-9]{1,}x[0-9]{1,})\+[0-9]{1,}\+[0-9]{1,} .*$/\1/') ) | |
# echo -ne "displayResArray:\\n${displayResArray[@]}\\n" | |
res=${displayResArray[$i]} #reassign | |
resX=${res%x*} | |
resY=${res#*x} | |
echo "res: $res" | |
# echo "resX: $resX" | |
# echo "resY: $resY" | |
# use longest selected edge to limit selection t same aspect ratio as total (single, not combined) screen resolution | |
# TODO: ?loop compare mod2 to get power of 2 size? can record without, BUT CANNOT reencode until i do this | |
let sizeX++ | |
let sizeY++ | |
if (( $(echo "$sizeY/$resY > $sizeX/$resX" |bc -l) ));then | |
echo "Y (vertical) edge is primary" | |
sizeX=$(echo "scale=3;$sizeY/$resY*$resX"|bc) | |
sizeX=$(echo "scale=0;($sizeX+0.5)/1"|bc) #ceil | |
# echo "sizeX: $sizeX" | |
if [[ $((posX + sizeX)) -gt $xScreenResX ]];then | |
echo "$((posX+sizeX)) > $xScreenResX : exceeds bounds on X - rescaling Y" | |
sizeX=$((xScreenResX-posX)) | |
sizeY=$(echo "scale=3;$sizeX/$resX*$resY"|bc) | |
sizeY=$(echo "scale=0;($sizeY+0.5)/1"|bc) #ceil | |
fi | |
else | |
echo "X (horizontal) edge is primary" | |
sizeY=$(echo "scale=3;$sizeX/$resX*$resY"|bc) | |
sizeY=$(echo "scale=0;($sizeY+0.5)/1"|bc) #ceil | |
if [[ $((posY + sizeY)) -gt $xScreenResY ]];then | |
echo "$((posY+sizeY)) > $xScreenResY : exceeds bounds on Y - rescaling X" | |
sizeY=$((xScreenResY-posY)) | |
sizeX=$(echo "scale=3;$sizeY/$resY*$resX"|bc) | |
sizeX=$(echo "scale=0;($sizeX+0.5)/1"|bc) #ceil | |
fi | |
fi | |
if [[ -z $sizeX || -z $sizeY || -z $posX || -z $posY ]];then | |
echo "GEOMETRY FAILURE - ${sizeX}x${sizeY}+${posX}+${posY}" | |
kdialog --title "${0##*/}" --passivepopup "GEOMETRY FAILURE\\n${sizeX}x${sizeY}+${posX}+${posY}" 5 | |
unset data size sizeX sizeY geom resX resY posX posY totalRes totalResX totalResY # cleanup because fail | |
exit | |
fi | |
# NOTE: make sure it's divisible by 2 or cant re-encode it later | |
# echo "$((sizeX % 2))" | |
# echo "$((sizeY % 2))" | |
# echo "size: ${sizeX}x${sizeY} (actual)" | |
[[ 0 -ne $((sizeX % 2)) ]] && let sizeX-- | |
[[ 0 -ne $((sizeY % 2)) ]] && let sizeY-- | |
echo "size: ${sizeX}x${sizeY} (screen aspect adjusted)" | |
geom="${sizeX}x${sizeY}+${posX}+${posY}" #adjusted to longest side dictates aspect ratio matching current screen | |
echo "geom (aspect adjusted): $geom" | |
kdialog --title "${0##*/}" --passivepopup "GEOMETRY:\\n$geom" 5 | |
unset data size | |
# 1920/1080=1.777777777777778 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment