149 lines
4.0 KiB
Markdown
149 lines
4.0 KiB
Markdown
|
|
# Custom Annotation GUI
|
|||
|
|
|
|||
|
|
A simple, **fully customizable** annotation tool built with Gradio (pure Python).
|
|||
|
|
|
|||
|
|
## Features
|
|||
|
|
|
|||
|
|
✅ **Auto-labeling** with your trained RF-DETR model
|
|||
|
|
✅ **Manual annotation** by entering box coordinates
|
|||
|
|
✅ **Edit/delete** annotations easily
|
|||
|
|
✅ **Navigation** between images
|
|||
|
|
✅ **Export** to COCO JSON format
|
|||
|
|
✅ **100% Python** - easy to modify and extend
|
|||
|
|
|
|||
|
|
## Quick Start
|
|||
|
|
|
|||
|
|
### 1. Install dependencies
|
|||
|
|
```bash
|
|||
|
|
/home/dillon/_code/saw_mill_knot_detection/.venv/bin/python -m pip install gradio>=4.0.0
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Run the GUI
|
|||
|
|
|
|||
|
|
**With auto-labeling (requires trained model):**
|
|||
|
|
```bash
|
|||
|
|
/home/dillon/_code/saw_mill_knot_detection/.venv/bin/python annotation_gui.py \
|
|||
|
|
--images-dir /path/to/images \
|
|||
|
|
--model-weights runs/knot_rfdetr_medium/checkpoint_best_total.pth
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Manual annotation only:**
|
|||
|
|
```bash
|
|||
|
|
/home/dillon/_code/saw_mill_knot_detection/.venv/bin/python annotation_gui.py \
|
|||
|
|
--images-dir /path/to/images
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Open in browser
|
|||
|
|
Opens automatically at http://localhost:7860
|
|||
|
|
|
|||
|
|
## Usage
|
|||
|
|
|
|||
|
|
1. **Auto-Label**: Click "🤖 Auto-Label" to detect knots with your model
|
|||
|
|
2. **Adjust threshold**: Lower = more detections, Higher = only confident ones
|
|||
|
|
3. **Manual boxes**: Enter coordinates (x1, y1, x2, y2) and click "➕ Add Box"
|
|||
|
|
4. **Delete mistakes**: Click "🗑️ Delete Last" to remove last box
|
|||
|
|
5. **Navigate**: Use "Previous" / "Next" buttons
|
|||
|
|
6. **Export**: Click "💾 Export COCO" when done
|
|||
|
|
|
|||
|
|
## Customization Examples
|
|||
|
|
|
|||
|
|
### Add keyboard shortcuts
|
|||
|
|
```python
|
|||
|
|
# In create_ui(), add:
|
|||
|
|
image_display.keyboard_shortcuts = {
|
|||
|
|
"d": delete_btn.click, # Press 'd' to delete
|
|||
|
|
"n": next_btn.click, # Press 'n' for next
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Add interactive drawing
|
|||
|
|
```python
|
|||
|
|
# Replace manual coordinates with image annotator:
|
|||
|
|
from gradio_image_annotation import image_annotator
|
|||
|
|
|
|||
|
|
annotator = image_annotator(
|
|||
|
|
label="Draw boxes",
|
|||
|
|
type="numpy"
|
|||
|
|
)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Change box colors by confidence
|
|||
|
|
```python
|
|||
|
|
# In draw_boxes_on_image():
|
|||
|
|
color = "green" if conf > 0.8 else "yellow" if conf > 0.5 else "red"
|
|||
|
|
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Add multiple label classes
|
|||
|
|
```python
|
|||
|
|
# Add a dropdown:
|
|||
|
|
label_choice = gr.Dropdown(
|
|||
|
|
choices=["knot", "crack", "hole"],
|
|||
|
|
value="knot",
|
|||
|
|
label="Label Type"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
# Update box dict:
|
|||
|
|
box = {
|
|||
|
|
"bbox": [x1, y1, x2, y2],
|
|||
|
|
"label": label_choice_value, # from the dropdown
|
|||
|
|
"confidence": 1.0
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Save checkpoints automatically
|
|||
|
|
```python
|
|||
|
|
# In _save_annotations(), add:
|
|||
|
|
import shutil
|
|||
|
|
backup_path = self.ann_file.with_suffix('.backup.json')
|
|||
|
|
shutil.copy(self.ann_file, backup_path)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Add image filters/preprocessing
|
|||
|
|
```python
|
|||
|
|
# Add before annotation:
|
|||
|
|
def preprocess_image(img: Image.Image) -> Image.Image:
|
|||
|
|
from PIL import ImageEnhance
|
|||
|
|
enhancer = ImageEnhance.Contrast(img)
|
|||
|
|
return enhancer.enhance(1.5) # Increase contrast
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## File Structure
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
annotation_gui.py
|
|||
|
|
├── AnnotationApp # Main logic (easy to extend)
|
|||
|
|
│ ├── auto_label_current() # Modify for different models
|
|||
|
|
│ ├── add_box_manual() # Customize annotation format
|
|||
|
|
│ ├── export_to_coco() # Change export format
|
|||
|
|
│ └── draw_boxes_on_image() # Customize visualization
|
|||
|
|
└── create_ui() # Gradio interface (add components)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## Advantages vs Label Studio
|
|||
|
|
|
|||
|
|
| Feature | Custom GUI | Label Studio |
|
|||
|
|
|---------|-----------|--------------|
|
|||
|
|
| **Modify code** | ✅ Easy (pure Python) | ❌ Complex (React + Python) |
|
|||
|
|
| **Add features** | ✅ ~10-50 lines | ❌ Hundreds of lines |
|
|||
|
|
| **Custom models** | ✅ Direct integration | ⚠️ Need ONNX export |
|
|||
|
|
| **Learning curve** | ✅ Simple Gradio | ⚠️ Larger codebase |
|
|||
|
|
| **Setup** | ✅ pip install | ⚠️ Docker/complex |
|
|||
|
|
|
|||
|
|
## Troubleshooting
|
|||
|
|
|
|||
|
|
**Port already in use:**
|
|||
|
|
```bash
|
|||
|
|
python annotation_gui.py --images-dir /path --port 7861
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Model not loading:**
|
|||
|
|
- Check the weights path exists
|
|||
|
|
- Verify it's a valid checkpoint file
|
|||
|
|
- Try without `--model-weights` for manual-only mode
|
|||
|
|
|
|||
|
|
**Need more features?**
|
|||
|
|
- Check Gradio docs: https://www.gradio.app/docs/
|
|||
|
|
- Add custom components easily
|
|||
|
|
- Fork and modify the code freely!
|