4.18. Несколько датчиков¶
Небольшое число камер OpenMV Cam объединяют два датчика изображения на одной плате – чаще всего цветную камеру вместе с тепловым датчиком FLIR® Lepton®, но та же схема применима к платам «цвет плюс события» и любому будущему оборудованию с двумя датчиками. Каждый датчик имеет собственную пиксельную матрицу, собственную шину управления и работает в собственном конвейере с собственной частотой кадров. API CSI расширяется для их поддержки, позволяя приложению создавать по одному объекту CSI на каждый физический датчик.
4.18.1. Выбор датчика¶
Конструктор CSI принимает аргумент cid, который называет конкретный датчик на плате. cid=-1 (значение по умолчанию) выбирает основной датчик; именованные константы cid выбирают вторичный по идентификатору микросхемы:
import csi
csi_rgb = csi.CSI() # primary colour sensor
csi_thermal = csi.CSI(cid=csi.LEPTON) # FLIR® Lepton®
Каждый экземпляр владеет собственной конфигурацией – пиксельный формат, размер кадра, регуляторы экспозиции / усиления, пул буфера кадра – и сбрасывается, настраивается и читается независимо от других. Константы для поддерживаемых вторичных датчиков (LEPTON, GENX320 и другие, перечисленные в справочнике CSI) называют микросхему, которую приложение ожидает на вторичном порту; драйвер прерывает создание объекта, если фактическая микросхема не совпадает.
4.18.2. Захват с обоих датчиков¶
Каждый датчик работает в своём конвейере захвата независимо от другого – цветной датчик может выдавать тридцать кадров в секунду, в то время как Lepton® выдаёт девять. Самый прямой способ справиться с этим несоответствием – позволить более быстрому датчику управлять циклом и читать более медленный датчик неблокирующе, забирая то, что готово, и пропуская итерацию, когда ничего нет:
import csi
csi_rgb = csi.CSI()
csi_thermal = csi.CSI(cid=csi.LEPTON)
csi_rgb.reset() # powers the rail, pulses RESET
csi_rgb.pixformat(csi.RGB565)
csi_rgb.framesize(csi.QVGA)
csi_thermal.reset(hard=False) # I2C reconfigure only
csi_thermal.pixformat(csi.GRAYSCALE)
csi_thermal.framesize(csi.QQVGA)
while True:
rgb_img = csi_rgb.snapshot() # blocks for next colour frame
thermal_img = csi_thermal.snapshot(blocking=False) # returns None if not ready
if thermal_img is not None:
# process aligned colour + thermal pair
pass
else:
# process colour only on this iteration
pass
Блокирующий snapshot() задаёт темп цикла; неблокирующий возвращает самый свежий тепловой кадр, если новый поступил с момента предыдущего вызова, и None в противном случае. Приложение продолжает работать с частотой кадров цветного датчика и получает тепловой кадр всякий раз, когда Lepton® его выдаёт.
Противоположная схема – два блокирующих снимка подряд – тоже работает, но тогда цикл выполняется с частотой более медленного из двух датчиков, при этом конвейер более быстрого датчика простаивает между итерациями. Выбирайте ту частоту, которую действительно должна задавать последующая обработка в приложении.
4.18.4. Выбор источника потока¶
Камеры с двумя датчиками имеют два экземпляра CSI, но при этом всего один буфер кадра потока на двоих. Аргумент конструктора выбирает, кадры какого датчика подаются в предпросмотр:
csi_rgb = csi.CSI() # primary
csi_thermal = csi.CSI(cid=csi.LEPTON,
stream=True) # preview source
stream=True делает именованный экземпляр источником. Без аргумента stream= источником является основной датчик (cid=-1, значение по умолчанию); экземпляры, построенные с cid= вторичного датчика, остаются «молчащими» в предпросмотре, если только явно не передано stream=True. Вызовы snapshot() на невыбранном датчике по-прежнему захватывают кадры в буферы кадра этого датчика как обычно – они просто не обновляют предпросмотр.