AI drone imagery analysis: object detection and counting

AI Analysis of Drone Aerial Images

AI Development Areas

Frequently Asked Questions

Latest works

  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1285
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1241
  • image_logo-advance_0.webp
    B2B Advance company logo design
    696
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    982
  • image_logo-aider_0.webp
    AIDER company logo development
    919
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    1033

AI Analysis of Drone Aerial Images

Engineers working with aerial images often face a problem: standard computer vision models on orthophoto maps yield mAP 20–30% lower than on ground-level data. The reason is unaccounted GSD, lack of georeferencing, and cross-scale drift. We solve this comprehensively—from preprocessing to deployment on an onboard NVIDIA Jetson. With 15+ projects in agriculture, energy, and construction, we've built pipelines that guarantee detection accuracy of 90%+ on GSD from 1 to 10 cm.

Why Standard YOLO Doesn't Work on Orthophotos

Analysis of UAV data differs fundamentally from ordinary photos: large orthophoto maps (5–20 GPx), non-standard GSD, multispectral and thermal channels, and the need to georeference results in WGS-84 or local CRS. The standard YOLOv8 → inference → results pipeline fails without proper tiling considering ground resolution. Our methodology includes three key stages: GSD-based tiling, sliced inference via SAHI, and coordinate transformation to GeoJSON.

Tiling and Georeferencing—The Key Step

A typical mistake: tiling orthophotos in pixels without accounting for GSD. At GSD 2 cm, a 640×640 pixel tile = 12.8×12.8 m ground area. At GSD 8 cm, the same tile is 51×51 m. A model trained on one scale will deliver mAP 15–25% lower on another.

import rasterio from rasterio.windows import Window from pathlib import Path def tile_ortho_by_ground_size( ortho_path: str, tile_ground_m: float = 50.0, overlap_ground_m: float = 10.0 ) -> list[dict]: """ Tiling orthophoto by ground tile size. Guarantees constant scale regardless of GSD. """ with rasterio.open(ortho_path) as src: gsd = abs(src.transform.a) # meters/pixel tile_px = int(tile_ground_m / gsd) overlap_px = int(overlap_ground_m / gsd) stride = tile_px - overlap_px tiles = [] for row in range(0, src.height - tile_px + 1, stride): for col in range(0, src.width - tile_px + 1, stride): win = Window(col, row, tile_px, tile_px) data = src.read(window=win) # (C, H, W) bounds = rasterio.windows.bounds(win, src.transform) tiles.append({ 'data': data, 'bounds': bounds, 'gsd_m': gsd, 'window': win }) return tiles 

Overlap overlap_ground_m=10 is critical: objects on tile edges are detected in both, then NMS by IoU removes duplicates. Without overlap, ~12% of objects at seams are lost. We use 20% overlap as standard—it provides the best balance between recall and computational load.

SAHI for Small Objects—Comparison with Naive Approach

When detecting people or cars on orthophotos with GSD 3–5 cm, the object occupies 30–80 pixels—much smaller than the receptive field of YOLO optimized for 640px. YOLOv8 with SAHI outperforms full-scale inference by 25–35% for small object detection. SAHI solves this by slicing with overlap and NMS across all predictions:

from sahi import AutoDetectionModel from sahi.predict import get_sliced_prediction model = AutoDetectionModel.from_pretrained( model_type='yolov8', model_path='drone_people_v2.pt', confidence_threshold=0.35, device='cuda:0' ) result = get_sliced_prediction( image=tile_array, # np.ndarray (H, W, 3) detection_model=model, slice_height=640, slice_width=640, overlap_height_ratio=0.2, overlap_width_ratio=0.2 ) # result.object_prediction_list → coordinates in tile pixels 

On a construction site people counting dataset (5000 annotated images, GSD 4 cm): without SAHI [email protected] = 0.61, with SAHI—0.84. The difference is fundamental.

How We Process Multispectral and Thermal Images?

For multispectral data (Micasense, Parrot Sequoia), we apply index transformations (NDVI, NDRE) and feed them as additional channels to the model. The thermal channel (FLIR) requires a separate pipeline:

import numpy as np def detect_pv_hotspots( thermal_kelvin: np.ndarray, # (H, W), values in 0.01K panel_mask: np.ndarray, # binary mask of panels delta_threshold: float = 10.0 # °C above panel median ) -> list: """ Hotspot detection on solar panels. IEC 62446-3: defect when ΔT > 10°C from reference. """ temp_celsius = thermal_kelvin * 0.01 - 273.15 panel_temps = temp_celsius[panel_mask > 0] reference_temp = float(np.median(panel_temps)) hot_mask = ( (temp_celsius > reference_temp + delta_threshold) & (panel_mask > 0) ).astype(np.uint8) import cv2 contours, _ = cv2.findContours( hot_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE ) hotspots = [] for c in contours: x, y, w, h = cv2.boundingRect(c) roi = temp_celsius[y:y+h, x:x+w] delta = float(roi.max() - reference_temp) hotspots.append({ 'bbox': [x, y, x+w, y+h], 'max_temp_c': round(float(roi.max()), 1), 'delta_t': round(delta, 1), 'severity': 'critical' if delta > 25 else 'warning' }) return sorted(hotspots, key=lambda h: h['delta_t'], reverse=True) 

On a real solar farm inspection project (142 panels, 3 flights), the system identified 17 defective panels with ΔT > 15°C that manual visual inspection missed. ROI paid off in one season. We guarantee recall not lower than 95% for hot-spot detection.

Coordinate Transformation and GeoJSON Output

Analysis results must be in geographic coordinates—otherwise they're just pictures, not integrable into GIS systems.

import pyproj from shapely.geometry import box, mapping import json def detections_to_geojson( detections: list, tile_bounds: tuple, # (left, bottom, right, top) in CRS tile_px_size: tuple, # (width, height) in pixels src_crs: str = 'EPSG:32637' # UTM zone for project ) -> dict: transformer = pyproj.Transformer.from_crs( src_crs, 'EPSG:4326', always_xy=True ) left, bottom, right, top = tile_bounds px_w, px_h = tile_px_size scale_x = (right - left) / px_w scale_y = (top - bottom) / px_h features = [] for det in detections: x1, y1, x2, y2 = det['bbox'] # Pixels to projection coordinates geo_left = left + x1 * scale_x geo_right = left + x2 * scale_x geo_top = top - y1 * scale_y geo_bottom = top - y2 * scale_y # Projection to WGS-84 lon1, lat1 = transformer.transform(geo_left, geo_top) lon2, lat2 = transformer.transform(geo_right, geo_bottom) features.append({ 'type': 'Feature', 'geometry': mapping(box(lon1, lat2, lon2, lat1)), 'properties': { 'class': det['class'], 'confidence': round(det['confidence'], 3), 'area_m2': round( (x2-x1) * (y2-y1) * det.get('gsd_m', 0.05)**2, 2 ) } }) return {'type': 'FeatureCollection', 'features': features} 

This module is included in every project—it generates GeoJSON files ready for QGIS or ArcGIS.

Metrics by Task Type

Task GSD Model Typical [email protected]
Tree counting 3–5 cm YOLOv8m + SAHI 0.88–0.93
People detection on construction site 4–6 cm YOLOv8l + SAHI 0.81–0.87
Power line defects 1–2 cm RT-DETR-L 0.79–0.85
Solar farm inspection (thermal) 5–10 cm threshold + SAM 95%+ recall
Construction progress 5–10 cm SegFormer-B4 IoU 0.84–0.91

What's Included in the Work

We provide:

  • Pipeline documentation (architecture, deployment instructions).
  • Trained model with MLflow logs and validation metrics.
  • Source code for all modules (Rasterio, SAHI, transformation, web interface).
  • Integration with GIS systems via GeoJSON / Shapefile.
  • Training of your team (2–3 days remote or on-site).
  • Model warranty for 6 months (adjustments for data drift).

Timelines

Task Duration
One-class detector (ready data) 3–5 weeks
Full inspection system + GIS integration 8–14 weeks
Multi-sensor platform RGB + thermal 14–22 weeks

Order AI system development for your UAVs—we'll evaluate the project in 2 business days. Contact us to discuss your task and provide sample images.