jebin2 commited on
Commit
ccc081e
Β·
1 Parent(s): 8498384

detect added

Browse files
comic_panel_extractor/annorator_server.py CHANGED
@@ -55,11 +55,22 @@ def get_label_path(image_name: str) -> str:
55
  return os.path.join(LABEL_ROOT, os.path.splitext(image_name)[0] + ".txt")
56
 
57
  # === Core Functions ===
58
- def load_yolo_boxes(image_path: str, label_path: str):
59
  try:
60
  img = Image.open(image_path)
61
  w, h = img.size
62
  boxes = []
 
 
 
 
 
 
 
 
 
 
 
63
  if os.path.exists(label_path):
64
  with open(label_path, "r") as f:
65
  for line in f:
@@ -160,6 +171,21 @@ async def get_annotations(image_name: str):
160
  "original_height": height
161
  }
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
  @app.post("/api/annotate/annotations")
164
  async def save_annotations(request: SaveAnnotationsRequest):
165
  label_path = get_label_path(request.image_name)
 
55
  return os.path.join(LABEL_ROOT, os.path.splitext(image_name)[0] + ".txt")
56
 
57
  # === Core Functions ===
58
+ def load_yolo_boxes(image_path: str, label_path: str, detect: bool = False):
59
  try:
60
  img = Image.open(image_path)
61
  w, h = img.size
62
  boxes = []
63
+ if detect and not os.path.exists(label_path):
64
+ from .yolo_manager import YOLOManager
65
+ from .utils import Config
66
+ yolo_manager = YOLOManager()
67
+ weights_path = f'{current_path}/{Config.YOLO_MODEL_NAME}.pt'
68
+
69
+ yolo_manager.load_model(weights_path)
70
+
71
+ # Run inference
72
+ _, label_path = yolo_manager.annotate_images(image_paths=[image_path], output_dir=IMAGE_LABEL_ROOT, save_image=False, label_path=label_path)
73
+
74
  if os.path.exists(label_path):
75
  with open(label_path, "r") as f:
76
  for line in f:
 
171
  "original_height": height
172
  }
173
 
174
+ @app.get("/api/annotate/detect_annotations/{image_name:path}")
175
+ async def get_annotations(image_name: str):
176
+ image_path = get_image_path(image_name)
177
+ label_path = get_label_path(image_name)
178
+
179
+ if not os.path.exists(image_path):
180
+ raise HTTPException(status_code=404, detail="Image not found")
181
+
182
+ boxes, (width, height) = load_yolo_boxes(image_path, label_path, True)
183
+ return {
184
+ "boxes": boxes,
185
+ "original_width": width,
186
+ "original_height": height
187
+ }
188
+
189
  @app.post("/api/annotate/annotations")
190
  async def save_annotations(request: SaveAnnotationsRequest):
191
  label_path = get_label_path(request.image_name)
comic_panel_extractor/static/annotator.html CHANGED
@@ -547,6 +547,9 @@
547
  <button class="btn btn-primary btn-block" id="reloadBtn">
548
  πŸ”„ Reload Annotations
549
  </button>
 
 
 
550
  <button class="btn btn-secondary btn-block" id="downloadBtn" style="display: none; margin-top: 8px;">
551
  πŸ“₯ Download
552
  </button>
@@ -661,6 +664,7 @@
661
  document.getElementById('undoBtn').addEventListener('click', () => this.undoLastBox());
662
  document.getElementById('clearBtn').addEventListener('click', () => this.clearAllBoxes());
663
  document.getElementById('reloadBtn').addEventListener('click', () => this.reloadAnnotations());
 
664
  document.getElementById('downloadBtn').addEventListener('click', () => this.downloadAnnotations());
665
 
666
  // Canvas events
@@ -1369,6 +1373,27 @@
1369
  }
1370
  }
1371
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1372
  downloadAnnotations() {
1373
  if (!this.currentImage) return;
1374
 
 
547
  <button class="btn btn-primary btn-block" id="reloadBtn">
548
  πŸ”„ Reload Annotations
549
  </button>
550
+ <button class="btn btn-primary btn-block" id="detectBtn">
551
+ πŸ”„ Detect Annotations
552
+ </button>
553
  <button class="btn btn-secondary btn-block" id="downloadBtn" style="display: none; margin-top: 8px;">
554
  πŸ“₯ Download
555
  </button>
 
664
  document.getElementById('undoBtn').addEventListener('click', () => this.undoLastBox());
665
  document.getElementById('clearBtn').addEventListener('click', () => this.clearAllBoxes());
666
  document.getElementById('reloadBtn').addEventListener('click', () => this.reloadAnnotations());
667
+ document.getElementById('detectBtn').addEventListener('click', () => this.detectAnnotations());
668
  document.getElementById('downloadBtn').addEventListener('click', () => this.downloadAnnotations());
669
 
670
  // Canvas events
 
1373
  }
1374
  }
1375
 
1376
+ async detectAnnotations() {
1377
+ if (!this.currentImage) return;
1378
+
1379
+ try {
1380
+ const response = await fetch(`/api/annotate/detect_annotations/${encodeURIComponent(this.currentImage)}`);
1381
+ const data = await response.json();
1382
+
1383
+ this.boxes = (data.boxes || []).map(box => ({
1384
+ ...box,
1385
+ saved: true
1386
+ }));
1387
+ this.selectedBoxIndex = -1;
1388
+ document.getElementById('boxCount').textContent = this.boxes.length;
1389
+ this.updateSelectedBoxInfo();
1390
+ this.drawCanvas();
1391
+ this.showAlert('Annotations reloaded from file', 'success');
1392
+ } catch (error) {
1393
+ this.showAlert('Error reloading annotations: ' + error.message, 'error');
1394
+ }
1395
+ }
1396
+
1397
  downloadAnnotations() {
1398
  if (!this.currentImage) return;
1399
 
comic_panel_extractor/yolo_manager.py CHANGED
@@ -66,7 +66,7 @@ import os
66
  import cv2
67
  from ultralytics import YOLO
68
  from typing import List, Optional, Dict, Any
69
- from utils import Config, get_abs_path, clean_directory
70
 
71
  class YOLOManager:
72
  """Manages YOLO model training and inference operations."""
@@ -153,13 +153,13 @@ class YOLOManager:
153
 
154
  return weights_path
155
 
156
- def annotate_images(self, image_paths: List[str], output_dir: str = 'temp_dir', image_size: int = None) -> None:
157
  """
158
- Annotate images with model predictions.
159
 
160
  Args:
161
  image_paths: List of image file paths
162
- output_dir: Directory to save annotated images
163
  image_size: Size for inference
164
  """
165
  if not self.model:
@@ -169,30 +169,63 @@ class YOLOManager:
169
  raise ValueError("❌ No images provided for annotation.")
170
 
171
  image_size = image_size or Config.DEFAULT_IMAGE_SIZE
172
- clean_directory(output_dir)
173
-
174
- print(f"🎨 Annotating {len(image_paths)} images...")
175
 
176
  for idx, image_path in enumerate(image_paths):
177
  if not os.path.isfile(image_path):
178
  print(f"⚠️ Warning: Skipping non-existent file {image_path}")
179
  continue
180
-
181
  print(f'πŸ” Processing ({idx+1}/{len(image_paths)}): {os.path.basename(image_path)}')
182
 
183
  try:
 
 
 
 
 
184
  results = self.model(image_path, imgsz=image_size)
185
  annotated_frame = results[0].plot()
186
 
187
- # Use original filename with prefix
188
  original_name = os.path.basename(image_path)
189
  name, ext = os.path.splitext(original_name)
190
- save_path = os.path.join(output_dir, f'annotated_{name}{ext}')
191
-
192
- cv2.imwrite(save_path, annotated_frame)
193
- print(f'βœ… Saved: {save_path}')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
 
195
  except Exception as e:
196
  print(f"❌ Error processing {image_path}: {str(e)}")
197
-
198
- print(f"πŸŽ‰ Annotation complete! Results saved to: {output_dir}")
 
66
  import cv2
67
  from ultralytics import YOLO
68
  from typing import List, Optional, Dict, Any
69
+ from .utils import Config, get_abs_path, clean_directory
70
 
71
  class YOLOManager:
72
  """Manages YOLO model training and inference operations."""
 
153
 
154
  return weights_path
155
 
156
+ def annotate_images(self, image_paths: List[str], output_dir: str = 'temp_dir', image_size: int = None, save_image: bool = True, label_path: str = None) -> None:
157
  """
158
+ Annotate images with model predictions and save YOLO-format label files.
159
 
160
  Args:
161
  image_paths: List of image file paths
162
+ output_dir: Directory to save annotated images and labels
163
  image_size: Size for inference
164
  """
165
  if not self.model:
 
169
  raise ValueError("❌ No images provided for annotation.")
170
 
171
  image_size = image_size or Config.DEFAULT_IMAGE_SIZE
172
+ # clean_directory(output_dir)
173
+ totla_images = len(image_paths)
174
+ print(f"🎨 Annotating {totla_images} images and saving labels...")
175
 
176
  for idx, image_path in enumerate(image_paths):
177
  if not os.path.isfile(image_path):
178
  print(f"⚠️ Warning: Skipping non-existent file {image_path}")
179
  continue
180
+
181
  print(f'πŸ” Processing ({idx+1}/{len(image_paths)}): {os.path.basename(image_path)}')
182
 
183
  try:
184
+ # Load image for size info
185
+ img = cv2.imread(image_path)
186
+ h, w = img.shape[:2]
187
+
188
+ # Run inference
189
  results = self.model(image_path, imgsz=image_size)
190
  annotated_frame = results[0].plot()
191
 
192
+ # Prepare save paths
193
  original_name = os.path.basename(image_path)
194
  name, ext = os.path.splitext(original_name)
195
+
196
+ save_img_path = None
197
+ save_txt_path = os.path.join(output_dir, f'{name}.txt') # YOLO label txt
198
+ if save_image:
199
+ save_img_path = os.path.join(output_dir, f'annotated_{name}{ext}')
200
+ # Save annotated image
201
+ cv2.imwrite(save_img_path, annotated_frame)
202
+
203
+ # Write YOLO label file
204
+ with open(save_txt_path, 'w') as f:
205
+ for box in results[0].boxes:
206
+ # box.xyxy format: (xmin, ymin, xmax, ymax)
207
+ xyxy = box.xyxy[0].tolist()
208
+ cls_id = int(box.cls[0].item()) # class id
209
+
210
+ xmin, ymin, xmax, ymax = xyxy
211
+ # Convert to YOLO format (normalized)
212
+ x_center = ((xmin + xmax) / 2) / w
213
+ y_center = ((ymin + ymax) / 2) / h
214
+ width = (xmax - xmin) / w
215
+ height = (ymax - ymin) / h
216
+
217
+ # Write one line per object
218
+ f.write(f"{cls_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\n")
219
+
220
+ if label_path:
221
+ shutil.copyfile(save_txt_path, label_path)
222
+ print(f'βœ… Saved annotated image: {save_img_path}')
223
+ print(f'βœ… Saved label file: {save_txt_path}')
224
+ print(f"πŸŽ‰ Annotation and label saving complete! Results saved to: {output_dir}")
225
+
226
+ if totla_images == 1:
227
+ return save_img_path, save_txt_path
228
 
229
  except Exception as e:
230
  print(f"❌ Error processing {image_path}: {str(e)}")
231
+ return None, None