diff --git a/.gitignore b/.gitignore index 4aa78ba9bc01853d3a09c620ae335a9ec6289b27..6b23bd4437798e6f60b3d408257670adc6542b4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ poetry.lock -dist \ No newline at end of file +dist +__pycache__ +setup.py +*.egg-info diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/pyproject.toml b/pyproject.toml old mode 100644 new mode 100755 index f514b508c06ac21db0991902d222849527ad8622..418475331a970120b15f305b79ac506dcdca3669 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,20 +1,20 @@ -[tool.poetry] -name = "imagelab" -version = "0.1.0" -description = "This package is DTU Compute imagelab SDK for research in image analysis, computer vision, and computational imaging." -authors = ["Søren K. S. Gregersen <sorgre@dtu.dk>"] -license = "MIT" -repository = "https://lab.compute.dtu.dk/imagelab/imagelab" - -[tool.poetry.dependencies] -python = "^3.6" -opencv-contrib-python = "^4.4.0" -numpy = "^1.17.2" -scipy = "^1.5.2" -plyfile = "^0.7.2" - -[tool.poetry.dev-dependencies] - -[build-system] -requires = ["poetry>=0.12"] -build-backend = "poetry.masonry.api" +[tool.poetry] +name = "imagelab" +version = "0.1.0" +description = "This package is DTU Compute imagelab SDK for research in image analysis, computer vision, and computational imaging." +authors = ["Søren K. S. Gregersen <sorgre@dtu.dk>"] +license = "MIT" +repository = "https://lab.compute.dtu.dk/imagelab/imagelab" + +[tool.poetry.dependencies] +python = "^3.6" +opencv-contrib-python = "^4.4.0" +numpy = "^1.17.2" +scipy = "^1.5.2" +plyfile = "^0.7.2" + +[tool.poetry.dev-dependencies] + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api" diff --git a/src/imagelab/__init__.py b/src/imagelab/__init__.py old mode 100644 new mode 100755 diff --git a/src/imagelab/cameramodel.py b/src/imagelab/cameramodel.py old mode 100644 new mode 100755 diff --git a/src/imagelab/construct.py b/src/imagelab/construct.py old mode 100644 new mode 100755 diff --git a/src/imagelab/io.py b/src/imagelab/io.py old mode 100644 new mode 100755 diff --git a/src/imagelab/phaseshift.py b/src/imagelab/phaseshift.py old mode 100644 new mode 100755 diff --git a/src/imagelab/pointcloud.py b/src/imagelab/pointcloud.py old mode 100644 new mode 100755 diff --git a/src/imagelab/utilities.py b/src/imagelab/utilities.py old mode 100644 new mode 100755 diff --git a/src/imagelab/visionhardware.py b/src/imagelab/visionhardware.py old mode 100644 new mode 100755 index c44605a76d79f8e83727701a1217f14340aa7c27..f789edab77b03224e7ee77776282724911ce8ccb --- a/src/imagelab/visionhardware.py +++ b/src/imagelab/visionhardware.py @@ -1,276 +1,276 @@ -import PySpin -import numpy as np -import time - - -def getCameras(): - sys = PySpin.System.GetInstance() - cameras = sys.GetCameras() - return [Camera(cam, sys) for cam in cameras] - - -class Camera: - """Camera wrapper class for a FLIR Spinnaker camera.""" - - def __init__(self, pyspin_cam, pyspin_sys): - self._cam = pyspin_cam - self._sys = pyspin_sys - - if not self._cam.IsValid(): - raise RuntimeWarning("Camera {} is not valid", self) - - self._init_cam() - - # Set the values to typical single-frame with no automatic stuff - self.acquisition_mode = "single_frame" - self.exposure_time_ms = 100 - self.gamma = None - self.gain = None - self.trigger_mode = None - - def _init_cam(self): - self._cam.Init() - nmap = self._cam.GetNodeMap() - stream_nmap = self._cam.GetTLStreamNodeMap() - - # GammaEnable can be called by two names (urgh...) - gamma_enable_node = nmap.GetNode("GammaEnable") - if gamma_enable_node is None: - gamma_enable_node = nmap.GetNode("GammaEnabled") - self._cam_GammaEnable = PySpin.CBooleanPtr(gamma_enable_node) - - # StreamBufferHandlingMode is not directly exposed (urgh...) - sbhm_node = stream_nmap.GetNode("StreamBufferHandlingMode") - sbhm_enum = PySpin.CEnumerationPtr(sbhm_node) - self._cam_StreamBufferHandlingMode = sbhm_enum - entry = sbhm_enum.GetEntryByName("OldestFirst").GetValue() - self._StreamBufferHandlingMode_OldestFirst = entry - entry = sbhm_enum.GetEntryByName("OldestFirstOverwrite").GetValue() - self._StreamBufferHandlingMode_OldestFirstOverwrite = entry - entry = sbhm_enum.GetEntryByName("NewestFirst").GetValue() - self._StreamBufferHandlingMode_NewestFirst = entry - entry = sbhm_enum.GetEntryByName("NewestOnly").GetValue() - self._StreamBufferHandlingMode_NewestOnly = entry - self._cam_StreamBufferHandlingMode.SetIntValue(entry) - - self._acquisition_begun = False - - def print_settings(self): - print("Camera settings:") - print("acquisition_mode:", self.acquisition_mode) - print("exposure_time [ms]:", self.exposure_time_ms) - print("gain:", self.gain) - print("gamma:", self.gamma) - print("trigger_mode:", self.trigger_mode) - - @property - def acquisition_mode(self): - """Can be 'continuous', 'single_frame', or 'multi_frame'. - - See continuous_acquisition_mode(), - single_frame_acquisition_mode(), and - multi_frame_acquisition_mode().""" - acq_mode = self._cam.AcquisitionMode.GetValue() - if acq_mode == PySpin.AcquisitionMode_Continuous: - return "continuous" - if acq_mode == PySpin.AcquisitionMode_SingleFrame: - return "single_frame" - if acq_mode == PySpin.AcquisitionMode_MultiFrame: - return "multi_frame" - - @acquisition_mode.setter - def acquisition_mode(self, value): - acq_mode = self._cam.AcquisitionMode.GetValue() - if value == "continuous": - self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_Continuous) - if value == "single_frame": - self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_SingleFrame) - if value == "multi_frame": - self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_MultiFrame) - - def continuous_acquisition_mode(self): - self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_Continuous) - - def single_frame_acquisition_mode(self): - self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_SingleFrame) - - def multi_frame_acquisition_mode(self): - self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_MultiFrame) - - @property - def exposure_time_mcs(self): - """The exposure time in microseconds.""" - return self._cam.ExposureTime.GetValue() - - @exposure_time_mcs.setter - def exposure_time_mcs(self, value): - self._cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed) - if value == "auto": - self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) - elif value == "once": - self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) - else: - self._cam.ExposureTime.SetValue(value * 1000) - - @property - def exposure_time_ms(self): - """The exposure time in milliseconds.""" - return self.exposure_time_mcs / 1000 - - @exposure_time_ms.setter - def exposure_time_ms(self, value): - self._cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed) - if value == "auto": - self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) - elif value == "once": - self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) - else: - self._cam.ExposureTime.SetValue(value * 1000) - - @property - def exposure_time_s(self): - """The exposure time in seconds.""" - return self.exposure_time_mcs / 1000 / 1000 - - @exposure_time_s.setter - def exposure_time_s(self, value): - self._cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed) - if value == "auto": - self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) - elif value == "once": - self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) - else: - self._cam.ExposureTime.SetValue(value * 1000 * 1000) - - @property - def gamma(self): - """Gamma value.""" - if self._cam_GammaEnable.GetValue(): - return self._cam.Gamma.GetValue() - return None - - @gamma.setter - def gamma(self, value): - self._cam_GammaEnable.SetValue(True) - if value is False or value is None: - self._cam.Gamma.SetValue(1.0) - self._cam_GammaEnable.SetValue(False) - elif value is not True: - self._cam.Gamma.SetValue(value) - - @property - def gain(self): - """Gain value.""" - auto_gain = self._cam.GainAuto.GetValue() - if auto_gain == PySpin.GainAuto_Continuous: - return "auto" - if auto_gain == PySpin.GainAuto_Once: - return "once" - if auto_gain == PySpin.GainAuto_Off: - return self._cam.Gain.GetValue() - - @gain.setter - def gain(self, value): - self._cam.GainAuto.SetValue(PySpin.GainAuto_Off) - if value == "auto": - self._cam.GainAuto.SetValue(PySpin.GainAuto_Continuous) - elif value == "once": - self._cam.GainAuto.SetValue(PySpin.GainAuto_Once) - elif value is False or value is None: - self._cam.Gain.SetValue(1.0) - else: - self._cam.Gain.SetValue(value) - - @property - def trigger_mode(self): - """Can be None, 'software', 'line 0', 'line 1', 'line 2', or 'line 3'. - - See software_trigger(), - line_0_trigger(), - line_1_trigger(), - line_2_trigger(), and - line_3_trigger().""" - if self._cam.TriggerMode.GetValue() == PySpin.TriggerMode_Off: - return None - trg_src = self._cam.TriggerSource.GetValue() - if trg_src == PySpin.TriggerSource_Software: - return "software" - if trg_src == PySpin.TriggerSource_UserOutput0: - return "line 0" - if trg_src == PySpin.TriggerSource_UserOutput1: - return "line 1" - if trg_src == PySpin.TriggerSource_UserOutput2: - return "line 2" - if trg_src == PySpin.TriggerSource_UserOutput3: - return "line 3" - - @trigger_mode.setter - def trigger_mode(self, value): - if value is False or value is None: - self._cam.TriggerMode.SetValue(PySpin.TriggerMode_Off) - return - else: - self._cam.TriggerMode.SetValue(PySpin.TriggerMode_On) - if value == "software": - self._cam.TriggerSource.SetValue(PySpin.TriggerSource_Software) - if value == "line 0": - self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput0) - if value == "line 1": - self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput1) - if value == "line 2": - self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput2) - if value == "line 3": - self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput3) - - def trigger(self): - if not self._acquisition_begun: - raise RuntimeError("Remember to start acquisition with triggers.") - return self.TriggerSoftware.Execute() - - def capture_single_frame(self, timeout_ms=None): - if not self._acquisition_begun: - self._cam.BeginAcquisition() - if timeout_ms is None: - timeout_ms = PySpin.EVENT_TIMEOUT_INFINITE - hw_im = self._cam.GetNextImage(int(timeout_ms)) - h, w, c = hw_im.GetHeight(), hw_im.GetWidth(), hw_im.GetNumChannels() - im = np.array(hw_im.GetData().copy()).reshape(h, w, c) - hw_im.Release() - if not self._acquisition_begun: - self._cam.EndAcquisition() - return im - - def begin_acquisition(self): - if self._acquisition_begun: - raise RuntimeWarning("Acquisition has begun, cannot begin again!") - self._cam.BeginAcquisition() - self._acquisition_begun = True - - def end_acquisition(self): - if not self._acquisition_begun: - raise RuntimeWarning("Acquisition has not begun, cannot end it!") - self._cam.EndAcquisition() - self._acquisition_begun = False - - -if __name__ == "__main__": - from imagelab import io - - for i, cam in enumerate(getCameras()): - cam.print_settings() - im = cam.capture_single_frame() - - io.imwrite("testcapture_{}.png".format(i), im) - - for i, cam in enumerate(getCameras()): - cam.trigger_mode = "software" - cam.print_settings() - cam.begin_acquisition() - time.sleep(0.1) # We need to let the camera be ready. - cam.trigger() - # Some cameras also need time to be ready for transfer - # time.sleep(0.1) - im = cam.capture_single_frame() - cam.end_acquisition() - - io.imwrite("testtrigger_{}.png".format(i), im) +import PySpin +import numpy as np +import time + + +def getCameras(): + sys = PySpin.System.GetInstance() + cameras = sys.GetCameras() + return [Camera(cam, sys) for cam in cameras] + + +class Camera: + """Camera wrapper class for a FLIR Spinnaker camera.""" + + def __init__(self, pyspin_cam, pyspin_sys): + self._cam = pyspin_cam + self._sys = pyspin_sys + + if not self._cam.IsValid(): + raise RuntimeWarning("Camera {} is not valid", self) + + self._init_cam() + + # Set the values to typical single-frame with no automatic stuff + self.acquisition_mode = "single_frame" + self.exposure_time_ms = 100 + self.gamma = None + self.gain = None + self.trigger_mode = None + + def _init_cam(self): + self._cam.Init() + nmap = self._cam.GetNodeMap() + stream_nmap = self._cam.GetTLStreamNodeMap() + + # GammaEnable can be called by two names (urgh...) + gamma_enable_node = nmap.GetNode("GammaEnable") + if gamma_enable_node is None: + gamma_enable_node = nmap.GetNode("GammaEnabled") + self._cam_GammaEnable = PySpin.CBooleanPtr(gamma_enable_node) + + # StreamBufferHandlingMode is not directly exposed (urgh...) + sbhm_node = stream_nmap.GetNode("StreamBufferHandlingMode") + sbhm_enum = PySpin.CEnumerationPtr(sbhm_node) + self._cam_StreamBufferHandlingMode = sbhm_enum + entry = sbhm_enum.GetEntryByName("OldestFirst").GetValue() + self._StreamBufferHandlingMode_OldestFirst = entry + entry = sbhm_enum.GetEntryByName("OldestFirstOverwrite").GetValue() + self._StreamBufferHandlingMode_OldestFirstOverwrite = entry + entry = sbhm_enum.GetEntryByName("NewestFirst").GetValue() + self._StreamBufferHandlingMode_NewestFirst = entry + entry = sbhm_enum.GetEntryByName("NewestOnly").GetValue() + self._StreamBufferHandlingMode_NewestOnly = entry + self._cam_StreamBufferHandlingMode.SetIntValue(entry) + + self._acquisition_begun = False + + def print_settings(self): + print("Camera settings:") + print("acquisition_mode:", self.acquisition_mode) + print("exposure_time [ms]:", self.exposure_time_ms) + print("gain:", self.gain) + print("gamma:", self.gamma) + print("trigger_mode:", self.trigger_mode) + + @property + def acquisition_mode(self): + """Can be 'continuous', 'single_frame', or 'multi_frame'. + + See continuous_acquisition_mode(), + single_frame_acquisition_mode(), and + multi_frame_acquisition_mode().""" + acq_mode = self._cam.AcquisitionMode.GetValue() + if acq_mode == PySpin.AcquisitionMode_Continuous: + return "continuous" + if acq_mode == PySpin.AcquisitionMode_SingleFrame: + return "single_frame" + if acq_mode == PySpin.AcquisitionMode_MultiFrame: + return "multi_frame" + + @acquisition_mode.setter + def acquisition_mode(self, value): + acq_mode = self._cam.AcquisitionMode.GetValue() + if value == "continuous": + self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_Continuous) + if value == "single_frame": + self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_SingleFrame) + if value == "multi_frame": + self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_MultiFrame) + + def continuous_acquisition_mode(self): + self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_Continuous) + + def single_frame_acquisition_mode(self): + self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_SingleFrame) + + def multi_frame_acquisition_mode(self): + self._cam.AcquisitionMode.SetValue(PySpin.AcquisitionMode_MultiFrame) + + @property + def exposure_time_mcs(self): + """The exposure time in microseconds.""" + return self._cam.ExposureTime.GetValue() + + @exposure_time_mcs.setter + def exposure_time_mcs(self, value): + self._cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed) + if value == "auto": + self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) + elif value == "once": + self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) + else: + self._cam.ExposureTime.SetValue(value * 1000) + + @property + def exposure_time_ms(self): + """The exposure time in milliseconds.""" + return self.exposure_time_mcs / 1000 + + @exposure_time_ms.setter + def exposure_time_ms(self, value): + self._cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed) + if value == "auto": + self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) + elif value == "once": + self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) + else: + self._cam.ExposureTime.SetValue(value * 1000) + + @property + def exposure_time_s(self): + """The exposure time in seconds.""" + return self.exposure_time_mcs / 1000 / 1000 + + @exposure_time_s.setter + def exposure_time_s(self, value): + self._cam.ExposureMode.SetValue(PySpin.ExposureMode_Timed) + if value == "auto": + self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) + elif value == "once": + self._cam.ExposureAuto.SetValue(PySpin.ExposureAuto_Continuous) + else: + self._cam.ExposureTime.SetValue(value * 1000 * 1000) + + @property + def gamma(self): + """Gamma value.""" + if self._cam_GammaEnable.GetValue(): + return self._cam.Gamma.GetValue() + return None + + @gamma.setter + def gamma(self, value): + self._cam_GammaEnable.SetValue(True) + if value is False or value is None: + self._cam.Gamma.SetValue(1.0) + self._cam_GammaEnable.SetValue(False) + elif value is not True: + self._cam.Gamma.SetValue(value) + + @property + def gain(self): + """Gain value.""" + auto_gain = self._cam.GainAuto.GetValue() + if auto_gain == PySpin.GainAuto_Continuous: + return "auto" + if auto_gain == PySpin.GainAuto_Once: + return "once" + if auto_gain == PySpin.GainAuto_Off: + return self._cam.Gain.GetValue() + + @gain.setter + def gain(self, value): + self._cam.GainAuto.SetValue(PySpin.GainAuto_Off) + if value == "auto": + self._cam.GainAuto.SetValue(PySpin.GainAuto_Continuous) + elif value == "once": + self._cam.GainAuto.SetValue(PySpin.GainAuto_Once) + elif value is False or value is None: + self._cam.Gain.SetValue(1.0) + else: + self._cam.Gain.SetValue(value) + + @property + def trigger_mode(self): + """Can be None, 'software', 'line 0', 'line 1', 'line 2', or 'line 3'. + + See software_trigger(), + line_0_trigger(), + line_1_trigger(), + line_2_trigger(), and + line_3_trigger().""" + if self._cam.TriggerMode.GetValue() == PySpin.TriggerMode_Off: + return None + trg_src = self._cam.TriggerSource.GetValue() + if trg_src == PySpin.TriggerSource_Software: + return "software" + if trg_src == PySpin.TriggerSource_UserOutput0: + return "line 0" + if trg_src == PySpin.TriggerSource_UserOutput1: + return "line 1" + if trg_src == PySpin.TriggerSource_UserOutput2: + return "line 2" + if trg_src == PySpin.TriggerSource_UserOutput3: + return "line 3" + + @trigger_mode.setter + def trigger_mode(self, value): + if value is False or value is None: + self._cam.TriggerMode.SetValue(PySpin.TriggerMode_Off) + return + else: + self._cam.TriggerMode.SetValue(PySpin.TriggerMode_On) + if value == "software": + self._cam.TriggerSource.SetValue(PySpin.TriggerSource_Software) + if value == "line 0": + self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput0) + if value == "line 1": + self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput1) + if value == "line 2": + self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput2) + if value == "line 3": + self._cam.TriggerSource.SetValue(PySpin.TriggerSource_UserOutput3) + + def trigger(self): + if not self._acquisition_begun: + raise RuntimeError("Remember to start acquisition with triggers.") + return self.TriggerSoftware.Execute() + + def capture_single_frame(self, timeout_ms=None): + if not self._acquisition_begun: + self._cam.BeginAcquisition() + if timeout_ms is None: + timeout_ms = PySpin.EVENT_TIMEOUT_INFINITE + hw_im = self._cam.GetNextImage(int(timeout_ms)) + h, w, c = hw_im.GetHeight(), hw_im.GetWidth(), hw_im.GetNumChannels() + im = np.array(hw_im.GetData().copy()).reshape(h, w, c) + hw_im.Release() + if not self._acquisition_begun: + self._cam.EndAcquisition() + return im + + def begin_acquisition(self): + if self._acquisition_begun: + raise RuntimeWarning("Acquisition has begun, cannot begin again!") + self._cam.BeginAcquisition() + self._acquisition_begun = True + + def end_acquisition(self): + if not self._acquisition_begun: + raise RuntimeWarning("Acquisition has not begun, cannot end it!") + self._cam.EndAcquisition() + self._acquisition_begun = False + + +if __name__ == "__main__": + from imagelab import io + + for i, cam in enumerate(getCameras()): + cam.print_settings() + im = cam.capture_single_frame() + + io.imwrite("testcapture_{}.png".format(i), im) + + for i, cam in enumerate(getCameras()): + cam.trigger_mode = "software" + cam.print_settings() + cam.begin_acquisition() + time.sleep(0.1) # We need to let the camera be ready. + cam.trigger() + # Some cameras also need time to be ready for transfer + # time.sleep(0.1) + im = cam.capture_single_frame() + cam.end_acquisition() + + io.imwrite("testtrigger_{}.png".format(i), im)