6.18. Зображення та ndarray¶
Клас Image є швидкою поверхнею для роботи з пікселями у рідному форматі камери: кожен його метод оперує безпосередньо з кадровим буфером у рідному піксельному форматі камери. numpy — це загальна числова поверхня для всього іншого. Два методи утворюють між ними міст:
image.Image.to_ndarray()– копіює пікселі зображення вndarray.Конструктор
image.Image– будує нове зображення зndarray.
Разом вони дозволяють застосунку зробити знімок кадру, передати його в numpy для виконання користувацького перетворення, а потім повернути результат назад у зображення для відображення, збереження або передачі до решти бібліотеки зображень.
6.18.1. Зображення до ndarray¶
to_ndarray() виділяє новий ndarray і копіює піксельні дані зображення в нього (з відображенням dtype, описаним нижче). Це ніколи не є виглядом на кадровий буфер зображення – масив 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. Звертайтеся до мосту лише для операцій, які бібліотека зображень ще не надає.