Зачем вообще считать дыхание через камеру?
Представьте: пациент в больнице, на нем куча датчиков. Провода мешают, клей раздражает кожу, а медсестры постоянно заходят проверять показатели. Теперь представьте другую картину: камера над кроватью тихо считает дыхательные движения, не трогая человека. Никаких проводов, никакого дискомфорта. Это не фантастика 03.02.2026 года - это реальность, которую можно собрать за пару дней на Python.
Бесконтактный мониторинг дыхания нужен не только в больницах. Домашний уход за пожилыми, контроль состояния спортсменов, даже проверка дыхания младенцев во сне - все это области, где контактные датчики либо неудобны, либо вообще неприменимы.
Ключевой момент: мы измеряем не объем воздуха, а частоту дыхательных движений (ЧДД). Для многих медицинских сценариев именно ЧДД - критически важный показатель, а не точные миллилитры.
Основная проблема: как отделить дыхание от всего остального?
Человек не застывшая статуя. Он дышит - грудная клетка поднимается и опускается. Но он также моргает, шевелит руками, меняет позу, а в комнате меняется освещение, двигаются тени, проходят другие люди. Сигнал дыхания - это микроскопическое изменение на фоне огромного количества шумов.
Если просто взять камеру и измерять яркость пикселей в области груди, вы получите красивый график, где дыхание будет похоронено под слоем помех. Нужно умнее.
1 Находим человека в кадре: сегментация против детекции
Первая задача - понять, где в кадре находится человек. Самый простой способ - детекция bounding box'ами. Берем YOLOv11 (на 03.02.2026 это уже стандарт, версии v8 и v9 давно устарели) и получаем прямоугольник вокруг человека. Просто? Да. Достаточно? Нет.
Проблема в том, что bounding box включает в себя голову, руки, ноги - все, что движется и создает шум. Нам нужна именно грудная клетка. Поэтому вместо детекции используем сегментацию.
MediaPipe Pose или более тяжелые модели сегментации тела дают нам не просто прямоугольник, а маску с разметкой частей тела. Мы можем точно выделить область груди, игнорируя конечности и голову. Если интересно, как работает сегментация на практике, посмотрите мой разбор про обучение YOLO для жестов - там похожие принципы, но другие задачи.
2 Извлекаем сигнал: не просто яркость пикселей
Вот типичная ошибка новичков: берут область груди, усредняют значение всех пикселей по кадрам и пытаются найти в этом сигнале дыхание. Результат - полная ерунда.
Почему? Потому что изменение яркости от дыхания - эффект второго порядка. Основной вклад в изменение яркости вносят:
- Движение человека (даже минимальное)
- Изменение освещения (облака за окном, включение/выключение света)
- Тени от движущихся объектов
- Шум матрицы камеры
Дыхание вызывает не изменение яркости, а изменение текстуры. Складки на одежде немного растягиваются и сжимаются, ткань по-разному отражает свет при разном натяжении. Нужно измерять не яркость, а локальные градиенты или паттерны текстуры.
Один из работающих методов - разбить область груди на маленькие блоки (например, 8x8 пикселей), для каждого блока вычислить гистограмму ориентированных градиентов (HOG), а затем отслеживать, как меняются эти гистограммы во времени. Дыхание будет синхронно менять текстуру во всех блоках, а случайные движения - только в некоторых.
3 Фильтруем как профессионалы: от сырого сигнала к чистому дыханию
Допустим, мы извлекли какой-то сигнал. Теперь он выглядит как кардиограмма пьяного человека: куча пиков, провалов, артефактов. Нормальная частота дыхания взрослого человека - 12-20 циклов в минуту (0.2-0.33 Гц). Все, что выше 1 Гц - точно не дыхание. Все, что ниже 0.1 Гц - вероятно, дрейф сигнала или медленные движения.
Первое, что делаем - полосовой фильтр. Оставляем только частоты от 0.1 до 0.5 Гц. Проще всего использовать фильтр Баттерворта, он дает плоскую АЧХ в полосе пропускания и крутой срез.
from scipy import signal
import numpy as np
# Частота кадров (предположим, 30 FPS)
fs = 30.0
# Нормальная частота дыхания в Гц
lowcut = 0.1 # 6 циклов в минуту
highcut = 0.5 # 30 циклов в минуту
# Создаем полосовой фильтр Баттерворта 4-го порядка
nyquist = 0.5 * fs
low = lowcut / nyquist
high = highcut / nyquist
b, a = signal.butter(4, [low, high], btype='band')
# Применяем фильтр к сигналу
filtered_signal = signal.filtfilt(b, a, raw_signal)
Но фильтрации по частоте недостаточно. Нужно еще убрать выбросы - резкие скачки сигнала, вызванные, например, кашлем или резким движением. Здесь помогает медианный фильтр или фильтр на основе вейвлет-преобразования.
Самая хитрая часть - отделить дыхание от сердечных сокращений. Сердце бьется с частотой 1-2 Гц (60-120 ударов в минуту), что частично попадает в наш полосовой фильтр. Но амплитуда колебаний от сердца обычно меньше, и они имеют другую форму. Если нужно совсем чистое дыхание, придется использовать методы разделения источников, например, независимый компонентный анализ (ICA).
Не переусердствуйте с фильтрацией! Слишком агрессивная фильтрация может "сгладить" само дыхание, особенно если человек дышит неравномерно (что часто бывает при некоторых заболеваниях). Начинайте с мягких фильтров и постепенно добавляйте обработку только там, где это действительно необходимо.
А если нейросеть ошибется? (Она ошибется)
Любая нейросеть для сегментации иногда дает сбой. Особенно в сложных условиях: плохое освещение, нестандартная поза, несколько людей в кадре. Нужно быть готовым к этому.
Первая линия защиты - трекинг. Не обрабатывайте каждый кадр независимо. Используйте алгоритмы трекинга (например, SORT или DeepSORT), чтобы следить за человеком между кадрами. Если в одном кадре нейросеть не нашла человека, используйте позицию из предыдущих кадров с экстраполяцией.
Вторая линия - валидация сигнала. После извлечения "дыхательного" сигнала проверьте его на осмысленность:
- Частота в разумных пределах? (6-30 циклов в минуту)
- Амплитуда не скачет безумно от кадра к кадру?
- Сигнал достаточно периодический? (можно проверить автокорреляцией)
Если сигнал не проходит валидацию, система должна перейти в режим "низкой уверенности" и либо использовать данные с меньшего интервала, либо вообще приостановить измерение, пока условия не улучшатся.
Про то, как нейросети ошибаются в, казалось бы, простых задачах, я подробно писал в статье про подсчет лосося. Принципы те же: ИИ не волшебство, а статистика.
Практический пайплайн: от камеры до числа на экране
Давайте соберем все вместе в работающую систему. Предположим, у нас есть обычная веб-камера с разрешением 1280x720 и 30 FPS.
| Этап | Что делаем | Инструменты | Время на кадр |
|---|---|---|---|
| Захват кадра | Чтение с камеры, ресайз до 640x480 | OpenCV | ~1 мс |
| Сегментация тела | Поиск человека и выделение области груди | MediaPipe Pose или кастомная сеть | ~10-50 мс |
| Извлечение признаков | Вычисление текстуры в области груди | OpenCV (HOG, LBP) | ~5 мс |
| Накопление сигнала | Сбор 30 секунд данных (900 кадров) | Кольцевой буфер | - |
| Обработка | Фильтрация, анализ частоты | SciPy, NumPy | ~5 мс каждые 30 сек |
| Визуализация | Отображение графика и ЧДД | Matplotlib или PyQt | ~10 мс |
Общее время обработки одного кадра - около 20-70 мс, что дает 14-50 FPS в реальном времени. Для мониторинга дыхания достаточно 10 FPS, так что даже на слабом компьютере система будет работать.
Где все ломается: самые частые проблемы
Я видел десятки попыток сделать такую систему. Вот топ-5 причин, почему они проваливались:
- Слишком темно. Камера увеличивает ISO, появляется цветовой шум, который полностью маскирует микродвижения от дыхания. Решение: инфракрасная подсветка или камера с хорошей светосилой.
- Одежда с рисунком. Крупный принт на футболке создает свои собственные "движения" при малейшем изменении ракурса. Полосатая рубашка - вообще катастрофа. Решение: просить носить однотонную одежду или использовать несколько камер для стереозрения.
- Движение не от дыхания. Человек чешется, поправляет одежду, разговаривает. Все это создает артефакты в сигнале. Решение: детектировать большие движения и временно приостанавливать анализ.
- Несколько людей в кадре. Нейросеть сегментирует всех, а какой сигнал чей - непонятно. Решение: использовать трекинг и анализировать только самого крупного человека или того, кто ближе к центру.
- Ковариационный сдвиг. Модель, обученная на данных из одной больницы, отлично работает там и полностью сходит с ума в домашних условиях. Про эту проблему я подробно писал в статье про ковариационный сдвиг.
Что дальше? От мониторинга к диагностике
Подсчет ЧДД - это только начало. По форме дыхательного сигнала можно вытащить гораздо больше информации:
- Глубина дыхания (по амплитуде сигнала)
- Регулярность (апноэ, периодическое дыхание Чейна-Стокса)
- Соотношение вдоха и выдоха (при астме выдох удлинен)
- Участие вспомогательной мускулатуры (по движению плеч и ключиц)
Для анализа этих параметров уже нужны более сложные методы - вейвлет-преобразование для анализа нестационарных сигналов, машинное обучение для классификации типов дыхания, возможно, даже небольшие нейросети, обученные на размеченных медицинских данных.
Интересно, что похожие методы компьютерного зрения сейчас используются для оценки боли без датчиков - об этом читайте в материале про распознавание боли по лицу. Технологии идут к полной бесконтактной диагностике.
Юридические и этические аспекты (да, они есть)
Система, которая следит за людьми через камеру, даже с благими намерениями, попадает под несколько регуляций:
- GDPR и аналоги: видеоданные - это персональные данные. Нужно согласие субъекта, нужно обеспечить безопасное хранение, нужно иметь политику удаления.
- Медицинская сертификация: если вы заявляете, что система измеряет медицинский параметр, она может считаться медицинским устройством. А это совсем другой уровень требований.
- Конфиденциальность: камера видит не только дыхание, но и все, что происходит в комнате. Нужно либо сразу удалять "лишние" данные, либо использовать методы, которые извлекают только нужные признаки без сохранения видеопотока.
Практический совет: на первых этапах делайте систему полностью оффлайн. Видео не покидает компьютер, все обработка локальная, результаты анонимизируются. И всегда предупреждайте людей, что за ними ведется наблюдение.
Собираем прототип за выходные
Вот минимальный стек технологий для быстрого старта:
# Устанавливаем зависимости
pip install opencv-python mediapipe numpy scipy matplotlib
Берете MediaPipe Pose для сегментации (он легкий и работает из коробки), OpenCV для работы с видео, SciPy для фильтрации сигналов. Начинаете с идеальных условий: хорошее освещение, человек в однотонной одежде, сидит неподвижно.
Сначала добейтесь стабильной работы в этих условиях. Потом постепенно усложняйте: добавляйте движение, меняйте освещение, пробуйте разных людей. Каждый раз, когда что-то ломается, разбирайтесь почему и добавляйте защиту.
И последний совет: не зацикливайтесь на точности до миллиметра. Для клинического использования важнее не абсолютная точность, а стабильность измерений во времени и способность зафиксировать изменения состояния. Если система показывает 16 вдохов в минуту вместо реальных 15, но при ухудшении состояния стабильно показывает рост до 24 - она уже полезна.
Бесконтактный мониторинг дыхания - это не замена пульсоксиметру или капнографу. Это дополнительный инструмент, который работает там, где традиционные методы неудобны или невозможны. И как любой инструмент, он требует понимания его ограничений и грамотного применения.