# cornvision

Offline corn kernel detection and row counting from ear images.

![CornVision demo](demo.png)

## Installation

```bash
pip install -e .
```

Model weights are downloaded automatically from Hugging Face on first run and cached at `~/.cornvision/model.pth`.

## Usage

### GUI

```bash
cornvision
```

The GUI supports both single and batch inference:

| Control | Description |
|---|---|
| **Add Images…** | Add one or more images to the queue |
| **Remove / Clear All** | Remove selected or all images from the queue |
| **Device** | Select inference device (`auto`, `cpu`, `cuda`, `mps`) |
| **Analyze All** | Run inference on all pending images in sequence |
| **Stop** | Halt the batch after the current image finishes |
| **Save Annotated…** | Save the annotated image for the selected result |
| **Export CSV…** | Export all completed results to a CSV file |

Queue items are colour-coded: `○` pending, `↻` running, `✓` done, `✗` error.

### Python API

```python
from cornvision import CornAnalyzer

analyzer = CornAnalyzer()          # loads model, downloads weights if needed
result = analyzer.analyze("corn.jpg")

print(result.total_kernels)        # int
print(result.total_rows)           # int
print(result.kernels_per_row)      # list[int]
print(result.inference_time_sec)   # float

result.save_viz("annotated.png")   # save annotated image to disk
```

`CornAnalyzer` loads the model once and can analyze multiple images:

```python
for path in image_paths:
    result = analyzer.analyze(path)
```

#### `CornAnalyzer(device="auto")`

| Parameter | Description |
|---|---|
| `device` | `"auto"` (default) picks CUDA → MPS → CPU automatically |

#### `AnalysisResult` fields

| Field | Type | Description |
|---|---|---|
| `total_kernels` | `int` | Total detected kernels |
| `total_rows` | `int` | Number of kernel rows |
| `kernels_per_row` | `list[int]` | Kernel count per row |
| `inference_time_sec` | `float` | Wall-clock inference time |
| `average_kernel_width_mm` | `float` | Estimated mean kernel width |
| `average_kernel_height_mm` | `float` | Estimated mean kernel height |
| `viz_image` | `PIL.Image \| None` | Annotated image (in-memory) |

## Dependencies

| Package | Purpose |
|---|---|
| `torch` / `torchvision` | Model inference |
| `Pillow` | Image I/O and visualization |
| `numpy` | Array operations |
| `requests` | Weight download |

`tkinter` is used for the GUI and ships with the standard Python installer.

## Project layout

```
src/cornvision/
├── __init__.py       # public API: CornAnalyzer, AnalysisResult
├── _gui.py           # tkinter GUI (entry point)
├── _analyzer.py      # CornAnalyzer — model load + analyze()
├── _inference.py     # end-to-end inference pipeline
├── _clustering.py    # kernel row clustering
├── _image_proc.py    # image pre/post-processing helpers
├── _model.py         # model architecture + weight loading
├── _download.py      # weight download + cache path
├── _config.py        # shared constants
└── _result.py        # AnalysisResult dataclass
```
