125 lines
3.6 KiB
Bash
125 lines
3.6 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# remove_text.sh — conceal on-screen text in a rectangular area for a chosen time window.
|
|
# Requires: ffmpeg (with delogo filter)
|
|
|
|
show_help() {
|
|
cat <<'EOF'
|
|
Usage:
|
|
remove_text.sh -i INPUT -o OUTPUT --x X --y Y --w W --h H [--start SEC] [--end SEC] [--feather PX] [--preview] [--dry-run]
|
|
|
|
Params:
|
|
-i, --input Input video file
|
|
-o, --output Output video file
|
|
--x Left coordinate of the box (pixels)
|
|
--y Top coordinate of the box (pixels)
|
|
--w Box width (pixels)
|
|
--h Box height (pixels)
|
|
--start Start time in seconds (e.g. 12 or 12.5). If omitted, applies from t=0.
|
|
--end End time in seconds (exclusive). If omitted, applies to end of video.
|
|
--feather Feather thickness for delogo (default: 8). Higher = softer edge.
|
|
--preview Instead of removing, draw a red box to help align the area.
|
|
--dry-run Print the ffmpeg command but don't execute it.
|
|
-h, --help Show this help
|
|
|
|
Examples:
|
|
Preview the area (draws box only):
|
|
remove_text.sh -i in.mp4 -o preview.mp4 --x 120 --y 940 --w 680 --h 60 --start 12.5 --end 28.3 --preview
|
|
|
|
Remove text in that area/time range:
|
|
remove_text.sh -i in.mp4 -o out.mp4 --x 120 --y 940 --w 680 --h 60 --start 12.5 --end 28.3
|
|
EOF
|
|
}
|
|
|
|
# ---- defaults ----
|
|
INPUT=""
|
|
OUTPUT=""
|
|
X=""
|
|
Y=""
|
|
W=""
|
|
H=""
|
|
START=""
|
|
END=""
|
|
FEATHER="8"
|
|
PREVIEW="0"
|
|
DRYRUN="0"
|
|
|
|
# ---- parse args ----
|
|
if [[ $# -eq 0 ]]; then show_help; exit 1; fi
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-i|--input) INPUT="${2:-}"; shift 2;;
|
|
-o|--output) OUTPUT="${2:-}"; shift 2;;
|
|
--x) X="${2:-}"; shift 2;;
|
|
--y) Y="${2:-}"; shift 2;;
|
|
--w) W="${2:-}"; shift 2;;
|
|
--h) H="${2:-}"; shift 2;;
|
|
--start) START="${2:-}"; shift 2;;
|
|
--end) END="${2:-}"; shift 2;;
|
|
--feather) FEATHER="${2:-}"; shift 2;;
|
|
--preview) PREVIEW="1"; shift;;
|
|
--dry-run) DRYRUN="1"; shift;;
|
|
-h|--help) show_help; exit 0;;
|
|
*) echo "Unknown argument: $1"; echo; show_help; exit 1;;
|
|
esac
|
|
done
|
|
|
|
# ---- validation ----
|
|
if [[ -z "$INPUT" || -z "$OUTPUT" || -z "$X" || -z "$Y" || -z "$W" || -z "$H" ]]; then
|
|
echo "Error: Missing required arguments."; echo
|
|
show_help; exit 1
|
|
fi
|
|
|
|
if ! command -v ffmpeg >/dev/null 2>&1; then
|
|
echo "Error: ffmpeg not found in PATH."; exit 1
|
|
fi
|
|
|
|
# Build enable expression based on start/end
|
|
ENABLE_EXPR=""
|
|
if [[ -n "$START" && -n "$END" ]]; then
|
|
ENABLE_EXPR="between(t\,${START}\,${END})"
|
|
elif [[ -n "$START" ]]; then
|
|
ENABLE_EXPR="gte(t\,${START})"
|
|
elif [[ -n "$END" ]]; then
|
|
ENABLE_EXPR="lte(t\,${END})"
|
|
else
|
|
ENABLE_EXPR="" # apply for whole video
|
|
fi
|
|
|
|
# Build filter chain
|
|
if [[ "$PREVIEW" == "1" ]]; then
|
|
if [[ -n "$ENABLE_EXPR" ]]; then
|
|
VFILTER="drawbox=x=${X}:y=${Y}:w=${W}:h=${H}:color=red@0.5:thickness=2:enable='${ENABLE_EXPR}'"
|
|
else
|
|
VFILTER="drawbox=x=${X}:y=${Y}:w=${W}:h=${H}:color=red@0.5:thickness=2"
|
|
fi
|
|
else
|
|
# delogo: x, y, w, h, t = feather thickness; show=0 (no outline)
|
|
# delogo: x, y, w, h, band = feather thickness; show=0 (no outline)
|
|
if [[ -n "$ENABLE_EXPR" ]]; then
|
|
VFILTER="delogo=x=${X}:y=${Y}:w=${W}:h=${H}:show=0:enable='${ENABLE_EXPR}'"
|
|
else
|
|
VFILTER="delogo=x=${X}:y=${Y}:w=${W}:h=${H}:show=0"
|
|
fi
|
|
|
|
fi
|
|
|
|
# Assemble ffmpeg command
|
|
# -c:v libx264 -crf 18: quality/size balance; adjust as needed
|
|
# -preset medium: speed/efficiency tradeoff
|
|
# -c:a copy: keep original audio
|
|
CMD=(ffmpeg -hide_banner -y -i "$INPUT" -vf "$VFILTER" -c:v libx264 -crf 18 -preset medium -c:a copy "$OUTPUT")
|
|
|
|
echo "Running:"
|
|
printf ' %q' "${CMD[@]}"; echo
|
|
|
|
if [[ "$DRYRUN" == "1" ]]; then
|
|
exit 0
|
|
fi
|
|
|
|
"${CMD[@]}"
|
|
|
|
echo "Done: $OUTPUT"
|