4.14. Noções básicas de CSI¶
O módulo csi é como o código Python controla o sensor da câmera. Todo script que captura um quadro segue o mesmo formato de três partes: imports no topo, configuração única no meio e um laço while True na parte inferior que puxa quadros da câmera um de cada vez.
4.14.1. O laço 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, timeTraz três módulos. O
csicontrola o sensor, oimagedefine a classeImagequesnapshot()retorna, e otimefornece o auxiliartime.clock()usado para medir os quadros por segundo.csi.CSI()Constrói uma instância de
CSIque encapsula um sensor físico de câmera. O construtor reivindica o periférico da câmera e registra a configuração específica do sensor. Câmeras com um único sensor têm uma instância deCSI; câmeras com dois sensores (cor mais térmico, cor mais evento) têm duas, cada uma selecionada por um argumentocidno construtor.csi0.reset()Liga e configura o sensor. Por padrão, ele pulsa o pino de reset do sensor e então grava os registradores I2C do sensor em um estado inicial conhecido. Chamadas de configuração subsequentes –
pixformat,framesize, os controles automáticos – enviam mais gravações de registrador pelo mesmo barramento de controle I2C.csi0.pixformat(csi.RGB565)Grava os registradores do sensor que escolhem o formato de pixel de saída. As opções disponíveis são os formatos que a página formatos de pixel apresentou:
RGB565,GRAYSCALE,BAYER,YUV422eJPEGnos sensores que o suportam.csi0.framesize(csi.QVGA)Grava os registradores que escolhem a resolução de saída.
QVGAé 320 × 240; os tamanhos nomeados chegam atéWQXGA2(2592 × 1944, cerca de 5 MP) nos sensores que os suportam. Uma tupla(width, height)personalizada também funciona, desde que esteja de acordo com as capacidades de saída do sensor.clock = time.clock()Cria um auxiliar de relógio. Cada chamada a
clock.tick()dentro do laço registra o horário de início da iteração;time.clock.fps()informa a taxa recente do laço em quadros por segundo.img = csi0.snapshot()Captura um quadro do sensor e o retorna como uma
Image. Vale a pena observar mais de perto a mecânica de como esse quadro vai parar na memória.
4.14.3. Como o snapshot preenche a memória¶
O sensor entrega pixels no barramento de dados de pixel descrito em barramentos do sensor a taxas de centenas de megabytes por segundo – rápido demais para a CPU copiar pixel a pixel em software.
Em vez disso, o MCU delega a transferência ao Direct Memory Access (DMA) – um mecanismo de hardware separado da CPU que copia bytes de um lugar para outro dentro do MCU sem envolver a CPU em nada. O periférico de entrada da câmera captura cada byte de pixel que chega em um pequeno FIFO on-chip; quaisquer estágios do ISP executados no lado do MCU processam os dados durante a passagem; e o mecanismo DMA grava os pixels finalizados em um framebuffer na RAM no deslocamento de pixel correspondente. Nada nessa cadeia precisa da CPU uma vez que o canal DMA tenha sido programado.
Quando snapshot() é chamado:
O driver CSI programa o mecanismo DMA com o endereço do framebuffer, o comprimento da transferência (os pixels equivalentes a um quadro) e um callback para a interrupção de conclusão do DMA.
O driver habilita o periférico de entrada da câmera e aguarda o sensor sinalizar o início do próximo quadro.
À medida que o sensor envia o quadro em fluxo, o periférico passa cada byte de pixel pelo ISP e em seguida para o mecanismo DMA, que grava o resultado na RAM no próximo deslocamento do framebuffer. A CPU fica livre para executar outro código durante a transferência.
Quando o último pixel do quadro chega, o DMA dispara sua interrupção de conclusão, o driver encapsula o framebuffer em uma
Image, esnapshot()o retorna ao código do usuário.
A Image retornada não possui uma cópia dos dados de pixel – ela aponta para um dos framebuffers da câmera na RAM. Quantos framebuffers a câmera mantém, e como eles são repassados entre o DMA e o código do usuário a cada chamada de snapshot(), depende do modo de buffering que a aplicação selecionou por meio de framebuffers().