johnlockejrr commited on
Commit
0ade37a
·
verified ·
1 Parent(s): 771933a

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +62 -44
app.py CHANGED
@@ -6,6 +6,9 @@ import cv2
6
  from huggingface_hub import hf_hub_download
7
  from ultralytics import YOLO
8
  from PIL import Image
 
 
 
9
 
10
  # Page config
11
  st.set_page_config(
@@ -48,20 +51,31 @@ def load_models():
48
  return models
49
 
50
  def simplify_polygons(polygons: List[np.ndarray], approx_level: float = 0.01) -> List[Optional[np.ndarray]]:
51
- """Simplify polygon contours using Douglas-Peucker algorithm."""
 
 
 
 
 
 
 
 
52
  result = []
53
  for polygon in polygons:
54
  if len(polygon) < 4:
55
  result.append(None)
56
  continue
 
57
  perimeter = cv2.arcLength(polygon, True)
58
  approx = cv2.approxPolyDP(polygon, approx_level * perimeter, True)
59
  if len(approx) < 4:
60
  result.append(None)
61
  continue
 
62
  result.append(approx.squeeze())
63
  return result
64
 
 
65
  class OutlineMaskAnnotator:
66
  def __init__(self, color: tuple = (255, 0, 0), thickness: int = 2, simplify: bool = False):
67
  self.color = color
@@ -71,6 +85,7 @@ class OutlineMaskAnnotator:
71
  def annotate(self, scene: np.ndarray, detections: sv.Detections) -> np.ndarray:
72
  if detections.mask is None:
73
  return scene
 
74
  scene = scene.copy()
75
  for mask in detections.mask:
76
  contours, _ = cv2.findContours(
@@ -81,6 +96,7 @@ class OutlineMaskAnnotator:
81
  if self.simplify:
82
  contours = simplify_polygons(contours)
83
  contours = [c for c in contours if c is not None]
 
84
  cv2.drawContours(
85
  scene,
86
  contours,
@@ -90,6 +106,7 @@ class OutlineMaskAnnotator:
90
  )
91
  return scene
92
 
 
93
  LABEL_ANNOTATOR = sv.LabelAnnotator(
94
  text_color=sv.Color.BLACK,
95
  text_scale=0.35,
@@ -104,20 +121,26 @@ def detect_and_annotate(
104
  iou_threshold: float,
105
  simplify_polygons_option: bool
106
  ) -> np.ndarray:
 
107
  model = models[model_name]
 
 
108
  results = model.predict(
109
  image,
110
  conf=conf_threshold,
111
  iou=iou_threshold
112
  )[0]
113
 
 
114
  boxes = results.boxes.xyxy.cpu().numpy()
115
  confidence = results.boxes.conf.cpu().numpy()
116
  class_ids = results.boxes.cls.cpu().numpy().astype(int)
117
 
 
118
  masks = None
119
  if results.masks is not None:
120
  masks = results.masks.data.cpu().numpy()
 
121
  masks = np.transpose(masks, (1, 2, 0))
122
  h, w = image.shape[:2]
123
  resized_masks = []
@@ -126,6 +149,7 @@ def detect_and_annotate(
126
  resized_masks.append(resized_mask > 0.5)
127
  masks = np.stack(resized_masks) if resized_masks else None
128
 
 
129
  detections = sv.Detections(
130
  xyxy=boxes,
131
  confidence=confidence,
@@ -133,18 +157,21 @@ def detect_and_annotate(
133
  mask=masks
134
  )
135
 
 
136
  labels = [
137
  f"{results.names[class_id]} ({conf:.2f})"
138
  for class_id, conf
139
  in zip(class_ids, confidence)
140
  ]
141
 
 
142
  mask_annotator = OutlineMaskAnnotator(
143
  color=(255, 0, 0),
144
  thickness=2,
145
  simplify=simplify_polygons_option
146
  )
147
 
 
148
  annotated_image = image.copy()
149
  if masks is not None:
150
  annotated_image = mask_annotator.annotate(scene=annotated_image, detections=detections)
@@ -161,12 +188,14 @@ st.title("Medieval Manuscript Segmentation with YOLO")
161
  # Sidebar for controls
162
  with st.sidebar:
163
  st.header("Detection Settings")
 
164
  model_name = st.selectbox(
165
  "Model",
166
  options=list(MODEL_OPTIONS.keys()),
167
  index=0,
168
  help="Select YOLO model variant"
169
  )
 
170
  conf_threshold = st.slider(
171
  "Confidence Threshold",
172
  min_value=0.0,
@@ -175,6 +204,7 @@ with st.sidebar:
175
  step=0.05,
176
  help="Minimum confidence score for detections"
177
  )
 
178
  iou_threshold = st.slider(
179
  "IoU Threshold",
180
  min_value=0.0,
@@ -183,57 +213,45 @@ with st.sidebar:
183
  step=0.05,
184
  help="Decrease for stricter detection, increase for more overlapping masks"
185
  )
 
186
  simplify_polygons_option = st.checkbox(
187
  "Simplify Polygons",
188
  value=False,
189
  help="Simplify polygon contours for cleaner outlines"
190
  )
191
 
192
- # Main content area - using columns with fixed width containers
193
  col1, col2 = st.columns(2)
194
 
195
  with col1:
196
- container1 = st.container(border=True)
197
- with container1:
198
- st.subheader("Input Image")
199
- uploaded_file = st.file_uploader(
200
- "Upload an image",
201
- type=["jpg", "jpeg", "png"],
202
- key="file_uploader"
203
- )
204
-
205
- if uploaded_file is not None:
206
- image = np.array(Image.open(uploaded_file))
207
- st.image(image, use_container_width=True)
 
208
 
209
  with col2:
210
- container2 = st.container(border=True)
211
- with container2:
212
- st.subheader("Detection Result")
213
- if uploaded_file is not None:
214
- if st.button("Detect", type="primary", key="detect_button"):
215
- with st.spinner("Processing image..."):
216
- annotated_image = detect_and_annotate(
217
- image,
218
- model_name,
219
- conf_threshold,
220
- iou_threshold,
221
- simplify_polygons_option
222
- )
223
- st.image(annotated_image, use_container_width=True)
224
- else:
225
- st.info("Click the Detect button to process the image")
226
- else:
227
- st.info("Upload an image to see detection results")
228
-
229
- # Add some custom CSS to ensure perfect alignment
230
- st.markdown("""
231
- <style>
232
- [data-testid="stHorizontalBlock"] {
233
- align-items: center;
234
- }
235
- .stContainer {
236
- height: 100%;
237
- }
238
- </style>
239
- """, unsafe_allow_html=True)
 
6
  from huggingface_hub import hf_hub_download
7
  from ultralytics import YOLO
8
  from PIL import Image
9
+ import torch
10
+
11
+ torch.cuda.is_available = lambda: False # Force CPU-only mode in HF Space
12
 
13
  # Page config
14
  st.set_page_config(
 
51
  return models
52
 
53
  def simplify_polygons(polygons: List[np.ndarray], approx_level: float = 0.01) -> List[Optional[np.ndarray]]:
54
+ """Simplify polygon contours using Douglas-Peucker algorithm.
55
+
56
+ Args:
57
+ polygons: List of polygon contours
58
+ approx_level: Approximation level (0-1), lower values mean more simplification
59
+
60
+ Returns:
61
+ List of simplified polygons (or None for invalid polygons)
62
+ """
63
  result = []
64
  for polygon in polygons:
65
  if len(polygon) < 4:
66
  result.append(None)
67
  continue
68
+
69
  perimeter = cv2.arcLength(polygon, True)
70
  approx = cv2.approxPolyDP(polygon, approx_level * perimeter, True)
71
  if len(approx) < 4:
72
  result.append(None)
73
  continue
74
+
75
  result.append(approx.squeeze())
76
  return result
77
 
78
+ # Custom MaskAnnotator for outline-only masks with simplified polygons
79
  class OutlineMaskAnnotator:
80
  def __init__(self, color: tuple = (255, 0, 0), thickness: int = 2, simplify: bool = False):
81
  self.color = color
 
85
  def annotate(self, scene: np.ndarray, detections: sv.Detections) -> np.ndarray:
86
  if detections.mask is None:
87
  return scene
88
+
89
  scene = scene.copy()
90
  for mask in detections.mask:
91
  contours, _ = cv2.findContours(
 
96
  if self.simplify:
97
  contours = simplify_polygons(contours)
98
  contours = [c for c in contours if c is not None]
99
+
100
  cv2.drawContours(
101
  scene,
102
  contours,
 
106
  )
107
  return scene
108
 
109
+ # Create annotators with new settings
110
  LABEL_ANNOTATOR = sv.LabelAnnotator(
111
  text_color=sv.Color.BLACK,
112
  text_scale=0.35,
 
121
  iou_threshold: float,
122
  simplify_polygons_option: bool
123
  ) -> np.ndarray:
124
+ # Get the selected model
125
  model = models[model_name]
126
+
127
+ # Perform inference
128
  results = model.predict(
129
  image,
130
  conf=conf_threshold,
131
  iou=iou_threshold
132
  )[0]
133
 
134
+ # Convert results to supervision Detections
135
  boxes = results.boxes.xyxy.cpu().numpy()
136
  confidence = results.boxes.conf.cpu().numpy()
137
  class_ids = results.boxes.cls.cpu().numpy().astype(int)
138
 
139
+ # Handle masks if they exist
140
  masks = None
141
  if results.masks is not None:
142
  masks = results.masks.data.cpu().numpy()
143
+ # Convert from (N,H,W) to (H,W,N) for processing
144
  masks = np.transpose(masks, (1, 2, 0))
145
  h, w = image.shape[:2]
146
  resized_masks = []
 
149
  resized_masks.append(resized_mask > 0.5)
150
  masks = np.stack(resized_masks) if resized_masks else None
151
 
152
+ # Create Detections object
153
  detections = sv.Detections(
154
  xyxy=boxes,
155
  confidence=confidence,
 
157
  mask=masks
158
  )
159
 
160
+ # Create labels with confidence scores
161
  labels = [
162
  f"{results.names[class_id]} ({conf:.2f})"
163
  for class_id, conf
164
  in zip(class_ids, confidence)
165
  ]
166
 
167
+ # Create mask annotator based on the simplify option
168
  mask_annotator = OutlineMaskAnnotator(
169
  color=(255, 0, 0),
170
  thickness=2,
171
  simplify=simplify_polygons_option
172
  )
173
 
174
+ # Annotate image
175
  annotated_image = image.copy()
176
  if masks is not None:
177
  annotated_image = mask_annotator.annotate(scene=annotated_image, detections=detections)
 
188
  # Sidebar for controls
189
  with st.sidebar:
190
  st.header("Detection Settings")
191
+
192
  model_name = st.selectbox(
193
  "Model",
194
  options=list(MODEL_OPTIONS.keys()),
195
  index=0,
196
  help="Select YOLO model variant"
197
  )
198
+
199
  conf_threshold = st.slider(
200
  "Confidence Threshold",
201
  min_value=0.0,
 
204
  step=0.05,
205
  help="Minimum confidence score for detections"
206
  )
207
+
208
  iou_threshold = st.slider(
209
  "IoU Threshold",
210
  min_value=0.0,
 
213
  step=0.05,
214
  help="Decrease for stricter detection, increase for more overlapping masks"
215
  )
216
+
217
  simplify_polygons_option = st.checkbox(
218
  "Simplify Polygons",
219
  value=False,
220
  help="Simplify polygon contours for cleaner outlines"
221
  )
222
 
223
+ # Main content area
224
  col1, col2 = st.columns(2)
225
 
226
  with col1:
227
+ st.subheader("Input Image")
228
+ uploaded_file = st.file_uploader(
229
+ "Upload an image",
230
+ type=["jpg", "jpeg", "png"],
231
+ key="file_uploader"
232
+ )
233
+
234
+ if uploaded_file is not None:
235
+ image = np.array(Image.open(uploaded_file))
236
+ st.image(image, caption="Uploaded Image", use_container_width=True) # Updated here
237
+ else:
238
+ image = None
239
+ st.info("Please upload an image file")
240
 
241
  with col2:
242
+ st.subheader("Detection Result")
243
+
244
+ if st.button("Detect", type="primary") and image is not None:
245
+ with st.spinner("Processing image..."):
246
+ annotated_image = detect_and_annotate(
247
+ image,
248
+ model_name,
249
+ conf_threshold,
250
+ iou_threshold,
251
+ simplify_polygons_option
252
+ )
253
+ st.image(annotated_image, caption="Detection Result", use_container_width=True) # Updated here
254
+ elif image is None:
255
+ st.warning("Please upload an image first")
256
+ else:
257
+ st.info("Click the Detect button to process the image")