Радиолог тратит 20–40 минут на анализ одного КТ-исследования с сотнями срезов. При нагрузке 30 исследований в день усталость накапливается, а мелкие узлы на срезах пропускаются. Мы разрабатываем AI-системы, которые берут на себя рутину: сегментацию органов, детекцию узлов, измерение объёмов. Наш опыт — 10+ лет в медицинском CV, 30+ внедрённых проектов. Результат — сокращение времени анализа на 50% и существенная экономия для клиники. Предлагаем решение «под ключ»: от сбора данных до интеграции в PACS.
Почему AI для КТ сложнее, чем для рентгена?
Компьютерная томография — трёхмерные данные: стек из 200–600 срезов толщиной 0.5–5 мм. Ключевая особенность — единицы Хаунсфилда (HU), количественная мера плотности тканей. AI должен работать в 3D, учитывать анизотропию вокселей и HU-диапазон, разный для разных задач (лёгкие: -1200..600 HU, мягкие ткани: -150..250 HU). Кроме того, вариабельность сканеров и протоколов требует robust-предобработки.
Как мы решаем проблемы 3D-сегментации
Стек: PyTorch, MONAI, nnU-Net. Для предобработки используем MONAI transforms — загрузку NIfTI, изменение ориентации на RAS, ресемплинг к изотропному spacing (1.5×1.5×2.0 мм), оконное контрастирование по HU. Базовая архитектура — 3D U-Net с residual-блоками.
import numpy as np import torch import nibabel as nib from monai.transforms import ( Compose, LoadImaged, AddChanneld, Orientationd, Spacingd, ScaleIntensityRanged, CropForegroundd, ResizeWithPadOrCropd, ToTensord ) class CTAnalysisSystem: def __init__(self, model_path: str, task: str = 'lung_nodule'): self.preprocessing = self._build_preprocessing(task) self.model = self._load_model(model_path) self.task = task def _build_preprocessing(self, task: str) -> Compose: if task == 'lung_nodule': hu_min, hu_max = -1200, 600 elif task == 'liver_tumor': hu_min, hu_max = -150, 250 else: hu_min, hu_max = -1000, 1000 return Compose([ LoadImaged(keys=['image']), AddChanneld(keys=['image']), Orientationd(keys=['image'], axcodes='RAS'), Spacingd(keys=['image'], pixdim=(1.5, 1.5, 2.0), mode='bilinear'), ScaleIntensityRanged( keys=['image'], a_min=hu_min, a_max=hu_max, b_min=0.0, b_max=1.0, clip=True ), ToTensord(keys=['image']) ]) def analyze(self, nifti_path: str) -> dict: data = {'image': nifti_path} data = self.preprocessing(data) volume = data['image'].unsqueeze(0) with torch.no_grad(): prediction = self.model(volume) if self.task == 'lung_nodule': return self._process_nodule_detection(prediction, data) elif self.task == 'organ_segmentation': return self._process_segmentation(prediction) MONAI и nnU-Net: стандарт индустрии
MONAI — фреймворк для медицинского CV от NVIDIA и King’s College. nnU-Net — self-configuring метод: автоматически определяет оптимальную архитектуру и предобработку для каждого датасета. Использование MONAI сокращает код предобработки в 2 раза и повышает Dice на 5% по сравнению с ручной имплементацией. Мы используем nnU-Net как baseline для сегментации органов.
from monai.networks.nets import UNet from monai.losses import DiceCELoss from monai.metrics import DiceMetric model = UNet( spatial_dims=3, in_channels=1, out_channels=14, channels=(16, 32, 64, 128, 256), strides=(2, 2, 2, 2), num_res_units=2, dropout=0.1 ) criterion = DiceCELoss( include_background=False, to_onehot_y=True, softmax=True ) Готовая модель TotalSegmentator сегментирует 104 анатомические структуры на КТ. Мы fine-tune её под конкретные задачи клиента (например, сегментация поджелудочной с учётом вариативности расположения).
from totalsegmentator.python_api import totalsegmentator totalsegmentator( input='ct_scan.nii.gz', output='segmentations/', task='total', fast=False ) Детекция лёгочных узлов: от LUNA16 к продакшену
Лёгочные узлы — первый признак рака лёгкого. Задача: найти узлы > 3 мм в 3D-объёме. Мы строим пайплайн: сегментация лёгкого → детекция (3D Retina U-Net) → постпроцессинг с кластеризацией.
class NoduleDetector: def __init__(self, model_path: str, min_nodule_mm: float = 3.0, confidence_threshold: float = 0.5): self.model = load_nodule_model(model_path) self.min_size = min_nodule_mm self.threshold = confidence_threshold def detect(self, ct_volume: np.ndarray, voxel_spacing: tuple) -> list[dict]: lung_mask = self._segment_lung(ct_volume) nodule_mask = self.model.predict(ct_volume * lung_mask) nodules = self._extract_nodules(nodule_mask, voxel_spacing) return [n for n in nodules if n['diameter_mm'] >= self.min_size and n['confidence'] >= self.threshold] Что даёт количественный анализ?
После сегментации измеряем объём органов и узлов в мл, диаметр по RECIST. Это позволяет отслеживать динамику опухолей и оценивать ответ на терапию.
def measure_volume_ml(mask: np.ndarray, voxel_spacing: tuple) -> float: voxel_volume_mm3 = np.prod(voxel_spacing) volume_mm3 = mask.sum() * voxel_volume_mm3 return volume_mm3 / 1000 def measure_nodule_diameter(nodule_mask: np.ndarray, voxel_spacing: tuple) -> dict: coords = np.where(nodule_mask) from scipy.spatial import ConvexHull points = np.column_stack(coords) * np.array(voxel_spacing) if len(points) < 4: return {'diameter_mm': 0} hull = ConvexHull(points) max_dist = 0 hull_pts = points[hull.vertices] for i in range(len(hull_pts)): for j in range(i+1, len(hull_pts)): d = np.linalg.norm(hull_pts[i] - hull_pts[j]) max_dist = max(max_dist, d) return {'diameter_mm': round(max_dist, 2)} Какие метрики гарантируют качество?
На публичных датасетах наша модель достигает следующих результатов:
| Задача | Датасет | Метрика | Значение |
|---|---|---|---|
| Сегментация лёгких | LUNA16 | Dice | 0.98 |
| Детекция узлов | LUNA16 | FROC | 0.89 |
| Сегментация печени | LiTS | Dice | 0.96 |
| Сегментация опухоли печени | LiTS | Dice | 0.75 |
| Multi-organ | BTCV | Dice | 0.88 |
В продакшене мы гарантируем Dice не ниже 0.95 для крупных органов и FROC > 0.85 для узлов. Каждый проект сопровождается model card с метриками на стратифицированных подгруппах (возраст, пол, тип сканера).
Процесс внедрения AI-модуля
| Этап | Длительность | Результат |
|---|---|---|
| Анализ данных | 3–5 дней | Отчёт о качестве данных, рекомендации по разметке |
| Предобработка и аугментация | 5–7 дней | Пайплайн загрузки и нормализации |
| Выбор и обучение модели | 2–4 недели | Baseline с метриками, отбор финальной архитектуры |
| Валидация на независимой выборке | 1 неделя | Model card, отчёт по метрикам |
| Деплой и интеграция | 1–2 недели | Docker-образ, Triton Inference Server, DICOM-шлюз |
| Пост-релизный мониторинг | 3 месяца | Логирование, алерты, еженедельные отчёты |
Что входит в работу
- Разработка пайплайна предобработки под конкретный сканер и протокол.
- Выбор и кастомизация архитектуры (nnU-Net, Retina U-Net, TotalSegmentator).
- Обучение и валидация с фиксацией метрик.
- Контейнеризация и деплой с Triton Inference Server.
- Интеграция с PACS через DICOM-шлюз и HL7/FHIR.
- Документация: model card, инструкция по эксплуатации, отчёт о тестировании.
- Обучение команды клиента (2-3 часовой воркшоп).
- 3 месяца пост-релизного мониторинга и поддержки.
Наш опыт и гарантии
5+ лет на рынке медицинских AI-решений, 30+ завершённых проектов в России и СНГ. Гарантируем качество: если метрики на тестовой выборке ниже оговорённых, дорабатываем бесплатно. Предоставляем сертификат соответствия стандартам обработки медицинских данных (152-ФЗ). В стоимость разработки входит обучение команды и 3 месяца пост-релизного мониторинга.
Свяжитесь с нами — оценим ваши данные за 2 дня, предложим архитектуру и реалистичные сроки. Закажите пилотный проект: сегментация одного органа на 50 сканах за 4 недели.







