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)