Как стать автором
Обновить

Как мы превратили iPhone в лабораторный микроскоп с AI и BLE: real-world edge-приложение

Уровень сложностиСредний
Время на прочтение18 мин
Количество просмотров2.1K

Вступление

В этой статье я хочу поделиться опытом разработки iOS-приложения для роботизированного микроскопа с AI-распознаванием клеток крови — как оно устроено, какие задачи пришлось решать, на какие грабли пришлось наткнуться и как iPhone можно использовать в качестве лабораторного инструмента.

Это не очередной todo-лист с авторизацией или приложение для наложения масок на селфи  — в центре внимания: видеопоток с окуляра микроскопа, нейронки, работа с железом, Bluetooth-управление перемещением стекол, и всё это — прямо на iPhone. 

При этом я постарался не уходить в чрезмерные технические детали, чтобы статья оставалась доступной для большей части аудитории. 

Немного про продукт

Даже с современными гематологическими анализаторами до 15% образцов всё равно требуют ручного пересмотра под микроскопом — особенно если в крови найдены аномалии. Автоматизированные системы микроскопирования есть, но стоят как крыло от боинга, поэтому большинство лабораторий продолжают смотреть мазки вручную. Мы делаем это иначе: наш набор превращает обычный лабораторный микроскоп в цифровой сканер с автоматической подачей и съёмкой — просто, дёшево и эффективно. Мы его разрабатываем вместе с @ansaril3 и командой. 

Наш СЕО (Ансар) рассказывает про продукт главе Татарстана на выставке
Наш СЕО (Ансар) рассказывает про продукт главе Татарстана на выставке

Комплект подключается к стандартному лабораторному микроскопу, превращая его в цифровой сканер. Его хардварная часть состоит из:

  • iPhone — управление системой, анализ клеток

  • Линза-адаптер —  подключение смартфона к окуляру микроскопа

  • Роботизированный предметный столик — позволяет перемещать препарат, управлять фокусировкой и переходить между образцами

Программно система состоит из:

  • мобильного приложения на iPhone

  • контроллера на столике

  • веб-портала/облака.

Схема взаимодействия компонентов системы
Схема взаимодействия компонентов системы

Мобильное приложение решает следующие задачи:

  • обрабатывает поток изображения с камеры, детектирует, классифицирует, подсчитывает клетки

  • отправляет их на веб-сервер вместе с другими артефактами анализа

  • управляет перемещением предметного столика, выполняя сканирование мазка по заложенному алгоритму

  • и также отвечает за конфигурацию/запуск анализа, настройку параметров съемки, просмотр краткого отчета анализа и другие сопутствующие задачи

Веб-портал предназначен для просмотра результатов, подтверждения анализа врачом и экспорта отчётов. Ниже видео, где показано как это работает вместе:

🂭
undefined...
embedd.srv.habr.com
Микроскоп с iPhone и моторизованным столиком внизу
Микроскоп с iPhone и моторизованным столиком внизу
Веб-портал, с деталями проведенного анализа
Веб-портал, с деталями проведенного анализа

Немного контекста про гематологическую диагностику

Основной анализ, который мы делаем — это микроскопия мазка крови. Он является частью общего анализа крови (ОАК), одного из самых распространённых и базовых медицинских тестов. Многие его делали и видели у себя подобные таблицы:

При выполнении ОАК, образцы крови прогоняются через геманализатор. Если аппарат показывает отклонение от нормы, то образец микроскопируют. 

Это выглядит следующим образом:

  • лаборант наносит каплю крови на стекло,

  • окрашивает по Романовскому (или аналогам)1, фиксирует

  • глазами изучает препарат под микроскопом.

Приготовление мазка крови
Приготовление мазка крови

Именно здесь можно:

  • увидеть аномальные формы клеток (незрелые нейтрофилы, атипичные лимфоциты, бластные клетки),

  • оценить зрелость, размер, гранулярность, включения и другие параметры,

  • иногда — поставить предварительный диагноз до получения данных из ПЦР2 или ИФА3

Но ручной анализ это боль:

  • очень субъективен,

  • зависит от опыта лаборанта,

  • человек подвержен усталости и ошибкам,

  • плохо масштабируется.

Автоматические системы микроскопирования хороши, но стоят дорого (от 5 млн руб и выше), поэтому более 90% лабораторий довольствуются ручным методом до сих пор!

Мы поставили задачу сделать недорогой комплект автоматизации микроскопа (в пределах нескольких сотен тысяч рублей), который можно массово ставить в лаборатории.  И тут на сцену выходит iPhone.

1 - образец крови на стекле обрабатывается специальным красителем, разработанным Дмитрием Леонидовичем Романовском (1861-1921). Этот краситель позволяет сделать различные компоненты клеток крови более видимыми под микроскопом, так как они окрашиваются в разные цвета.

2 - ПЦР (полимеразная цепная реакция) делает возможным обнаружение даже очень малого количества генетического материала, например, вирусов или бактерий, что важно для диагностики инфекционных заболеваний. Помним с ковидных времен

3 - ИФА (иммуноферментный анализ) применяется, когда важно определить наличие специфических белков.

Врач проверяет мазок крови под микроскопом через iPhone
Врач проверяет мазок крови под микроскопом через iPhone

Что умеет iPhone

Когда мы говорим «AI на смартфоне», чаще всего представляют какие-то фильтры в камере, автодополнение текста или чат-боты. Но современные iPhone — это мини-компьютеры с выделенными нейромодулями, способные выполнять серьезную работу: в нашем случае анализ клеток крови в реальном времени, рассмотрим 3 ключевые компонента, которые делают это возможным:

  • Графический процессор (GPU). Используется для операций с изображениями: преобработка, фильтрация, коррекция. Например: оценка блюра, цветокоррекция, удаление артефактов и другие специфичные операции с графикой / анализом картинок.

  • Нейронный движок (NPU / Neural Engine) Apple встраивает Neural Engine в свои устройства начиная с A11 (iPhone 8/X), а с A12 (iPhone XR и новее) уже можно выполнять 5+ трлн операций в секунду на NPU (TOPS). На момент написании статьи последнии A17 Pro и A18/A18 Pro выполняют 35 TOPS.  Это используется для inference моделей детекции и классификации клеток, оценки фона препарата и т.п, освобождая CPU/GPU.

  • Процессор (CPU) Отвечает за общую логику, управление, обработку конфигураций, сериализацию/десериализацию, работу с API и файловой системой - все, что не вошло в предыдущие два пункта

Говорить будем на примере iPhone XR  (A12 Bionic, 2018), как некий baseline, хоть и старый.  Даже на нем у нас получалось:

  • обрабатывать видеопоток 50fps с камеры микроскопа,

  • одновременно выполнять CoreML-инференс (~15ms per frame),

  • параллельно сохранять данные на диск и синхронизировать с облаком,

  • удерживать температуру в допустимых пределах (если аккуратно настроить throttling и приоритизацию задач) 

Тем не менее, устройство могло заметно нагреваться и начинать тормозить. Например, при анализе мазков на малярии, где требуется обрабатывать 100+ клеток на одном кадре, уже на втором-третьем мазке начинался thermal throttling — снижалась частота CPU, появлялись лаги и подтормаживания интерфейса. К тому же плотное прилегание адаптера к задней панели устройства мешает отводу тепла. 

Тепловизионное изображение iPhone во время работы
Тепловизионное изображение iPhone во время работы

На скриншоте ниже показан другой анализ, не малярия — но тут важно, сколько срабатывают детекций на один кадр.

Большое кол-во детекций клеток на кадр
Большое кол-во детекций клеток на кадр

В целом, в iOS можно отслеживать термальное состояние системы через ProcessInfo.processInfo.thermalState. У нас в бою до Critical дело не доходило, но Serious происходит регулярно при очень интенсивной нагрузке. Для perfomance замеров, мы использовали Xcode Profiler, где можно измерять как загрузку CPU, GPU, памяти, так и Thermal State:

Отладка производительности через Xcode Profiler
Отладка производительности через Xcode Profiler

А вот Таблица значений thermalState с расшифровкой из документации:

Состояние (Thermal State)

Рекомендации (Recommendations)

Действия системы (System Actions)

Nominal (Нормальное)

Корректирующие действия не требуются.

Fair (Умеренное)

Слегка повышена температура.Приложения могут заранее начинать энергосберегающие меры.

Анализ фото ставится на паузу

Serious (Серьёзное)

Производительность системы снижается. Приложения должны сократить использование CPU, GPU и операций ввода-вывода.

ARKit и FaceTime снижают частоту кадров (FPS)Восстановление из резервной копии iCloud приостанавливается

Critical (Критическое)

Приложения должны сократить использование CPU, GPU и операций ввода-вывода, а также прекратить использование периферийных устройств (например, камеры)

ARKit и FaceTime значительно снижают частоту кадров (FPS)

Полный тепловой и энергетический разбор тянет на отдельную статью — как в начале говорил, глубоко уходить не хочу.  По открытым источникам можно грубо предположить, что serious соответствует 80-90°C у чипа и ~40°C у поверхности.

iPhone работает с любыми Bluetooth Low Energy устройствами. Для других отдельный флоу, где устройство должно иметь сертификат MFI (made for iPhone), работать по протоколу iAP2 (Apple Accessory Protocol) и т.д. Короче говоря — это не наш кейс.

Тут полезно напомнить базовые роли и структуру протокола:

  • Peripheral (периферийное устройство) — это устройство, к которому подключаются. Обычно именно периферия рассылает данные или ждёт подключения (пример: часы, термометр, пульсометр).

  • Central (центральное устройство) — это устройство, которое подключается к периферийному. Он инициирует соединение, отправляет команды и получает данные.

  • GATT (Generic Attribute Profile) — это структура, по которой BLE-устройства обмениваются данными. GATT описывает, какие “поля” доступны, что можно прочитать, записать или подписаться на уведомление.

  • Services и Characteristics — данные внутри BLE-соединения структурированы в виде сервисов — логических групп и характеристик — конкретных параметров. Например, у фитнес-браслета может быть сервис Heart Rate, в котором есть характеристика Heart Rate Measurement (текущий пульс). 

В нашем случае, iPhone управляет столиком через встроенный BLE модуль, который  распознается как Peripheral с кастомным GATT-сервисом и выполняет 2 задачи:

  • передавать контроллеру команды перемещения по (осям XY) и фокусировки (ось Z)

  • получал данные от контроллера (статус, позиция)

К слову о тепловой нагрузке, BLE-соединение не должно вносить заметного вклада. Если верить данным Silicon Labs в их доке по энергопотреблению BLE, передача команд или получение статуса с частотой 20 Гц (интервал 50 мс):

  • даёт прирост <1 мВт. У iPhone XR типичная нагрузка в режиме ожидания ~50–100 мВт. Добавка <1 мВт — почти незаметна, особенно по сравнению с нейронками, GPU и экраном 

  • радиоканал включается всего на ~2% времени, остальное время спит

В блоке “Работа с моторизованным столиком”, где чуть подробнее погрузимся в детали работы приложения с BLE модулем и контроллером.

Теперь немного про камеру. Используем основную (широкоугольную заднюю) камеру: получаем видео H.264 с разрешением 1280x720 и битрейтом около 40 Мбит/с.

  • Чем выше битрейт, тем больше данных на единицу времени → выше качество изображения. 40 Мбит/с — достаточно высокий для разрешения 1280×720 (HD). Более чем хватает для изображения для анализа клеток.

  • H.264 — это международный стандарт видеокодирования, также известный как AVC — Advanced Video Coding или MPEG-4 Part 10. Он убирает избыточные данные (межкадровое и внутрикадровое сжатие), снижая битрейт и как следствие размер файла. (У нас, кстати, была задача записывать видео всего анализа для отладки и валидации)

Вот и получаем это не просто мобильный UI-клиент, а вполне себе edge-девайс, то есть устройство, которое самостоятельно обрабатывает данные на месте, без постоянной связи с сервером.

Мобильное приложение

Теперь, когда мы разобрались с хардварной частью, посмотрим, как всё это работает на уровне приложения. Начнем сначала с постановки задачи:

  • На вход приложению с камеры подается поток кадров — движение поля зрения микроскопа по мазку.

  • На выходе приложение должно:

    • детектировать лейкоциты (и другие клетки в зависимости от анализа)

    • отображать BBox'ами детектируемых объектов

    • делать их подсчет

    • отсылать в фоне данные на бекенд (изображения клеток, скана, отдельных кадров)

Общий ход обработки кадра в приложении
Общий ход обработки кадра в приложении

Как видно на схеме выше, всё крутится вокруг кадра с камеры — на нём держится и детекция, и навигация по стеклу, и какие артефакты из него нужно отправлять в облако. Поэтому в центре всего — потоковая обработка кадров, опишем ее основные этапы и важные моменты.

1) Преобработать кадры. Сюда входит коррекция дисторсии, удаление артефактов, определение уровня размытия (блюр), световая и цветовая коррекция

Например, в каждой лаборатории или микроскопе специфичное освещение, из-за чего нейронка может сбоить. Здесь нужно сделать нормировку белого цвета — навести поле зрения на пустую область и запустить баланс белого цвета на камере.

Изображение до и после калибровки
Изображение до и после калибровки

Еще у нас был такой баг: клетки на портал приходили без калибровки цвета. Из-за того, что детекция в паралель запускалась раньше применения настроек камеры.

Изображение с портала с кадром клетки без калибровки
Изображение с портала с кадром клетки без калибровки

2) Задетектить, классифицировать и подсчитать без дублей клетки. 

Например, на фото ниже красным отмечены некоторые дубли в одном из старых анализов:

Изображение с портала с повторяющимеся клетками
Изображение с портала с повторяющимеся клетками

3) Проконтролировать микроскоп, чтобы он правильно двигался по стеклу, переходил с одного на другого и, самое важное, точно фокусировался на стекле, определял, когда он выходит за границы стекла или попадает на пустые пространства

4) Загружать на облако пачку клеток  (снапшоты, метаданные) и не блокировать этим прогон следующего анализа

5) Повторить n раз, так как анализы делаются пачкой

6) и сделать это так, чтобы телефон не взорвался от перегрева

Приложение развивалось как обычно в стартапах: был быстро набросан proof-of-concept, затем доведен до MVP (minimum viable product), чтобы можно было пилотировать в лабах и питчить инвесторам. . В итоге архитектура приложения получилась гибридной: часть экранов реализована на UIKit-овских MVP экранах (model-view-presenter), а новые фичи и интерфейсы пишутся на Swift c MVVM (Model-View-ViewModel).

Используем сервисный слой для изоляции бизнес-логики: CameraService, BluetoothController, AnalysisService. Все зависимости инжектируются через конструкторы или через DI-контейнеры. В плане реактивности и асинхронных цепочек с подписками на события у нас был “эволюционный путь”: сначала завезли RxSwift, потом начали переходить на Combine, а с выходом async/await часть цепочек ушла на них. Получился такой “франкенштейн”, но затем мы изолировали эти куски в отдельные компоненты — чтобы в будущем можно было просто заменить их компонент с новым стеком. Всё приложение прошито подробными логами, а для сложных случаев (особенно связанных с обработкой кадров) используем NSLogger: туда можно логировать не только текст, но и изображения — это не раз спасало при отладке пайплайна обработки клеток перед их отправкой на сервер.

Скриншот отладки через NSLogger с изображениями клеток
Скриншот отладки через NSLogger с изображениями клеток

Про тестирование можно было бы написать отдельную статью: от мокирования отдельных частей анализа и быстрой настройки нужных состояний через ProcessInfo (у меня, кстати, есть ⁠⁠небольшая тех. заметка⁠⁠ на эту тему), до симуляции отдельных шагов анализа и покрытия всего этого интеграционными и юнит-тестами.

Но вернемся к обработке кадров и рассмотрим чуть более подробную, чем выше архитектурную схему:

®
®
  • Analysis Controller — центр принятия решений: получает кадры, запускает обработку в Frame Pipeline.

  • Camera Service — получает сырой поток кадров с камеры, преобразует их и отправляет дальше

  • Microscope Controller управляет контроллером микроскопа

  • Frame Pipeline — цепочка из нескольких стадий:

    • Preprocessing — коррекция, фильтрация

    • Detection — поиск объектов/клеток

    • Counting — подсчет уникальных объектов

    • Postprocessing — финальная фильтрация и подготовка к визуализации

  • UI — отвечает за отображение результатов пользователю в реальном времени (bounding boxes, статистика, алерты).

  • Uploader — синхронизирует артефакты анализа (снапшоты, клетки, конфиг) с бэкэндом.

В плане менеджера зависимостей: использовался CocoaPods (перешёл в режим поддержки и не развивается активно с 2024 года), но затем мы завезли SPM (Swift Package Manager). Часть сервисов (CV, Bluetooth, утилиты) были вынесены в SPM модули, также были попытки вынесения ObjC/C++ кода в отдельные xcframework-ы, но было некогда разгребать, поэтому оставили этот код в основном проекте.  ObjC нужен для обертки над C++, чтобы его можно было вызывать из Swift-а. Получались такие ObjC++ классы: интерфейс у них чисто ObjC-шный и с ним может работать Swift, а в реализации перемешан код ObjC и C++.  Это было еще до поддержки вызова C++ прямо из Swift. Оговорюсь, что я далеко не гуру C++ и Computer Vision алгоритмов, но в мои задачи входило базовые погружение и портирование алгоритмов, эвристик с Python, на которым у нас был основной R&D.  Ниже опишу некоторые из них.

Задачи

Удаление дисторсии

У одного из адаптеров был артефакт оптической дисторсии на изображении. В результате клетка, которая должна быть круглой, кажется вытянутой или кривой, особенно по краям кадра.  Мы использовали калибровку на шахматной сетке и OpenCV cv::undistort() для восстановления геометрии кадра:

  1. Калибруем камеру — снимаем шахматную доску/сетку с известной геометрией:

  2. OpenCV вычисляет:

    1. матрицу камеры K (параметры проекции)

    2. коэффициенты дисторсии D = [k1, k2, p1, p2, k3, …]

  3. Применяем cv::undistort() или cv::initUndistortRectifyMap() + remap():

    1. вычисляется, куда реально «должна была попасть» каждая точка

    2. изображение “разгибается” назад

Примеры оптической дисторсии
Примеры оптической дисторсии

В последствии, адаптер поменяли — этот шаг убрали

Определение позиции на стекле:

Чтобы точно считать клетки, нужно максимально точно знать их координаты. Вот тут на видео видно, что происходит с кривым определением сдвига.

Изначально, мы пытались посчитать относительный сдвиг между двумя кадрами и суммировать абсолютный сдвиг. Перепробовали несколько вариантов: 

  • классический метод регистрации изображений через фазовую корреляцию, основанную на быстром преобразовании Фурье. Реализовали на OpenCV и даже использовали Apple Accelerate

  • Методы на основе локальных ключевых точек с дескрипторами: SURF, SIFT, ORB и другие.

  • Optical Flow

  • встроенный в Apple Vision: VNTranslationalImageRegistrationRequest

С одной стороны, у нас были некоторые допущения: 

  • отсутствовало изменение масштаба, поворотов.

  • оптические: чистый, несмазанный мазок, без пустых пространств

Но несмотря на это, все равно были проблемы: из-за изменения освещения, фокуса, накопления ошибки, резких сдвигов, шумов/артефактов на изображении.
Получилась вот такая вот таблица их сравнения:

Метод

Преимущества

Недостатки

Особенности использования

Скорость

Комментарий

FFT + кросс-корреляция (OpenCV, Accelerate)

Очень быстрый, глобальный сдвиг, прост в реализации

Накапливается ошибка, не устойчив к резким сдвигам.

Требует одинакового размера изображений, подходит для “чистого” сдвига

Очень высокая


Использовался как основной

SIFT

Высокая точность, масштаб/поворот-инвариантность

Медленный, раньше был не free

Отлично для разнообразных сцен, с текстурой и сложными преобразованиями

Медленный

Экспериментальный вариант

SURF

Быстрее SIFT, тоже устойчив к масштабу/повороту

Проприетарный, не всегда доступен

Подходит для real-time чуть лучше, но тоже “тяжёлый”

Средний

Экспериментальный вариант, тем более что под патентом

ORB

Быстрый, бесплатный, инвариантен к повороту

Чувствителен к освещению, неустойчив к масштабу

Неплохо показывать себя в склейке изображений

Высокая

Пока не перенесли склейку в облаке, были версии с ним

Optical Flow (Lucas-Kanade)

Отслеживает движение точек между кадрами, хорош для видео

Не работает на глобальных трансформациях, зависит от освещения

Лучше всего в видео или сериях с малым движением

Средняя

Были эксперименты в оцифровке (склейке) изображений

Optical Flow (Farneback)

Плотная карта движения, применим к целому изображению

Медленный, чувствителен к шуму

Хорош для анализа локальных движений в кадре

Медленный

Были эксперименты в оцифровке (склейке) изображений

Apple Vision (VNTranslationalImageRegistrationRequest)

Очень удобный API, быстрый, железо-оптимизирован

В нашем случае точность была слабая.

Отлично подходит для простых кейсов на iOS/macOS

Очень высокая

Попробовали и закопали


Для каждой варианта мы пытались найти оптимальную по точности/производительности  конфигурацию сравнения с эталонным сдвигом: меняли разрешения изображения, параметры алгоритмы, разные настройки камеры и оптики микроскопа. Ниже пару графиков из такого рода экспериментов

Эксперименты со сдвигом
Эксперименты со сдвигом

А вот как выглядела отладка поиска ключевых точек, по которым мы в дальнейшем хотели строить сдвиг.

Часть из Jupyter notebook с отладкой клювых точек для поиска сдвига
Часть из Jupyter notebook с отладкой клювых точек для поиска сдвига

В итоге, когда у нас в системе появился роботизированный столик, мы начали использовать координаты его контроллера, которые мы уже уточняли с помощью CV-эвристик.

Подсчет клеток

По сути, задача подсчета клеток — это частный случай object tracking & deduplication: “увидеть, что за клетка, не посчитать дважды, не пересчитать лишнее, и не пропустить нужное — всё это за доли секунды, в онлайне через камеру и на железе телефона. Как мы это решали:

  1. Обнаружение объектов. Используем нейронки для детекции объектов на кадре (Bounding Box, BB). Каждый BB имеет свой “confidence score” (доверие сети) и класс клетки.

Bbox-ы задетектированных клеток
Bbox-ы задетектированных клеток

Для борьбы с фоном и ложными срабатываниями применяем быструю фильтрацию:

  • цветовая: например, по интенсивности или цветовому диапазону. Например، здесь слева красным подсвечен эритроцит — но нейронка приняла его за лейкоцит

  • Однако дальше в дело вступили цветовые фильтры и он был отсеян.

Красным отмечен отброшенный через флиьтры эритроцит
Красным отмечен отброшенный через флиьтры эритроцит
  • геометрическая: отбрасываем объекты, размер которых выходит за рамки типичных клеток.

  • также отбрасываем клетки, которые частично выходят за пределы кадра — нам такие не интересны 

Не прошедшая геометрическую фильтрацию клетка
Не прошедшая геометрическую фильтрацию клетка
  1. Подсчет уникальных объектов. Некоторые BB могут посчитаться более одного раза для одной и той же клетки, нужно уметь ловить такие срабатывания и учитывать лишь один раз. В свое время мы вдохновлялись гайдом от MTurk, где описаны 2 варианта:

Вариант 1: Сравниваем расстояния между центрами BBs — если новый BB находится слишком близко к уже прочитанному, это скорее “та же” клетка.

Вариант 2: Считаем IoU (intersection over union, Jaccard Index) — метрику пересечения прямоугольников. Если новый BB сильно пересекается с существующим, учитываем его только один раз.

Метрики для сравния двух BBox-ов
Метрики для сравния двух BBox-ов

В целом нужно поддерживать трекинг объектов и между кадрами, если мы возвращаемся на уже пройденные куски мазка. Тут снова архиважно корректно определять позицию на стекле — иначе весь подсчет уйдет коту под хвост.

Оцифровка 

Одной из задач была оцифровка скана, своего рода программный гистологический сканер для мазка. Фото ниже как он выглядит: стрелочками отмечено движение для построения скана, собираем кадры и сшиваем их в одну большую картинку.

Ход поля зрения по мазку
Ход поля зрения по мазку

Здесь снова критично важным было определение позиции, а далее бесшовная склейка.

Надо сказать, что изначально у нас не было моторизированного столика и мы полагались на ручную навигацию. Представьте, что вы клеите мозаику из сотен осколков. Промахнетесь с координатами — и мозаика поплыла.

Вот как выглядели первые эксперименты: прыгающие поля зрения, швы, разница в освещении, пустые пространства.

Слева — карта с неравномерной яркостью и экспозицией, на стыках кадров видны “швы. Справа — неровно сшитые изображения ткани с пропусками
Слева — карта с неравномерной яркостью и экспозицией, на стыках кадров видны “швы. Справа — неровно сшитые изображения ткани с пропусками

Или вот пользователь делает скан мазка, резко двигаясь по мазку - некоторые области получаются смазанными (Motion Blur). Мы пробовали дропать такие кадры, если они не прошли допустимый порог блюра или для них не посчитался сдвиг.

Наложенные на скан смазанные кадры при резком сдвиге
Наложенные на скан смазанные кадры при резком сдвиге

Постепенно продвинулись к такому варианту:

Более проавильно сшитое изображение скана
Более проавильно сшитое изображение скана

Итераций было много: склейка на девайсе, через разные методы, на разных разрешениях кадров и конфигах камеры. Пришли к тому, что скан собирается в облаке, а мобилка отсылает кадры с калибровкой баланса белого и экспозиции. 

Ниже пример, как мы замеряли скорость отдельных кусков обработки кадров в зависимости от конфига: настройка камеры, выбранные алгоритмы их параметры.

Таблица экспериментов с производительностью
Таблица экспериментов с производительностью

Работа с моторизованным столиком

Теперь — подробности про связку iPhone и моторизованного столика: как общаемся по BLE, какие команды шлем и как настраивали автофокус. Мобилка связывается через Bluetooth с контроллером на столике и двигается по XYZ координатам. Точнее, двигается сам столик, но для с точки зрения изображения с объектива, которое видит мобилка: движение происходит визуально по стеклу. 

Столик у нас тоже самодельный — не потому что «хотим всё своё», а потому что рыночные решения стоят от $10k, и это не шутка. Мы наняли конструкторское бюро и собрали свою версию за ~$800. Получилось сильно дешевле, потому что один из инженеров вовремя заметил: конструкция моторизованного микроскопного столика подозрительно напоминает 3D-принтер. Та же кинематика по XYZ, те же шаговики, те же рельсы. В итоге используем массовые и дешёвые компоненты, но под наши задачи. Конструктивно столик состоит из трёх частей: сама платформа XY, блок фокусировки (ось Z, мотор крепится к ручке тонкой фокусировки) и управляющий блок — контроллер, который принимает команды по Bluetooth и отдает их на шаговики. Всё это работает в связке с мобилкой.

Составные части моторизованного столика для микроскопа
Составные части моторизованного столика для микроскопа

Для ручного перемещения столика используем виртуальный джойстик (отображаем пользователю на экране кнопки перемещения) — он используется в сценариях калибровок и настройки системы. Во время же анализа всегда автоматическое управление. Вот как работал джойстик в первых версиях — потом мы уже докрутили и звук и задержку.

Протокол взаимодействия

В качестве блютуз интерфейса используется плата HC-08. BLE модуль работает по умолчанию в режиме текстового терминала, то есть запросы/ответы просто проходят туда-сюда. Для конфигурации и системных задач (смена имени, скорости обмена) используются at-команды. 

Сам контроллер работает на прошивке GRBL, через g-команды. Основные сценарии здесь:

  • инициализация подключения (телефон должен понять, что столик подключен)

  • сканирование стекла (перемещение столика по всем осям)

  • остановка/возобновление сканирования

  • обработка исключительных ситуаций: дошли до концевика, прерываем перемещение, переполняется буфер команд. По ошибкам есть отдельная дока

Общая схема роботизированного микроскопа
Общая схема роботизированного микроскопа

GRBL обладает собственным набором команд, который начинается с символа $, например:

  • $H - хоминг или калибровка и поиск аппаратного нуля по концевика. Обычно выполняется при первом старте и далее по необходимости в случае большой накопленной ошибки в процессе движения.

  • $J=<команда> - режим Jogging, то есть имитация управления джойстиком. Сама команда должна описывать относительное перемещение по всем осям. Пример такой команды: $J=G21G91Y1F10

    • G21 - режим расстояния в миллиметрах

    • G91 - относительное смещение

    • Y1 - перемещение по оси Y на 1 миллиметр

    • F10 - скорость перемещения. 

  • ? запрос состояния grbl. Возвращает строку с основными параметрами машины. Пример выполнения: <Alarm|MPos:0.000,0.000,0.000|FS:0,0|Pn:XYZ|WCO:-5.300,0.000,-2.062>

Нам нужны первые два параметра:

  • состояние. Может быть "Idle", "Running", "Alarm"

  • MPos - текущая позиция столика 

В детали GRBL и протоколов управления столиком сильно уходить не буду — это тянет на отдельную статью. Если коротко: GRBL — это опенсорсная прошивка из мира ЧПУ, отлично подходит для управления трех осевыми системами (XY+Z) через простые G-коды. BLE-модуль взяли максимально простой — HC-08, чтобы не возиться с MFi и iAP. Нам было важно, чтобы iPhone мог надежно отдавать команды и получать статус с минимальной задержкой и стоимость комплекта сильно не поменялась.

Задачи

Автофокус

Выше я упоминал про фокусирование.  Этот процесс выполняется с периодичностью во время сканирования мазка, так как препарат нанесен неравномерно, особенно это заметно на больших увеличениях. Приходится мониторить уровень размытия и своевременно обновлять фокус. Выглядит это так.

На графике ниже показана зависимость уровня фокусировки от времени. Начинаем с размытого изображения, постепенно устанавливая столик в положение оптимального фокуса

Сканирование

Я уже упоминал про оцифровку скана со стороны мобилки. Тут полезно упомянуть, что оцифровка может происходить на разных увеличениях: от 5x до 40x. На маленьком зуме — проще ориентироваться и искать границы пятна, на большом — видно детали клеток.

Пирамидальная структура виртуального препарата
Пирамидальная структура виртуального препарата

 В нашем случае мы работаем с 2-мя уровнями:

  1. Поиск границ на увеличении 4x. Алгоритм проходит по всему стеклу, определяет область пятна и выдает карту границ на следующий этап. На выходе получается что-то вроде тепловой карты. Например, из изображения на низком увеличении слева мы получим матрицу, по которым уже построим шаги, чтобы двигаться на высоком увеличении:

    Гистологическое изображение ткани на увеличении 4x
    Гистологическое изображение ткани на увеличении 4x
  1. Сканирование пятна на увеличении 20x (или другом). Алгоритм сканирует и сохраняет изображения для последующего построения в единую карту. Сканирование идет построчно, в рамках границ пятна. Фото для склейки берется когда:

    1. изображение сфокусировано

    2. контроллер в состоянии idle, т.е не двигается

Процесс сканирования мазка
Процесс сканирования мазка

Чтоб пользователю не менять объектив каждый раз, мы делаем поиск границ и сканирование сразу по всем стеклам в батче, параллельно загружая предыдущий батч на облако. На нем уже происходит стичинг или сшивка изображения, но это тема для отдельной статьи.

Заключение

Этот проект показал: даже смартфон из 2018 года  может тянуть задачи, которые раньше решались десктопами, серверами и дорогими автоматическими микроскопами.  Конечно, за кадром осталось много всего: от сбора датасета до тонкой настройки экспозиции. Если интересно — могу отдельно разобрать это. Задавайте вопросы, делитесь своим опытом, и, возможно, вместе соберём продолжение или разберём отдельные аспекты глубже. Спасибо, что дочитали!

Теги:
Хабы:
+10
Комментарии20

Публикации

Работа

Ближайшие события