6.18. Изображения и ndarray¶
Класс Image – это быстрый интерфейс для работы с пикселями в нативном формате камеры: каждый его метод работает напрямую с буфером кадра в нативном формате пикселей камеры. numpy – это универсальный числовой интерфейс для всего остального. Два метода связывают их между собой:
image.Image.to_ndarray()– копирует пиксели изображения вndarray.Конструктор
image.Image– создаёт новое изображение изndarray.
Вместе они позволяют приложению захватить кадр, передать его в numpy для произвольного преобразования, а затем вернуть результат обратно в изображение, чтобы отобразить его, сохранить или передать дальше в остальную часть библиотеки изображений.
6.18.1. Изображение в ndarray¶
to_ndarray() выделяет новый ndarray и копирует в него данные пикселей изображения (с приведением типов согласно таблице ниже). Это никогда не представление на буфер кадра изображения – массив numpy всегда владеет собственными байтами. Сигнатура имеет вид to_ndarray(dtype, *, buffer=None), а форма результата зависит от формата изображения:
GRAYSCALE – двумерный массив формы
(height, width).RGB565 – трёхмерный массив формы
(height, width, 3), плоскости в порядке R/G/B.
Аргумент dtype определяет, как отображается каждое 8-битное значение пикселя v:
|
элемент |
отображение для 8-битного значения пикселя |
|---|---|---|
|
|
|
|
|
|
|
|
|
Пример – представление кадра в оттенках серого как матрицы uint8:
import csi
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
a = img.to_ndarray('B') # shape (240, 320), dtype=uint8
print(a.shape, a.dtype)
print("mean brightness:", np.mean(a))
Ключевое слово buffer= позволяет приложению повторно использовать уже выделенный bytearray, чтобы камере не приходилось выделять новый на каждый кадр:
buf = bytearray(320 * 240)
while True:
img = csi0.snapshot()
a = img.to_ndarray('B', buffer=buf)
# ... process a ...
6.18.2. ndarray в изображение¶
Для обратного преобразования передайте ndarray в качестве первого аргумента в image.Image. Конструктор выделяет новый буфер изображения и копирует в него значения массива, ограниченные и округлённые до диапазона 0..255:
image.Image(arr, *, buffer=None, copy_to_fb=False)
Конструктор определяет геометрию и формат пикселей по форме массива:
форма
(h, w)– изображениеGRAYSCALE.форма
(h, w, 3)– изображениеRGB565.
ndarray должен иметь dtype float; на сегодня конструктор поддерживает только этот случай. Значения округляются и ограничиваются диапазоном 0..255.
buffer= позволяет приложению передать уже выделенный bytearray для результирующего изображения. copy_to_fb=True записывает результат в буфер кадра камеры, что является правильным выбором, когда результат должен появиться в предпросмотре IDE.
6.18.3. Полный цикл¶
import csi
import image
from ulab import numpy as np
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.GRAYSCALE)
csi0.framesize(csi.QVGA)
img = csi0.snapshot()
a = img.to_ndarray('f') # work in float space
a = 255.0 * (a / 255.0) ** 0.5 # gamma correction
out = image.Image(a, copy_to_fb=True) # back to an image
6.18.4. Когда использовать мост¶
Этот мост – правильный ответ, когда приложению требуется универсальная числовая операция, которую не предоставляют встроенные методы image – произвольные фильтры, произвольное смешивание, нестандартные нелинейности – или когда данные пикселей нужно объединить с не-изображенческими данными (оси IMU, аудиосэмплы) в одном вычислении.
Это не правильный ответ для высокопроизводительной обработки пикселей, которую класс Image уже покрывает. Встроенные методы работают напрямую с буфером кадра в нативном формате пикселей камеры и работают намного быстрее, чем эквивалентное выражение numpy. Используйте мост для операций, которые библиотека изображений ещё не предоставляет.