5.12. Бинарная пороговая обработка

Множество конвейеров обработки изображений сводится к вопросу о каждом пикселе: находится ли эта яркость внутри диапазона, который означает «передний план»? Достаточно ли этот цвет близок к красному, чтобы быть маркером, который отслеживает приложение? Является ли этот пиксель частью набора кандидатов, на который должен смотреть следующий этап конвейера? Пороговая обработка – это операция, которая превращает эти вопросы в бинарный ответ в каждой позиции – включено, если пиксель соответствует, выключено, если нет, – и сводит всё изображение к маске, с которой может работать остальная часть конвейера.

5.12.1. Метод binary

Метод binary() выполняет эту классификацию по каждому пикселю одним вызовом. Он принимает список диапазонов порогов – условий, которым может соответствовать пиксель, чтобы считаться «включённым», – и переписывает изображение так, что каждый пиксель, соответствующий хотя бы одному из диапазонов, устанавливается в максимальное значение формата, а каждый, который не соответствует, устанавливается в нуль. Результат – это бинарная маска, которую остальная часть конвейера может использовать напрямую.

В простейшей форме список порогов содержит один диапазон, и вызов возвращает маску пикселей в этом диапазоне:

img.binary([(120, 255)])

Именно форма списка делает binary мощным. Конвейер, который хочет отслеживать два цветных маркера или диапазон яркости плюс изолированный пик насыщенности, передаёт оба диапазона в одном списке и получает единую выходную маску, охватывающую все совпадения.

Горизонтальный градиент оттенков серого сверху с подписью "вход -- значения пикселей от 0 до 255". Под ним вдоль градиента скобками отмечен включающий диапазон порога от lo до hi, охватывающий диапазон значений яркости, которые считаются совпадениями. Полоса бинарного выхода внизу показывает белый цвет внутри диапазона от lo до hi и чёрный за его пределами.

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

5.12.2. Кортеж для оттенков серого

Для изображения в оттенках серого каждая запись в списке порогов – это кортеж из двух элементов (lo, hi), описывающий включающий диапазон яркости. Пиксели со значениями между lo и hi (включительно) совпадают; всё за пределами этого диапазона – нет. Естественные шаблоны просты:

  • (0, 60) совпадает с тёмными пикселями – от чёрного до тёмно-серого.

  • (180, 255) совпадает с яркими пикселями – от светло-серого до белого.

  • (100, 160) совпадает с пикселями среднего серого – полоса в середине диапазона яркости.

Порядок двух значений внутри кортежа не имеет значения; метод меняет их местами внутренне, если lo больше hi, поэтому (60, 0) работает так же, как (0, 60).

5.12.3. Кортеж LAB для цвета

Для изображения RGB565 каждая запись – это кортеж из шести элементов (l_lo, l_hi, a_lo, a_hi, b_lo, b_hi), описывающий включающий диапазон в цветовом пространстве LAB, а не напрямую в красном, зелёном и синем. Пороги – это L (светлота), A (хроматическая ось от зелёного к красному) и B (хроматическая ось от синего к жёлтому), каждый из которых сравнивается со значением пикселя в этом канале.

Причина использования LAB вместо прямой пороговой обработки RGB – это свойство, ради которого было разработано цветовое пространство LAB: LAB отделяет светлоту от цветности. Два пикселя, которые показывают один и тот же цвет, но при разной яркости, оказываются с разными значениями L, но примерно с одинаковыми значениями A и B. Это разделение позволяет диапазонам порогов описывать цвет по его положению на осях A и B и оставлять диапазон L широко открытым, чтобы принимать этот цвет при любой яркости от тени до света. Порог на основе RGB этого сделать не может – любое изменение освещения сдвигает все три значения R, G, B одновременно, и трекер, построенный на порогах RGB, ломается в первый же раз, когда облако проходит мимо солнца.

Практический шаблон: выбрать диапазоны A и B, описывающие цвет, который отслеживает приложение, и оставить диапазон L широким – часто (0, 100), чтобы принимать любую яркость, – если только приложению специально не нужно применять порог к яркости наряду с цветом.

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

Примечание

По-настоящему широко открытый диапазон L имеет подвох на нижнем конце. По мере падения светлоты к нулю каждый цвет сходится к чёрному, при этом значения A и B стремятся к нулю и становятся подвержены доминированию шума – поэтому тёмные пиксели могут попасть в диапазоны A и B и отслеживаться как целевой цвет. Если чёрные области сцены загораются как совпадения, поднимайте l_lo, пока они не выпадут.

5.12.4. Флаги

Три именованных аргумента управляют выводом:

  • invert=True инвертирует результат. Каждый пиксель, который совпал бы, становится нулём, а каждый пиксель, который был бы нулём, становится максимальным значением. Полезно, когда естественный способ описать передний план – через то, чем он не является.

  • zero=True меняет режим работы: совпадающие пиксели обнуляются, а несовпадающие пиксели сохраняют свои исходные значения. Используйте это, когда цель – стереть совпадающие пиксели из изображения, а не свести изображение к их бинарной маске.

  • to_bitmap=True возвращает результат как изображение BINARY вместо перезаписи существующего формата источника. Результат с одним битом на пиксель – это то, что последующие аргументы маски принимают напрямую, и преобразование часто экономит память, избавляя от необходимости носить с собой маску полного формата.

Маска и ROI следуют той же конвенции, что и остальной интерфейс: прямоугольник roi ограничивает операцию подобластью, а изображение mask ограничивает её произвольным набором позиций.

5.12.5. По умолчанию на месте

Как и арифметические операции, binary по умолчанию выполняется на месте: пиксели исходного изображения перезаписываются бинарным выводом, и исходные значения теряются после вызова. Форма to_bitmap=True – это альтернатива, когда источник нужно сохранить, а вывод должен быть свежевыделенным изображением BINARY. Форма copy=True также принимается для результата в том же формате в новом буфере.