SiemonCha commited on
Commit
ff9e377
Β·
1 Parent(s): 3ce21dc

fix DataParallel loading, add limitation note to UI

Browse files
Files changed (1) hide show
  1. app.py +38 -4
app.py CHANGED
@@ -9,35 +9,60 @@ from src.services.gradcam import generate_gradcam
9
  import tempfile
10
  import os
11
 
12
- # Load model once
 
 
13
  model = load_model()
14
  generator_model = load_generator_model()
15
 
 
16
  def analyze_image(image):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  if image is None:
18
  return "Please upload an image.", None
19
 
20
- # Save PIL image to temp file
21
  with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
22
  image.save(tmp.name)
23
  tmp_path = tmp.name
24
 
25
  try:
 
26
  prediction = predict(tmp_path, model=model)
27
  metadata = get_metadata(tmp_path)
28
  cam_array = generate_gradcam(tmp_path, model=model)
29
- cam_image = Image.fromarray(cam_array)
30
  generator_result = predict_generator(tmp_path, model=generator_model)
31
  finally:
 
32
  os.remove(tmp_path)
33
 
34
  label = prediction["label"]
35
  confidence = prediction["confidence"]
36
 
 
37
  result_text = f"**{label}** β€” Confidence: {confidence}%\n\n"
38
  result_text += f"⚠️ This is a model-based estimate, not definitive proof.\n\n"
 
 
39
 
40
- # Generator type
41
  gen_type = generator_result["generator_type"]
42
  gen_conf = generator_result["confidence"]
43
  result_text += f"**Generator Type:** {gen_type} ({gen_conf}%)\n\n"
@@ -45,18 +70,27 @@ def analyze_image(image):
45
  for cls, prob in generator_result["class_probabilities"].items():
46
  result_text += f"- {cls}: {prob}%\n"
47
 
 
48
  result_text += f"\n**Metadata:**\n"
49
  result_text += f"- Format: {metadata['format']}\n"
50
  result_text += f"- Dimensions: {metadata['dimensions']}\n"
51
  result_text += f"- File Size: {metadata['file_size_kb']} KB\n"
52
  result_text += f"- EXIF Data: {'Yes' if metadata['has_exif'] else 'No'}\n"
53
 
 
54
  if not metadata["has_exif"]:
55
  result_text += f"\n_{metadata['exif_note']}_"
56
 
 
 
 
57
  return result_text, cam_image
58
 
59
 
 
 
 
 
60
  demo = gr.Interface(
61
  fn=analyze_image,
62
  inputs=gr.Image(type="pil", label="Upload Image"),
 
9
  import tempfile
10
  import os
11
 
12
+ # Load both models once at startup β€” singleton pattern
13
+ # Models stay in memory for the lifetime of the app
14
+ # Avoids 2-3 second reload penalty on every user request
15
  model = load_model()
16
  generator_model = load_generator_model()
17
 
18
+
19
  def analyze_image(image):
20
+ """
21
+ Main function called by Gradio when user submits an image.
22
+
23
+ Flow:
24
+ 1. Save PIL image to a temp file (inference functions require file paths)
25
+ 2. Run binary prediction (real vs fake)
26
+ 3. Run generator type prediction (Real/GAN/Diffusion/Other)
27
+ 4. Extract image metadata and EXIF
28
+ 5. Generate Grad-CAM heatmap
29
+ 6. Build formatted markdown result string
30
+ 7. Delete temp file
31
+ 8. Return (result_text, cam_image) β€” matches Gradio output components
32
+
33
+ Why temp file:
34
+ Gradio passes images as PIL objects but our inference functions
35
+ expect file paths. We save temporarily and clean up after.
36
+ """
37
  if image is None:
38
  return "Please upload an image.", None
39
 
40
+ # Save PIL image to temp file with unique name
41
  with tempfile.NamedTemporaryFile(suffix=".jpg", delete=False) as tmp:
42
  image.save(tmp.name)
43
  tmp_path = tmp.name
44
 
45
  try:
46
+ # Run all analysis pipelines
47
  prediction = predict(tmp_path, model=model)
48
  metadata = get_metadata(tmp_path)
49
  cam_array = generate_gradcam(tmp_path, model=model)
50
+ cam_image = Image.fromarray(cam_array) # convert numpy array to PIL for Gradio
51
  generator_result = predict_generator(tmp_path, model=generator_model)
52
  finally:
53
+ # Always clean up temp file regardless of success or failure
54
  os.remove(tmp_path)
55
 
56
  label = prediction["label"]
57
  confidence = prediction["confidence"]
58
 
59
+ # Build markdown-formatted result string for Gradio Markdown component
60
  result_text = f"**{label}** β€” Confidence: {confidence}%\n\n"
61
  result_text += f"⚠️ This is a model-based estimate, not definitive proof.\n\n"
62
+ result_text += f"πŸ“Œ **Note:** Model performs best on Stable Diffusion, StyleGAN, and DDPM images. "
63
+ result_text += f"Performance drops on unseen generators (DALL-E, MidJourney).\n\n"
64
 
65
+ # Generator type section β€” shows architecture family
66
  gen_type = generator_result["generator_type"]
67
  gen_conf = generator_result["confidence"]
68
  result_text += f"**Generator Type:** {gen_type} ({gen_conf}%)\n\n"
 
70
  for cls, prob in generator_result["class_probabilities"].items():
71
  result_text += f"- {cls}: {prob}%\n"
72
 
73
+ # Metadata section
74
  result_text += f"\n**Metadata:**\n"
75
  result_text += f"- Format: {metadata['format']}\n"
76
  result_text += f"- Dimensions: {metadata['dimensions']}\n"
77
  result_text += f"- File Size: {metadata['file_size_kb']} KB\n"
78
  result_text += f"- EXIF Data: {'Yes' if metadata['has_exif'] else 'No'}\n"
79
 
80
+ # Show EXIF note in italics β€” honest disclaimer about what missing EXIF means
81
  if not metadata["has_exif"]:
82
  result_text += f"\n_{metadata['exif_note']}_"
83
 
84
+ # Gradio expects return values matching the outputs list order:
85
+ # outputs[0] = Markdown β†’ result_text
86
+ # outputs[1] = Image β†’ cam_image
87
  return result_text, cam_image
88
 
89
 
90
+ # Gradio Interface β€” simplest Gradio pattern
91
+ # fn: function to call on submit
92
+ # inputs: single image upload component
93
+ # outputs: markdown text + image side by side
94
  demo = gr.Interface(
95
  fn=analyze_image,
96
  inputs=gr.Image(type="pil", label="Upload Image"),