[Перевод] Система спектрозональной съемки на Raspberry Pi

yugspjqx5xl2cknbskgllmhehbe.jpeg
Пояснение переводчика: в статье речь пойдет об изготовлении устройства, которое позволяет делать снимки объекта в различных участках спектра электромагнитных волн.

Цитата из Википедии: спектрозональная съёмка производится для получения изображений деталей объекта, неразличимых в видимом свете.

В статье много фото.

В данном случае автор приводит пример самостоятельного изготовления подобного непростого устройства.

Я уже давно планировал собрать эту систему, и как только у меня появилось свободное время, направился в наш местный хакспейс (TAMI), где этим и занялся. Состоит оно в итоге из нескольких сотен светодиодов, посаженных на одну шину I2C, и темного ящика, который на ⅓ изнутри обклеен алюминиевой фольгой (альтернатива — пенополистирол) для максимально равномерного рассеивания света, а на 2/3 черным картоном для минимизации отражения лучей. Все светодиоды я объединил группами по 4 штуки, чтобы добиться большей яркости.

Начался весь проект с покупки светодиодов, которые обошлись мне около $0.5 каждый.

txglh_fps_cqmkwkqgbbwhgk3q0.png

Затем я красиво разместил их все на алюминиевом листе, который также выступил в роли теплоотвода.

eedcykhpvej1zmg-z9imu07o5vw.png

Каждая группа из 4-х светодиодов подключена к МОП-транзистору и мультиплексору (pca9685). Я подумываю заменить этот pca9685 на pca9635 (1кГц против 97кГц) и добавить фототранзистор для настройки ШИМ светодиодов с обратной связью. Также можно проверить время нарастания и спада свечения каждой группы. В качестве источника постоянного тока я задействовал драйвер LDD-700H, а в качестве контроллера Raspberry Pi (код будет приведен далее).

sbitjnzrhvmicoevltfhquo27tm.png

Тестирование светодиодов перед установкой:

Дополнительное тестирование (белый лист А4 и эталон отражения): Все цвета я проанализировал спектрометром: получилось от ~370нм до ~880нм, а цветовая температура белого 2.7К, 6К, 10К:

nj8mfn5niergfrafm0li2nomjms.jpeg
Пардон за косячное фото экрана — получил бракованный дисплей для планшета x61t

Далее нужно просверлить большое отверстие в центре алюминиевой пластины под монохроматическую камеру.

Памятка себе: найти фотодиод FDS010?). Для получения реальных значений ШИМ и яркости света фотодиод/фототранзистор должен соответствовать спектру светодиодов. Нашел подходящий в ящике инструментов (FDS10×10?, S2387–1010R?, 340нм-110нм ?).
xjpnbdbd3yquctcvheewuf6zawo.jpeg


Думаю, пока этого будет достаточно. Похоже, что стандартным решением для преобразования тока фотодиода в напряжение является LTC1050.

Съемка экрана планшета (лист авокадо) на телефон. Изображение получено с монохроматической камеры, еще не откалиброванной и не синхронизированной со светодиодами.

Апдейт по сборке: добавил сенсорный дисплей — нужно еще исправить драйвер eGalax (apt install xserver-xorg-input-evdev, отредактировать /etc/X11/xorg.conf, заменить libinput на evdev, apt install xinput-calibrator, поменять местами оси x-y дисплея).

dkxkxjg1plez9tgse55aiwo79mi.png

Все запитано от одного БП (драйвер светодиодов 15В, дисплей 12В, Pi 5В).

dcaut-lf53thr1gn7t_by0ityuq.jpeg


Просверлил отверстие

chrf9slazsg5paaemqp7_bkycrq.jpeg

Тест системы с временным держателем камеры

Притащил из TAMI несколько кусков анодированного алюминия, из которых собрал настраиваемый кронштейн для камеры.

yigjhjecrbwvontp-qs4uj7emcu.jpeg


Дополнительные фото


Апдейт: важно, чтобы в объектив попадал только отраженный от тестируемого объекта свет, поэтому я измерил отверстие и подобрал подходящую ПВХ-трубу, которая вошла в него достаточно плотно. Обрезал я ее коротковато — по факту оказалось, что лучше ее сделать длиннее.

fsqokz9daezp9jn6cj3veym-wnu.jpeg

Измерение отверстия

isps27jcvesvrkdf63s7etimds0.jpeg

Измерение ПВХ-трубы

11m69sh5irpda3pxlsqy6jtstlq.jpeg

Установил объектив с более широким углом обзора

Похоже, что мне удалось добиться улучшения, несмотря на слишком короткую трубу (думаю, желательна длина от 10 см). В качестве временного решения я ее наращу с помощью черного картона и оберну внешнюю сторону фольгой.

oa_rbiisnjhxsrbnzstvzyus460.jpeg


ely1sks3ikcyoanq9j6icj-c-xq.jpeg

Покрасил в черный. После высыхания снаружи оберну фольгой

Ниже очередное видео, теперь с листом винограда. Яркость светодиодов еще не откалибрована, и некоторые диапазоны лист поглощает. Плюс не помогло практически полное закрытие затвора объектива. Попробую снять другое видео завтра.

Изображение получилось «в кружке» из-за того, что я излишне нарастил трубу (20 см), в результате чего угол обзора захватывает ее край. В дальнейшем я планирую сделать трубу уже с настраиваемой длинной под разные объективы.

mzi7acedsvjmbv5-pknvmihzqvc.jpeg

Обновленный вид установки

zvhslyjmgqphbvfekfwv_55_eh0.jpeg

Добавил магнитный фиксатор дверцы

Еще одно видео. Здесь потеряна пара кадров из-за недостатка питания для Pi. На этот раз снимал лист картофеля с обработкой изображения при помощи OpenCV (функция CLAHE).

Находясь в ожидании заказанного апгрейда в виде Nvidia Jetson, решил поработать над косметикой.

7x4s4swewrmdz0qsdgd2ay2ooyc.jpeg


Дополнительные фото
ooc7xdrfrcign9hu57etxoaiapo.jpeg
yugspjqx5xl2cknbskgllmhehbe.jpeg

И вот мой «презент» готов к ведению многозональной съемки.

Как и обещал, привожу:

Код
from __future__ import division
import time
import Adafruit_PCA9685
import cv2

from pyueye import ueye
import ctypes
import datetime

pwm = Adafruit_PCA9685.PCA9685(address = 0x40) #1st 
pwm1 = Adafruit_PCA9685.PCA9685(address = 0x41) #2nd
pwm2 = Adafruit_PCA9685.PCA9685(address = 0x42) #3rd
pwm.set_pwm_freq(1000)
pwm1.set_pwm_freq(1000)
pwm2.set_pwm_freq(1000)

spec = "avo_leaf" #name of speciment

led = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31]
lux = [1900,1901,1902,1903,1904,1905,1906,1907,1908,1909,1910,1911,1912,1913,1914,1915,1916,1917,1918,1919,1920,1921,1922,1923,1924,1925,1926,1927,1928,1929,1930,1931] #temporary initial value 0, 4095

for x,y in zip(led, lux):
    if (x < 16):
       pwm.set_pwm(x,y,0) #led on 1st mux
    elif ( x>14 and x<32):
       pwm1.set_pwm(x-16,y,0) #led on 2nd mux 
   elif (x>31 and x<33):
       pwm2.set_pwm(x-32,y,0) #led on 3rd mux
   else:
       print(x)
    hcam = ueye.HIDS(1)
    pccmem = ueye.c_mem_p()
    memID = ueye.c_int()
    hWnd = ctypes.c_voidp()
    ueye.is_InitCamera(hcam, hWnd)
    sensorinfo = ueye.SENSORINFO()
    ueye.is_GetSensorInfo(hcam, sensorinfo)
    ueye.is_AllocImageMem(hcam, sensorinfo.nMaxWidth, sensorinfo.nMaxHeight,24, pccmem, memID)
    ueye.is_SetImageMem(hcam, pccmem, memID)
    nret = ueye.is_FreezeVideo(hcam, ueye.IS_WAIT)
    if (nret == 0):
      print("camera")
    else:
      print("____problem!!!____")
    FileParams = ueye.IMAGE_FILE_PARAMS()
    FileParams.pwchFileName = spec + "_led_"+str(x+1)+"_cam_"+ datetime.datetime.now().strftime("%s") + ".bmp"
    FileParams.nFileType = ueye.IS_IMG_BMP
    FileParams.ppcImageMem = None
    FileParams.pnImageID = None

    nret = ueye.is_ImageFile(hcam, ueye.IS_IMAGE_FILE_CMD_SAVE, FileParams, ueye.sizeof(FileParams))
    if (nret == 0):
      print("camera")
    else:
      print("____problem!!!____")
    ueye.is_FreeImageMem(hcam, pccmem, memID)
    ueye.is_ExitCamera(hcam)

    if (x < 16):
       pwm.set_pwm(x,0,0) #led off 1st mux
    elif ( x>14 and x<32):
       pwm1.set_pwm(x-16,0,0) #led off 2nd mux 
   elif (x>31 and x<33):
       pwm2.set_pwm(x-32,0,0) #led off 3rd mux
   else:
       print(x)


Заключение


В целом получился занятный проект, и я планирую довести его до ревизии 2.0. В частности:

  • заменить Raspberry Pi (~35$) на Nvidia Jetson Nano (~99$);
  • возможно, использовать машинное обучение для улучшения контраста;
  • пересобрать нижнюю часть для лучшего поглощения света;
  • достать несколько многоцветных лазеров;
  • использовать оптоволокно для подсветки в углах;
  • сделать установку портативной (уменьшить, реализовать возможность складывания и добавить аккумулятор).


В качестве дополнения рекомендую ознакомиться с реализацией продвинутого аналога подобной системы специалистами NASA. Вот их небольшое видео:

Конкурс статей от RUVDS.COM. Три денежные номинации. Главный приз — 100 000 рублей.

sz7jpfj8i1pa6ocj-eia09dev4q.png

© Habrahabr.ru