5.5. Области и маски¶
Каждая операция в модуле image по умолчанию затрагивает каждый пиксель исходного изображения. Это самое простое поведение для описания и правильное в тех случаях, когда задача алгоритма действительно охватывает весь кадр – равномерная цветокоррекция, глобальная гистограмма, проход кодирования для передачи. Но большинство алгоритмов на практике хотят рассматривать меньшую часть. Трекер блобов, следящий за цветным маркером, интересуется той частью сцены, где может появиться маркер, а не стеной за ним. Проход морфологической очистки безопасен только над теми пикселями, которые более ранний этап пометил как кандидаты. Детектор лиц может запускаться только внутри ограничивающей рамки, которую уже сузил более грубый детектор. Модуль image поддерживает такую работу через два механизма, ограничивающих операцию подмножеством пикселей: прямоугольные области интереса и двоичные маски. Они свободно сочетаются, и почти каждый метод, затрагивающий пиксели, принимает один из них – или оба – в качестве именованного аргумента.
5.5.1. Области интереса¶
Область интереса (ROI) – это прямоугольник пикселей, заданный четвёркой (x, y, w, h), представленной на странице о координатах. Около тридцати методов на поверхности принимают именованный аргумент roi; когда он присутствует, операция выполняется только над пикселями внутри этого прямоугольника и оставляет остальную часть изображения нетронутой. Когда roi равен None или опущен, операция выполняется над всем изображением – так же, как если бы было передано roi=(0, 0, width, height).
В коде это именованное слово стоит рядом с любыми другими аргументами, которые принимает операция:
# Compute a histogram over a centred crop of the image.
h = img.get_histogram(roi=(64, 64, 128, 128))
Первое, что дают области интереса (ROI), – это контроль ложных срабатываний. Цветовой трекер, который смотрит только на стол, никогда не сработает на проходящей мимо рубашке; детектор границ, который запускается только внутри заданной рабочей зоны, никогда не сообщит о границах самого крепления камеры. Сокращение области поиска до той части сцены, которая действительно важна для алгоритма, – это самое дешёвое улучшение, которое конвейер может внести в собственную надёжность.
Второе, что они дают, – это конвейер от грубого к точному. Объекты результатов обнаружения – blob, rect, apriltag и так далее – предоставляют свои ограничивающие рамки в виде той же четвёрки (x, y, w, h), которую принимает roi. Поэтому грубый первый этап может вернуть ограничивающую рамку, рамка напрямую подставляется в roi следующего этапа, и второй этап выполняется над более узкой областью. Каждое последовательное сужение и ускоряет следующий этап, и делает его результаты более надёжными, поскольку пространство поиска уже отфильтровано.
5.5.2. Двоичные маски¶
Прямоугольник – правильная форма, когда область интереса выровнена по осям. Когда это не так – криволинейная область, невыпуклая или пиксели, которые какой-то более ранний этап классифицировал как «совпадения», – операции нужно сообщить, чтобы она ограничила себя произвольным узором пикселей. Механизм для этого – двоичная маска: отдельное Image тех же размеров, что и исходное, используемое как попиксельный переключатель вкл/выкл. Ненулевой пиксель в маске означает «включить соответствующий исходный пиксель»; нулевой пиксель означает «оставить исходный пиксель без изменений».
Маска обычно представляет собой изображение BINARY – формат с одним битом на пиксель, существующий именно для этой цели, – но подойдёт любое одноканальное изображение, поскольку потребитель трактует любое ненулевое значение как включённое.
Методы фильтрации, пороговой обработки и арифметики принимают именованный аргумент mask. Форма одинакова для каждого: отдельно выделенное двоичное изображение тех же размеров, что и исходное, передаваемое внутрь.
Области интереса (ROI) и маски сочетаются. Передайте оба, и операция будет выполняться только над пикселями, которые находятся внутри ROI и включены в маске. Эти два механизма дают прикладному коду независимые рычаги – один для прямоугольной области интереса, другой для произвольного узора внутри неё – не заставляя ни одну из форм наследовать ограничения от другой.
ROI ограничивает операцию выровненным по осям прямоугольником. Маска дополнительно сужает её до произвольного узора пикселей. Они сочетаются: изменяются только пиксели внутри ROI и включённые в маске.¶
5.5.3. Построение масок¶
Три метода Image строят распространённые геометрии масок на месте, обнуляя пиксели вне выбранной области:
mask_rectangle()сохраняет прямоугольник.mask_circle()сохраняет круг.mask_ellipse()сохраняет эллипс.
Каждый принимает (x, y, w, h) (для прямоугольника и эллипса) или (x, y, radius) (для круга). Вызов любого из них без аргументов центрирует геометрию и задаёт её размер так, чтобы заполнить изображение, – именно к этой форме прибегает приложение, когда целью является простой полноразмерный овал или круг, который скрывает только углы.
mask = image.Image(img.width(), img.height(), image.BINARY)
mask.clear() # start from all zeros
mask.mask_ellipse() # centred, full-size oval
Интересные маски редко получаются из одних только методов mask_*. Они приходят из более ранних этапов конвейера: проход пороговой обработки производит двоичное изображение, ненулевые пиксели которого отмечают совпадения, – именно в нужной форме для подачи в аргумент mask= следующего этапа. Проход морфологической очистки уточняет эту маску, не меняя её формы. Всё, что в итоге становится одноканальным изображением, само по себе является допустимой маской.
5.5.4. Как операции изменяют изображение¶
Шаблон, видимый в каждом фрагменте кода на последних нескольких страницах, – операция возвращает то же самое img для сцепления вызовов – стоит явно выделить, чтобы его не приходилось повторять каждый раз при представлении нового метода. На поверхности Image появляются три семейства методов, каждое из которых трактует исходное изображение по-разному:
Оперирующие методы изменяют пиксели исходного изображения на месте и возвращают то же изображение для сцепления вызовов. Семейства рисования, арифметики, пороговой обработки и фильтрации все ведут себя именно так.
img.gaussian(1)размываетimgи возвращает то жеimg; переприсваивание –img = img.gaussian(1)– безвредно, но излишне.Методы преобразования по умолчанию работают на месте так же, как оперирующие методы, но они принимают
copy=Trueиcopy_to_fb=True, чтобы выделить отдельное результирующее изображение, когда исходное нужно сохранить. Преобразования форматов и геометрические копии – основные представители этого семейства.Методы инспекции читают пиксели и возвращают объект результата – список обнаруженных признаков, гистограмму, набор статистик – вообще не изменяя исходное изображение.
Эта трихотомия единообразна на всей поверхности. Знание того, к какому семейству принадлежит метод, говорит приложению, чего ожидать от вызова: останутся ли пиксели исходного изображения нетронутыми, будет ли выделено отдельное результирующее изображение и является ли возвращаемое значение самим исходным изображением или чем-то иным.