AI for Drone Aerial Photo Analysis

We design and deploy artificial intelligence systems: from prototype to production-ready solutions. Our team combines expertise in machine learning, data engineering and MLOps to make AI work not in the lab, but in real business.
Showing 1 of 1 servicesAll 1566 services
AI for Drone Aerial Photo Analysis
Complex
~1-2 weeks
FAQ
AI Development Areas
AI Solution Development Stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1212
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Website development for BELFINGROUP
    852
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_logo-advance_0.png
    B2B Advance company logo design
    561
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822

AI-анализ аэрофотоснимков с дронов

Анализ данных с БПЛА отличается от работы с обычными фотографиями несколькими фундаментальными особенностями: большие ортофотопланы (5–20 Гпкс), нестандартный GSD (1–10 см/пиксель), мультиспектральные и тепловые каналы, необходимость геопривязки результатов в WGS-84 или локальной CRS. Стандартный пайплайн YOLOv8 → inference → результаты здесь не работает без предварительного тайлинга с учётом наземного разрешения.

Тайлинг и геопривязка — ключевой этап

Типичная ошибка: нарезка ортофото в пикселях без учёта GSD. При GSD 2 см тайл 640×640 пикселей = 12.8×12.8 м наземной площади. При GSD 8 см тот же тайл — уже 51×51 м. Модель, обученная на одном масштабе, даст на другом mAP на 15–25% ниже.

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]:
    """
    Нарезка ортофото по наземному размеру тайла.
    Гарантирует постоянный масштаб независимо от GSD.
    """
    with rasterio.open(ortho_path) as src:
        gsd = abs(src.transform.a)          # метров/пиксель
        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_ground_m=10 критично: объекты на границах тайлов детектируются в обоих, затем NMS по IoU убирает дубли. Без перекрытия теряется ~12% объектов на стыках.

SAHI для мелких объектов

При детекции людей или машин на ортофото GSD 3–5 см объект занимает 30–80 пикселей — значительно меньше рецептивного поля YOLO, оптимизированного под 640px. SAHI (Slicing Aided Hyper Inference) решает это нарезкой с перекрытием и NMS по всем предсказаниям:

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 → координаты в пикселях тайла

На датасете подсчёта людей на стройплощадке (5000 аннотированных снимков, GSD 4 см): без SAHI [email protected] = 0.61, с SAHI — 0.84. Разница принципиальная.

Трансформация координат и GeoJSON-вывод

Результаты анализа должны быть в геодезических координатах — иначе это просто картинки, не интегрируемые в ГИС-системы.

import pyproj
from shapely.geometry import box, mapping
import json

def detections_to_geojson(
    detections: list,
    tile_bounds: tuple,           # (left, bottom, right, top) в CRS
    tile_px_size: tuple,          # (width, height) в пикселях
    src_crs: str = 'EPSG:32637'   # UTM зона для проекта
) -> 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']
        # Пиксели → проекционные координаты
        geo_left   = left + x1 * scale_x
        geo_right  = left + x2 * scale_x
        geo_top    = top  - y1 * scale_y
        geo_bottom = top  - y2 * scale_y
        # Проекция → 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}

Тепловой канал (FLIR)

Мультироторные БПЛА с тепловизорами DJI Zenmuse H20T или FLIR Vue Pro дают 16-битные TIFF с температурными значениями. Детекция горячих точек на солнечных панелях — типичный кейс:

import numpy as np

def detect_pv_hotspots(
    thermal_kelvin: np.ndarray,    # (H, W), значения в 0.01K
    panel_mask: np.ndarray,        # бинарная маска панелей
    delta_threshold: float = 10.0  # °C выше медианы панели
) -> list:
    """
    Hotspot detection на солнечных панелях.
    IEC 62446-3: дефект при ΔT > 10°C от референса.
    """
    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)

На реальном проекте инспекции СЭС (142 панели, 3 полёта) система выявила 17 дефектных панелей с ΔT > 15°C, которые пропустила ручная визуальная проверка. ROI окупился за один сезон.

Metrics по типам задач

Задача GSD Модель Типичный [email protected]
Подсчёт деревьев 3–5 см YOLOv8m + SAHI 0.88–0.93
Детекция людей на стройке 4–6 см YOLOv8l + SAHI 0.81–0.87
Дефекты ЛЭП 1–2 см RT-DETR-L 0.79–0.85
Инспекция СЭС (тепло) 5–10 см threshold + SAM 95%+ recall
Прогресс строительства 5–10 см SegFormer-B4 IoU 0.84–0.91

Сроки

Задача Срок
Детектор одного класса (готовые данные) 3–5 недель
Полная инспекционная система + ГИС-интеграция 8–14 недель
Мультисенсорная платформа RGB + thermal 14–22 недели