Spaces:
Running on Zero
Running on Zero
Upload 5 files
Browse files- README.md +7 -9
- app.py +26 -23
- training/classes.txt +7 -9
README.md
CHANGED
|
@@ -146,15 +146,13 @@ The result image also shows the detected bounding boxes and confidence values.
|
|
| 146 |
## Default classes
|
| 147 |
|
| 148 |
```text
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
Cadbury
|
| 157 |
-
Mega
|
| 158 |
other
|
| 159 |
```
|
| 160 |
|
|
|
|
| 146 |
## Default classes
|
| 147 |
|
| 148 |
```text
|
| 149 |
+
cornetto
|
| 150 |
+
magnum
|
| 151 |
+
correto
|
| 152 |
+
cone
|
| 153 |
+
cup
|
| 154 |
+
sandwich
|
| 155 |
+
stick
|
|
|
|
|
|
|
| 156 |
other
|
| 157 |
```
|
| 158 |
|
app.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
|
|
|
|
|
| 1 |
import io
|
| 2 |
import json
|
| 3 |
import os
|
|
@@ -168,7 +170,7 @@ def annotation_canvas_html(image_id):
|
|
| 168 |
return """<div class="anno-wrap">
|
| 169 |
<div class="anno-toolbar"><b>Draw boxes directly on the image</b><span>Click + drag + release = create box</span><span>Choose the class first</span></div>
|
| 170 |
<div class="anno-canvas-wrap"><canvas id="anno-canvas"></canvas></div>
|
| 171 |
-
<div class="anno-help">Drag from one corner of the object to the opposite corner, then release. Repeat for every object.</div>
|
| 172 |
</div>
|
| 173 |
<script>
|
| 174 |
(function(){
|
|
@@ -182,6 +184,7 @@ function setField(id,val){const box=document.querySelector('#'+id);const el=box?
|
|
| 182 |
canvas.addEventListener('pointerdown',e=>{e.preventDefault();canvas.setPointerCapture(e.pointerId);const p=pos(e);sx=p.x;sy=p.y;drawing=true;current={x:sx,y:sy,w:0,h:0};redraw();});
|
| 183 |
canvas.addEventListener('pointermove',e=>{if(!drawing)return;const p=pos(e);current={x:Math.min(sx,p.x),y:Math.min(sy,p.y),w:Math.abs(p.x-sx),h:Math.abs(p.y-sy)};redraw();});
|
| 184 |
canvas.addEventListener('pointerup',e=>{if(!drawing)return;drawing=false;const p=pos(e),sc=+canvas.dataset.scale||1;const x=Math.min(sx,p.x)/sc,y=Math.min(sy,p.y)/sc,w=Math.abs(p.x-sx)/sc,h=Math.abs(p.y-sy)/sc;current=null;redraw();if(w<3||h<3)return;setField('anno-x',Math.round(x));setField('anno-y',Math.round(y));setField('anno-w',Math.round(w));setField('anno-h',Math.round(h));const hint=document.getElementById('anno-hint');if(hint)hint.textContent='Box created. Click “Save Box” to store it.';});
|
|
|
|
| 185 |
img.onload=fit;img.src=imgSrc;window.addEventListener('resize',fit);
|
| 186 |
})();</script>""" % (json.dumps(src), int(item["width"]), int(item["height"]), boxes)
|
| 187 |
|
|
@@ -235,11 +238,11 @@ def draw_annotations(image_id):
|
|
| 235 |
|
| 236 |
|
| 237 |
def add_annotation(image_id, cls, x, y, w, h):
|
| 238 |
-
if not image_id:return
|
| 239 |
-
if not cls:return
|
| 240 |
try:x,y,w,h=map(float,[x,y,w,h])
|
| 241 |
-
except:return
|
| 242 |
-
if w<=0 or h<=0:return
|
| 243 |
data=load_dataset()
|
| 244 |
item=next((z for z in data["images"] if z["id"]==image_id),None)
|
| 245 |
if not item:return None,"Image not found.",[]
|
|
@@ -247,29 +250,29 @@ def add_annotation(image_id, cls, x, y, w, h):
|
|
| 247 |
w=min(w,item["width"]-x); h=min(h,item["height"]-y)
|
| 248 |
item.setdefault("annotations",[]).append({"class":cls,"box":[x,y,w,h]})
|
| 249 |
save_dataset(data)
|
| 250 |
-
return
|
| 251 |
|
| 252 |
|
| 253 |
def remove_annotation(image_id,index):
|
| 254 |
-
if not image_id:return
|
| 255 |
data=load_dataset()
|
| 256 |
item=next((z for z in data["images"] if z["id"]==image_id),None)
|
| 257 |
if not item:return None,"Image not found.",[]
|
| 258 |
try:idx=int(index)-1
|
| 259 |
-
except:return
|
| 260 |
anns=item.get("annotations",[])
|
| 261 |
-
if idx<0 or idx>=len(anns):return
|
| 262 |
deleted=anns.pop(idx);save_dataset(data)
|
| 263 |
-
return
|
| 264 |
|
| 265 |
|
| 266 |
def clear_annotations(image_id):
|
| 267 |
-
if not image_id:return
|
| 268 |
data=load_dataset()
|
| 269 |
item=next((z for z in data["images"] if z["id"]==image_id),None)
|
| 270 |
if not item:return None,"Image not found.",[]
|
| 271 |
item["annotations"]=[];save_dataset(data)
|
| 272 |
-
return
|
| 273 |
|
| 274 |
|
| 275 |
def save_classes(text):
|
|
@@ -277,11 +280,11 @@ def save_classes(text):
|
|
| 277 |
if not classes:
|
| 278 |
return "At least one class is required.", gr.update(choices=[]), dataset_status()
|
| 279 |
if len(set(classes)) != len(classes):
|
| 280 |
-
return "Classes must be unique.", gr.update(choices=
|
| 281 |
CLASSES_FILE.write_text("\n".join(classes) + "\n", encoding="utf-8")
|
| 282 |
data = load_dataset()
|
| 283 |
save_dataset(data)
|
| 284 |
-
return f"Saved {len(classes)} classes.", gr.update(choices=
|
| 285 |
|
| 286 |
|
| 287 |
def build_coco():
|
|
@@ -460,14 +463,14 @@ with gr.Blocks(title="Ice Cream Dataset + Counter") as demo:
|
|
| 460 |
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.")
|
| 461 |
with gr.Row():
|
| 462 |
with gr.Column(scale=3):
|
| 463 |
-
|
| 464 |
editor_info = gr.Markdown()
|
| 465 |
with gr.Column(scale=1):
|
| 466 |
ann_class = gr.Dropdown(choices=read_classes(), value=(read_classes()[0] if read_classes() else None), label="Class")
|
| 467 |
-
x = gr.Number(label="X (left)", value=0, precision=0)
|
| 468 |
-
y = gr.Number(label="Y (top)", value=0, precision=0)
|
| 469 |
-
w = gr.Number(label="Width", value=0, precision=0)
|
| 470 |
-
h = gr.Number(label="Height", value=0, precision=0)
|
| 471 |
add_btn = gr.Button("💾 Save Box", variant="primary")
|
| 472 |
gr.Markdown("Example: X=120, Y=80, Width=180, Height=300.")
|
| 473 |
delete_index = gr.Number(label="Annotation # to delete", value=1, precision=0)
|
|
@@ -475,10 +478,10 @@ with gr.Blocks(title="Ice Cream Dataset + Counter") as demo:
|
|
| 475 |
clear_btn = gr.Button("Clear All Boxes")
|
| 476 |
annotations = gr.JSON(label="Saved annotations")
|
| 477 |
ann_msg = gr.Markdown()
|
| 478 |
-
image_select.change(refresh_editor, image_select, [
|
| 479 |
-
add_btn.click(add_annotation, [image_select, ann_class, x, y, w, h], [
|
| 480 |
-
delete_btn.click(remove_annotation, [image_select, delete_index], [
|
| 481 |
-
clear_btn.click(clear_annotations, image_select, [
|
| 482 |
|
| 483 |
with gr.Tab("3 · Training"):
|
| 484 |
gr.Markdown("### Train RT-DETR")
|
|
|
|
| 1 |
+
CLASSES = ['Carnavalita', 'Kimo-COno', 'Squizz', 'Oreo', 'Moro', 'Dulce', 'KitKat', 'Cadbury', 'Mega', 'other']
|
| 2 |
+
|
| 3 |
import io
|
| 4 |
import json
|
| 5 |
import os
|
|
|
|
| 170 |
return """<div class="anno-wrap">
|
| 171 |
<div class="anno-toolbar"><b>Draw boxes directly on the image</b><span>Click + drag + release = create box</span><span>Choose the class first</span></div>
|
| 172 |
<div class="anno-canvas-wrap"><canvas id="anno-canvas"></canvas></div>
|
| 173 |
+
<div class="anno-help" id="anno-hint">Drag from one corner of the object to the opposite corner, then release. The coordinates are filled automatically; click <b>Save Box</b>. Repeat for every object.</div>
|
| 174 |
</div>
|
| 175 |
<script>
|
| 176 |
(function(){
|
|
|
|
| 184 |
canvas.addEventListener('pointerdown',e=>{e.preventDefault();canvas.setPointerCapture(e.pointerId);const p=pos(e);sx=p.x;sy=p.y;drawing=true;current={x:sx,y:sy,w:0,h:0};redraw();});
|
| 185 |
canvas.addEventListener('pointermove',e=>{if(!drawing)return;const p=pos(e);current={x:Math.min(sx,p.x),y:Math.min(sy,p.y),w:Math.abs(p.x-sx),h:Math.abs(p.y-sy)};redraw();});
|
| 186 |
canvas.addEventListener('pointerup',e=>{if(!drawing)return;drawing=false;const p=pos(e),sc=+canvas.dataset.scale||1;const x=Math.min(sx,p.x)/sc,y=Math.min(sy,p.y)/sc,w=Math.abs(p.x-sx)/sc,h=Math.abs(p.y-sy)/sc;current=null;redraw();if(w<3||h<3)return;setField('anno-x',Math.round(x));setField('anno-y',Math.round(y));setField('anno-w',Math.round(w));setField('anno-h',Math.round(h));const hint=document.getElementById('anno-hint');if(hint)hint.textContent='Box created. Click “Save Box” to store it.';});
|
| 187 |
+
canvas.addEventListener('pointercancel',()=>{drawing=false;current=null;redraw();});
|
| 188 |
img.onload=fit;img.src=imgSrc;window.addEventListener('resize',fit);
|
| 189 |
})();</script>""" % (json.dumps(src), int(item["width"]), int(item["height"]), boxes)
|
| 190 |
|
|
|
|
| 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 |
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):
|
|
|
|
| 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():
|
|
|
|
| 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)
|
|
|
|
| 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")
|
training/classes.txt
CHANGED
|
@@ -1,10 +1,8 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
Cadbury
|
| 9 |
-
Mega
|
| 10 |
other
|
|
|
|
| 1 |
+
cornetto
|
| 2 |
+
magnum
|
| 3 |
+
correto
|
| 4 |
+
cone
|
| 5 |
+
cup
|
| 6 |
+
sandwich
|
| 7 |
+
stick
|
|
|
|
|
|
|
| 8 |
other
|