Shahriar-jaman commited on
Commit
6f35e17
·
verified ·
1 Parent(s): 468eb2e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +75 -40
app.py CHANGED
@@ -3,12 +3,13 @@ import torch
3
  from transformers import AutoModelForVision2Seq, AutoProcessor
4
  from PIL import Image
5
  import numpy as np
6
- from fastapi import FastAPI, UploadFile, File
7
  from typing import List
8
  import io
9
  import gradio as gr
 
10
 
11
- # Initialize FastAPI app
12
  app = FastAPI()
13
 
14
  # Load SmolVLM-Instruct model and processor
@@ -19,35 +20,54 @@ model = AutoModelForVision2Seq.from_pretrained(model_id, token=os.environ.get("H
19
  # Harmful objects list for detection
20
  harmful_objects = ["knife", "gun", "weapon", "blood", "syringe", "bomb", "blade"]
21
 
 
 
 
 
 
22
  @app.post("/predict")
23
  async def predict(files: List[UploadFile] = File(...)):
 
 
 
24
  results = []
25
  image_embeddings = []
 
26
 
27
- for file in files:
28
  try:
29
- # Read image
30
  image_data = await file.read()
 
 
 
 
31
  image = Image.open(io.BytesIO(image_data)).convert("RGB")
 
32
 
33
  # Generate description
34
- prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, and context."
35
  inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
36
  outputs = model.generate(**inputs, max_length=512)
37
  description = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip()
38
 
39
  # Extract signs/number plates (OCR)
40
- prompt_ocr = "<image> Extract all visible text in the image, such as signs or license plates."
41
  inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
42
  ocr_outputs = model.generate(**inputs_ocr, max_length=512)
43
  signs_text = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip()
44
 
45
- # Detect harmful objects/blood
46
- prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly."
47
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
48
  detect_outputs = model.generate(**inputs_detect, max_length=512)
49
  detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
50
- harmful_detected = any(obj in detected_objects for obj in harmful_objects) and "Detected: " + ", ".join([obj for obj in harmful_objects if obj in detected_objects]) or "None detected"
 
 
 
 
 
51
 
52
  # Get image embedding for similarity
53
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
@@ -56,15 +76,19 @@ async def predict(files: List[UploadFile] = File(...)):
56
  image_embeddings.append(emb)
57
 
58
  results.append({
 
 
59
  "description": description if description else "No description generated.",
60
  "signs": signs_text if signs_text else "None detected",
61
- "harmful": harmful_detected
62
  })
63
  except Exception as e:
64
  results.append({
 
 
65
  "description": "Error processing image.",
66
  "signs": "Error",
67
- "harmful": "Error",
68
  "error": str(e)
69
  })
70
 
@@ -75,40 +99,50 @@ async def predict(files: List[UploadFile] = File(...)):
75
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
76
  np.linalg.norm(base_embedding) * np.linalg.norm(image_embeddings[i])
77
  )
78
- results[i]["similarity"] = float(sim[0][0])
79
 
80
- return results
81
 
82
  # Gradio interface for Hugging Face Spaces
83
  def gradio_predict(*images):
 
 
 
84
  results = []
85
  image_embeddings = []
 
86
 
87
- for image in images:
88
  if image is None:
89
  continue
90
  try:
91
- # Convert Gradio image input to PIL
92
  image = Image.fromarray(image).convert("RGB")
 
93
 
94
  # Generate description
95
- prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, and context."
96
  inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
97
  outputs = model.generate(**inputs, max_length=512)
98
  description = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip()
99
 
100
  # Extract signs/number plates (OCR)
101
- prompt_ocr = "<image> Extract all visible text in the image, such as signs or license plates."
102
- inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
103
  ocr_outputs = model.generate(**inputs_ocr, max_length=512)
104
  signs_text = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip()
105
 
106
- # Detect harmful objects/blood
107
- prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly."
108
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
109
  detect_outputs = model.generate(**inputs_detect, max_length=512)
110
  detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
111
- harmful_detected = any(obj in detected_objects for obj in harmful_objects) and "Detected: " + ", ".join([obj for obj in harmful_objects if obj in detected_objects]) or "None detected"
 
 
 
 
 
112
 
113
  # Get image embedding for similarity
114
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
@@ -117,15 +151,19 @@ def gradio_predict(*images):
117
  image_embeddings.append(emb)
118
 
119
  results.append({
 
 
120
  "description": description if description else "No description generated.",
121
  "signs": signs_text if signs_text else "None detected",
122
- "harmful": harmful_detected
123
  })
124
  except Exception as e:
125
  results.append({
 
 
126
  "description": "Error processing image.",
127
  "signs": "Error",
128
- "harmful": "Error",
129
  "error": str(e)
130
  })
131
 
@@ -136,30 +174,27 @@ def gradio_predict(*images):
136
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
137
  np.linalg.norm(base_embedding) * np.linalg.norm(image_embeddings[i])
138
  )
139
- results[i]["similarity"] = float(sim[0][0])
140
 
141
- # Format output for Gradio
142
- output = ""
143
- for i, result in enumerate(results):
144
- output += f"**Image {i+1}**\n"
145
- output += f"- Description: {result['description']}\n"
146
- output += f"- Signs/Number Plates: {result['signs']}\n"
147
- output += f"- Harmful Objects/Blood: {result['harmful']}\n"
148
- if "similarity" in result:
149
- output += f"- Similarity to Image 1: {result['similarity']:.2f}\n"
150
  if "error" in result:
151
- output += f"- Error: {result['error']}\n"
152
- output += "\n"
153
-
154
  return output
155
 
156
  # Gradio interface
157
  iface = gr.Interface(
158
  fn=gradio_predict,
159
- inputs=[gr.Image(label=f"Upload Image {i+1}") for i in range(3)], # Allow up to 3 images
160
- outputs=gr.Textbox(label="Results"),
161
- title="VisionSage: Image Analysis with SmolVLM",
162
- description="Upload up to 3 images to get detailed descriptions, extract signs/number plates, detect harmful objects/blood, and compute similarity to the first image."
163
  )
164
 
165
  if __name__ == "__main__":
 
3
  from transformers import AutoModelForVision2Seq, AutoProcessor
4
  from PIL import Image
5
  import numpy as np
6
+ from fastapi import FastAPI, UploadFile, File, HTTPException
7
  from typing import List
8
  import io
9
  import gradio as gr
10
+ from datetime import datetime
11
 
12
+ # Initialize FastAPI app with increased upload limit (10MB)
13
  app = FastAPI()
14
 
15
  # Load SmolVLM-Instruct model and processor
 
20
  # Harmful objects list for detection
21
  harmful_objects = ["knife", "gun", "weapon", "blood", "syringe", "bomb", "blade"]
22
 
23
+ # Resize image to max 1024x1024 while preserving aspect ratio
24
+ def resize_image(image: Image.Image, max_size: int = 1024) -> Image.Image:
25
+ image.thumbnail((max_size, max_size), Image.Resampling.LANCZOS)
26
+ return image
27
+
28
  @app.post("/predict")
29
  async def predict(files: List[UploadFile] = File(...)):
30
+ if len(files) > 3:
31
+ raise HTTPException(status_code=400, detail="Maximum 3 images allowed.")
32
+
33
  results = []
34
  image_embeddings = []
35
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
36
 
37
+ for idx, file in enumerate(files, 1):
38
  try:
39
+ # Check file size (limit to 10MB)
40
  image_data = await file.read()
41
+ if len(image_data) > 10 * 1024 * 1024:
42
+ raise ValueError("Image file size exceeds 10MB limit.")
43
+
44
+ # Read and resize image
45
  image = Image.open(io.BytesIO(image_data)).convert("RGB")
46
+ image = resize_image(image, max_size=1024)
47
 
48
  # Generate description
49
+ prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, people, and environmental context."
50
  inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
51
  outputs = model.generate(**inputs, max_length=512)
52
  description = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip()
53
 
54
  # Extract signs/number plates (OCR)
55
+ prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
56
  inputs_ocr = processor(text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
57
  ocr_outputs = model.generate(**inputs_ocr, max_length=512)
58
  signs_text = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip()
59
 
60
+ # Detect harmful objects/blood with confidence simulation
61
+ prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
62
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
63
  detect_outputs = model.generate(**inputs_detect, max_length=512)
64
  detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
65
+ harmful_detected = []
66
+ for obj in harmful_objects:
67
+ if obj in detected_objects:
68
+ confidence = 90 if obj in detected_objects.split() else 60
69
+ harmful_detected.append({"object": obj, "confidence": confidence})
70
+ harmful_output = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
71
 
72
  # Get image embedding for similarity
73
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
 
76
  image_embeddings.append(emb)
77
 
78
  results.append({
79
+ "image_id": f"Image_{idx}",
80
+ "timestamp": timestamp,
81
  "description": description if description else "No description generated.",
82
  "signs": signs_text if signs_text else "None detected",
83
+ "harmful_objects": harmful_output
84
  })
85
  except Exception as e:
86
  results.append({
87
+ "image_id": f"Image_{idx}",
88
+ "timestamp": timestamp,
89
  "description": "Error processing image.",
90
  "signs": "Error",
91
+ "harmful_objects": [{"object": "Error", "confidence": 0}],
92
  "error": str(e)
93
  })
94
 
 
99
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
100
  np.linalg.norm(base_embedding) * np.linalg.norm(image_embeddings[i])
101
  )
102
+ results[i]["similarity_to_image_1"] = float(sim[0][0])
103
 
104
+ return {"results": results, "analysis_timestamp": timestamp}
105
 
106
  # Gradio interface for Hugging Face Spaces
107
  def gradio_predict(*images):
108
+ if len([img for img in images if img is not None]) > 3:
109
+ return "Error: Maximum 3 images allowed."
110
+
111
  results = []
112
  image_embeddings = []
113
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
114
 
115
+ for idx, image in enumerate(images, 1):
116
  if image is None:
117
  continue
118
  try:
119
+ # Convert Gradio image input to PIL and resize
120
  image = Image.fromarray(image).convert("RGB")
121
+ image = resize_image(image, max_size=1024)
122
 
123
  # Generate description
124
+ prompt_desc = "<image> Provide a detailed description of the image, including objects, colors, people, and environmental context."
125
  inputs = processor(text=[prompt_desc], images=[image], return_tensors="pt", padding=True)
126
  outputs = model.generate(**inputs, max_length=512)
127
  description = processor.decode(outputs[0], skip_special_tokens=True).replace(prompt_desc, "").strip()
128
 
129
  # Extract signs/number plates (OCR)
130
+ prompt_ocr = "<image> Extract all visible text in the image, such as road signs, license plates, or billboards, with exact wording."
131
+ inputs_ocr = processor[text=[prompt_ocr], images=[image], return_tensors="pt", padding=True)
132
  ocr_outputs = model.generate(**inputs_ocr, max_length=512)
133
  signs_text = processor.decode(ocr_outputs[0], skip_special_tokens=True).replace(prompt_ocr, "").strip()
134
 
135
+ # Detect harmful objects/blood with confidence simulation
136
+ prompt_detect = "<image> Identify any harmful objects (e.g., knife, gun, blood, syringe, bomb, blade) in the image. List them explicitly and estimate confidence (0-100%) for each detection based on clarity."
137
  inputs_detect = processor(text=[prompt_detect], images=[image], return_tensors="pt", padding=True)
138
  detect_outputs = model.generate(**inputs_detect, max_length=512)
139
  detected_objects = processor.decode(detect_outputs[0], skip_special_tokens=True).lower()
140
+ harmful_detected = []
141
+ for obj in harmful_objects:
142
+ if obj in detected_objects:
143
+ confidence = 90 if obj in detected_objects.split() else 60
144
+ harmful_detected.append({"object": obj, "confidence": confidence})
145
+ harmful_output = harmful_detected if harmful_detected else [{"object": "None", "confidence": 0}]
146
 
147
  # Get image embedding for similarity
148
  inputs_emb = processor(images=[image], return_tensors="pt", padding=True)
 
151
  image_embeddings.append(emb)
152
 
153
  results.append({
154
+ "image_id": f"Image_{idx}",
155
+ "timestamp": timestamp,
156
  "description": description if description else "No description generated.",
157
  "signs": signs_text if signs_text else "None detected",
158
+ "harmful_objects": harmful_output
159
  })
160
  except Exception as e:
161
  results.append({
162
+ "image_id": f"Image_{idx}",
163
+ "timestamp": timestamp,
164
  "description": "Error processing image.",
165
  "signs": "Error",
166
+ "harmful_objects": [{"object": "Error", "confidence": 0}],
167
  "error": str(e)
168
  })
169
 
 
174
  sim = np.dot(base_embedding, image_embeddings[i].T) / (
175
  np.linalg.norm(base_embedding) * np.linalg.norm(image_embeddings[i])
176
  )
177
+ results[i]["similarity_to_image_1"] = float(sim[0][0])
178
 
179
+ # Format output for Gradio as tabulated markdown
180
+ output = f"**Analysis Timestamp**: {timestamp}\n\n"
181
+ output += "| Image ID | Description | Signs/Number Plates | Harmful Objects | Similarity to Image 1 |\n"
182
+ output += "|----------|-------------|---------------------|-----------------|-----------------------|\n"
183
+ for result in results:
184
+ harmful_str = ", ".join([f"{obj['object']} ({obj['confidence']}%)" for obj in result['harmful_objects']])
185
+ similarity = f"{result['similarity_to_image_1']:.2f}" if 'similarity_to_image_1' in result else "N/A"
186
+ output += f"| {result['image_id']} ({result['timestamp']}) | {result['description']} | {result['signs']} | {harmful_str} | {similarity} |\n"
 
187
  if "error" in result:
188
+ output += f"| **Error** | {result['error']} | - | - | - |\n"
 
 
189
  return output
190
 
191
  # Gradio interface
192
  iface = gr.Interface(
193
  fn=gradio_predict,
194
+ inputs=[gr.Image(label=f"Upload Image {i+1} (up to 10MB)") for i in range(3)], # Allow up to 3 images
195
+ outputs=gr.Markdown(label="Investigation Results"),
196
+ title="VisionSage: Image Analysis for Investigation",
197
+ description="Upload up to 3 images (up to 10MB each, any resolution) to get detailed descriptions, extract signs/number plates, detect harmful objects/blood with confidence scores, and compute similarity to the first image. Results are formatted for investigative analysis."
198
  )
199
 
200
  if __name__ == "__main__":