Package: rotpy
RotPyRotPy provides python bindings for the Spinnaker SDK
to enable Pythonic control of Teledyne/FLIR/Point Grey USB and GigE cameras.Seethe websitefor the completedocumentation.InstallationYou can install RotPy using pre-compiled wheels on Windows, Linux, or Mac or by installing
the Spinnaker SDK and then installing RotPy from source.Either way, you’ll likely need to install theSpinnaker driversso that the cameras
are recognized. Please download it fromtheir websiteand follow the instructions to install the drivers if the cameras are not found.Pre-compiled wheelsTo install from the pre-compiled wheels (assuming it’s available on your platform), simply do:python -m pip install rotpyNoteFor Windows, if rotpy errors out saying dll not found, you probably need
to install theMicrosoft Visual C++ Redistributable(64-bit,32-bit).WarningRotPy is licensed under MIT. However, the pre-compiled wheels contain some of the
Spinnaker SDK runtime libraries which have their own license. Please see the
license file packaged along with the binaries in the wheel.From sourceTo install RotPy from source, you need to:Install theSpinnaker SDKdevelopment
libraries.Install a C++ compiler that supports C++11. E.g. on Windows Visual Studio etc.
On Mac you may have to export the following environment variables:export CXX="/usr/bin/clang"
export CXXFLAGS="-std=c++11"
export CPPFLAGS="-std=c++11"Set the environment variables so Python can locate the Spinnaker SDK.You need to setROTPY_INCLUDEto the include directory, e.g. on Windows it may be something likesetROTPY_INCLUDE="C:\ProgramFiles\FLIR\Spinnaker\include". On Linux and Mac it should typically
be automatically found.You need to setROTPY_LIBto the paths that contain the libraries and binaries. E.g. on Windows it may
besetROTPY_LIB="C:\ProgramFiles\FLIR\Spinnaker\bin64\vs2015;C:\ProgramFiles\FLIR\Spinnaker\lib64\vs2015".
On Linux and Mac, again, it should typically be automatically found.Then install RotPy with:python -m pip install rotpy --no-binary rotpyAt runtime, you’ll need to ensure the Spinnaker runtime binaries are on the
systemPATHusing e.g.os.add_dll_directory.You may also have to set the environmental variable (depending on the OS bitness)GENICAM_GENTL32_PATH/GENICAM_GENTL64_PATHto the directory containing theFLIR_GenTL*.ctifile as well as any or all variablesFLIR_GENTL32_CTI_VS140/FLIR_GENTL64_CTI_VS140/FLIR_GENTL32_CTI/FLIR_GENTL64_CTIto thefull pathto theFLIR_GenTL*.ctifile.Additionally, theFLIR_GenTL*.ctifile containing directory may also need to be added
to the systemPATH. Spinnaker will raise an error if the cti file cannot be loaded.ExamplesGetting an image from a GigE camera>>>fromrotpy.systemimportSpinSystem>>>fromrotpy.cameraimportCameraList>>># get a system ref and a list of all attached cameras>>>system=SpinSystem()>>>cameras=CameraList.create_from_system(system,update_cams=True,update_interfaces=True)>>>cameras.get_size()1>>># get the camera attached from the list>>>camera=cameras.create_camera_by_index(0)>>>camera.get_unique_id()'77T45WD4A84C_86TA1684_GGGGGG14_64CW3987'>>># init the camera and get one image>>>camera.init_cam()>>># get its serial number>>>camera.camera_nodes.DeviceSerialNumber.get_node_value()'36548975'>>>camera.begin_acquisition()>>>image_cam=camera.get_next_image(timeout=5)>>># we copy the image so that we can release its camera buffer>>>image=image_cam.deep_copy_image(image_cam)>>>image_cam.release()>>>camera.end_acquisition()>>># save the image>>>image.save_png('image.png')>>># get image metadata>>>image.get_bits_per_pixel()8>>>image.get_height()512>>>image.get_width()612>>>image.get_frame_id()1>>>image.get_frame_timestamp()67557050882>>>image.get_pix_fmt()'Mono8'>>>image.get_image_data_size()313344>>>data=image.get_image_data()>>>type(data)bytearray>>>len(data)313344>>>512*612313344>>>camera.deinit_cam()>>>camera.release()Configuring and getting an image from a USB3 camera>>>fromrotpy.systemimportSpinSystem>>>fromrotpy.cameraimportCameraList>>># create system/camera list instance and create the camera by serial number>>>system=SpinSystem()>>>cameras=CameraList.create_from_system(system,True,True)>>>cameras.get_size()1>>>camera=cameras.create_camera_by_serial('87785435')>>># init so we can read the pixel format node>>>camera.init_cam()>>># the names of the pixel formats available for the camera>>>camera.camera_nodes.PixelFormat.get_entries_names()['Mono8','Mono12Packed','Mono12p','Mono16','BayerGR8',...,'BayerBG16','YCbCr411_8_CbYYCrYY','YCbCr422_8_CbYCrY','YCbCr8_CbYCr','RGB8']>>># the current one is BayerRG8>>>node=camera.camera_nodes.PixelFormat.get_node_value()>>>node<rotpy.node.SpinEnumItemNodeat0x236edec43c8>>>>node.get_enum_name()'BayerRG8'>>># instead set it to RGB8>>>camera.camera_nodes.PixelFormat.set_node_value_from_str('RGB8')>>>camera.camera_nodes.PixelFormat.get_node_value().get_enum_name()'RGB8'>>># set acquired image height to 800 pixels>>>camera.camera_nodes.Height.get_node_value()1200>>>camera.camera_nodes.Height.set_node_value(800)>>>camera.camera_nodes.Height.get_node_value()800>>>camera.camera_nodes.Height.get_max_value()1200>>># get the current framerate>>>camera.camera_nodes.AcquisitionFrameRate.is_readable()True>>>camera.camera_nodes.AcquisitionFrameRate.get_node_value()42.7807502746582>>># get one image and copy and release it so we don't tie up the buffers>>>camera.begin_acquisition()>>>image_cam=camera.get_next_image()>>>image=image_cam.deep_copy_image(image_cam)>>>image_cam.release()>>>camera.end_acquisition()>>># get some image metadat>>>image.get_frame_timestamp()/1e9512.51940629>>>image.get_height()800>>>image.get_buffer_size()4608000>>>1920*800*34608000>>>image.get_pix_fmt()'RGB8'>>># cleanup>>>camera.deinit_cam()>>>camera.release()System and camera propertiesThe system and camera properties can be read and set usingnodeobjects. These nodes, each represent a camera or system property, and they
can be integer nodes, float nodes, boolean nodes, string nodes, command
nodes etc.These nodes derive from Spinnaker’sGenICamimplementation for their cameras. RotPy provides access to a generic node access API
as well as to some pre-listed nodes available on many cameras.The generic API is accessed through theNodeMapusing
e.g.SpinSystem.get_tl_node_map,InterfaceDevice.get_tl_node_map,Camera.get_node_map,Camera.get_tl_dev_node_map, orCamera.get_tl_stream_node_map.The pre-listed nodes can be accessed through e.g.SpinSystem.system_nodes,InterfaceDevice.interface_nodes,Camera.camera_nodes,Camera.tl_dev_nodes, orCamera.tl_stream_nodes.
These link to the following respective
objects:SystemNodes,InterfaceNodes,CameraNodes,TLDevNodes, andTLStreamNodes.E.g. to access some of the system nodes usingsystem_nodes:>>>fromrotpy.systemimportSpinSystem>>>system=SpinSystem()>>># get a list of all boolean nodes>>>system.system_nodes.bool_nodes['EnumerateGEVInterfaces','EnumerateUSBInterfaces','EnumerateGen2Cameras']>>># let's inspect the USB node>>>system.system_nodes.EnumerateUSBInterfaces<rotpy.node.SpinBoolNodeat0x26822c20d68>>>># first make sure this node is actually available for this system>>>system.system_nodes.EnumerateUSBInterfaces.is_available()True>>>system.system_nodes.EnumerateUSBInterfaces.get_node_value()True>>>system.system_nodes.EnumerateUSBInterfaces.get_description()'Enables or disables enumeration of USB Interfaces.'>>>system.system_nodes.EnumerateUSBInterfaces.get_name()'EnumerateUSBInterfaces'>>>system.system_nodes.EnumerateUSBInterfaces.get_node_value_as_str()'1'>>>system.system_nodes.EnumerateUSBInterfaces.get_short_description()'Enables or disables enumeration of USB Interfaces.'We can similarly use the node map to get the same node if it’s available:>>>fromrotpy.systemimportSpinSystem>>>system=SpinSystem()>>>node_map=system.get_tl_node_map()>>>node=node_map.get_node_by_name('EnumerateUSBInterfaces')>>>nodeisnotNoneandnode.is_available()True>>>node.get_node_value()True>>>node.get_description()'Enables or disables enumeration of USB Interfaces.'Similarly, for the camera, we can use the pre-listed nodes:>>># make sure to init the camera, otherwise many nodes won't be available>>>camera.init_cam()>>># check that the auto-exposure setting is available>>>camera.camera_nodes.ExposureAuto.is_available()True>>>camera.camera_nodes.ExposureAuto.get_description()'Sets the automatic exposure mode when Exposure Mode is Timed.'>>># the auto-exposure is a enum node with children items>>>camera.camera_nodes.ExposureAuto.get_node_value()<rotpy.node.SpinEnumItemNodeat0x26822c2bc18>>>>camera.camera_nodes.ExposureAuto.get_node_value().get_enum_name()'Continuous'>>># but we can just get the symbolic string name directly>>>camera.camera_nodes.ExposureAuto.get_node_value_as_str()'Continuous'>>># to see what options are available for this enum node, look in the names module>>>fromrotpy.names.cameraimportExposureAuto_names>>>ExposureAuto_names{'Off':0,'Once':1,'Continuous':2}>>># or for pre-listed enum nodes, we can get it as an attribute>>>camera.camera_nodes.ExposureAuto.enum_names{'Off':0,'Once':1,'Continuous':2}>>># try setting it to an incorrect value>>>camera.camera_nodes.ExposureAuto.set_node_value_from_str('off',verify=True)Traceback(mostrecentcalllast):File"<ipython-input-48-d16a67f0044c>",line1,in<module>camera.camera_nodes.ExposureAuto.set_node_value_from_str('off',verify=True)File"rotpy\node.pyx",line650,inrotpy.node.SpinValueNode.set_node_value_from_strcpdefset_node_value_from_str(self,strvalue,cboolverify=True):File"rotpy\node.pyx",line664,inrotpy.node.SpinValueNode.set_node_value_from_strself._value_handle.FromString(s,verify)RuntimeError:Spinnaker:GenICam::InvalidArgumentException=Feature'ExposureAuto':cannotconvertvalue'off',thevalueisinvalid.:InvalidArgumentExceptionthrowninnode'ExposureAuto'whilecalling'ExposureAuto.FromString()'(file'Enumeration.cpp',line132)[-2001]>>># now set it correctly>>>camera.camera_nodes.ExposureAuto.set_node_value_from_str('Off',verify=True)>>>camera.camera_nodes.ExposureAuto.get_node_value_as_str()'Off'Similarly, we can use the node map to set the exposure back to"Continuous":>>>node_map=camera.get_node_map()>>>node=node_map.get_node_by_name('ExposureAuto')>>>nodeisnotNoneandnode.is_available()True>>>node.get_node_value_as_str()'Off'>>>node.set_node_value_from_str('Continuous',verify=True)>>>node.get_node_value_as_str()'Continuous'>>># now de-init the camera and the node won't be available>>>camera.deinit_cam()>>>node.is_available()False>>>camera.camera_nodes.ExposureAuto.is_available()FalseAttaching event handlersCamera detection eventsWe can register callbacks to be called when the system detects a camera arrival or
removal on any interface, or on specific interfaces. E.g. to be notified on any interface:>>>fromrotpy.systemimportSpinSystem>>>system=SpinSystem()>>># register a callback for both arrival and removal>>>defarrival(handler,system,serial):...print('Arrived:',serial)>>>defremoval(handler,system,serial):...print('Removed:',serial)>>># register and then plug and unplug a camera twice>>>handler=system.attach_interface_event_handler(arrival,removal,update=True)Arrived:36548975Removed:36548975Arrived:36548975Removed:36548975>>>system.detach_interface_event_handler(handler)Logging handlerWe can also register logging event handlers to get any logging events on the system or devices:>>>fromrotpy.systemimportSpinSystem>>>fromrotpy.cameraimportCameraList>>># create system and set logging level to debug>>>system=SpinSystem()>>>system.set_logging_level('debug')>>># create a callback that prints the message>>>deflog_handler(handler,system,item):...print('Log:',item['category'],item['priority'],item['message'])>>># attach the callback and do something that causes logs>>>handler=system.attach_log_event_handler(log_handler)>>>cameras=CameraList.create_from_system(system,update_cams=True,update_interfaces=True)Log:SpinnakerCallbackDEBUGSpinnaker:GetCameras()Log:GenTLCallbackDEBUGEnteringInterfaceGev::InterfaceGev()Log:GenTLCallbackDEBUGLeavingInterfaceGev::InterfaceGev()Log:GenTLCallbackDEBUGGenTLTrace:system.cpp,line125,GenTL::EnumerateGigEInterfacesLog:GenTLCallbackDEBUGEnteringHAL_UsbGetInterfaces()Log:GenTLCallbackDEBUGEnumeratinghostControllerPCI\VEN_8086&DEV_A12F&SUBSYS_06E41028&REV_31\3&11458735&0&A0Log:GenTLCallbackDEBUGHostController's child instance ID: USB\VID_8087&PID_0029\5&587A6F87&0&4Log:GenTLCallbackDEBUGEnteringInterfaceUsb::InterfaceUsb()Log:GenTLCallbackDEBUGLeavingInterfaceUsb::InterfaceUsb()Log:GenTLCallbackDEBUGGenTLTrace:system.cpp,line162,GenTL::EnumerateUsbInterfacesLog:GenTLCallbackDEBUGGenTLTrace:system.cpp,line191,GenTL::InitializeInterfacesLog:GenTLCallbackDEBUGGenTLTrace:system.cpp,line225,GenTL::System::RefreshInterfacesLog:GenTLCallbackDEBUGGenTLTrace:system.cpp,line535,GenTL::System::UpdateInterfaceList>>># now detach the handler>>>system.detach_log_event_handler(handler)Camera image handlerWe can also register a callback that is called on every new image that is received from the
device, as opposed to polling for new images:>>>fromrotpy.cameraimportCameraList>>>fromrotpy.systemimportSpinSystem>>># create system and get a camera>>>system=SpinSystem()>>>cameras=CameraList.create_from_system(system,update_cams=True,update_interfaces=True)>>>camera=cameras.create_camera_by_index(0)>>>camera.init_cam()>>># create an image handler that prints the frame ID and time>>>defimage_callback(handler,camera,image):...print('Image:',image.get_frame_id(),image.get_frame_timestamp())>>># attach callback and start getting frames>>>handler=camera.attach_image_event_handler(image_callback)>>>camera.begin_acquisition()Image:1388361262364Image:2388366605529...Image:135389077033335>>># stop frames and printing>>>camera.end_acquisition()>>>camera.detach_image_event_handler(handler)>>>camera.deinit_cam()>>>camera.release()Camera eventsWe can also register a callback that is called on camera events. E.g.:>>>fromrotpy.cameraimportCameraList>>>fromrotpy.systemimportSpinSystem>>># create system and get a camera>>>system=SpinSystem()>>>cameras=CameraList.create_from_system(system,update_cams=True,update_interfaces=True)>>>camera=cameras.create_camera_by_index(0)>>>camera.init_cam()>>># define the callback and attach it>>>defevent_callback(handler,camera,event):...print('Event:',event,handler.get_event_data(event),handler.get_event_metadata())>>>handler=camera.attach_device_event_handler(event_callback)>>># now use the EventSelector enum to get the enum items which>>>correspondtotheeventnamesthatareavailable.>>>nodes=camera.camera_nodes.EventSelector.get_entries()>>># not all are actually available, so only activate the ones available>>>nodes=[nodefornodeinnodesifnode.is_available()]>>>fornodeinnodes:...print(node.get_enum_name())...camera.camera_nodes.EventSelector.set_node_value_from_str(node.get_enum_name())...camera.camera_nodes.EventNotification.set_node_value_from_str('On')ExposureEnd>>># this printed just ExposureEnd, indicating only this event was available>>># start acquisition so that the events occur>>>camera.begin_acquisition()Event:EventExposureEnd{'frame_id':62629213124996}('device','EventExposureEnd',40003)Event:EventExposureEnd{'frame_id':62633508092293}('device','EventExposureEnd',40003)...Event:EventExposureEnd{'frame_id':62676457765304}('device','EventExposureEnd',40003)>>>camera.end_acquisition()>>>camera.detach_device_event_handler(handler)>>>camera.deinit_cam()>>>camera.release()