4.14. Conceptos básicos de CSI¶
El módulo csi es la forma en que el código Python controla el sensor de la cámara. Todo script que captura un fotograma sigue la misma estructura de tres partes: las importaciones en la parte superior, la configuración única en el medio y un bucle while True en la parte inferior que extrae los fotogramas de la cámara de uno en uno.
4.14.1. El bucle 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. Qué hace cada llamada¶
import csi, image, timeIncorpora tres módulos.
csicontrola el sensor,imagedefine la claseImageque devuelvesnapshot(), ytimeproporciona el ayudantetime.clock()que se usa para medir los fotogramas por segundo.csi.CSI()Construye una instancia de
CSIque envuelve un sensor físico de cámara. El constructor reclama el periférico de la cámara y registra la configuración específica del sensor. Las cámaras con un solo sensor tienen una instancia deCSI; las cámaras con dos sensores (color más térmico, color más eventos) tienen dos, cada una seleccionada mediante un argumentociddel constructor.csi0.reset()Alimenta y configura el sensor. De forma predeterminada genera un pulso en el pin de reinicio del sensor y luego escribe los registros I2C del sensor a un estado inicial conocido. Las llamadas de configuración posteriores –
pixformat,framesize, los controles automáticos – envían más escrituras de registro por el mismo bus de control I2C.csi0.pixformat(csi.RGB565)Escribe los registros del sensor que seleccionan el formato de píxel de salida. Las opciones disponibles son los formatos que presentó la página de formatos de píxel:
RGB565,GRAYSCALE,BAYER,YUV422yJPEGen los sensores que lo admiten.csi0.framesize(csi.QVGA)Escribe los registros que seleccionan la resolución de salida.
QVGAes 320 × 240; los tamaños con nombre llegan hastaWQXGA2(2592 × 1944, unos 5 MP) en los sensores que lo admiten. Una tupla(width, height)personalizada también funciona, siempre que se ajuste a las capacidades de salida del sensor.clock = time.clock()Crea un ayudante de reloj. Cada llamada a
clock.tick()dentro del bucle registra el tiempo de inicio de la iteración;time.clock.fps()informa de la tasa reciente del bucle en fotogramas por segundo.img = csi0.snapshot()Captura un fotograma del sensor y lo devuelve como un
Image. Vale la pena examinar más de cerca la mecánica de cómo ese fotograma acaba en memoria.
4.14.3. Cómo snapshot llena la memoria¶
El sensor entrega píxeles por el bus de datos de píxel descrito en buses del sensor a velocidades de cientos de megabytes por segundo – demasiado rápido para que la CPU los copie píxel a píxel por software.
En su lugar, el MCU delega la transferencia al acceso directo a memoria (DMA) – un motor de hardware separado de la CPU que copia bytes de un lugar a otro dentro del MCU sin involucrar a la CPU en absoluto. El periférico de entrada de la cámara captura cada byte de píxel entrante en una pequeña FIFO en el chip; las etapas del ISP que se ejecuten en el lado del MCU procesan los datos a su paso; y el motor DMA escribe los píxeles terminados en un framebuffer en la RAM, en el desplazamiento de píxel correspondiente. Nada en esa cadena necesita la CPU una vez programado el canal DMA.
Cuando se llama a snapshot():
El controlador CSI programa el motor DMA con la dirección del framebuffer, la longitud de la transferencia (los píxeles de un fotograma) y una función de retorno (callback) para la interrupción de fin de DMA.
El controlador habilita el periférico de entrada de la cámara y espera a que el sensor señale el inicio del siguiente fotograma.
A medida que el sensor transmite el fotograma, el periférico pasa cada byte de píxel a través del ISP y luego al motor DMA, que escribe el resultado en la RAM en el siguiente desplazamiento del framebuffer. La CPU queda libre para ejecutar otro código durante la transferencia.
Cuando llega el último píxel del fotograma, el DMA dispara su interrupción de finalización, el controlador envuelve el framebuffer en un
Image, ysnapshot()lo devuelve al código de usuario.
El Image devuelto no posee una copia de los datos de píxel – apunta a uno de los framebuffers de la cámara en la RAM. Cuántos framebuffers mantiene la cámara, y cómo se transfieren entre el DMA y el código de usuario en cada llamada a snapshot(), depende del modo de almacenamiento en búfer que la aplicación haya seleccionado mediante framebuffers().