Разработка AI для анализа гистологических изображений
Гистологические изображения — снимки тонких срезов биологической ткани под микроскопом. Задачи AI: классификация тканей (злокачественная vs доброкачественная), детекция и подсчёт клеток, сегментация структур (железы, опухоль, строма), определение степени злокачественности (Gleason score для рака простаты, градация по системе Elston-Ellis для молочной железы).
Специфика Whole Slide Images (WSI)
Гистологические слайды сканируются в формате WSI (Whole Slide Image) — гигапиксельные изображения: 100,000×200,000 пикселей, 5–20 ГБ на файл. Прямая загрузка в память невозможна. Работа через тайлинг:
import openslide
import numpy as np
from PIL import Image
class WSIProcessor:
def __init__(self, wsi_path: str, level: int = 0):
self.slide = openslide.OpenSlide(wsi_path)
self.level = level
self.dimensions = self.slide.level_dimensions[level]
self.mpp = float(self.slide.properties.get(
openslide.PROPERTY_NAME_MPP_X, 0.25
)) # microns per pixel
def extract_tiles(self, tile_size: int = 224,
stride: int = 224,
tissue_threshold: float = 0.5) -> list[dict]:
"""Генерация тайлов с фильтрацией фона"""
W, H = self.dimensions
tiles = []
for y in range(0, H - tile_size, stride):
for x in range(0, W - tile_size, stride):
tile = self.slide.read_region(
(x, y), self.level, (tile_size, tile_size)
).convert('RGB')
# Фильтрация тайлов с фоном (белый фон)
if self._has_tissue(np.array(tile), tissue_threshold):
tiles.append({
'image': tile,
'x': x, 'y': y,
'mpp': self.mpp
})
return tiles
def _has_tissue(self, tile_array: np.ndarray,
threshold: float) -> bool:
"""Определение наличия ткани по насыщенности HSV"""
from skimage.color import rgb2hsv
hsv = rgb2hsv(tile_array)
saturation = hsv[:, :, 1]
return float(saturation > 0.15).mean() > threshold
Multiple Instance Learning (MIL)
Для задач классификации WSI (рак есть/нет) без пиксельной разметки — Multiple Instance Learning: каждый тайл — это «instance», WSI-слайд — «bag». Если хотя бы один тайл содержит рак, слайд позитивен.
import torch
import torch.nn as nn
class AttentionMIL(nn.Module):
"""Attention-based MIL для классификации WSI"""
def __init__(self, feature_dim: int = 512, num_classes: int = 2):
super().__init__()
# Attention mechanism
self.attention = nn.Sequential(
nn.Linear(feature_dim, 128),
nn.Tanh(),
nn.Linear(128, 1)
)
# Классификатор
self.classifier = nn.Sequential(
nn.Linear(feature_dim, 256),
nn.GELU(),
nn.Dropout(0.4),
nn.Linear(256, num_classes)
)
def forward(self, tile_features: torch.Tensor) -> dict:
"""
tile_features: [N_tiles, feature_dim]
"""
# Attention weights
A = self.attention(tile_features) # [N, 1]
A = torch.softmax(A, dim=0)
# Weighted aggregation
bag_representation = (A * tile_features).sum(dim=0, keepdim=True)
# Classification
logits = self.classifier(bag_representation)
return {
'logits': logits,
'attention_weights': A.squeeze(), # важность каждого тайла
'bag_representation': bag_representation
}
Грейдирование рака простаты (Gleason Score)
Систему Gleason заменяет ISUP Grade. AI достигает Kappa 0.76 против 0.68 у среднего патологоанатома:
ISUP_GRADES = {
0: 'Benign (no cancer)',
1: 'Grade 1 (Gleason 3+3)',
2: 'Grade 2 (Gleason 3+4)',
3: 'Grade 3 (Gleason 4+3)',
4: 'Grade 4 (Gleason 4+4)',
5: 'Grade 5 (Gleason 9/10)'
}
Cell Detection: обнаружение и подсчёт клеток
from ultralytics import YOLO
# HoVer-Net или YOLO для cell detection
cell_detector = YOLO('cell_detector.pt')
def count_cells_in_tile(tile: np.ndarray) -> dict:
results = cell_detector(tile, conf=0.4)
cell_counts = {}
for box in results[0].boxes:
cell_type = cell_detector.model.names[int(box.cls)]
cell_counts[cell_type] = cell_counts.get(cell_type, 0) + 1
return cell_counts
Ключевые датасеты
| Датасет | Задача | Изображений |
|---|---|---|
| TCGA | Multiple cancers | 1M+ WSI |
| CAMELYON16/17 | Метастазы рака молочной железы | 400 WSI |
| PANDA | Рак простаты (Gleason) | 10,616 WSI |
| PanNuke | Сегментация ядер | 7,904 тайлов |
| Задача | Срок |
|---|---|
| Классификация тайлов WSI | 8–12 недель |
| MIL для WSI-level классификации | 12–18 недель |
| Cell detection + грейдирование | 16–24 недели |







