anky2002 commited on
Commit
7b95f04
·
verified ·
1 Parent(s): 4c5518a

Upload agents/metadata_agent.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. agents/metadata_agent.py +47 -22
agents/metadata_agent.py CHANGED
@@ -44,6 +44,10 @@ def d02_software_check(img):
44
  return {"test":"Software Detection","score":s,"note":n}
45
 
46
  def d03_ela(img, quality=90):
 
 
 
 
47
  buf=io.BytesIO(); img_rgb=img.convert("RGB"); img_rgb.save(buf,"JPEG",quality=quality); buf.seek(0)
48
  resaved=Image.open(buf).convert("RGB"); ela=ImageChops.difference(img_rgb,resaved)
49
  ext=ela.getextrema(); mx=max(e[1] for e in ext) or 1
@@ -53,9 +57,12 @@ def d03_ela(img, quality=90):
53
  for i in range(0,h-bs,bs):
54
  for j in range(0,w-bs,bs): bm.append(float(np.mean(ea[i:i+bs,j:j+bs])))
55
  bm=np.array(bm); bstd=float(np.std(bm)); br=float(np.max(bm)-np.min(bm))
56
- if bstd>8 and br>30: s,n=0.6,f"High ELA variance (σ={bstd:.1f}) — manipulation"
 
 
57
  elif bstd>4: s,n=0.3,f"Moderate ELA (σ={bstd:.1f})"
58
- elif float(np.std(ea))<1: s,n=0.2,"Uniform ELA — AI"
 
59
  else: s,n=-0.2,f"Consistent ELA (σ={bstd:.1f})"
60
  return {"test":"Error Level Analysis","block_std":round(bstd,3),"score":s,"note":n,"ela_image":ela_vis}
61
 
@@ -129,12 +136,36 @@ def d10_gps_plausibility(img):
129
  gps=exif.get(34853)
130
  if not gps: return {"test":"GPS Plausibility","score":0.0,"note":"No GPS data"}
131
  try:
132
- # Check if GPS coordinates are physically possible
133
- lat_ref=gps.get(1,"N"); lon_ref=gps.get(3,"E")
134
- lat=gps.get(2,(0,0,0)); lon=gps.get(4,(0,0,0))
135
- # Simple validation: lat [-90,90], lon [-180,180]
136
- s,n=-0.2,f"GPS present ({lat_ref}, {lon_ref})"
137
- except: s,n=0.0,"GPS parse error"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  return {"test":"GPS Plausibility","score":s,"note":n}
139
 
140
  def d11_maker_note(img):
@@ -163,17 +194,11 @@ ALL_TESTS=[d01_exif_completeness,d02_software_check,d03_ela,d04_ai_metadata,d05_
163
  d10_gps_plausibility,d11_maker_note,d12_file_structure]
164
 
165
  def run_metadata_agent(img):
166
- findings,scores=[],[]
167
- ela_img=None
168
- for fn in ALL_TESTS:
169
- try:
170
- r=fn(img); findings.append(r); scores.append(r["score"])
171
- if "ela_image" in r: ela_img=r.pop("ela_image")
172
- except Exception as e: findings.append({"test":fn.__name__,"error":str(e),"score":0})
173
- avg=float(np.mean(scores)) if scores else 0.0; conf=min(1.0,0.5+0.5*abs(avg))
174
- viol=[f["test"] for f in findings if f.get("score",0)>0.2]
175
- comp=[f["test"] for f in findings if f.get("score",0)<-0.1]
176
- rat=f"Metadata violations: {', '.join(viol)}." if viol else f"Metadata consistent: {', '.join(comp)}." if comp else "Metadata inconclusive."
177
- for f in findings:
178
- if f.get("note"): rat+=f" [{f['test']}]: {f['note']}."
179
- return AgentEvidence("Metadata Agent",np.clip(avg,-1,1),conf,max(0,1-len(scores)/len(ALL_TESTS)),rat,findings,ela_img)
 
44
  return {"test":"Software Detection","score":s,"note":n}
45
 
46
  def d03_ela(img, quality=90):
47
+ # P4: Detect source format — ELA is only meaningful for JPEG inputs
48
+ source_format = getattr(img, 'format', None)
49
+ is_jpeg = source_format and source_format.upper() in ("JPEG", "JPG")
50
+
51
  buf=io.BytesIO(); img_rgb=img.convert("RGB"); img_rgb.save(buf,"JPEG",quality=quality); buf.seek(0)
52
  resaved=Image.open(buf).convert("RGB"); ela=ImageChops.difference(img_rgb,resaved)
53
  ext=ela.getextrema(); mx=max(e[1] for e in ext) or 1
 
57
  for i in range(0,h-bs,bs):
58
  for j in range(0,w-bs,bs): bm.append(float(np.mean(ea[i:i+bs,j:j+bs])))
59
  bm=np.array(bm); bstd=float(np.std(bm)); br=float(np.max(bm)-np.min(bm))
60
+ if not is_jpeg and float(np.std(ea))<1:
61
+ s,n=0.0,"PNG/lossless source — ELA comparison is lossless→JPEG, not meaningful"
62
+ elif bstd>8 and br>30: s,n=0.6,f"High ELA variance (σ={bstd:.1f}) — manipulation"
63
  elif bstd>4: s,n=0.3,f"Moderate ELA (σ={bstd:.1f})"
64
+ elif float(np.std(ea))<1 and is_jpeg: s,n=0.2,"Uniform ELA on JPEG possible AI"
65
+ elif float(np.std(ea))<1: s,n=0.0,"Uniform ELA (non-JPEG source)"
66
  else: s,n=-0.2,f"Consistent ELA (σ={bstd:.1f})"
67
  return {"test":"Error Level Analysis","block_std":round(bstd,3),"score":s,"note":n,"ela_image":ela_vis}
68
 
 
136
  gps=exif.get(34853)
137
  if not gps: return {"test":"GPS Plausibility","score":0.0,"note":"No GPS data"}
138
  try:
139
+ def _dms_to_dd(dms):
140
+ """Convert (degrees, minutes, seconds) tuple to decimal degrees."""
141
+ if isinstance(dms, (list, tuple)) and len(dms) == 3:
142
+ d = float(dms[0]) if not hasattr(dms[0], 'numerator') else float(dms[0])
143
+ m = float(dms[1]) if not hasattr(dms[1], 'numerator') else float(dms[1])
144
+ s_val = float(dms[2]) if not hasattr(dms[2], 'numerator') else float(dms[2])
145
+ return d + m/60.0 + s_val/3600.0
146
+ return float(dms)
147
+
148
+ lat_ref = gps.get(1, "N")
149
+ lon_ref = gps.get(3, "E")
150
+ lat_raw = gps.get(2, (0,0,0))
151
+ lon_raw = gps.get(4, (0,0,0))
152
+
153
+ lat = _dms_to_dd(lat_raw)
154
+ lon = _dms_to_dd(lon_raw)
155
+ if lat_ref == "S": lat = -lat
156
+ if lon_ref == "W": lon = -lon
157
+
158
+ # Bounds check
159
+ if not (-90 <= lat <= 90) or not (-180 <= lon <= 180):
160
+ s,n = 0.4, f"Impossible GPS coordinates (lat={lat:.4f}, lon={lon:.4f})"
161
+ elif abs(lat) < 0.001 and abs(lon) < 0.001:
162
+ s,n = 0.3, f"GPS at Null Island (0,0) — likely fabricated"
163
+ elif abs(lat) < 0.1 and abs(lon) < 0.1:
164
+ s,n = 0.2, f"GPS near (0,0) — suspicious (lat={lat:.4f}, lon={lon:.4f})"
165
+ else:
166
+ s,n = -0.2, f"Plausible GPS ({lat:.4f}°{'N' if lat>=0 else 'S'}, {lon:.4f}°{'E' if lon>=0 else 'W'})"
167
+ except Exception as e:
168
+ s,n = 0.0, f"GPS parse error: {str(e)[:50]}"
169
  return {"test":"GPS Plausibility","score":s,"note":n}
170
 
171
  def d11_maker_note(img):
 
194
  d10_gps_plausibility,d11_maker_note,d12_file_structure]
195
 
196
  def run_metadata_agent(img):
197
+ from agents.utils import run_agent_tests
198
+ findings_raw, avg, conf, fail, rat = run_agent_tests(ALL_TESTS, img, "Metadata Agent")
199
+ # Extract ELA image for visual evidence
200
+ ela_img = None
201
+ for f in findings_raw:
202
+ if "ela_image" in f:
203
+ ela_img = f.pop("ela_image")
204
+ return AgentEvidence("Metadata Agent", np.clip(avg,-1,1), conf, fail, rat, findings_raw, ela_img)