anky2002 commited on
Commit
4f3dfe5
Β·
verified Β·
1 Parent(s): 8cd421b

Upload agents/sensor_agent.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. 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"Uniform sensor noise (uniformity={u:.3f})"
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.2, f"No defects β€” possible AI"
 
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=gray[(gray>100)&(gray<150)]
107
- if len(flat)<1000: return {"test":"Read Noise Floor","score":0.0,"note":"No flat regions"}
108
- rn=float(np.std(flat-gaussian_filter(flat.reshape(-1,1),1).ravel()))
 
 
 
 
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 s17_sensor_crop_factor(img):
191
- """Check aspect ratio against common sensor sizes."""
192
- w,h=img.size; ratio=max(w,h)/min(w,h)
193
- common=[1.0,4/3,3/2,16/9,1.85,2.35,2.39]
194
- min_diff=min(abs(ratio-c) for c in common)
195
- if min_diff<0.02: s,n=-0.1, f"Standard aspect ratio ({ratio:.3f})"
196
- elif min_diff>0.1: s,n=0.2, f"Unusual aspect ratio ({ratio:.3f})"
197
- else: s,n=0.0, f"Aspect ratio={ratio:.3f}"
198
- return {"test":"Sensor Aspect Ratio","ratio":round(ratio,4),"score":s,"note":n}
 
 
 
 
 
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,s17_sensor_crop_factor,s18_demosaic_interpolation]
217
 
218
  def run_sensor_agent(img):
219
- findings,scores=[],[]
220
- for fn in ALL_TESTS:
221
- try: r=fn(img); findings.append(r); scores.append(r["score"])
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)