5.6. Рисование фигур и текста

Алгоритм, который что-то определяет по изображению, часто нуждается в том, чтобы это решение было видимым. Детектор блобов находит область, которая важна приложению; приложение хочет, чтобы эта область была нарисована на кадре, чтобы оператор – или разработчик, запускающий скрипт – мог увидеть, что было найдено. Преобразование координат отображает входную позицию в выходную; отладка обычно означает отметку обеих позиций на одном изображении. Предпросмотр в IDE считывает всё, что находится в буфере кадра в момент опроса, поэтому простейший способ сделать вывод алгоритма видимым – записать аннотации прямо в буфер кадра. Семейство методов рисования класса Image – это набор инструментов именно для такой работы.

5.6.1. Примитивы

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

  • draw_line() – прямой отрезок линии между двумя конечными точками.

  • draw_rectangle() – выровненный по осям прямоугольник, полый или закрашенный.

  • draw_circle() – окружность вокруг центра, полая или закрашенная.

  • draw_ellipse() – эллипс с произвольным поворотом.

  • draw_cross() – знак плюс в точке, обычная метка для центроида или цели клика.

  • draw_arrow() – стрелка от начальной точки к конечной.

  • draw_edges() – четыре стороны произвольного четырёхугольника по четырём угловым точкам; естественный способ обвести обнаруженный тег или область с перспективным искажением.

  • draw_string() – текст из встроенного растрового шрифта.

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

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

Восемь примитивов рисования, по одному на панель. Каждый метод создаёт один вид метки.

5.6.2. Цвет

Каждый метод рисования принимает аргумент color, который решает, какое значение записать в каждый закрашиваемый пиксель. Форма этого аргумента зависит от формата изображения. Для изображения RGB565 естественной формой является кортеж (r, g, b), где каждый канал находится в диапазоне 0255; модуль упаковывает это в 16-битное слово RGB565 перед записью. Для изображения в оттенках серого естественной формой является одно целое число яркости от 0 (чёрный) до 255 (белый). Методы также принимают сырое хранимое значение формата – 16-битное упакованное слово для RGB565, 8-битное целое для оттенков серого – что является эффективной формой, когда цвет был вычислен где-то ещё и уже находится в хранимой форме.

Опущение аргумента color закрашивает белым. Это значение по умолчанию удобно для работы с оттенками серого, где белый – максимальное значение и чётко читается на большинстве фонов. Для отладочных наложений RGB565 это почти всегда неправильно: зелёный или красный обычно лучше читается на той сцене, которую камера на самом деле видит, а явный цвет передаёт намерение.

5.6.3. Толщина и заливка

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

  • thickness=N задаёт ширину линии в пикселях. По умолчанию равно 1, что подходит для большинства наложений; большее значение полезно, когда аннотация должна оставаться видимой на насыщенной сцене или после того, как последующий этап конвейера ещё больше изменит изображение.

  • fill=True переключает метку с контура на сплошную, закрашивая каждый внутренний пиксель выбранным цветом. По умолчанию равно False.

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

5.6.4. Рисование текста

draw_string() выводит символы из встроенного растрового шрифта размером 8 на 10 пикселей. x и y – это верхний левый угол ячейки первого символа, text – строка для рисования, а color следует тому же соглашению, что и геометрические методы. Шрифт несёт полный диапазон печатаемого ASCII и не имеет кернинга – каждый символ занимает одну и ту же ячейку шириной 8 пикселей – что делает вывод легко позиционируемым.

img.draw_string(10, 10, "blobs: 3", color=(0, 255, 0))

Строка может включать символы новой строки (\n); каждый символ новой строки перемещает следующий символ к началу новой строки на десять пикселей ниже предыдущей. Аргумент scale рисует каждый символ большего размера с коэффициентом с плавающей точкой, а x_spacing и y_spacing добавляют отступ вокруг каждого символа. Небольшой набор флагов поворота / зеркалирования / отражения применяется либо ко всей строке, либо к каждому символу по отдельности – достаточно контроля, чтобы расположить текст вдоль угла или у негоризонтальной границы, когда того требует разметка.

5.6.5. Очистка холста

Один метод в этом семействе не рисует никакой конкретной метки. Он просто сбрасывает каждый пиксель изображения в ноль:

  • clear() – обнуляет каждый пиксель, опционально с ограничением до ROI или с областью действия через маску.

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

Только что выделенное изображение уже обнулено конструктором, поэтому clear() имеет значение именно для буферов, повторно используемых между кадрами.