salescode-recapture-detector / scripts /analyze_phone_failures.py
Kartikeya Mishra
Deploy SalesCode recapture detector to Space
7c34b66
Raw
History Blame Contribute Delete
4.12 kB
import os
import sys
import cv2
import pandas as pd
import numpy as np
import json
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from features import extract_features
def main():
reports_dir = os.path.join(os.path.dirname(__file__), '..', 'reports')
csv_path = os.path.join(reports_dir, 'phone_test_results.csv')
if not os.path.exists(csv_path):
print("Test results CSV not found.")
sys.exit(1)
df = pd.read_csv(csv_path)
failures = df[df['correct'] == False].copy()
if failures.empty:
print("No failures found! Perfect score.")
sys.exit(0)
print(f"Analyzing {len(failures)} failures...")
analysis_results = []
for _, row in failures.iterrows():
filepath = row['filepath']
true_label = row['true_label']
predicted_score = row['predicted_score']
# Reconstruct path
subfolder = 'real' if true_label == 0 else 'screen'
full_path = os.path.join(os.path.dirname(__file__), '..', 'dataset', 'my_photos', subfolder, filepath)
if not os.path.exists(full_path):
print(f"File not found: {full_path}")
continue
img = cv2.imread(full_path)
if img is None:
continue
h, w = img.shape[:2]
is_whatsapp = "WhatsApp" in filepath or "WA" in filepath
_, group_dict = extract_features(img)
# Flatten features dict
flat_feats = {}
for feat_name, feat_val in group_dict.items():
flat_feats[feat_name] = feat_val
analysis_results.append({
"filepath": filepath,
"true_label": true_label,
"predicted_score": predicted_score,
"failure_type": "False Positive" if true_label == 0 else "False Negative",
"is_whatsapp": is_whatsapp,
"resolution": f"{w}x{h}",
**flat_feats
})
analysis_df = pd.DataFrame(analysis_results)
out_csv = os.path.join(reports_dir, 'phone_failure_analysis.csv')
analysis_df.to_csv(out_csv, index=False)
# Write MD summary
out_md = os.path.join(reports_dir, 'phone_failure_summary.md')
with open(out_md, 'w') as f:
f.write("# Phone Failure Analysis\n\n")
f.write(f"**Total Failures:** {len(failures)}\n")
f.write(f"- False Positives (Real flagged as Screen): {len(failures[failures['true_label'] == 0])}\n")
f.write(f"- False Negatives (Screen flagged as Real): {len(failures[failures['true_label'] == 1])}\n\n")
f.write("## Insights\n")
wa_count = analysis_df['is_whatsapp'].sum()
f.write(f"- **WhatsApp Compression:** {wa_count} out of {len(failures)} failed images were heavily compressed via WhatsApp.\n")
avg_res = analysis_df['resolution'].value_counts().idxmax()
f.write(f"- **Most Common Resolution of failures:** {avg_res}\n\n")
f.write("### Average Feature Values for False Positives\n")
fp_df = analysis_df[analysis_df['true_label'] == 0]
if not fp_df.empty:
f.write("False positives (real images) are likely triggering on these features (high values typically indicate a screen):\n")
f.write(f"- `fft_hf_ratio`: {fp_df['fft_hf_ratio'].mean():.4f}\n")
f.write(f"- `laplacian_var`: {fp_df['laplacian_var'].mean():.4f}\n")
f.write(f"- `edge_density`: {fp_df['edge_density'].mean():.4f}\n")
f.write(f"- `banding_score`: {fp_df['banding_score'].mean():.4f}\n")
f.write("\n### Conclusion\n")
f.write("The current features are heavily sensitive to high-frequency noise introduced by lossy JPEG compression. Before feature extraction, images must be resized/normalized and optionally smoothed to strip out compression blockiness while retaining true physical screen Moiré.\n")
print(f"Saved analysis to {out_csv} and {out_md}")
if __name__ == "__main__":
main()