# 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!
