4.14. Основы CSI¶
Модуль csi – это способ, которым код на Python управляет датчиком камеры. Каждый скрипт, который захватывает кадр, следует одной и той же трёхчастной структуре: импорты вверху, однократная настройка в середине и цикл while True внизу, который извлекает кадры с камеры по одному за раз.
4.14.1. Типичный цикл¶
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. Что делает каждый вызов¶
import csi, image, timeПодключает три модуля.
csiуправляет датчиком,imageопределяет классImage, который возвращаетsnapshot(), аtimeпредоставляет помощникtime.clock(), используемый для измерения числа кадров в секунду.csi.CSI()Создаёт экземпляр
CSI, который обёртывает один физический датчик камеры. Конструктор захватывает периферийное устройство камеры и записывает конфигурацию для конкретного датчика. Камеры с одним датчиком имеют один экземплярCSI; камеры с двумя датчиками (цветной плюс тепловой, цветной плюс событийный) имеют два, каждый из которых выбирается аргументомcidконструктора.csi0.reset()Включает питание и конфигурирует датчик. По умолчанию он подаёт импульс на вывод сброса датчика, затем записывает регистры I2C датчика в известное начальное состояние. Последующие вызовы конфигурации –
pixformat,framesize, ручки автоматического управления – отправляют дополнительные записи в регистры по той же управляющей шине I2C.csi0.pixformat(csi.RGB565)Записывает регистры датчика, которые выбирают выходной формат пикселей. Доступные варианты – это форматы, представленные на странице форматы пикселей:
RGB565,GRAYSCALE,BAYER,YUV422иJPEGна датчиках, которые его поддерживают.csi0.framesize(csi.QVGA)Записывает регистры, которые выбирают выходное разрешение.
QVGA– это 320 × 240; именованные размеры доходят доWQXGA2(2592 × 1944, около 5 МП) на датчиках, которые их поддерживают. Пользовательский кортеж(width, height)тоже работает, если он согласуется с выходными возможностями датчика.clock = time.clock()Создаёт вспомогательный объект часов. Каждый вызов
clock.tick()внутри цикла записывает время начала итерации;time.clock.fps()сообщает недавнюю частоту цикла в кадрах в секунду.img = csi0.snapshot()Захватывает один кадр с датчика и возвращает его как
Image. Механика того, как этот кадр оказывается в памяти, заслуживает более пристального рассмотрения.
4.14.3. Как snapshot заполняет память¶
Датчик доставляет пиксели по шине пиксельных данных, описанной в шины датчика, со скоростью в сотни мегабайт в секунду – слишком быстро, чтобы CPU копировал их пиксель за пикселем программно.
Вместо этого MCU перекладывает передачу на прямой доступ к памяти (DMA) – аппаратный механизм, отдельный от CPU, который копирует байты из одного места в другое внутри MCU, вообще не задействуя CPU. Входное периферийное устройство камеры захватывает каждый входящий байт пикселя в небольшой FIFO на кристалле; те этапы ISP, которые выполняются на стороне MCU, обрабатывают данные по пути; а механизм DMA записывает готовые пиксели в буфер кадра в RAM по соответствующему пиксельному смещению. Ничто в этой цепочке не нуждается в CPU после того, как канал DMA запрограммирован.
Когда вызывается snapshot():
Драйвер CSI программирует механизм DMA адресом буфера кадра, длиной передачи (объёмом пикселей одного кадра) и функцией обратного вызова для прерывания завершения DMA.
Драйвер включает входное периферийное устройство камеры и ждёт, пока датчик не сигнализирует о начале следующего кадра.
По мере того как датчик передаёт кадр, периферийное устройство пропускает каждый байт пикселя через ISP и далее в механизм DMA, который записывает результат в RAM по следующему смещению буфера кадра. CPU свободен для выполнения другого кода во время передачи.
Когда приходит последний пиксель кадра, DMA генерирует прерывание завершения, драйвер обёртывает буфер кадра в
Image, иsnapshot()возвращает его пользовательскому коду.
Возвращаемый Image не владеет копией пиксельных данных – он указывает на один из буферов кадра камеры в RAM. Сколько буферов кадра хранит камера и как они передаются между DMA и пользовательским кодом при каждом вызове snapshot(), зависит от режима буферизации, выбранного приложением через framebuffers().