Upload agents/modality_detector.py with huggingface_hub
Browse files- agents/modality_detector.py +43 -12
agents/modality_detector.py
CHANGED
|
@@ -30,6 +30,25 @@ def detect_modality(img: Image.Image) -> ModalityResult:
|
|
| 30 |
|
| 31 |
w, h = img.size
|
| 32 |
gray = np.array(img.convert("L")).astype(np.float64)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
# βββ CONTENT-BASED DETECTION (works without metadata) βββββββββββββ
|
| 35 |
|
|
@@ -75,18 +94,22 @@ def detect_modality(img: Image.Image) -> ModalityResult:
|
|
| 75 |
indicators["transition_abruptness"] = round(transition, 2)
|
| 76 |
indicators["has_detail"] = has_detail
|
| 77 |
|
| 78 |
-
# Portrait mode scoring β
|
|
|
|
|
|
|
|
|
|
|
|
|
| 79 |
portrait_score = 0.0
|
| 80 |
-
if
|
| 81 |
-
portrait_score += 0.25
|
| 82 |
-
if
|
| 83 |
-
portrait_score += 0.2
|
| 84 |
-
if
|
| 85 |
-
portrait_score += 0.2
|
| 86 |
-
if
|
| 87 |
-
portrait_score += 0.15
|
| 88 |
-
|
| 89 |
-
if portrait_score > 0.3:
|
| 90 |
scores["PORTRAIT_MODE"] = portrait_score
|
| 91 |
indicators["portrait_detected"] = True
|
| 92 |
|
|
@@ -174,12 +197,20 @@ def detect_modality(img: Image.Image) -> ModalityResult:
|
|
| 174 |
modality = "PORTRAIT_MODE"
|
| 175 |
conf = min(1.0, scores["PORTRAIT_MODE"])
|
| 176 |
|
| 177 |
-
# SAFETY GUARD: No detail = possible AI. Disable all suppression.
|
| 178 |
if not has_detail:
|
| 179 |
modality = "UNKNOWN"
|
| 180 |
conf = 0.2
|
| 181 |
indicators["safety_override"] = "Low-detail image β suppression disabled"
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
# βββ BUILD ADJUSTMENTS ββββββββββββββββββββββββββββββββββββββββββββ
|
| 184 |
|
| 185 |
adjustments = _get_modality_adjustments(modality)
|
|
|
|
| 30 |
|
| 31 |
w, h = img.size
|
| 32 |
gray = np.array(img.convert("L")).astype(np.float64)
|
| 33 |
+
rgb = np.array(img.convert("RGB")).astype(np.float64)
|
| 34 |
+
|
| 35 |
+
# βββ CRITICAL PRE-CHECK: Bayer CFA pattern βββββββββββββββββββββββ
|
| 36 |
+
# Real camera images have Bayer demosaicing traces: Ο_green < Ο_red β Ο_blue
|
| 37 |
+
# If this is absent, the image CANNOT be from a real camera sensor.
|
| 38 |
+
# This blocks portrait mode suppression from protecting AI images.
|
| 39 |
+
noise_std = {}
|
| 40 |
+
for c, nm in enumerate(["red", "green", "blue"]):
|
| 41 |
+
ch = rgb[:, :, c]
|
| 42 |
+
dn = gaussian_filter(ch, sigma=1.5)
|
| 43 |
+
noise_std[nm] = float(np.std(ch - dn))
|
| 44 |
+
|
| 45 |
+
has_bayer = noise_std["green"] < min(noise_std["red"], noise_std["blue"])
|
| 46 |
+
bayer_margin = min(noise_std["red"], noise_std["blue"]) - noise_std["green"]
|
| 47 |
+
indicators["has_bayer"] = has_bayer
|
| 48 |
+
indicators["bayer_margin"] = round(bayer_margin, 4)
|
| 49 |
+
indicators["noise_g"] = round(noise_std["green"], 3)
|
| 50 |
+
indicators["noise_r"] = round(noise_std["red"], 3)
|
| 51 |
+
indicators["noise_b"] = round(noise_std["blue"], 3)
|
| 52 |
|
| 53 |
# βββ CONTENT-BASED DETECTION (works without metadata) βββββββββββββ
|
| 54 |
|
|
|
|
| 94 |
indicators["transition_abruptness"] = round(transition, 2)
|
| 95 |
indicators["has_detail"] = has_detail
|
| 96 |
|
| 97 |
+
# Portrait mode scoring β requires BOTH content signals AND real camera evidence
|
| 98 |
+
# CRITICAL: If no Bayer pattern, this CANNOT be a smartphone portrait photo.
|
| 99 |
+
# AI images that happen to have blurred backgrounds must NOT get portrait suppression.
|
| 100 |
+
can_be_portrait = has_detail and has_bayer
|
| 101 |
+
|
| 102 |
portrait_score = 0.0
|
| 103 |
+
if can_be_portrait and bimodal_ratio > 1.0:
|
| 104 |
+
portrait_score += 0.25
|
| 105 |
+
if can_be_portrait and blur_uniformity > 0.5:
|
| 106 |
+
portrait_score += 0.2
|
| 107 |
+
if can_be_portrait and transition > 4.0:
|
| 108 |
+
portrait_score += 0.2
|
| 109 |
+
if can_be_portrait and blur_frac > 0.2 and sharp_frac > 0.1:
|
| 110 |
+
portrait_score += 0.15
|
| 111 |
+
|
| 112 |
+
if portrait_score > 0.3 and has_bayer:
|
| 113 |
scores["PORTRAIT_MODE"] = portrait_score
|
| 114 |
indicators["portrait_detected"] = True
|
| 115 |
|
|
|
|
| 197 |
modality = "PORTRAIT_MODE"
|
| 198 |
conf = min(1.0, scores["PORTRAIT_MODE"])
|
| 199 |
|
| 200 |
+
# SAFETY GUARD 1: No detail = possible AI. Disable all suppression.
|
| 201 |
if not has_detail:
|
| 202 |
modality = "UNKNOWN"
|
| 203 |
conf = 0.2
|
| 204 |
indicators["safety_override"] = "Low-detail image β suppression disabled"
|
| 205 |
|
| 206 |
+
# SAFETY GUARD 2: No Bayer pattern = not from a real camera sensor.
|
| 207 |
+
# AI-generated images lack Bayer demosaicing traces.
|
| 208 |
+
# Never apply camera-related suppression without Bayer evidence.
|
| 209 |
+
if not has_bayer and modality in ("PORTRAIT_MODE", "SMARTPHONE", "MESSAGING"):
|
| 210 |
+
modality = "UNKNOWN"
|
| 211 |
+
conf = 0.2
|
| 212 |
+
indicators["safety_override"] = f"No Bayer CFA pattern (margin={bayer_margin:.3f}) β not from a real camera sensor. All suppression disabled."
|
| 213 |
+
|
| 214 |
# βββ BUILD ADJUSTMENTS ββββββββββββββββββββββββββββββββββββββββββββ
|
| 215 |
|
| 216 |
adjustments = _get_modality_adjustments(modality)
|