fix: replace Laplacian bokeh check with local-variance method in autocorrelation override
Browse filesThe old Laplacian-based bokeh detection in m05_autocorrelation() failed for macro
DSLR images because the 10%-of-peak-sharpness threshold didn't reliably capture
the bokeh region when the sharp-to-smooth transition was gradual.
New approach uses uniform_filter to compute local variance (E[XΒ²]-E[X]Β²) over
32Γ32 windows, then thresholds at 5% of the P95 variance. This works because:
- Real bokeh: P95 is high (sharp subject), most pixels are far below β detected
- AI smooth: P95 is low (<50), guard clause blocks bokeh excuse β override fires
- Normal photo: variance spread evenly, few pixels below 5% of P95 β no false trigger
For the Unsplash macro DSLR test image, this should:
- Drop Autocorrelation Peak from +0.80 to +0.15 (bokeh explained)
- Remove override_suppression flag β modality Γ0.1 can further reduce to +0.015
- Move Generative Model Agent from ~+0.054 to ~-0.02 (neutral/authentic)
- Push P(Fake) from ~46.9% to ~44-45%
- agents/model_agent.py +43 -27
|
@@ -78,37 +78,53 @@ def m05_autocorrelation(img):
|
|
| 78 |
re=max(h,w)//20; Y,X=np.mgrid[0:h,0:w]; cm=((X-cx)**2+(Y-cy)**2)<re**2; acm[cm]=0
|
| 79 |
ms=float(np.max(acm))
|
| 80 |
|
| 81 |
-
# Before firing hard override, check if high autocorrelation is explained by bokeh
|
| 82 |
-
# Macro/portrait photos have large uniform bokeh regions that create
|
| 83 |
-
# from optical physics, not AI generation.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 84 |
bokeh_explained = False
|
|
|
|
| 85 |
if ms > 0.95:
|
| 86 |
-
from scipy.
|
| 87 |
-
lap = np.array([[0,1,0],[1,-4,1],[0,1,0]], dtype=np.float64)
|
| 88 |
-
sharpness = gaussian_filter(np.abs(convolve2d(gray, lap, mode="same", boundary="symm")), sigma=10)
|
| 89 |
|
| 90 |
-
#
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
else:
|
| 109 |
-
#
|
| 110 |
bokeh_fraction = 0.0
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
if ms > 0.95 and not bokeh_explained:
|
| 114 |
s, n = 0.8, f"CRITICAL: Autocorrelation {ms:.3f} exceeds physical camera limit β AI generated"
|
|
@@ -116,7 +132,7 @@ def m05_autocorrelation(img):
|
|
| 116 |
result["override_suppression"] = True
|
| 117 |
return result
|
| 118 |
elif ms > 0.95 and bokeh_explained:
|
| 119 |
-
s, n = 0.15, f"High autocorrelation ({ms:.3f}) but large bokeh region ({bokeh_fraction:.0%}) β likely optical, not AI"
|
| 120 |
return {"test":"Autocorrelation Peak","max_secondary":round(ms,4),"score":s,"note":n,"bokeh_explained":True}
|
| 121 |
elif ms>0.3: s,n=0.6,f"Strong secondary peak ({ms:.3f}) β GAN checkerboard"
|
| 122 |
elif ms>0.15: s,n=0.3,f"Moderate peak ({ms:.3f})"
|
|
|
|
| 78 |
re=max(h,w)//20; Y,X=np.mgrid[0:h,0:w]; cm=((X-cx)**2+(Y-cy)**2)<re**2; acm[cm]=0
|
| 79 |
ms=float(np.max(acm))
|
| 80 |
|
| 81 |
+
# Before firing hard override, check if high autocorrelation is explained by bokeh.
|
| 82 |
+
# Macro/portrait/shallow-DoF photos have large uniform bokeh regions that create
|
| 83 |
+
# high autocorrelation from optical physics, not AI generation.
|
| 84 |
+
#
|
| 85 |
+
# Method: Local variance via uniform_filter. Bokeh regions have very low local
|
| 86 |
+
# variance (uniform pixel intensity). This is more robust than the Laplacian
|
| 87 |
+
# approach because it directly measures what bokeh is β spatial uniformity β
|
| 88 |
+
# rather than inferring it from edge magnitude.
|
| 89 |
bokeh_explained = False
|
| 90 |
+
bokeh_fraction = 0.0
|
| 91 |
if ms > 0.95:
|
| 92 |
+
from scipy.ndimage import uniform_filter as uf
|
|
|
|
|
|
|
| 93 |
|
| 94 |
+
# Compute local variance: E[XΒ²] - E[X]Β² over 32Γ32 windows
|
| 95 |
+
local_mean = uf(gray, size=32)
|
| 96 |
+
local_sq_mean = uf(gray**2, size=32)
|
| 97 |
+
local_var = np.clip(local_sq_mean - local_mean**2, 0, None)
|
| 98 |
+
|
| 99 |
+
# Bimodal variance test: in a real bokeh/macro/shallow-DoF image, the
|
| 100 |
+
# variance distribution is strongly bimodal β sharp foreground has high
|
| 101 |
+
# local variance while the defocused background has near-zero variance.
|
| 102 |
+
#
|
| 103 |
+
# Strategy: Use the P95 of local variance (captures the sharp subject's
|
| 104 |
+
# typical variance) and threshold at 5% of that value. Pixels below this
|
| 105 |
+
# threshold are effectively uniform/bokeh. This works because:
|
| 106 |
+
# - Real bokeh: P95 is high (sharp subject), most pixels are far below it
|
| 107 |
+
# - AI smooth: P95 is low, so threshold is tiny, nothing qualifies as
|
| 108 |
+
# "bokeh relative to sharp detail" because there IS no sharp detail
|
| 109 |
+
# - Normal photo: variance is spread out, few pixels are <5% of P95
|
| 110 |
+
p95_var = float(np.percentile(local_var, 95))
|
| 111 |
+
|
| 112 |
+
if p95_var > 50.0: # Guard: sharp region must have real detail
|
| 113 |
+
var_thresh = p95_var * 0.05 # 5% of peak variance
|
| 114 |
+
low_var_mask = local_var < var_thresh
|
| 115 |
+
bokeh_fraction = float(np.mean(low_var_mask))
|
| 116 |
+
has_genuine_detail = True # Already guaranteed by p95 > 50 check
|
| 117 |
else:
|
| 118 |
+
# No region with genuine high-variance detail β not a bokeh image
|
| 119 |
bokeh_fraction = 0.0
|
| 120 |
+
low_var_mask = np.zeros_like(local_var, dtype=bool)
|
| 121 |
+
has_genuine_detail = False
|
| 122 |
+
|
| 123 |
+
# Bokeh explains autocorrelation if:
|
| 124 |
+
# 1. Large uniform region (>35% of image has variance < 5% of P95)
|
| 125 |
+
# 2. AND genuine sharp detail exists (P95 variance > 50)
|
| 126 |
+
if bokeh_fraction > 0.35 and has_genuine_detail:
|
| 127 |
+
bokeh_explained = True
|
| 128 |
|
| 129 |
if ms > 0.95 and not bokeh_explained:
|
| 130 |
s, n = 0.8, f"CRITICAL: Autocorrelation {ms:.3f} exceeds physical camera limit β AI generated"
|
|
|
|
| 132 |
result["override_suppression"] = True
|
| 133 |
return result
|
| 134 |
elif ms > 0.95 and bokeh_explained:
|
| 135 |
+
s, n = 0.15, f"High autocorrelation ({ms:.3f}) but large bokeh region ({bokeh_fraction:.0%} uniform background) β likely optical, not AI"
|
| 136 |
return {"test":"Autocorrelation Peak","max_secondary":round(ms,4),"score":s,"note":n,"bokeh_explained":True}
|
| 137 |
elif ms>0.3: s,n=0.6,f"Strong secondary peak ({ms:.3f}) β GAN checkerboard"
|
| 138 |
elif ms>0.15: s,n=0.3,f"Moderate peak ({ms:.3f})"
|