6.18. Imagens e ndarrays¶
A classe Image é a superfície rápida para trabalho com pixels nativos da câmara: cada método nela opera diretamente no buffer de fotograma no formato de pixel nativo da câmara. O módulo numpy é a superfície numérica genérica para tudo o resto. Dois métodos fazem a ponte entre eles:
image.Image.to_ndarray()– copia os pixels de uma imagem para umndarray.O construtor
image.Image– constrói uma nova imagem a partir de umndarray.
Em conjunto, permitem que uma aplicação capture um fotograma, passe-o ao numpy para uma transformação personalizada e, depois, coloque o resultado de volta numa imagem para apresentar, guardar ou injetar no resto da biblioteca de imagem.
6.18.1. Imagem para ndarray¶
to_ndarray() aloca um novo ndarray e copia os dados de pixel da imagem para ele (com o mapeamento de dtype indicado abaixo). Nunca é uma vista sobre o buffer de fotograma da imagem – o array numpy possui sempre os seus próprios bytes. A assinatura é to_ndarray(dtype, *, buffer=None), e a forma da saída depende do formato da imagem:
GRAYSCALE – array 2-D, forma
(height, width).RGB565 – array 3-D, forma
(height, width, 3), planos na ordem R/G/B.
O argumento dtype controla como cada valor de pixel de 8 bits v é mapeado:
|
elemento |
mapeamento para um valor de pixel de 8 bits |
|---|---|---|
|
|
|
|
|
|
|
|
|
Exemplo – ver um fotograma em escala de cinzentos como uma matriz 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))
A palavra-chave buffer= permite à aplicação reutilizar um bytearray já alocado, evitando assim que a câmara tenha de alocar um novo em cada fotograma:
buf = bytearray(320 * 240)
while True:
img = csi0.snapshot()
a = img.to_ndarray('B', buffer=buf)
# ... process a ...
6.18.2. ndarray para imagem¶
No sentido inverso, passe o ndarray como primeiro argumento para image.Image. O construtor aloca um novo buffer de imagem e copia os valores do array para ele, limitados e arredondados para 0..255
image.Image(arr, *, buffer=None, copy_to_fb=False)
O construtor infere a geometria e o formato de pixel a partir da forma do array:
forma
(h, w)– imagemGRAYSCALE.forma
(h, w, 3)– imagemRGB565.
O ndarray deve ter dtype float; o construtor apenas suporta este caso atualmente. Os valores são arredondados e limitados ao intervalo 0..255.
buffer= permite à aplicação fornecer um bytearray já alocado para a imagem resultante. copy_to_fb=True escreve o resultado no buffer de fotograma da câmara, o que é a escolha certa quando o resultado deve aparecer na pré-visualização da IDE.
6.18.3. Viagem de ida e volta¶
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. Quando usar a ponte¶
Esta ponte é a resposta certa quando a aplicação precisa de uma operação numérica genérica que os métodos integrados do image não fornecem – filtros personalizados, combinações personalizadas, não-linearidades invulgares – ou quando os dados de pixel precisam de ser combinados com dados que não são de imagem (eixos IMU, amostras de áudio) num único cálculo.
Não é a resposta certa para o processamento de pixels de alto débito que a classe Image já cobre. Os métodos integrados operam diretamente no buffer de fotograma no formato de pixel nativo da câmara e são muito mais rápidos do que a expressão equivalente em numpy. Recorra à ponte apenas para as operações que a biblioteca de imagem ainda não fornece.