ibsocr1 commited on
Commit
b3f7b6e
·
verified ·
1 Parent(s): e0560e9

Upload 6 files

Browse files
Files changed (3) hide show
  1. __pycache__/app.cpython-313.pyc +0 -0
  2. app.py +68 -29
  3. training/classes.txt +9 -7
__pycache__/app.cpython-313.pyc CHANGED
Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ
 
app.py CHANGED
@@ -238,11 +238,11 @@ def draw_annotations(image_id):
238
 
239
 
240
  def add_annotation(image_id, cls, x, y, w, h):
241
- if not image_id:return annotation_canvas_html(image_id),"Select an image first.",[]
242
- if not cls:return annotation_canvas_html(image_id),"Select a class first.",[]
243
  try:x,y,w,h=map(float,[x,y,w,h])
244
- except:return annotation_canvas_html(image_id),"Enter box coordinates first.",[]
245
- if w<=0 or h<=0:return annotation_canvas_html(image_id),"Box must have a width and height.",[]
246
  data=load_dataset()
247
  item=next((z for z in data["images"] if z["id"]==image_id),None)
248
  if not item:return None,"Image not found.",[]
@@ -250,41 +250,72 @@ def add_annotation(image_id, cls, x, y, w, h):
250
  w=min(w,item["width"]-x); h=min(h,item["height"]-y)
251
  item.setdefault("annotations",[]).append({"class":cls,"box":[x,y,w,h]})
252
  save_dataset(data)
253
- return annotation_canvas_html(image_id),f"Saved {cls}: [{x:.0f}, {y:.0f}, {w:.0f}, {h:.0f}]",item["annotations"]
254
 
255
 
256
  def remove_annotation(image_id,index):
257
- if not image_id:return annotation_canvas_html(image_id),"Select an image first.",[]
258
  data=load_dataset()
259
  item=next((z for z in data["images"] if z["id"]==image_id),None)
260
  if not item:return None,"Image not found.",[]
261
  try:idx=int(index)-1
262
- except:return annotation_canvas_html(image_id),"Enter an annotation number.",item.get("annotations",[])
263
  anns=item.get("annotations",[])
264
- if idx<0 or idx>=len(anns):return annotation_canvas_html(image_id),"Annotation number not found.",anns
265
  deleted=anns.pop(idx);save_dataset(data)
266
- return annotation_canvas_html(image_id),f"Deleted annotation {index}: {deleted['class']}",anns
267
 
268
 
269
  def clear_annotations(image_id):
270
- if not image_id:return annotation_canvas_html(image_id),"Select an image first.",[]
271
  data=load_dataset()
272
  item=next((z for z in data["images"] if z["id"]==image_id),None)
273
  if not item:return None,"Image not found.",[]
274
  item["annotations"]=[];save_dataset(data)
275
- return annotation_canvas_html(image_id),"Annotations cleared.",[]
276
 
277
 
278
  def save_classes(text):
279
  classes = [x.strip() for x in (text or "").splitlines() if x.strip()]
280
  if not classes:
281
- return "At least one class is required.", gr.update(choices=[]), dataset_status()
282
  if len(set(classes)) != len(classes):
283
- return "Classes must be unique.", gr.update(choices=CLASSES), dataset_status()
284
  CLASSES_FILE.write_text("\n".join(classes) + "\n", encoding="utf-8")
285
  data = load_dataset()
286
  save_dataset(data)
287
- return f"Saved {len(classes)} classes.", gr.update(choices=CLASSES, value=classes[0]), dataset_status()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
 
290
  def build_coco():
@@ -435,6 +466,7 @@ CSS = """
435
  .gradio-container { max-width: 1250px !important; }
436
  h1 { margin-bottom: 0.2rem !important; }
437
  .anno-wrap{width:100%}.anno-toolbar{display:flex;gap:14px;flex-wrap:wrap;padding:10px 12px;margin-bottom:8px;border-radius:10px;background:#20242a}.anno-toolbar span{opacity:.85}.anno-canvas-wrap{width:100%;overflow:auto;border:1px solid #555;border-radius:10px;background:#111;padding:8px}.anno-canvas-wrap canvas{display:block;max-width:none;cursor:crosshair;touch-action:none;margin:auto}.anno-help{padding:8px 2px;opacity:.75}.anno-empty{padding:50px;text-align:center;border:1px dashed #777;border-radius:10px}
 
438
  .status { padding: 10px 14px; border-radius: 10px; }
439
  """
440
 
@@ -459,29 +491,35 @@ with gr.Blocks(title="Ice Cream Dataset + Counter") as demo:
459
  # Class dropdown is updated after the Annotate tab creates it.
460
 
461
  with gr.Tab("2 · Annotate"):
462
- gr.Markdown("### See the real training image and annotate it")
463
- gr.Markdown("The actual uploaded photo is displayed below. Select a class and enter the box coordinates in the original image pixels, then click **Save Box**. Saved boxes appear in red.")
464
  with gr.Row():
465
  with gr.Column(scale=3):
466
- annotation_canvas = gr.HTML(value='<div class="anno-empty">Select an image.</div>', label="Training image")
467
- editor_info = gr.Markdown()
468
  with gr.Column(scale=1):
469
- ann_class = gr.Dropdown(choices=read_classes(), value=(read_classes()[0] if read_classes() else None), label="Class")
470
- x = gr.Number(label="X (left)", value=0, precision=0, elem_id="anno-x")
471
- y = gr.Number(label="Y (top)", value=0, precision=0, elem_id="anno-y")
472
- w = gr.Number(label="Width", value=0, precision=0, elem_id="anno-w")
473
- h = gr.Number(label="Height", value=0, precision=0, elem_id="anno-h")
474
  add_btn = gr.Button("💾 Save Box", variant="primary")
475
- gr.Markdown("Example: X=120, Y=80, Width=180, Height=300.")
476
  delete_index = gr.Number(label="Annotation # to delete", value=1, precision=0)
477
  delete_btn = gr.Button("Delete Box")
478
  clear_btn = gr.Button("Clear All Boxes")
479
  annotations = gr.JSON(label="Saved annotations")
480
  ann_msg = gr.Markdown()
481
- image_select.change(lambda image_id: (draw_annotations(image_id), refresh_editor(image_id)[1], refresh_editor(image_id)[2]), image_select, [annotation_canvas, editor_info, annotations])
482
- add_btn.click(add_annotation, [image_select, ann_class, x, y, w, h], [annotation_canvas, ann_msg, annotations])
483
- delete_btn.click(remove_annotation, [image_select, delete_index], [annotation_canvas, ann_msg, annotations])
484
- clear_btn.click(clear_annotations, image_select, [annotation_canvas, ann_msg, annotations])
 
 
 
 
 
 
485
 
486
  with gr.Tab("3 · Training"):
487
  gr.Markdown("### Train RT-DETR")
@@ -513,9 +551,10 @@ with gr.Blocks(title="Ice Cream Dataset + Counter") as demo:
513
  preprocess=False,
514
  queue=False,
515
  )
 
516
  # The earlier placeholder event is harmlessly superseded by this real event.
517
 
518
- demo.load(lambda: (dataset_status(), gr.update(choices=image_choices()), gr.update(choices=read_classes())),
519
  None, [status, image_select, ann_class])
520
 
521
  if __name__ == "__main__":
 
238
 
239
 
240
  def add_annotation(image_id, cls, x, y, w, h):
241
+ if not image_id:return annotation_preview_with_boxes(image_id),"Select an image first.",[]
242
+ if not cls:return annotation_preview_with_boxes(image_id),"Select a class first.",[]
243
  try:x,y,w,h=map(float,[x,y,w,h])
244
+ except:return annotation_preview_with_boxes(image_id),"Enter box coordinates first.",[]
245
+ if w<=0 or h<=0:return annotation_preview_with_boxes(image_id),"Box must have a width and height.",[]
246
  data=load_dataset()
247
  item=next((z for z in data["images"] if z["id"]==image_id),None)
248
  if not item:return None,"Image not found.",[]
 
250
  w=min(w,item["width"]-x); h=min(h,item["height"]-y)
251
  item.setdefault("annotations",[]).append({"class":cls,"box":[x,y,w,h]})
252
  save_dataset(data)
253
+ return annotation_preview_with_boxes(image_id),f"Saved {cls}: [{x:.0f}, {y:.0f}, {w:.0f}, {h:.0f}]",item["annotations"]
254
 
255
 
256
  def remove_annotation(image_id,index):
257
+ if not image_id:return annotation_preview_with_boxes(image_id),"Select an image first.",[]
258
  data=load_dataset()
259
  item=next((z for z in data["images"] if z["id"]==image_id),None)
260
  if not item:return None,"Image not found.",[]
261
  try:idx=int(index)-1
262
+ except:return annotation_preview_with_boxes(image_id),"Enter an annotation number.",item.get("annotations",[])
263
  anns=item.get("annotations",[])
264
+ if idx<0 or idx>=len(anns):return annotation_preview_with_boxes(image_id),"Annotation number not found.",anns
265
  deleted=anns.pop(idx);save_dataset(data)
266
+ return annotation_preview_with_boxes(image_id),f"Deleted annotation {index}: {deleted['class']}",anns
267
 
268
 
269
  def clear_annotations(image_id):
270
+ if not image_id:return annotation_preview_with_boxes(image_id),"Select an image first.",[]
271
  data=load_dataset()
272
  item=next((z for z in data["images"] if z["id"]==image_id),None)
273
  if not item:return None,"Image not found.",[]
274
  item["annotations"]=[];save_dataset(data)
275
+ return annotation_preview_with_boxes(image_id),"Annotations cleared.",[]
276
 
277
 
278
  def save_classes(text):
279
  classes = [x.strip() for x in (text or "").splitlines() if x.strip()]
280
  if not classes:
281
+ return "At least one class is required.", gr.update(choices=read_classes()), dataset_status()
282
  if len(set(classes)) != len(classes):
283
+ return "Classes must be unique.", gr.update(choices=read_classes()), dataset_status()
284
  CLASSES_FILE.write_text("\n".join(classes) + "\n", encoding="utf-8")
285
  data = load_dataset()
286
  save_dataset(data)
287
+ return f"Saved {len(classes)} classes.", gr.update(choices=classes, value=classes[0]), dataset_status()
288
+
289
+
290
+ def handle_annotation_click(image_id, cls, click_state, evt: gr.SelectData):
291
+ """Use two clicks on the real Gradio image to define a box.
292
+ First click = top-left corner, second click = opposite corner.
293
+ This avoids the unreliable HTML canvas/script path and works in Gradio itself.
294
+ """
295
+ if not image_id:
296
+ return 0, 0, 0, 0, [], "Select an image first."
297
+ if not cls:
298
+ return 0, 0, 0, 0, [], "Select a class first."
299
+ data = load_dataset()
300
+ item = next((x for x in data["images"] if x["id"] == image_id), None)
301
+ if not item:
302
+ return 0, 0, 0, 0, [], "Image not found."
303
+ try:
304
+ point = evt.index
305
+ px, py = float(point[0]), float(point[1])
306
+ except Exception:
307
+ return 0, 0, 0, 0, click_state or [], "Could not read the image click position."
308
+ px = max(0, min(px, item["width"] - 1))
309
+ py = max(0, min(py, item["height"] - 1))
310
+ state = list(click_state or [])
311
+ if not state:
312
+ return round(px), round(py), 0, 0, [px, py], f"First corner: ({px:.0f}, {py:.0f}). Now click the opposite corner."
313
+ x0, y0 = state[:2]
314
+ x = min(x0, px); y = min(y0, py)
315
+ w = abs(px - x0); h = abs(py - y0)
316
+ if w < 2 or h < 2:
317
+ return round(x), round(y), 0, 0, [], "Box is too small. Click the first corner again."
318
+ return round(x), round(y), round(w), round(h), [], f"Box ready: [{x:.0f}, {y:.0f}, {w:.0f}, {h:.0f}] for {cls}. Click Save Box."
319
 
320
 
321
  def build_coco():
 
466
  .gradio-container { max-width: 1250px !important; }
467
  h1 { margin-bottom: 0.2rem !important; }
468
  .anno-wrap{width:100%}.anno-toolbar{display:flex;gap:14px;flex-wrap:wrap;padding:10px 12px;margin-bottom:8px;border-radius:10px;background:#20242a}.anno-toolbar span{opacity:.85}.anno-canvas-wrap{width:100%;overflow:auto;border:1px solid #555;border-radius:10px;background:#111;padding:8px}.anno-canvas-wrap canvas{display:block;max-width:none;cursor:crosshair;touch-action:none;margin:auto}.anno-help{padding:8px 2px;opacity:.75}.anno-empty{padding:50px;text-align:center;border:1px dashed #777;border-radius:10px}
469
+ #annotation-image img { max-height: 650px !important; object-fit: contain !important; }
470
  .status { padding: 10px 14px; border-radius: 10px; }
471
  """
472
 
 
491
  # Class dropdown is updated after the Annotate tab creates it.
492
 
493
  with gr.Tab("2 · Annotate"):
494
+ gr.Markdown("### Annotate training images")
495
+ gr.Markdown("Select an image above, choose a class, then **click the first corner and click the opposite corner** of each object. The real uploaded image is shown below. Click **Save Box** after each box.")
496
  with gr.Row():
497
  with gr.Column(scale=3):
498
+ annotation_image = gr.Image(value=None, type="pil", interactive=False, label="Training image", height=650, elem_id="annotation-image")
499
+ editor_info = gr.Markdown("Select an image from the Dataset tab.")
500
  with gr.Column(scale=1):
501
+ ann_class = gr.Dropdown(choices=read_classes(), value=(read_classes()[0] if read_classes() else None), label="Class", interactive=True)
502
+ x = gr.Number(label="X (left)", value=0, precision=0)
503
+ y = gr.Number(label="Y (top)", value=0, precision=0)
504
+ w = gr.Number(label="Width", value=0, precision=0)
505
+ h = gr.Number(label="Height", value=0, precision=0)
506
  add_btn = gr.Button("💾 Save Box", variant="primary")
507
+ gr.Markdown("**Box method:** click corner 1 → click corner 2 → Save Box.")
508
  delete_index = gr.Number(label="Annotation # to delete", value=1, precision=0)
509
  delete_btn = gr.Button("Delete Box")
510
  clear_btn = gr.Button("Clear All Boxes")
511
  annotations = gr.JSON(label="Saved annotations")
512
  ann_msg = gr.Markdown()
513
+ click_state = gr.State([])
514
+
515
+ def load_annotation_image(image_id):
516
+ return annotation_preview_with_boxes(image_id), refresh_editor(image_id)[1], refresh_editor(image_id)[2], []
517
+
518
+ image_select.change(load_annotation_image, image_select, [annotation_image, editor_info, annotations, click_state])
519
+ annotation_image.select(handle_annotation_click, [image_select, ann_class, click_state], [x, y, w, h, click_state, ann_msg])
520
+ add_btn.click(add_annotation, [image_select, ann_class, x, y, w, h], [annotation_image, ann_msg, annotations])
521
+ delete_btn.click(remove_annotation, [image_select, delete_index], [annotation_image, ann_msg, annotations])
522
+ clear_btn.click(clear_annotations, image_select, [annotation_image, ann_msg, annotations])
523
 
524
  with gr.Tab("3 · Training"):
525
  gr.Markdown("### Train RT-DETR")
 
551
  preprocess=False,
552
  queue=False,
553
  )
554
+ save_class_btn.click(save_classes, class_text, [class_msg, ann_class, status])
555
  # The earlier placeholder event is harmlessly superseded by this real event.
556
 
557
+ demo.load(lambda: (dataset_status(), gr.update(choices=image_choices()), gr.update(choices=read_classes(), value=(read_classes()[0] if read_classes() else None))),
558
  None, [status, image_select, ann_class])
559
 
560
  if __name__ == "__main__":
training/classes.txt CHANGED
@@ -1,8 +1,10 @@
1
- cornetto
2
- magnum
3
- correto
4
- cone
5
- cup
6
- sandwich
7
- stick
 
 
8
  other
 
1
+ Carnavalita
2
+ Kimo-COno
3
+ Squizz
4
+ Oreo
5
+ Moro
6
+ Dulce
7
+ KitKat
8
+ Cadbury
9
+ Mega
10
  other