Upload agents/sensor_agent.py with huggingface_hub
Browse files- agents/sensor_agent.py +30 -26
agents/sensor_agent.py
CHANGED
|
@@ -16,7 +16,7 @@ def s01_prnu_uniformity(img):
|
|
| 16 |
ne=np.mean(np.stack(res,axis=-1)**2,axis=-1)
|
| 17 |
lv=uniform_filter(ne,32); std=float(np.std(lv)); mn=float(np.mean(lv))
|
| 18 |
u=1.0-min(std/(mn+1e-9),1.0)
|
| 19 |
-
if u>0.7: s,n=-0.4, f"
|
| 20 |
elif u<0.4: s,n=0.5, f"Inconsistent noise ({u:.3f}) β splicing/AI"
|
| 21 |
else: s,n=0.1, f"Moderate noise uniformity ({u:.3f})"
|
| 22 |
return {"test":"PRNU Uniformity","uniformity":round(u,4),"score":s,"note":n,"noise_map":ne}
|
|
@@ -76,8 +76,11 @@ def s06_hot_dead(img):
|
|
| 76 |
hot=int(np.sum(diff>np.percentile(diff,99.9)))
|
| 77 |
dead=int(np.sum((gray<5)&(diff>np.percentile(diff,99.5))))
|
| 78 |
rate=(hot+dead)/(h*w)
|
|
|
|
|
|
|
| 79 |
if 0.00001<rate<0.001: s,n=-0.2, f"Sensor defects ({hot+dead}px, rate={rate:.6f})"
|
| 80 |
-
elif rate<0.000001: s,n=0.
|
|
|
|
| 81 |
else: s,n=0.0, f"Defect rate={rate:.6f}"
|
| 82 |
return {"test":"Hot/Dead Pixels","count":hot+dead,"score":s,"note":n}
|
| 83 |
|
|
@@ -103,9 +106,13 @@ def s08_dark_current(img):
|
|
| 103 |
|
| 104 |
def s09_read_noise(img):
|
| 105 |
rgb=_rgb(img); gray=np.mean(rgb,axis=-1); h,w=gray.shape
|
| 106 |
-
flat
|
| 107 |
-
|
| 108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
if 0.5<rn<5: s,n=-0.2, f"Read noise={rn:.2f} β real sensor"
|
| 110 |
elif rn<0.2: s,n=0.3, f"No read noise ({rn:.2f})"
|
| 111 |
else: s,n=0.0, f"Read noise={rn:.2f}"
|
|
@@ -187,15 +194,20 @@ def s16_green_imbalance(img):
|
|
| 187 |
else: s,n=0.0, f"Green diff={diff:.3f}"
|
| 188 |
return {"test":"Green Pixel Imbalance","diff":round(diff,4),"score":s,"note":n}
|
| 189 |
|
| 190 |
-
def
|
| 191 |
-
"""
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
if
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
|
| 200 |
def s18_demosaic_interpolation(img):
|
| 201 |
rgb=_rgb(img); h,w,_=rgb.shape
|
|
@@ -213,17 +225,9 @@ def s18_demosaic_interpolation(img):
|
|
| 213 |
ALL_TESTS=[s01_prnu_uniformity,s02_prnu_correlation,s03_noise_model,s04_bayer,s05_cfa_nyquist,
|
| 214 |
s06_hot_dead,s07_fixed_pattern,s08_dark_current,s09_read_noise,s10_pixel_nonlinearity,
|
| 215 |
s11_color_matrix,s12_quantization,s13_bit_depth,s14_saturation_clipping,
|
| 216 |
-
s15_noise_spatial_freq,s16_green_imbalance,
|
| 217 |
|
| 218 |
def run_sensor_agent(img):
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
except Exception as e: findings.append({"test":fn.__name__,"error":str(e),"score":0})
|
| 223 |
-
avg=float(np.mean(scores)) if scores else 0.0; conf=min(1.0,0.5+0.5*abs(avg))
|
| 224 |
-
viol=[f["test"] for f in findings if f.get("score",0)>0.2]
|
| 225 |
-
comp=[f["test"] for f in findings if f.get("score",0)<-0.1]
|
| 226 |
-
rat=f"Sensor violations: {', '.join(viol)}." if viol else f"Sensor consistent: {', '.join(comp)}." if comp else "Sensor inconclusive."
|
| 227 |
-
for f in findings:
|
| 228 |
-
if f.get("note"): rat+=f" [{f['test']}]: {f['note']}."
|
| 229 |
-
return AgentEvidence("Sensor Characteristics Agent",np.clip(avg,-1,1),conf,max(0,1-len(scores)/len(ALL_TESTS)),rat,findings)
|
|
|
|
| 16 |
ne=np.mean(np.stack(res,axis=-1)**2,axis=-1)
|
| 17 |
lv=uniform_filter(ne,32); std=float(np.std(lv)); mn=float(np.mean(lv))
|
| 18 |
u=1.0-min(std/(mn+1e-9),1.0)
|
| 19 |
+
if u>0.7: s,n=-0.4, f"Spatially consistent noise pattern (uniformity={u:.3f}) β authentic sensor"
|
| 20 |
elif u<0.4: s,n=0.5, f"Inconsistent noise ({u:.3f}) β splicing/AI"
|
| 21 |
else: s,n=0.1, f"Moderate noise uniformity ({u:.3f})"
|
| 22 |
return {"test":"PRNU Uniformity","uniformity":round(u,4),"score":s,"note":n,"noise_map":ne}
|
|
|
|
| 76 |
hot=int(np.sum(diff>np.percentile(diff,99.9)))
|
| 77 |
dead=int(np.sum((gray<5)&(diff>np.percentile(diff,99.5))))
|
| 78 |
rate=(hot+dead)/(h*w)
|
| 79 |
+
# Note: JPEG compression removes hot pixel signatures, so absence is not strong evidence
|
| 80 |
+
is_jpeg = img.format == "JPEG" if hasattr(img, 'format') and img.format else False
|
| 81 |
if 0.00001<rate<0.001: s,n=-0.2, f"Sensor defects ({hot+dead}px, rate={rate:.6f})"
|
| 82 |
+
elif rate<0.000001 and not is_jpeg: s,n=0.15, f"No defects, non-JPEG β mild AI indicator"
|
| 83 |
+
elif rate<0.000001 and is_jpeg: s,n=0.0, f"No defects (JPEG strips hot pixels β inconclusive)"
|
| 84 |
else: s,n=0.0, f"Defect rate={rate:.6f}"
|
| 85 |
return {"test":"Hot/Dead Pixels","count":hot+dead,"score":s,"note":n}
|
| 86 |
|
|
|
|
| 106 |
|
| 107 |
def s09_read_noise(img):
|
| 108 |
rgb=_rgb(img); gray=np.mean(rgb,axis=-1); h,w=gray.shape
|
| 109 |
+
# Find flat regions in 2D (preserve spatial structure)
|
| 110 |
+
flat_mask = (gray > 100) & (gray < 150)
|
| 111 |
+
if np.sum(flat_mask) < 1000: return {"test":"Read Noise Floor","score":0.0,"note":"No flat regions"}
|
| 112 |
+
# Compute noise as std of (original - smoothed) within flat regions only
|
| 113 |
+
smoothed = gaussian_filter(gray, sigma=2.0)
|
| 114 |
+
noise_residual = gray - smoothed
|
| 115 |
+
rn = float(np.std(noise_residual[flat_mask]))
|
| 116 |
if 0.5<rn<5: s,n=-0.2, f"Read noise={rn:.2f} β real sensor"
|
| 117 |
elif rn<0.2: s,n=0.3, f"No read noise ({rn:.2f})"
|
| 118 |
else: s,n=0.0, f"Read noise={rn:.2f}"
|
|
|
|
| 194 |
else: s,n=0.0, f"Green diff={diff:.3f}"
|
| 195 |
return {"test":"Green Pixel Imbalance","diff":round(diff,4),"score":s,"note":n}
|
| 196 |
|
| 197 |
+
def s17_noise_autocorrelation(img):
|
| 198 |
+
"""Real sensor noise has spatial correlation from the anti-aliasing filter.
|
| 199 |
+
AI noise is typically white (uncorrelated) or has non-physical correlation."""
|
| 200 |
+
gray=_g(img); noise=gray-gaussian_filter(gray,2.0)
|
| 201 |
+
h,w=noise.shape
|
| 202 |
+
if h<20 or w<20: return {"test":"Noise Autocorrelation","score":0.0,"note":"Too small"}
|
| 203 |
+
step=max(1,h*w//200000)
|
| 204 |
+
ac1=float(np.corrcoef(noise[:,:-1].ravel()[::step],noise[:,1:].ravel()[::step])[0,1])
|
| 205 |
+
ac2=float(np.corrcoef(noise[:,:-2].ravel()[::step],noise[:,2:].ravel()[::step])[0,1])
|
| 206 |
+
decay=ac1-ac2
|
| 207 |
+
if ac1>0.05 and decay>0.02: s,n=-0.3, f"AA-filtered noise (ac1={ac1:.3f}, decay={decay:.3f}) β real sensor"
|
| 208 |
+
elif ac1<0.01: s,n=0.2, f"White noise (ac1={ac1:.3f}) β no AA filter"
|
| 209 |
+
else: s,n=0.0, f"Noise ac1={ac1:.3f}, decay={decay:.3f}"
|
| 210 |
+
return {"test":"Noise Autocorrelation","ac1":round(ac1,4),"decay":round(decay,4),"score":s,"note":n}
|
| 211 |
|
| 212 |
def s18_demosaic_interpolation(img):
|
| 213 |
rgb=_rgb(img); h,w,_=rgb.shape
|
|
|
|
| 225 |
ALL_TESTS=[s01_prnu_uniformity,s02_prnu_correlation,s03_noise_model,s04_bayer,s05_cfa_nyquist,
|
| 226 |
s06_hot_dead,s07_fixed_pattern,s08_dark_current,s09_read_noise,s10_pixel_nonlinearity,
|
| 227 |
s11_color_matrix,s12_quantization,s13_bit_depth,s14_saturation_clipping,
|
| 228 |
+
s15_noise_spatial_freq,s16_green_imbalance,s17_noise_autocorrelation,s18_demosaic_interpolation]
|
| 229 |
|
| 230 |
def run_sensor_agent(img):
|
| 231 |
+
from agents.utils import run_agent_tests
|
| 232 |
+
findings, avg, conf, fail, rat = run_agent_tests(ALL_TESTS, img, "Sensor Characteristics Agent")
|
| 233 |
+
return AgentEvidence("Sensor Characteristics Agent",np.clip(avg,-1,1),conf,fail,rat,findings)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|