Obsługa kamer przemysłowych Ximea z poziomu IronPythona
Kamery przemysłowe Ximea obsługiwane są przez XiAPI. API zaimplementowane jest w bibliotekach dla platformy .NET. W tym artykule zaprezentuję wykorzystanie IronPythona do obsługi kamery xiQ za pomocą tego API. IronPython to implementacja Pythona na platformie .NET i z poziomów skryptów IronPythona można wykorzystywać biblioteki dostępne dla tej platformy.
Mając kamerkę (w moim przypadku xiQ MQ013MG-E2) należy zainstalować software package zawierające sterowniki i biblioteki. Całość zainstalowana zostanie w katalogu "Ximea" na głównym dysku (C:). W podkatalogu "libraries" znajdziemy pliki xiApi.NET.dll i m3api.dll. Oczywiście potrzebny będzie też IronPython. Do pustego katalogu skopiowałem wspomniane pliki DLL i plik ipy.exe IronPythona by mieć prostsze wykonywanie pisanych skryptów.
Najprostszy skrypt zapisujący jedną klatkę wygląda tak:#-*- coding: utf-8 -*-
import clr
import System
clr.AddReference("xiApi.NET.dll")
clr.AddReference("System.Drawing")
import System.Drawing
from System.Drawing.Imaging import PixelFormat
from xiApi.NET import *
cam = xiCam()
cam.OpenDevice(0)
cam.SetParam(PRM.BUFFER_POLICY, BUFF_POLICY.SAFE)
cam.SetParam(PRM.EXPOSURE, 9000)
cam.SetParam(PRM.GAIN, 10.0)
cam.SetParam(PRM.IMAGE_DATA_FORMAT, IMG_FORMAT.MONO8)
fileobj = System.Drawing.Bitmap(1280, 1024, PixelFormat.Format8bppIndexed)
cam.StartAcquisition()
cam.GetImage(fileobj, 1000)
fileobj.Save("a.bmp")
cam.StopAcquisition()
cam.CloseDevice()
Na początku ładujemy bibliotekę XiAPI oraz System.Drawing potrzebne do obsługi plików BMP, z których korzysta kamera (w 8-bitowym trybie obrazowania). Następnie inicjalizuję kamerę poprzez klasę xiCam. Ustawiam czas ekspozycji (9 ms), gain w dB, ustawiam format danych na monochromatyczne 8-bitów (kamera posiada monochromatyczną matrycę) i mogę już rozpoczynać "nagrywanie". GetImage potrzebuje instancję Bitmap o odpowiednich wymiarach i typie. Wszystkie te metody są opisane w dokumentacji. W odróżnieniu od Pythona musimy tutaj używać klas i typów takich jakie zostały przewidziane w xiAPI dla .NET.
Można także pobrać informacje z kamery za pomogą GetParam.
#-*- coding: utf-8 -*-
import clr
import System
clr.AddReference("xiApi.NET.dll")
from xiApi.NET import *
strongBox = clr.Reference[str]()
cam = xiCam()
cam.OpenDevice(0)
cam.GetParam(PRM.DEVICE_NAME, strongBox)
print strongBox
cam.GetParam(PRM.DEVICE_TYPE, strongBox)
print strongBox
cam.GetParam(PRM.WIDTH, strongBox)
print strongBox
cam.GetParam(PRM.HEIGHT, strongBox)
print strongBox
cam.GetParam(PRM.AVAILABLE_BANDWIDTH, strongBox)
print strongBox
cam.GetParam(PRM.CHIP_TEMP, strongBox)
print strongBox
cam.GetParam(PRM.API_VERSION, strongBox)
print strongBox
cam.GetParam(PRM.DRV_VERSION, strongBox)
print strongBox
cam.CloseDevice()
GetParam jako drugi parametr oczekuje instancji klasy System.Runtime.CompilerServices.StrongBox - która to przechowuje wskaźnik do jakiejś wartości.
Zapisywanie jednej klatki jest raczej mało przydatne. Na potrzeby testów kamery napisałem bardziej rozbudowaną klasę zapisującą klatki do podkatalogów:
#-*- coding: utf-8 -*-
import clr
import System
clr.AddReference("xiApi.NET.dll")
clr.AddReference("System.Drawing")
import System.Drawing
from System.Drawing.Imaging import PixelFormat
from System.IO import Directory, Path
from xiApi.NET import *
class XimeaImager(object):
def __init__(self, exposure_time, gain, frames_count, hdr, folder):
self.exposure_time = exposure_time
self.gain = gain
self.hdr = hdr
self.frames_count = frames_count
self.folder = folder
self._perform()
def _perform(self):
self._setup_capture_folder()
self._setup_device()
if self.hdr:
self._set_hdr_exposure()
else:
self._set_exposure()
self._capture_frames()
self._finish_acquisition()
def _setup_capture_folder(self):
current_folder = Directory.GetCurrentDirectory()
capture_folder = Path.Combine(current_folder, self.folder)
if not Directory.Exists(capture_folder):
Directory.CreateDirectory(capture_folder)
self.capture_folder = capture_folder
def _setup_device(self):
self.camera = xiCam()
self.camera.OpenDevice(0)
self.camera.SetParam(PRM.BUFFER_POLICY, BUFF_POLICY.SAFE)
self.camera.SetParam(PRM.IMAGE_DATA_FORMAT, IMG_FORMAT.MONO8)
self.camera.SetParam(PRM.GAIN, self.gain)
def _set_exposure(self):
self.camera.SetParam(PRM.EXPOSURE, self.exposure_time)
def _set_hdr_exposure(self):
self.camera.SetParam(PRM.HDR, 1)
self.camera.SetParam(PRM.HDR_T1, self.exposure_time * 10)
self.camera.SetParam(PRM.HDR_T2, self.exposure_time * 5)
self.camera.SetParam(PRM.HDR_T3, self.exposure_time)
def _capture_frames(self):
self.camera.StartAcquisition()
for frame in range(0, self.frames_count):
bmp = self._get_bmp_frame()
self.camera.GetImage(bmp, 1000)
self._save_frame(bmp, frame)
self.camera.StopAcquisition()
def _get_bmp_frame(self):
return System.Drawing.Bitmap(1280, 1024, PixelFormat.Format8bppIndexed)
def _save_frame(self, bmp, frame_index):
filename = 'frame%04d.bmp' % frame_index
filepath = Path.Combine(self.capture_folder, filename)
bmp.Save(filepath)
def _finish_acquisition(self):
self.camera.CloseDevice()
Wystarczy stworzyć instancję klasy, np. XimeaImager(9000, 10, 100, False, 'test') i gotowe. Dodałem też obsługę trybu HDR, które z trzech ekspozycji składa zdjęcie o większym zakresie dynamicznym. Zastosowałem te same stosunki czasów ekspozycji jak dla domyślnych ustawień. Także obsługę katalogów i ścieżek obsługiwane jest przez .NETowskie API ("os" nie istnieje).
Skryptowanie obsługi sprzętu w IronPythonie może być przydatne przy testach, czy szybkim sprawdzaniu różnych ustawień, koncepcji fotograficznych. Zastosowanie IronPythona wymaga mimo wszystko znajomości API platformy .NET.
Co do "fotografowania" za pomocą samej kamery - zapraszam do oddzielnego artykułu.
Comment article