Pull to refresh

Comments 10

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

У нас может быть множество кадров, попадающих на одну область, некоторые из них могут быть смазанными. Если ститчеру и удается правильно сопоставить изображения (что тоже не всегда работало), то потом при блендинге получалась полная каша. Т.е. как минимум нужна более хитрая фильтрация кадров и выборка нужных пикселей.

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

В Hugin, я так понимаю, тоже имеется Mosaic Mode для этого. Но за наводку спасибо, можно будет поизучать подробнее.

UFO landed and left these words here

Вы там случайно не сговаривались?

Вот комментарий Глубоко Больного Пациента на предыдущую статью

https://habr.com/ru/articles/1067168/#comment_30302964

А, это его совместный разбор обоих. Ничего личного.

-----------------------------

Отличная статья! Видно, что команда прошла через классический “ад энтузиаста” — от блестящего прототипа до продакшен-ада. Давайте разберём её под микроскопом (простите за каламбур), жёстко покритикуем инженерные решения и проведём параллели с предыдущей темой сшивки изображений.

  1. Критика статьи и технических решений MotionCode

Проблема №1: Самоуспокоенность по поводу “Replay Attack” (Атака повторным воспроизведением)

В статье говорится: «Скриншот или запись старой последовательности быстро теряют практический смысл». Это опасное заблуждение. Если я запишу видео панели длительностью 5 секунд на другой телефон и просто покажу это видео камере целевого устройства, алгоритм CV увидит точно такую же динамику, временные интервалы и яркость. Если вы не добавили рандомный временной челлендж (например, смена состояния должна произойти строго после того, как камера моргнула, или с привязкой к текущей миллисекунде сервера), система уязвима. Ваш CV анализирует только геометрию и яркость — он не видит, что это запись с другого экрана.

Проблема №2: Отказ от нейросети — это победа, но вы потеряли контекст

Вы заменили детектор на поиск ROI и яркость. Однако классический CV слеп к частичным перекрытиям. Если палец пользователя перекроет половину ячейки, ваша яркостная метрика сломается. Более того, при съёмке под углом геометрические искажения делают центр ячейки совсем не там, где вы его ждёте. В статье не упомянута аффинная коррекция перспективы перед сравнением областей — а без неё при наклоне телефона > 30° точность резко падает.

Проблема №3: “Магия” калибровки и отсутствие нормализации

Вы пишете: «Параметры, работавшие на одном телефоне, ломались на другом». Это классическая ошибка — настройка абсолютных порогов яркости. Почему вы не использовали адаптивную нормализацию (например, CLAHE или локальный контраст) прямо в потоке? Вместо подбора 50 параметров вручную, можно было привести гистограмму любого кадра к эталонному распределению за 2 мс на CPU. А ещё проще — инвертировать анализ: искать не абсолютную яркость, а границы (edges) ячеек. Границы устойчивы к экспозиции, в отличие от уровня серого.

Проблема №4: Временная фильтрация “в лоб”

Вы накапливаете состояния кадров, чтобы подтвердить их. Это увеличивает задержку (латентность). Для авторизации это может быть ОК, но в сценарии учета времени (когда сотрудник стоит у панели 5 секунд) — ОК. Но вы упустили детекцию смены состояния (переход 0->1). Самый сложный момент — это именно граница переключения, потому что в этот момент на экране может быть артефакт (половина ячейки перерисовывается). Вы не упомянули, как обрабатываете переходные кадры — скорее всего, они у вас падают в фильтр и вызывают ложные срабатывания.

  1. Жёсткая связь с предыдущей задачей (Сшивка микроскопа)

Читая MotionCode, я узнал ту же самую архитектурную драму, что была в микроскопии. Вот 4 точки пересечения:

Аспект Сшивка микроскопа (прошлая статья) MotionCode (текущая) Общий вывод Выбор подхода Отказались от глубоких нейросетей (детекторы точек) в пользу фазовой корреляции. Отказались от Object Detection в пользу классического CV (яркость + ROI). Золотое правило: В узких задачах с известной геометрией классика на CPU всегда побеждает тяжелые модели на GPU. Проблема времени (Sequence) Боролись с накоплением ошибок (дрейфом) при склейке. Использовали глобальную оптимизацию. Борются с ошибочными кадрами через накопление (фильтр большинством). Обе системы поняли, что один кадр — ничто. Только поток (время) дает истину. Однако в микроскопии вы делали глобальный пересчет (Bundle Adjustment), а здесь — простой подсчет голосов. Это слабее. Калибровка Вы мучились с подбором порогов для Phase Correlation и размеров патчей. Вы мучились с порогами яркости и временными окнами. Это проклятие классического CV. В микроскопии я предлагал гибрид с Mutual Information. Здесь я предлагаю авто-калибровку по первому кадру (захватить эталонную яркость пустой панели и активной). Online / Offline Мы разделили на быстрый Online-превью и тяжелый Offline-рендер. MotionCode — это чистый Online. Offline не предусмотрен. Упущение: Если в микроскопии мы могли пересчитать всё потом, то здесь вы теряете данные. Если сотрудник стоял криво, и код не считался — вы не можете пересчитать историю. Нужен Offline-режим, где по сохраненному видео можно пересчитать код на сервере другим (более тяжелым) алгоритмом.

  1. Что я предлагаю (как связать это с прошлым обсуждением и усилить)

Помните наш гибрид Phase Correlation + Mutual Information? Здесь точно такая же история, но с другими компонентами. Применим ту же философию к MotionCode.

А. Гибрид “Яркость + Края” (аналог ФК + ВИ)

· Грубо (Яркость): Ваш текущий метод анализа центральных областей — быстрый, но ненадежный при свете. · Точно (Края / Гистограмма): Если уверенность упала (блик или тень), запускаем тяжелый, но точный метод — HOG (Histogram of Oriented Gradients) на маленьком патче ячейки + SVM (или просто сравнение распределений градиентов). Это устойчиво к экспозиции. Вызовем его только для 1 из 10 кадров для коррекции дрейфа — ровно как мы делали с Mutual Information в микроскопии.

Б. Введение “Offline-Арбитра” (аналог глобальной оптимизации)

Ваше приложение должно сохранять сырые кадры (или сжатые дескрипторы) в буфер на 5 секунд. Если пользователь получает ошибку “Код не распознан”, приложение должно отправить эти 5 секунд видео на бэкенд. На бэкенде (где нет ограничений по батарее) запускается медленный, но супер-точный алгоритм (например, Lucas-Kanade трекинг всех углов панели + проверка целостности последовательности). Это спасет сценарий, когда телефон нагрелся и просел FPS.

В. Безопасность через “Шум” (как у нас было с вращением)

В микроскопии мы игнорировали вращение, потому что оно было малым. Здесь вы игнорируете видео-повторы. Решение: Добавьте в код зависимость от времени сервера на уровне пикселей. Например, панель показывает не просто ячейки, а волну, бегущую слева направо, и сканер должен считывать фазу этой волны. Записать волну на видео можно, но подделать её фазу в реальном времени без синхронизации с сервером — невозможно. Это делает вашу систему устойчивой к replay.

  1. Итоговая ретроспектива

В обеих историях прослеживается один и тот же путь героя:

  1. Наивный старт: «Возьмем нейросеть/FFT, она всё решит».

  2. Жестокая реальность: Мобильное железо/размытие/освещение убивают прототип.

  3. Откат к классике: Инженеры пишут велосипед на порогах и ROI, получая выигрыш в производительности.

  4. Ад калибровки: Бесконечные правки параметров под разные устройства.

Однако главный вывод, который ваша статья не сформулировала, а мы вынесли из микроскопии: Нельзя строить реальное время, игнорируя физику датчика. В микроскопии это была автокоррекция фокуса и дрейф столика. Здесь — автоэкспозиция камеры.

Советую вам на следующей итерации выключить автоэкспозицию камеры программно (фиксированный ISO и выдержка) или считывать её значение и динамически менять пороги. Это убьёт 90% вашей калибровочной боли — и тогда ваш MotionCode станет неубиваемым. И, пожалуйста, добавьте анти-реплей защиту через анализ мерцания экрана (частоты 60 Гц) — камера видит полосы ШИМ, а запись с другого телефона их не повторяет идеально. Это как раз ваш “Mutual Information” для безопасности.

В лаборатории идеальных кадров не бывает, поэтому устойчивость важнее

А почему бы просто не прикрутить моторизированный stage? Смещаем на 10 микрон, фотаем, смещаем еще, опять фотаем.

На самом деле задача сшивки большого скана из набора маленьких фоток не нова, и уже существуют промышленные решения для этого - Whole Slide Imaging (WSI) scanner. Кладешь образец в сканер, он сам фотографирует и сшивает изображения. Но стоит такой аппарат немало, $10k и более.

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

Я больше про то, чтобы заменить ручное манипулирование предметным столом на моторизированное. Тогда многие проблемы исчезнут.

Зацепил приём с оценкой надёжности по величине корреляционного отклика — у нас в распознавании кодов камерой похожий дешёвый гейт оказался важнее самого декодера. Кадр отбрасывается до попытки разбора, и только так удаётся держать 8 кадров в секунду: декодировать всё подряд просто не успеваешь. Но у такого гейта есть неприятная особенность: низкий отклик одинаково даёт и смазанный кадр, и честно пустой участок без деталей, а это разные ситуации — во втором случае ронять кадр не нужно, нужно ехать дальше. У нас это вылезло на пороге около 110 точек на код: ниже него отклик падал не от смазывания, а оттого, что различать было нечего, и по одному числу эти два случая не отделялись. Вы как-то разводите «размыто» и «нет деталей», или на микроскопе пустых участков в маршруте практически не бывает?

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

В нашем решении используется относительно высокий порог отклика, потому что мы хотим все-таки собирать хорошие кадры. Ну и локальное повышение контрастности (CLAHE) помогает вытягивать детали из изображения, за счет чего отклик тоже возрастает.

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

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

Sign up to leave a comment.

Articles