4.14. Conceitos básicos de CSI¶
O módulo csi é a forma como o código Python controla o sensor da câmara. Cada script que captura um fotograma segue a mesma estrutura de três partes: importações no topo, configuração inicial no meio, e um ciclo while True na parte inferior que vai buscar fotogramas à câmara um de cada vez.
4.14.1. O ciclo típico¶
import csi, image, time
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.QVGA)
clock = time.clock()
while True:
clock.tick()
img = csi0.snapshot()
# process img here
print(clock.fps())
4.14.2. O que cada chamada faz¶
import csi, image, timeImporta três módulos. O
csicontrola o sensor, oimagedefine a classeImagequesnapshot()devolve, e otimefornece o auxiliartime.clock()utilizado para medir os fotogramas por segundo.csi.CSI()Constrói uma instância de
CSIque encapsula um sensor de câmara físico. O construtor reivindica o periférico da câmara e regista a configuração por sensor. Câmaras com um único sensor têm uma instância deCSI; câmaras com dois sensores (cor mais térmico, cor mais eventos) têm duas, cada uma selecionada por um argumentocidao construtor.csi0.reset()Liga e configura o sensor. Por defeito, envia um pulso no pino de reset do sensor e depois escreve os registos I2C do sensor para um estado inicial conhecido. As chamadas de configuração subsequentes –
pixformat,framesize, os controlos automáticos – enviam mais escritas de registo pelo mesmo barramento de controlo I2C.csi0.pixformat(csi.RGB565)Escreve os registos do sensor que selecionam o formato de pixel de saída. As opções disponíveis são os formatos apresentados na página de formatos de pixel:
RGB565,GRAYSCALE,BAYER,YUV422, eJPEGnos sensores que o suportam.csi0.framesize(csi.QVGA)Escreve os registos que selecionam a resolução de saída. O
QVGAé 320 × 240; os tamanhos nomeados vão atéWQXGA2(2592 × 1944, cerca de 5 MP) nos sensores que os suportam. Um tuplo personalizado(width, height)também funciona, desde que se alinhe com as capacidades de saída do sensor.clock = time.clock()Cria um auxiliar de relógio. Cada chamada a
clock.tick()dentro do ciclo regista o tempo de início da iteração; otime.clock.fps()reporta a taxa de ciclo recente em fotogramas por segundo.img = csi0.snapshot()Captura um fotograma do sensor e devolve-o como uma
Image. A mecânica de como esse fotograma fica em memória merece uma análise mais atenta.
4.14.3. Como snapshot preenche a memória¶
O sensor entrega pixels no barramento de dados de pixel descrito em barramentos do sensor a velocidades de centenas de megabytes por segundo – demasiado rápido para o CPU copiar pixel a pixel em software.
Em vez disso, o MCU delega a transferência ao Direct Memory Access (DMA) – um motor de hardware separado do CPU que copia bytes de um lugar para outro dentro do MCU sem envolver o CPU. O periférico de entrada da câmara recebe cada byte de pixel recebido para um pequeno FIFO no chip; as fases ISP que correm no lado do MCU processam os dados durante a passagem; e o motor DMA escreve os pixels acabados num framebuffer na RAM no offset de pixel correspondente. Nada nessa cadeia necessita do CPU uma vez que o canal DMA tenha sido programado.
Quando snapshot() é chamado:
O driver CSI programa o motor DMA com o endereço do framebuffer, o comprimento da transferência (os pixels de um fotograma completo), e um callback para a interrupção de DMA concluído.
O driver ativa o periférico de entrada da câmara e aguarda que o sensor sinalize o início do próximo fotograma.
À medida que o sensor transmite o fotograma, o periférico passa cada byte de pixel pelo ISP e para o motor DMA, que escreve o resultado na RAM no próximo offset do framebuffer. O CPU está livre para executar outro código durante a transferência.
Quando o último pixel do fotograma chega, o DMA dispara a sua interrupção de conclusão, o driver encapsula o framebuffer numa
Image, esnapshot()devolve-a ao código do utilizador.
A Image devolvida não possui uma cópia dos dados de pixel – aponta para um dos framebuffers da câmara na RAM. O número de framebuffers que a câmara mantém, e como são passados entre o DMA e o código do utilizador em cada chamada a snapshot(), depende do modo de buffering que a aplicação selecionou através de framebuffers().