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

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

5.6.1. Примитивы#

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

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

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

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

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

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

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

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

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

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

A grid of small panels showing each of the eight drawing primitives applied once. Each panel contains a line, a rectangle, a circle, an ellipse, a cross, an arrow, a quadrilateral, or a short text string, with the name of the method that produced it labelled underneath.

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

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() имеет значение именно для буферов, повторно используемых между кадрами.