Сайт Олега Милантьева
Не так давно я «постиг Дзен» написания простого плугина обработки изображения в MaximDL. Пришло время реализовать то, зачем я затеял эту кашу. Начать писать плугин автофокусировки по маске Павла Бахтинова.
Начал я с того, что пробежал по картинке (двумерному массиву float чисел) квадратом 7х7 в поисках координат максимума. Тут самое главное, как я понимаю, верно подобрать размер квадрата. Слишком большой будет сбоить на слипшихся звёздах. Маленький же не точно найдёт центр на горелой звезде.
Ах нет. Начать надо с того, что я в жизни не занимался обработкой изображений ни на каком языке. И в жизни не писал на Visual Basic. Ещё хотел сказать, что я в жизни не писал плугинов к максиму! 🙂
Итак, центр звезды я нашёл. На анимашке слева виден кадр «как было» и «что натворил плугин». Пока что центр находится с недостаточной точностью — до целого числа. Но дальше — больше. Пока что мне нужно врубиться в задачу, оптимизацией же займусь на втором этапе.
Сам Павел Бахтинов предложил для компьютерного анализа другой вариант маски, дающий отклонение отрезка луча от прямой. Но я выбрал уже ставшей классической маску Бахтинова потому как, во-первых, она у меня есть и не надо резать новую. А, во-вторых, эта модификация маски очень распространена и, стало быть, не нужно будет заставлять людей резать новую маску только для моего автомата. Мало кто будет так заморачиваться и плугин проигнорируют. В третьих же, я побаиваюсь за искажения картины от маски, вызванные хроматизмом и дисперсией. Отклонения от линии даже при чёткой фокусировке могут убить всю кажущуюся простоту детектирования одной прямой вместо трёх.
Центр найден. Дальше нужно искать лучи и их пересечение. Лучи в разных фильтрах и на разных камерах с разной спектральной чувствительностью могут быть как линиями , так длинными штрихами (L или CFA, а так же R, G, B), так и набором коротких точек (все узкополосники). Масштаб картинки зависит от углового размера пикселя и шага маски, как я понимаю. Может и светосилы? Не суть. Я решаю задачу распознавания картинки. Задачу создания маски Павел Бахтинов уже успешно решил.
Что есть звезда? Нет, я не про термоядерные процессы в недрах светил. А про вид звезды «в маске» на фотографии. Вот 3D модель, где высотой показана интенсивность пикселя.
Мне сразу видится Гора. Отличная-симметричная такая Гора. Большая. С крутыми склонами и огненной шапкой. Зелёный лес посредине и синее… наверное, это гора в море :). Отвлёкся. Но красиво же? В MaximDL, где построена эта 3D-карта интенсивности, так же есть возможность покрутить гору тыды-сюды мышкой. Очень удобно, рекомендую.
Гора, кроме своей красоты, имеет чётко выраженные отроги (хребты). Которые, собственно, и нужно найти. Вот предо мною 2D массив со значением интенсивности каждого пикселя. И что с ним делать? Ладно, центр я нашёл, тут всё просто. Это была хорошая тренировка. Но что дальше?
Перебрав несколько вариантов я пришёл к такому. Буду строить круги и анализировать интенсивности на них. Искать срезы хребтов, как сначала мало, но больше фона. Потом больше, чем первое, потом меньше второго и так до перехода в фон.
Что-то даже начало получаться, но или слишком мало определялось, или слишком много, как на картинке слева. Это не распознавание, а усложнение себе жизни.
А потом, в процессе проб и, в основном, ошибок, я упростил алгоритм. И, на удивление, наилучшим решением оказалось простое. Найти среднее по радиусу и искать группы, длинее 1 пикселя, выше среднего (картинка справа).
Оставалось лишь найти центр найденных отрезков (пиков). Это несложно сделать с субпиксельной точностью. Множим координату на интенсивность, делим на количество. Плюс интерполяция угла по найденному субпиксельному индексу. Звучит громоздко, но решается десятком строк кода.
Теперь просканирую все радиусы от 5 пикселей до края листа и вуаля… у меня есть искомые точки. По ним уже можно строить уравнение прямых и сравнивать их математически. Возможно, этот метод я реализую. Если пойму, что придуманный мною простой метод не удовлетворяет требуемой точности.
На э… эскизе справа схематично показана анатомия жука в разрезе. Как у любого насекомого, у него шесть лапок и пузико :). Боковые передние и задние лапки обычно симметричны. А искомые передняя средняя и задняя средняя гуляют налево-направо, означая дефокус.
Сначала я хотел мерять развёрнутый угол, обозначенный синим на картинке. Меньше или больше 180 означано бы дефокус. Но потом я понял, что замеряя разницу между «красными» углами (см. картинку) я получу данные чуть точнее. К тому же этих «красных» углов вдвое больше синих. Два сверху и два снизу. Есть что усреднить.
Однако, пришлось взять паузу и отложить анализ данных. Начать понемногу формировать интерфейс приложения. Ничего сверхсложного не планировалось, однако кой-какие настройки нужно было вынести в диалог плугина.
Покончив с интерфейсом я вернулся к распознаванию жука. Стала актуальной задача поиска эффективного размера жука по количеству определённых пиков > 4. Сделал автопоиск и ручную подгонку центра.
Что есть эффективный радиус? Зачем он нужен, почему так важен? Очень просто.
Если в радиусе 5 пикселей длина окружности лишь 2*pi*r = 31 пиксель, то и точность определения азимута луча будет не сильно выше 1/31 * 360 = 11.4 градуса. Да, её можно чуть повысить субпиксельным определением веса, поиском вершины хребта. Допустим вдвое. 5 градусов? Очень мало! Метод работает точнее на большом радиусе. На максимально возможном! Там, где отчётливо видны все шесть лучей и где они максимально далеко от центра. На радиусе 100 пикселей длина окружности уже 628 пикселей и пиксельная точность определения азимута 1/628 * 360 = 0.57°, то есть в 20 раз выше!
Так что нашёл я эффективный радиус. От него шесть радиусов вниз, усредняя по пути азимуты лучей. В итоге нашёл красные углы.
Остаётся только найти какие из углов меньше, чтобы найти ориентацию жука. Мальчик он или девочка, вот в чём вопрос! Для вертикального жука номера углов отличаются от горизонтального.
Для повышения точности анализа добавил медианный фильтр и возможность увеличить картинку в 2 и 4 раза.
Вуаля. v0.1alpha готова к использованию.
Дефокус, точнее разность красных углов составляет 27 угловых минут. И правда, если присмотреться, на вроде как идеально сфокусированном жуке есть лёгкое смещение центрального луча направо-вверх.
… продолжение следует! А пока займусь тестированием «альфа-версии».
Центр определять не надо 🙂 Достаточно круга с центром где-то в районе звезды.
Выпустил бета-версию, но пока проблемы с установкой плугина.
Сейчас важно отладить весь цикл, перечисленный в roadmap. А потом уж буду заниматься оптимизацией. В том числе введу несколько алгоритмов поиска дефокуса в программу. С возможностью переключаться на понравившийся из настроек программы.