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() на невыбранном датчике по-прежнему захватывают кадры в буферы кадра этого датчика как обычно – они просто не обновляют предпросмотр.