91 lines
3.2 KiB
Bash
Executable File
91 lines
3.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# detect_pink_intro_hsv.sh <file.mp4> [duration_s] [fps] [hue_min] [hue_max] [sat_min] [val_min] [min_ratio]
|
|
#
|
|
# Detect a magenta/pink intro by averaging frames to 1x1 and classifying in HSV.
|
|
#
|
|
# Defaults:
|
|
# duration_s = 1.0
|
|
# fps = 6
|
|
# hue_min = 285 # degrees, magenta band start
|
|
# hue_max = 330 # degrees, magenta band end
|
|
# sat_min = 0.35 # require some saturation
|
|
# val_min = 0.35 # avoid very dark frames
|
|
# min_ratio = 0.6 # % of sampled frames that must be pink-ish
|
|
#
|
|
# Exit codes: 0 detected, 1 not detected, 2 usage error
|
|
|
|
set -euo pipefail
|
|
|
|
file="${1:-}"
|
|
duration="${2:-1.0}"
|
|
sample_fps="${3:-6}"
|
|
HMIN="${4:-285}"
|
|
HMAX="${5:-330}"
|
|
SMIN="${6:-0.35}"
|
|
VMIN="${7:-0.35}"
|
|
MIN_RATIO="${8:-0.6}"
|
|
|
|
if [[ -z "$file" ]]; then
|
|
echo "Usage: $0 <file.mp4> [duration_s] [fps] [hue_min] [hue_max] [sat_min] [val_min] [min_ratio]" >&2
|
|
exit 2
|
|
fi
|
|
[[ -f "$file" ]] || { echo "File not found: $file" >&2; exit 2; }
|
|
|
|
# Stream 1x1 RGB for sampled frames; each frame = 3 bytes. Parse with od+awk.
|
|
summary="$(
|
|
LC_ALL=C ffmpeg -hide_banner -v error \
|
|
-i "$file" -t "$duration" \
|
|
-vf "fps=${sample_fps},scale=1:1:flags=area,format=rgb24" \
|
|
-f rawvideo - 2>/dev/null \
|
|
| od -An -tu1 -w3 \
|
|
| awk -v HMIN="$HMIN" -v HMAX="$HMAX" -v SMIN="$SMIN" -v VMIN="$VMIN" '
|
|
function max(a,b){return a>b?a:b}
|
|
function min(a,b){return a<b?a:b}
|
|
# Convert RGB(0..255) to HSV; return H in degrees [0,360), S,V in [0,1]
|
|
function rgb2hsv(R,G,B, r,g,b,M,m,d,H,S,V) {
|
|
r=R/255.0; g=G/255.0; b=B/255.0;
|
|
M=r; if(g>M) M=g; if(b>M) M=b;
|
|
m=r; if(g<m) m=g; if(b<m) m=b;
|
|
d=M-m; V=M;
|
|
if (M==0) { S=0 } else { S=(d==0?0:d/M) }
|
|
if (d==0) { H=0 }
|
|
else if (M==r) { H = 60.0 * fmod(((g-b)/d), 6) }
|
|
else if (M==g) { H = 60.0 * (((b-r)/d) + 2) }
|
|
else { H = 60.0 * (((r-g)/d) + 4) }
|
|
if (H < 0) H += 360.0
|
|
hsv[1]=H; hsv[2]=S; hsv[3]=V
|
|
}
|
|
function fmod(a,b){ return a - int(a/b)*b }
|
|
BEGIN{ total=0; pink=0; }
|
|
NF==3 {
|
|
R=$1; G=$2; B=$3;
|
|
total++;
|
|
rgb2hsv(R,G,B);
|
|
H=hsv[1]; S=hsv[2]; V=hsv[3];
|
|
# classify: hue in magenta band AND sufficiently saturated/bright
|
|
if (H>=HMIN && H<=HMAX && S>=SMIN && V>=VMIN) pink++;
|
|
# save last metrics to show a couple of examples (optional)
|
|
if (total<=10) printf "DBG frame%02d: RGB=(%3d,%3d,%3d) HSV=(%6.1f°, %4.2f, %4.2f)\n", total,R,G,B,H,S,V > "/dev/stderr"
|
|
}
|
|
END {
|
|
if (total==0) { printf "TOTAL=0 PINK=0 RATIO=0\n"; exit; }
|
|
printf "TOTAL=%d PINK=%d RATIO=%.3f\n", total, pink, pink/total;
|
|
}'
|
|
)"
|
|
|
|
if ! printf "%s" "$summary" | grep -q 'TOTAL='; then
|
|
echo "No frames parsed (clip too short or pipeline issue)."
|
|
exit 1
|
|
fi
|
|
|
|
eval "$summary" # sets TOTAL,PINK,RATIO
|
|
echo "File: $file"
|
|
echo "Analyzed: first ${duration}s @ ${sample_fps} fps → frames: ${TOTAL}"
|
|
echo "Pink-ish frames (hue in ${HMIN}-${HMAX}°, S>=${SMIN}, V>=${VMIN}): ${PINK}"
|
|
echo "Ratio: ${RATIO} (threshold: ${MIN_RATIO})"
|
|
|
|
awk -v r="$RATIO" -v thr="$MIN_RATIO" 'BEGIN{ exit !(r+0 >= thr+0) }' \
|
|
&& { echo "DETECTED: Pink intro present"; exit 0; }
|
|
echo "Not detected"; exit 1
|
|
|