6.18. Imagini și ndarray-uri

Clasa Image este suprafața rapidă pentru lucrul cu pixeli în formatul nativ al camerei: fiecare metodă a sa operează direct asupra tamponului de cadre (frame buffer) în formatul de pixeli nativ al camerei. numpy este suprafața numerică generică pentru tot restul. Două metode fac legătura între ele:

Împreună, ele permit unei aplicații să capteze un cadru, să îl predea lui numpy pentru o transformare personalizată, apoi să pună rezultatul înapoi într-o imagine pentru a-l afișa, salva sau a-l reintroduce în restul bibliotecii de imagini.

6.18.1. De la imagine la ndarray

to_ndarray() alocă un nou ndarray și copiază în el datele de pixeli ale imaginii (cu maparea dtype de mai jos). Nu este niciodată o vizualizare (view) asupra tamponului de cadre al imaginii – tabloul numpy deține întotdeauna propriii octeți. Semnătura este to_ndarray(dtype, *, buffer=None), iar forma rezultatului depinde de formatul imaginii:

  • GRAYSCALE – tablou 2-D, formă (height, width).

  • RGB565 – tablou 3-D, formă (height, width, 3), planurile în ordinea R/G/B.

Argumentul dtype controlează modul în care fiecare valoare de pixel pe 8 biți v este mapată:

dtype

element

maparea pentru o valoare de pixel pe 8 biți v

'B'

uint8

v (brut)

'b'

int8

v - 128 (recentrat în jurul lui zero)

'f'

float32

float(v) (0.0 … 255.0)

Exemplu – vizualizarea unui cadru în tonuri de gri ca o matrice 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))

Cuvântul-cheie buffer= permite aplicației să reutilizeze un bytearray deja alocat, astfel încât camera să nu fie nevoită să aloce unul nou la fiecare cadru:

buf = bytearray(320 * 240)
while True:
    img = csi0.snapshot()
    a   = img.to_ndarray('B', buffer=buf)
    # ... process a ...

6.18.2. De la ndarray la imagine

În sens invers, transmite ndarray ca prim argument către image.Image. Constructorul alocă un nou tampon de imagine și copiază în el valorile tabloului, limitate și rotunjite la 0..255

image.Image(arr, *, buffer=None, copy_to_fb=False)

Constructorul deduce geometria și formatul de pixeli din forma tabloului:

  • forma (h, w) – imagine GRAYSCALE.

  • forma (h, w, 3) – imagine RGB565.

Tabloul ndarray trebuie să aibă dtype float; constructorul acceptă în prezent doar acest caz. Valorile sunt rotunjite și limitate la intervalul 0..255.

buffer= permite aplicației să furnizeze un bytearray deja alocat pentru imaginea rezultată. copy_to_fb=True scrie rezultatul în tamponul de cadre al camerei, ceea ce este alegerea potrivită atunci când rezultatul trebuie să apară în previzualizarea din IDE.

6.18.3. Dus-întors

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. Când să folosești puntea

Această punte este răspunsul potrivit atunci când aplicația are nevoie de o operație numerică generică pe care metodele încorporate din image nu o oferă – filtre personalizate, amestecuri personalizate, neliniarități neobișnuite – sau atunci când datele de pixeli trebuie combinate cu date care nu sunt imagini (axe IMU, eșantioane audio) într-un singur calcul.

Nu este răspunsul potrivit pentru procesarea de pixeli cu debit mare pe care clasa Image o acoperă deja. Metodele încorporate operează direct asupra tamponului de cadre în formatul de pixeli nativ al camerei și sunt mult mai rapide decât expresia numpy echivalentă. Recurge la punte pentru operațiile pe care biblioteca de imagini nu le oferă deja.