Counting 718,751 oil palms from aerial imagery
A commercial oil palm plantation in West Africa needed a tree-by-tree inventory of its estate. I built a pipeline that finds each palm crown in aerial imagery and turns it into a mapped, countable point.
- Palms detected
- 718,751
- Net area covered
- 12,166.6 ha
- Production blocks
- 26
- Average density
- 59.1 palms/ha
Enlarge
The question
How many palms does the plantation have, and where is each one? On an estate this size, manual counts are slow and they don't produce a map. The deliverable here is one mapped point per palm, block by block, that the plantation can query and check.
How it works
- ImageryAn RGB orthomosaic for each block
- Tiles1280-pixel chips with 256 pixels of overlap
- DetectionA YOLOv8 model with one class: oil palm
- MergeTile results joined and duplicates removed
- InventoryOne point per palm, in GeoPackage and CSV
The model learned from 4,041 crowns labelled by hand on 435 tiles from two blocks. The pilot block's tiles are a stratified sample, chosen so that weed-only, palm-only and mixed scenes are all covered. Training, validation and test sets were split 70/15/15 by spatial sector rather than by random tile, so the test score reflects ground the model had not seen.
Improving the model in production
As blocks came in, two failure patterns showed up: dense mature canopy, where neighbouring crowns run together, and young palms that are small and easy to miss. I labelled examples of both from a second block and retrained. The released model is the result of 9 training iterations, each compared against the same held-out test set. Compared with the earlier baseline, it finds more of the palms and makes fewer false detections, so neither gain came at the cost of the other.
How accurate it is
I scored the released model on 79 test tiles holding 716 hand-labelled palms, from ground it never trained on, and ran the earlier baseline on the same tiles.
| Released model | Earlier baseline | |
|---|---|---|
| Precision: share of detections that are real palms | 0.845 | 0.727 |
| Recall: share of real palms found | 0.774 | 0.637 |
| Palms counted in tile interiors (hand count: 518) | 519 | 454 |
| Difference from the hand count | +0.2% | −12.4% |
The count uses tile interiors because the pipeline's tiles overlap: a crown cut off at one tile's edge is seen whole in the next tile, and the duplicates are merged. The baseline fell short mostly in dense canopy and young palms, and retraining on those cases closed most of the gap. The full evaluation is in the model card.
Making the count add up
Blocks were delivered one at a time, and many of them overlapped. A check of every pair found 5,194.7 ha of shared coverage across 51 block pairs. Adding up each block's count on its own would have counted the palms in those zones two or three times.
I resolved it with one fixed rule: shared ground belongs to the block delivered first, and later blocks drop their detections inside that overlap. Every hectare is counted once.
Where elevation data was available (5 of 26 blocks), a canopy-height check flags detections that are implausibly short and probably not palms. It flags them for review; it does not delete them.
What it doesn't claim
- Overlaps were resolved with each block's rectangular extent, not surveyed property boundaries. Block totals could shift if true boundaries are applied. The plantation total should not change much, because the overlap is real imagery overlap.
- A few blocks keep only a thin sliver of ground after the overlap rule, so their palms-per-hectare figure looks very low. Those figures need a field check before anyone quotes them.
- The interior count is close partly because errors cancel out: about 100 labelled palms are missed, and about as many detections have no matching label. By block, the differences were +3.2% and −4.3%. Totals over many palms hold up; not every single detection is right.
- These are checks on held-out tiles, not a field count over a whole block. The plantation's palms are mostly young, and the model hasn't been measured on other plantations or on tall mature palms with heavily overlapping crowns.
What the plantation received
- A GeoPackage per block with two layers: crown outlines, and one point per palm with an ID, confidence score and crown area.
- A plantation-wide master inventory that combines every block.
- A runbook for processing new blocks without retraining, unless a new failure pattern appears.
Open code and model
With the plantation owner's permission, the code and the trained model are public under the AGPL-3.0 licence. The imagery and labels stay with the plantation. The model can also be exported to ONNX and run in QGIS through the Deepness plugin.
From the field
Photos and video from the estate. I removed location data from every file.



