6.18. Kuvat ja ndarrayt

Image-luokka on nopea rajapinta kameran natiiviin pikselikäsittelyyn: jokainen sen metodi toimii suoraan kehyspuskuriin kameran natiivissa pikseliformaatissa. numpy on yleiskäyttöinen numeerinen rajapinta kaikkeen muuhun. Kaksi metodia yhdistävät ne:

Yhdessä ne antavat sovelluksen ottaa kehyksen, luovuttaa sen numpy:lle mukautettua muunnosta varten ja sijoittaa tuloksen sitten takaisin kuvaan näytettäväksi, tallennettavaksi tai syötettäväksi muulle kuvakirjastolle.

6.18.1. Kuvasta ndarrayksi

to_ndarray() varaa uuden ndarray-olion ja kopioi siihen kuvan pikselidatan (alla olevan dtype-kuvauksen mukaisesti). Se ei koskaan ole näkymä kuvan kehyspuskuriin – numpy-taulukko omistaa aina omat tavunsa. Allekirjoitus on to_ndarray(dtype, *, buffer=None), ja tuloksen muoto riippuu kuvaformaatista:

  • GRAYSCALE – 2-ulotteinen taulukko, muoto (height, width).

  • RGB565 – 3-ulotteinen taulukko, muoto (height, width, 3), tasot järjestyksessä R/G/B.

dtype-argumentti hallitsee, miten kukin 8-bittinen pikseliarvo v kuvataan:

dtype

alkio

kuvaus 8-bittiselle pikseliarvolle v

'B'

uint8

v (raaka)

'b'

int8

v - 128 (keskitetty uudelleen nollan ympärille)

'f'

float32

float(v) (0.0 … 255.0)

Esimerkki – tarkastele harmaasävykehystä uint8-matriisina:

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=-avainsana antaa sovelluksen käyttää uudelleen jo varaamaansa bytearray-oliota, joten kameran ei tarvitse varata uutta jokaista kehystä varten:

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

6.18.2. ndarraysta kuvaksi

Toiseen suuntaan: välitä ndarray ensimmäisenä argumenttina image.Image:lle. Konstruktori varaa uuden kuvapuskurin ja kopioi taulukon arvot siihen, rajattuna ja pyöristettynä välille 0..255

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

Konstruktori päättelee geometrian ja pikseliformaatin taulukon muodosta:

  • muoto (h, w)GRAYSCALE-kuva.

  • muoto (h, w, 3)RGB565-kuva.

ndarray:n dtypen on oltava float; konstruktori tukee tällä hetkellä vain tätä tapausta. Arvot pyöristetään ja rajataan välille 0..255.

buffer= antaa sovelluksen tarjota bytearray-olion, jonka se on jo varannut tulokseksi tulevaa kuvaa varten. copy_to_fb=True kirjoittaa tuloksen kameran kehyspuskuriin, mikä on oikea valinta silloin, kun tuloksen tulee näkyä IDE:n esikatselussa.

6.18.3. Edestakainen muunnos

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. Milloin yhdistää

Tämä silta on oikea ratkaisu silloin, kun sovellus tarvitsee yleiskäyttöisen numeerisen operaation, jota sisäänrakennetut image-metodit eivät tarjoa – mukautettuja suodattimia, mukautettuja sekoituksia, epätavallisia epälineaarisuuksia – tai kun pikselidata on yhdistettävä ei-kuvadataan (IMU-akselit, äänitteet) yhdessä laskennassa.

Se ei ole oikea ratkaisu korkean läpäisykyvyn pikselikäsittelyyn, jonka Image-luokka jo kattaa. Sisäänrakennetut metodit toimivat suoraan kehyspuskuriin kameran natiivissa pikseliformaatissa ja ovat paljon nopeampia kuin vastaava numpy-lauseke. Käytä siltaa operaatioihin, joita kuvakirjasto ei jo tarjoa.