File size: 11,244 Bytes
16adf30
04baed2
 
 
 
 
 
 
 
 
 
 
 
16adf30
04baed2
 
9b86d83
 
 
 
 
f61c93c
 
9b86d83
91ea409
9b86d83
 
 
 
380e4f0
04baed2
 
 
 
 
 
 
 
 
9b86d83
04baed2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ee469c9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
04baed2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
---
license: agpl-3.0
library_name: ultralytics
pipeline_tag: object-detection
tags:
  - yolo
  - ultralytics
  - object-detection
  - agriculture
  - livestock
  - cattle
  - estrus-detection
  - computer-vision
---

# Cowcatcher
<p align="left">
  <a href="https://jacobsfarm.github.io/website/"><img src="https://img.shields.io/badge/Website-cowcatcherai.com-blue?logo=googlechrome&logoColor=white" alt="Website"></a>
  <a href="https://huggingface.co/spaces/CowcatcherAI/CowCatcherAI"><img src="https://img.shields.io/badge/πŸ€—%20Hugging%20Face-Spaces-yellow" alt="Hugging Face"></a>
  <a href="https://t.me/+SphG4deaWVNkYTQ8"><img src="https://img.shields.io/badge/Telegram-Join%20Chat-2CA5E0?logo=telegram&logoColor=white" alt="Telegram"></a>
  <a href="https://github.com/eschouten/ai-detector"><img src="https://img.shields.io/badge/GitHub-AI--Detector-181717?logo=github&logoColor=white" alt="AI-Detector Repo"></a>
  <a href="https://github.com/CowCatcherAI/CowCatcherAI"><img src="https://img.shields.io/badge/GitHub-CowCatcherAI-181717?logo=github&logoColor=white" alt="CowCatcherAI Repo"></a>
  <a href="https://github.com/CowCatcherAI/CalvingCatcherAI"><img src="https://img.shields.io/badge/GitHub-CalvingCatcherAI-181717?logo=github&logoColor=white" alt="CalvingCatcherAI Repo"></a>
</p>

### 
Check out the website for all the information: [cowcatcherai.com](https://jacobsfarm.github.io/website/)
**The website is available in multiple languages such as Deutsch, FranΓ§ais, Nederlands, and EspaΓ±ol.**

Cowcatcher is a vision model that detects **mounting behaviour** in cattle from camera images.
Mounting is one of the clearest visual signals that a cow is in heat, so a reliable detector lets a barn
camera flag oestrus automatically instead of relying on someone watching the herd.

All models are single-class (`mounting`) Ultralytics models, trained at 640 px on farm camera footage
from multiple dairy and beef sites, in daylight, night/IR and robot-barn conditions.

**Latest and best model: `cowcatcherV17.2.pt`.** It is the only model in the comparison that produces
**zero false alarms** on the null set, at any threshold from 0.50 upward.


---

## Available models

| File | Task | Base | Train imgsz / epochs | Size | Notes |
|---|---|---|---|---|---|
| **`cowcatcherV17.2.pt`** | Detection | `yolo26m` | 640 / 50 | 44 MB | **Recommended.** Best precision and best confidence separation |
| `cowcatcherV17.pt` | Detection | `yolo26m` | 640 / 400 | 131 MB | Previous main model, higher recall at low thresholds |
| `cowcatcherV16.pt` | Detection | `yolo26m` | 640 / 400 | 88 MB | Highest recall at 0.88, but 21 false alarms |
| `cowcatcherV15.pt` | Detection | `yolo12m` | 640 / 100 | 81 MB | Older generation |
| `cowcatcherV14.pt` | Detection | `yolo12m` | 640 / 100 | 41 MB | Older generation, very few false alarms but low recall |
| `cowcatcherV13.pt` | Detection | `yolo12m` | 640 / 100 | 81 MB | Oldest published model, kept for reference |
| `cowcatcherV1-seg.pt` | Segmentation | `yolo11m` | 640 / 100 | 45 MB | Experimental mask model, not part of the benchmark below |

All six detection models are published here and all six appear in the benchmark tables below.

---

## Quick start

```bash
pip install ultralytics
```

```python
from ultralytics import YOLO

model = YOLO("cowcatcherV17.2.pt")

# Single image
results = model.predict("barn_frame.jpg", conf=0.50)

for r in results:
    for box in r.boxes:
        print("mounting", float(box.conf), box.xyxy.tolist())
```

```python
# Live camera / RTSP stream
for result in model.predict(source="rtsp://camera/stream", stream=True, conf=0.50):
    if len(result.boxes):
        print("Mounting detected")
```

### Which confidence threshold?

For `cowcatcherV17.2` the false-positive count stays at **0** across the entire 0.50–0.99 sweep, so the
threshold only trades away recall. F1 peaks at **0.50**; anything up to ~0.80 is still a sensible
operating point if you want extra headroom.

| Threshold | Found (TP) | Missed (FN) | False alarms | Recall | Precision | F1 |
|---|---|---|---|---|---|---|
| **0.50** | 896 | 4 | 0 | 99.56% | 100% | **99.78%** |
| 0.60 | 892 | 8 | 0 | 99.11% | 100% | 99.55% |
| 0.70 | 879 | 21 | 0 | 97.67% | 100% | 98.82% |
| 0.80 | 869 | 31 | 0 | 96.56% | 100% | 98.25% |
| 0.88 | 822 | 78 | 0 | 91.33% | 100% | 95.47% |
| 0.92 | 638 | 262 | 0 | 70.89% | 100% | 82.96% |
| 0.95 | 125 | 775 | 0 | 13.89% | 100% | 24.39% |

---

## Training

Every model in this repository was trained with the same script ([`train.py`](train.py)) β€” only the base
checkpoint and the epoch count change per version.

```python
from ultralytics import YOLO

# Load model
model = YOLO("yolo26m.pt")

# Train with added augmentation
model.train(
    data="dataset.yaml",
    imgsz=640,
    batch=16,
    epochs=50,
    patience=10,
    #fraction=0.05, # currently uses 100% of the dataset; 0.8 = 80% of the set
    save_period=5,
    workers=0,
    device=0,
    #close_mosaic=10,

    # Augmentation settings
    augment=True,      # General augmentation on/off
    degrees=10,        # Rotation up to 10 degrees
    translate=0.1,     # Translation up to 10%
    scale=0.5,         # Scaling between 0.5 and 1.5
    fliplr=0.5,        # 50% chance of horizontal flip
    hsv_h=0.015,       # Minor hue variations
    hsv_s=0.7,         # Saturation variations
    hsv_v=0.4,         # Brightness variations
    mosaic=1.0         # Mosaic augmentation (combines 4 images)
)
```

Per-version settings β€” everything else in the script stays identical:

| Model | Base checkpoint | Epochs |
|---|---|---|
| `cowcatcherV17.2.pt` | `yolo26m.pt` | 50 |
| `cowcatcherV17.pt` | `yolo26m.pt` | 400 |
| `cowcatcherV16.pt` | `yolo26m.pt` | 400 |
| `cowcatcherV15.pt` | `yolo12m.pt` | 100 |
| `cowcatcherV14.pt` | `yolo12m.pt` | 100 |
| `cowcatcherV13.pt` | `yolo12m.pt` | 100 |
| `cowcatcherV1-seg.pt` | `yolo11m-seg.pt` | 100 |

`dataset.yaml` points at the single-class (`mounting`) farm-camera dataset, which is not published here.
`patience=10` stops a run early once validation stops improving, so the effective epoch count can be
lower than the number listed. Training ran on a single GPU (`device=0`), with `workers=0` for Windows
compatibility.

---

## Benchmark

**Test set:** 1000 held-out images β€” 900 real mounting events and 100 null/noise images (cattle, barns
and robots without mounting). Six models evaluated on 2026-07-23.

Of the 900 mounting events, 11 are found by *no* model at 0.88, and 536 are missed by at least one model.

### Performance at threshold 0.88

| Model | Recall % | Precision % | F1 % | False alarms | Missed | Accuracy % | ms/image |
|---|---|---|---|---|---|---|---|
| **cowcatcherV17.2** | 91.33 | **100.00** | 95.47 | **0** | 78 | 92.2 | 24.1 |
| cowcatcherV16 | **96.33** | 97.64 | **96.98** | 21 | 33 | 94.6 | 24.4 |
| cowcatcherV17 | 86.89 | 99.11 | 92.60 | 7 | 118 | 87.5 | 24.3 |
| cowcatcherV15 | 74.44 | 97.95 | 84.60 | 14 | 230 | 75.6 | 27.3 |
| cowcatcherV14 | 65.00 | 99.66 | 78.68 | 2 | 315 | 68.3 | 27.4 |
| cowcatcherV13 | 50.11 | 99.34 | 66.62 | 3 | 449 | 54.8 | 29.0 |

Note that 0.88 is a legacy production threshold. It suits V16, but it sits far above the optimum for
V17.2 β€” see the next table.

### Best achievable operating point (max F1)

| Model | Best threshold | Found | Missed | False alarms | Recall % | Precision % | F1 % |
|---|---|---|---|---|---|---|---|
| **cowcatcherV17.2** | 0.50 | 896 | 4 | **0** | 99.56 | **100.00** | **99.78** |
| cowcatcherV17 | 0.50 | 897 | 3 | 14 | 99.67 | 98.46 | 99.06 |
| cowcatcherV14 | 0.50 | 886 | 14 | 24 | 98.44 | 97.36 | 97.90 |
| cowcatcherV16 | 0.80 | 891 | 9 | 33 | 99.00 | 96.43 | 97.70 |
| cowcatcherV15 | 0.61 | 894 | 6 | 41 | 99.33 | 95.61 | 97.44 |
| cowcatcherV13 | 0.50 | 867 | 33 | 24 | 96.33 | 97.31 | 96.82 |

### Confidence separation

The gap between the mean confidence on real mounts and on null images decides how easy the model is to
threshold. V17.2 is in a different class here: it is almost silent on noise (mean 0.008) while staying
confident on real events (mean 0.918).

| Model | Avg conf β€” real mounts | Avg conf β€” null noise | Margin |
|---|---|---|---|
| **cowcatcherV17.2** | 0.9180 | **0.0079** | **0.9101** |
| cowcatcherV17 | 0.9026 | 0.1469 | 0.7557 |
| cowcatcherV14 | 0.8718 | 0.2215 | 0.6503 |
| cowcatcherV13 | 0.8409 | 0.2250 | 0.6159 |
| cowcatcherV16 | 0.9316 | 0.4190 | 0.5126 |
| cowcatcherV15 | 0.8893 | 0.3875 | 0.5018 |

The same effect shows in the safety margin (5th percentile of real mounts minus 95th percentile of
nulls). Every other model is negative β€” its distributions overlap β€” while V17.2 reaches **+0.827**:

| Model | Lowest conf on a real mount | Highest conf on a null | P05 mounts | P95 nulls | Safety margin |
|---|---|---|---|---|---|
| **cowcatcherV17.2** | 0.1558 | 0.4382 | 0.8450 | 0.0176 | **+0.8274** |
| cowcatcherV16 | 0.0051 | 0.9304 | 0.8866 | 0.9130 | βˆ’0.0264 |
| cowcatcherV17 | 0.4547 | 0.9107 | 0.8251 | 0.8907 | βˆ’0.0656 |
| cowcatcherV15 | 0.4584 | 0.9060 | 0.8194 | 0.8965 | βˆ’0.0772 |
| cowcatcherV14 | 0.0004 | 0.8925 | 0.7553 | 0.8407 | βˆ’0.0854 |
| cowcatcherV13 | 0.0001 | 0.8995 | 0.6154 | 0.8643 | βˆ’0.2489 |

### Confidence distribution on null images

Number of null images falling in each confidence bin β€” lower and further left is better.

| Bin | V13 | V14 | V15 | V16 | V17 | **V17.2** |
|---|---|---|---|---|---|---|
| 0.00–0.20 | 68 | 70 | 51 | 47 | 79 | **99** |
| 0.20–0.60 | 11 | 6 | 6 | 7 | 9 | **1** |
| 0.60–0.86 | 15 | 19 | 28 | 17 | 4 | **0** |
| 0.86–0.94 | 6 | 5 | 15 | 29 | 8 | **0** |
| 0.94–1.00 | 0 | 0 | 0 | 0 | 0 | **0** |

---

## Choosing a model

- **Default choice β€” `cowcatcherV17.2`.** Best F1, zero false alarms, widest usable threshold window.
  Pick this unless you have a specific reason not to.
- **`cowcatcherV16`** still has the highest raw recall at the legacy 0.88 threshold, but pays for it with
  21 false alarms out of 100 nulls and heavily overlapping confidence distributions.

## Limitations

- Single class only: the model detects mounting, not cow identity, lameness or other behaviours.
- Trained and evaluated on fixed barn cameras. Very different mounting angles, heavy occlusion or
  unusual camera placements are under-represented.
- The 100-image null set is small; a false-positive rate of 0 on it should be read as "no false alarms
  observed", not as a guarantee.
- Benchmark timings (24–29 ms/image) were measured on the evaluation machine and will differ on other
  hardware.

## License

**AGPL-3.0.**

These weights are trained with [Ultralytics](https://github.com/ultralytics/ultralytics), which is
licensed under AGPL-3.0. Every checkpoint in this repository carries the Ultralytics AGPL-3.0 notice in
its metadata, so the derived weights inherit that license.

In practice this means that if you use these models in a network-accessible service, AGPL-3.0 requires
you to make the corresponding source of that service available to its users. If that does not work for
your deployment, Ultralytics sells a commercial Enterprise License that removes the copyleft obligation β€”
see [ultralytics.com/license](https://www.ultralytics.com/license).