diff --git a/dlclivegui/camera/__init__.py b/dlclivegui/camera/__init__.py index 54e8a29..2368198 100644 --- a/dlclivegui/camera/__init__.py +++ b/dlclivegui/camera/__init__.py @@ -36,3 +36,8 @@ from dlclivegui.camera.pseye import PSEyeCam except Exception as e: pass + +try: + from dlclivegui.camera.basler import BaslerCam +except Exception as e: + pass diff --git a/dlclivegui/camera/basler.py b/dlclivegui/camera/basler.py new file mode 100644 index 0000000..1706208 --- /dev/null +++ b/dlclivegui/camera/basler.py @@ -0,0 +1,104 @@ +""" +DeepLabCut Toolbox (deeplabcut.org) +© A. & M. Mathis Labs + +Licensed under GNU Lesser General Public License v3.0 +""" + +#import pypylon as pylon +from pypylon import pylon +from imutils import rotate_bound +import time + +from dlclivegui.camera import Camera, CameraError +TIMEOUT = 100 + +def get_devices(): + tlFactory = pylon.TlFactory.GetInstance() + devices = tlFactory.EnumerateDevices() + return devices + +class BaslerCam(Camera): + @staticmethod + def arg_restrictions(): + """ Returns a dictionary of arguments restrictions for DLCLiveGUI + """ + devices = get_devices() + device_ids = list(range(len(devices))) + return {"device": device_ids, "display": [True, False]} + + def __init__( + self, + device=0, + resolution=[640, 480], + exposure=15000, + rotate=0, + crop=None, + gain=0.0, + fps=30, + display=True, + display_resize=1.0, + ): + + super().__init__( + device, + resolution=resolution, + exposure=exposure, + rotate=rotate, + crop=crop, + gain=gain, + fps=fps, + use_tk_display=display, + display_resize=display_resize, + ) + + self.display = display + + def set_capture_device(self): + + devices = get_devices() + self.cam = pylon.InstantCamera( + pylon.TlFactory.GetInstance().CreateDevice(devices[self.id]) + ) + self.cam.Open() + + self.cam.Gain.SetValue(self.gain) + self.cam.ExposureTime.SetValue(self.exposure) + self.cam.Width.SetValue(self.im_size[0]) + self.cam.Height.SetValue(self.im_size[1]) + + self.cam.StartGrabbing(pylon.GrabStrategy_LatestImageOnly) + self.converter = pylon.ImageFormatConverter() + self.converter.OutputPixelFormat = pylon.PixelType_BGR8packed + self.converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned + + return True + + def get_image(self): + grabResult = self.cam.RetrieveResult( + TIMEOUT, pylon.TimeoutHandling_ThrowException) + + frame = None + + if grabResult.GrabSucceeded(): + + image = self.converter.Convert(grabResult) + frame = image.GetArray() + + if self.rotate: + frame = rotate_bound(frame, self.rotate) + if self.crop: + frame = frame[self.crop[2]: self.crop[3], + self.crop[0]: self.crop[1]] + + else: + + raise CameraError("Basler Camera did not return an image!") + + grabResult.Release() + + return frame + + def close_capture_device(self): + + self.cam.StopGrabbing() diff --git a/docs/camera_support.md b/docs/camera_support.md index ee6d8a3..6e36e22 100644 --- a/docs/camera_support.md +++ b/docs/camera_support.md @@ -63,3 +63,73 @@ If you're camera has built in methods to ensure the correct frame rate (e.g. whe The `get_image` method has no input arguments, but must return an image as a numpy array. We also recommend converting images to 8-bit integers (data type `uint8`). The `get_image_on_time` method has no input arguments, but must return an image as a numpy array (as in `get_image`) and the timestamp at which the image is returned (using python's `time.time()` function). + +### Camera Specific Tips for Installation & Use: + +#### Basler cameras + +Basler USB3 cameras are compatible with Aravis. However, integration with DeepLabCut-live-GUI can also be obtained with `pypylon`, the python module to drive Basler cameras, and supported by the company. Please note using `pypylon` requires you to install Pylon viewer, a free of cost GUI also developed and supported by Basler and available on several platforms. + +* **Pylon viewer**: https://www.baslerweb.com/en/sales-support/downloads/software-downloads/#type=pylonsoftware;language=all;version=all +* `pypylon`: https://github.com/basler/pypylon/releases + +If you want to use DeepLabCut-live-GUI with a Basler USB3 camera via pypylon, see the folllowing instructions. Please note this is tested on Ubuntu 20.04. It may (or may not) work similarly in other platforms (contributed by [@antortjim](https://github.com/antortjim)). This procedure should take around 10 minutes: + +**Install Pylon viewer** + +1. Download .deb file +Download the .deb file in the downloads center of Basler. Last version as of writing this was **pylon 6.2.0 Camera Software Suite Linux x86 (64 Bit) - Debian Installer Package**. + + +2. Install .deb file + +``` +sudo dpkg -i pylon_6.2.0.21487-deb0_amd64.deb +``` + +**Install swig** + +Required for compilation of non python code within pypylon + +1. Install swig dependencies + +You may have to install these in a fresh Ubuntu 20.04 install + +``` +sudo apt install gcc g++ +sudo apt install libpcre3-dev +sudo apt install make +``` + +2. Download swig + +Go to http://prdownloads.sourceforge.net/swig/swig-4.0.2.tar.gz and download the tar gz + +3. Install swig +``` +tar -zxvf swig-4.0.2.tar.gz +cd swig-4.0.2 +./configure +make +sudo make install +``` + +**Install pypylon** + +1. Download pypylon + +``` +wget https://github.com/basler/pypylon/archive/refs/tags/1.7.2.tar.gz +``` + +or go to https://github.com/basler/pypylon/releases and get the version you want! + +2. Install pypylon + +``` +tar -zxvf 1.7.2.tar.gz +cd pypylon-1.7.2 +python setup.py install +``` + +Once you have completed these steps, you should be able to call your Basler camera from DeepLabCut-live-GUI using the BaslerCam camera type that appears after clicking "Add camera")