AVT相机arm版本SDK

This commit is contained in:
zhangpeng
2025-04-30 09:26:04 +08:00
parent 837c870f18
commit 78a1c63a95
705 changed files with 148770 additions and 0 deletions

View File

@@ -0,0 +1,163 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Tuple
from vimba import *
def print_preamble():
print('/////////////////////////////////////////')
print('/// Vimba API Action Commands Example ///')
print('/////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python action_commands.py <camera_id> <interface_id>')
print(' python action_commands.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to be used')
print(' interface_id ID of network interface to send out Action Command')
print(' \'ALL\' enables broadcast on all interfaces')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Tuple[str, str]:
args = sys.argv[1:]
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if len(args) != 2:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return (args[0], args[1])
def get_input() -> str:
prompt = 'Press \'a\' to send action command. Press \'q\' to stop example. Enter:'
print(prompt, flush=True)
return input()
def get_camera(camera_id: str) -> Camera:
with Vimba.get_instance() as vimba:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera {}. Abort.'.format(camera_id))
def get_command_sender(interface_id):
# If given interface_id is ALL, ActionCommand shall be sent from all Ethernet Interfaces.
# This is achieved by run ActionCommand on the Vimba instance.
if interface_id == 'ALL':
return Vimba.get_instance()
with Vimba.get_instance() as vimba:
# A specific Interface was given. Lookup via given Interface id and verify that
# it is an Ethernet Interface. Running ActionCommand will be only send from this Interface.
try:
inter = vimba.get_interface_by_id(interface_id)
except VimbaInterfaceError:
abort('Failed to access Interface {}. Abort.'.format(interface_id))
if inter.get_type() != InterfaceType.Ethernet:
abort('Given Interface {} is no Ethernet Interface. Abort.'.format(interface_id))
return inter
def frame_handler(cam: Camera, frame: Frame):
if frame.get_status() == FrameStatus.Complete:
print('Frame(ID: {}) has been received.'.format(frame.get_id()), flush=True)
cam.queue_frame(frame)
def main():
print_preamble()
camera_id, interface_id = parse_args()
with Vimba.get_instance():
cam = get_camera(camera_id)
sender = get_command_sender(interface_id)
with cam, sender:
# Prepare Camera for ActionCommand - Trigger
device_key = 1
group_key = 1
group_mask = 1
cam.GVSPAdjustPacketSize.run()
while not cam.GVSPAdjustPacketSize.is_done():
pass
cam.TriggerSelector.set('FrameStart')
cam.TriggerSource.set('Action0')
cam.TriggerMode.set('On')
cam.ActionDeviceKey.set(device_key)
cam.ActionGroupKey.set(group_key)
cam.ActionGroupMask.set(group_mask)
# Enter Streaming mode and wait for user input.
try:
cam.start_streaming(frame_handler)
while True:
ch = get_input()
if ch == 'q':
break
elif ch == 'a':
sender.ActionDeviceKey.set(device_key)
sender.ActionGroupKey.set(group_key)
sender.ActionGroupMask.set(group_mask)
sender.ActionCommand.run()
finally:
cam.stop_streaming()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,136 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional, Tuple
from vimba import *
def print_preamble():
print('///////////////////////////////////////////')
print('/// Vimba API Asynchronous Grab Example ///')
print('///////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python asynchronous_grab.py [/x] [-x] [camera_id]')
print(' python asynchronous_grab.py [/h] [-h]')
print()
print('Parameters:')
print(' /x, -x If set, use AllocAndAnnounce mode of buffer allocation')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Tuple[Optional[str], AllocationMode]:
args = sys.argv[1:]
argc = len(args)
allocation_mode = AllocationMode.AnnounceFrame
cam_id = ""
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
elif arg in ('/x', '-x'):
allocation_mode = AllocationMode.AllocAndAnnounceFrame
elif not cam_id:
cam_id = arg
if argc > 2:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return (cam_id if cam_id else None, allocation_mode)
def get_camera(camera_id: Optional[str]) -> Camera:
with Vimba.get_instance() as vimba:
if camera_id:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Cameras accessible. Abort.')
return cams[0]
def setup_camera(cam: Camera):
with cam:
# Try to adjust GeV packet size. This Feature is only available for GigE - Cameras.
try:
cam.GVSPAdjustPacketSize.run()
while not cam.GVSPAdjustPacketSize.is_done():
pass
except (AttributeError, VimbaFeatureError):
pass
def frame_handler(cam: Camera, frame: Frame):
print('{} acquired {}'.format(cam, frame), flush=True)
cam.queue_frame(frame)
def main():
print_preamble()
cam_id, allocation_mode = parse_args()
with Vimba.get_instance():
with get_camera(cam_id) as cam:
setup_camera(cam)
print('Press <enter> to stop Frame acquisition.')
try:
# Start Streaming with a custom a buffer of 10 Frames (defaults to 5)
cam.start_streaming(handler=frame_handler, buffer_count=10, allocation_mode=allocation_mode)
input()
finally:
cam.stop_streaming()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,177 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import threading
import sys
import cv2
from typing import Optional
from vimba import *
def print_preamble():
print('///////////////////////////////////////////////////////')
print('/// Vimba API Asynchronous Grab with OpenCV Example ///')
print('///////////////////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python asynchronous_grab_opencv.py [camera_id]')
print(' python asynchronous_grab_opencv.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Optional[str]:
args = sys.argv[1:]
argc = len(args)
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if argc > 1:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return None if argc == 0 else args[0]
def get_camera(camera_id: Optional[str]) -> Camera:
with Vimba.get_instance() as vimba:
if camera_id:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Cameras accessible. Abort.')
return cams[0]
def setup_camera(cam: Camera):
with cam:
# Enable auto exposure time setting if camera supports it
try:
cam.ExposureAuto.set('Continuous')
except (AttributeError, VimbaFeatureError):
pass
# Enable white balancing if camera supports it
try:
cam.BalanceWhiteAuto.set('Continuous')
except (AttributeError, VimbaFeatureError):
pass
# Try to adjust GeV packet size. This Feature is only available for GigE - Cameras.
try:
cam.GVSPAdjustPacketSize.run()
while not cam.GVSPAdjustPacketSize.is_done():
pass
except (AttributeError, VimbaFeatureError):
pass
# Query available, open_cv compatible pixel formats
# prefer color formats over monochrome formats
cv_fmts = intersect_pixel_formats(cam.get_pixel_formats(), OPENCV_PIXEL_FORMATS)
color_fmts = intersect_pixel_formats(cv_fmts, COLOR_PIXEL_FORMATS)
if color_fmts:
cam.set_pixel_format(color_fmts[0])
else:
mono_fmts = intersect_pixel_formats(cv_fmts, MONO_PIXEL_FORMATS)
if mono_fmts:
cam.set_pixel_format(mono_fmts[0])
else:
abort('Camera does not support a OpenCV compatible format natively. Abort.')
class Handler:
def __init__(self):
self.shutdown_event = threading.Event()
def __call__(self, cam: Camera, frame: Frame):
ENTER_KEY_CODE = 13
key = cv2.waitKey(1)
if key == ENTER_KEY_CODE:
self.shutdown_event.set()
return
elif frame.get_status() == FrameStatus.Complete:
print('{} acquired {}'.format(cam, frame), flush=True)
msg = 'Stream from \'{}\'. Press <Enter> to stop stream.'
cv2.imshow(msg.format(cam.get_name()), frame.as_opencv_image())
cam.queue_frame(frame)
def main():
print_preamble()
cam_id = parse_args()
with Vimba.get_instance():
with get_camera(cam_id) as cam:
# Start Streaming, wait for five seconds, stop streaming
setup_camera(cam)
handler = Handler()
try:
# Start Streaming with a custom a buffer of 10 Frames (defaults to 5)
cam.start_streaming(handler=handler, buffer_count=10)
handler.shutdown_event.wait()
finally:
cam.stop_streaming()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,51 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from vimba import *
def main():
print('////////////////////////////////////////////')
print('/// VimbaPython Create Trace Log Example ///')
print('////////////////////////////////////////////\n')
# Enable logging mechanism, creating a trace log. The log file is
# stored at the location this script was executed from.
vimba = Vimba.get_instance()
vimba.enable_log(LOG_CONFIG_TRACE_FILE_ONLY)
# While entering this scope, feature, camera and interface discovery occurs.
# All function calls to VimbaC are captured in the log file.
with vimba:
pass
vimba.disable_log()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,147 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional
from vimba import *
def print_preamble():
print('////////////////////////////////////////')
print('/// Vimba API Event Handling Example ///')
print('////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python event_handling.py [camera_id]')
print(' python event_handling.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Optional[str]:
args = sys.argv[1:]
argc = len(args)
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if argc > 1:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return args[0] if argc == 1 else None
def get_camera(cam_id: Optional[str]):
with Vimba.get_instance() as vimba:
# Lookup Camera if it was specified.
if cam_id:
try:
cam = vimba.get_camera_by_id(cam_id)
except VimbaCameraError:
abort('Failed to access Camera {}. Abort.'.format(cam_id))
# If no camera was specified, use first detected camera.
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Camera detected. Abort.')
cam = cams[0]
# This example works only with GigE Cameras. Verify that Camera is connected to an
# Ethernet Interface.
inter = vimba.get_interface_by_id(cam.get_interface_id())
if inter.get_type() != InterfaceType.Ethernet:
abort('Example supports only GigE Cameras. Abort.')
return cam
def setup_camera(cam: Camera):
with cam:
# Try to adjust GeV packet size. This Feature is only available for GigE - Cameras.
try:
cam.GVSPAdjustPacketSize.run()
while not cam.GVSPAdjustPacketSize.is_done():
pass
except (AttributeError, VimbaFeatureError):
pass
def feature_changed_handler(feature):
msg = 'Feature \'{}\' changed value to \'{}\''
print(msg.format(str(feature.get_name()), str(feature.get())), flush=True)
def main():
print_preamble()
cam_id = parse_args()
with Vimba.get_instance():
with get_camera(cam_id) as cam:
setup_camera(cam)
# Disable all events notifications
for event in cam.EventSelector.get_available_entries():
cam.EventSelector.set(event)
cam.EventNotification.set('Off')
# Enable event notifications on 'AcquisitionStart'
cam.EventSelector.set('AcquisitionStart')
cam.EventNotification.set('On')
# Register callable on all Features in the '/EventControl/EventData' - Category
feats = cam.get_features_by_category('/EventControl/EventData')
for feat in feats:
feat.register_change_handler(feature_changed_handler)
# Acquire a single Frame to trigger events.
cam.get_frame()
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,139 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional
from vimba import *
def print_preamble():
print('//////////////////////////////////////////////////////')
print('/// Vimba API List Ancillary Data Features Example ///')
print('//////////////////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python list_ancillary_data.py [camera_id]')
print(' python list_ancillary_data.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Optional[str]:
args = sys.argv[1:]
argc = len(args)
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if len(args) > 1:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return args[0] if argc == 1 else None
def get_camera(camera_id: Optional[str]) -> Camera:
with Vimba.get_instance() as vimba:
if camera_id:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Cameras accessible. Abort.')
return cams[0]
def setup_camera(cam: Camera):
with cam:
# Try to adjust GeV packet size. This Feature is only available for GigE - Cameras.
try:
cam.GVSPAdjustPacketSize.run()
while not cam.GVSPAdjustPacketSize.is_done():
pass
except (AttributeError, VimbaFeatureError):
pass
# Try to enable ChunkMode
try:
cam.ChunkModeActive.set(True)
except (AttributeError, VimbaFeatureError):
abort('Failed to enable ChunkMode on Camera \'{}\'. Abort.'.format(cam.get_id()))
def main():
print_preamble()
cam_id = parse_args()
with Vimba.get_instance():
with get_camera(cam_id) as cam:
setup_camera(cam)
# Capture single Frame and print all contained ancillary data
frame = cam.get_frame()
anc_data = frame.get_ancillary_data()
if anc_data:
with anc_data:
print('Print ancillary data contained in Frame:')
for feat in anc_data.get_all_features():
print('Feature Name : {}'.format(feat.get_name()))
print('Display Name : {}'.format(feat.get_display_name()))
print('Tooltip : {}'.format(feat.get_tooltip()))
print('Description : {}'.format(feat.get_description()))
print('SFNC Namespace : {}'.format(feat.get_sfnc_namespace()))
print('Value : {}'.format(feat.get()))
print()
else:
abort('Frame {} does not contain AncillaryData. Abort'.format(frame.get_id()))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,57 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from vimba import *
def print_preamble():
print('//////////////////////////////////////')
print('/// Vimba API List Cameras Example ///')
print('//////////////////////////////////////\n')
def print_camera(cam: Camera):
print('/// Camera Name : {}'.format(cam.get_name()))
print('/// Model Name : {}'.format(cam.get_model()))
print('/// Camera ID : {}'.format(cam.get_id()))
print('/// Serial Number : {}'.format(cam.get_serial()))
print('/// Interface ID : {}\n'.format(cam.get_interface_id()))
def main():
print_preamble()
with Vimba.get_instance() as vimba:
cams = vimba.get_all_cameras()
print('Cameras found: {}'.format(len(cams)))
for cam in cams:
print_camera(cam)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,119 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional
from vimba import *
def print_preamble():
print('///////////////////////////////////////')
print('/// Vimba API List Features Example ///')
print('///////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python list_features.py [camera_id]')
print(' python list_features.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Optional[str]:
args = sys.argv[1:]
argc = len(args)
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if argc > 1:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return None if argc == 0 else args[0]
def print_feature(feature):
try:
value = feature.get()
except (AttributeError, VimbaFeatureError):
value = None
print('/// Feature name : {}'.format(feature.get_name()))
print('/// Display name : {}'.format(feature.get_display_name()))
print('/// Tooltip : {}'.format(feature.get_tooltip()))
print('/// Description : {}'.format(feature.get_description()))
print('/// SFNC Namespace : {}'.format(feature.get_sfnc_namespace()))
print('/// Unit : {}'.format(feature.get_unit()))
print('/// Value : {}\n'.format(str(value)))
def get_camera(camera_id: Optional[str]) -> Camera:
with Vimba.get_instance() as vimba:
if camera_id:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Cameras accessible. Abort.')
return cams[0]
def main():
print_preamble()
cam_id = parse_args()
with Vimba.get_instance():
with get_camera(cam_id) as cam:
print('Print all features of camera \'{}\':'.format(cam.get_id()))
for feature in cam.get_all_features():
print_feature(feature)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,125 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional
from vimba import *
def print_preamble():
print('////////////////////////////////////////////')
print('/// Vimba API Load Save Settings Example ///')
print('////////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python load_save_settings.py [camera_id]')
print(' python load_save_settings.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Optional[str]:
args = sys.argv[1:]
argc = len(args)
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if argc > 1:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return None if argc == 0 else args[0]
def get_camera(camera_id: Optional[str]) -> Camera:
with Vimba.get_instance() as vimba:
if camera_id:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Cameras accessible. Abort.')
return cams[0]
def main():
print_preamble()
cam_id = parse_args()
with Vimba.get_instance():
print("--> Vimba has been started")
with get_camera(cam_id) as cam:
print("--> Camera has been opened (%s)" % cam.get_id())
# Save camera settings to file.
settings_file = '{}_settings.xml'.format(cam.get_id())
cam.save_settings(settings_file, PersistType.All)
print("--> Feature values have been saved to '%s'" % settings_file)
# Restore settings to initial value.
try:
cam.UserSetSelector.set('Default')
except (AttributeError, VimbaFeatureError):
abort('Failed to set Feature \'UserSetSelector\'')
try:
cam.UserSetLoad.run()
print("--> All feature values have been restored to default")
except (AttributeError, VimbaFeatureError):
abort('Failed to run Feature \'UserSetLoad\'')
# Load camera settings from file.
cam.load_settings(settings_file, PersistType.All)
print("--> Feature values have been loaded from given file '%s'" % settings_file)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,299 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import copy
import cv2
import threading
import queue
import numpy
from typing import Optional
from vimba import *
FRAME_QUEUE_SIZE = 10
FRAME_HEIGHT = 480
FRAME_WIDTH = 480
def print_preamble():
print('////////////////////////////////////////////')
print('/// Vimba API Multithreading Example ///////')
print('////////////////////////////////////////////\n')
print(flush=True)
def add_camera_id(frame: Frame, cam_id: str) -> Frame:
# Helper function inserting 'cam_id' into given frame. This function
# manipulates the original image buffer inside frame object.
cv2.putText(frame.as_opencv_image(), 'Cam: {}'.format(cam_id), org=(0, 30), fontScale=1,
color=255, thickness=1, fontFace=cv2.FONT_HERSHEY_COMPLEX_SMALL)
return frame
def resize_if_required(frame: Frame) -> numpy.ndarray:
# Helper function resizing the given frame, if it has not the required dimensions.
# On resizing, the image data is copied and resized, the image inside the frame object
# is untouched.
cv_frame = frame.as_opencv_image()
if (frame.get_height() != FRAME_HEIGHT) or (frame.get_width() != FRAME_WIDTH):
cv_frame = cv2.resize(cv_frame, (FRAME_WIDTH, FRAME_HEIGHT), interpolation=cv2.INTER_AREA)
cv_frame = cv_frame[..., numpy.newaxis]
return cv_frame
def create_dummy_frame() -> numpy.ndarray:
cv_frame = numpy.zeros((50, 640, 1), numpy.uint8)
cv_frame[:] = 0
cv2.putText(cv_frame, 'No Stream available. Please connect a Camera.', org=(30, 30),
fontScale=1, color=255, thickness=1, fontFace=cv2.FONT_HERSHEY_COMPLEX_SMALL)
return cv_frame
def try_put_frame(q: queue.Queue, cam: Camera, frame: Optional[Frame]):
try:
q.put_nowait((cam.get_id(), frame))
except queue.Full:
pass
def set_nearest_value(cam: Camera, feat_name: str, feat_value: int):
# Helper function that tries to set a given value. If setting of the initial value failed
# it calculates the nearest valid value and sets the result. This function is intended to
# be used with Height and Width Features because not all Cameras allow the same values
# for height and width.
feat = cam.get_feature_by_name(feat_name)
try:
feat.set(feat_value)
except VimbaFeatureError:
min_, max_ = feat.get_range()
inc = feat.get_increment()
if feat_value <= min_:
val = min_
elif feat_value >= max_:
val = max_
else:
val = (((feat_value - min_) // inc) * inc) + min_
feat.set(val)
msg = ('Camera {}: Failed to set value of Feature \'{}\' to \'{}\': '
'Using nearest valid value \'{}\'. Note that, this causes resizing '
'during processing, reducing the frame rate.')
Log.get_instance().info(msg.format(cam.get_id(), feat_name, feat_value, val))
# Thread Objects
class FrameProducer(threading.Thread):
def __init__(self, cam: Camera, frame_queue: queue.Queue):
threading.Thread.__init__(self)
self.log = Log.get_instance()
self.cam = cam
self.frame_queue = frame_queue
self.killswitch = threading.Event()
def __call__(self, cam: Camera, frame: Frame):
# This method is executed within VimbaC context. All incoming frames
# are reused for later frame acquisition. If a frame shall be queued, the
# frame must be copied and the copy must be sent, otherwise the acquired
# frame will be overridden as soon as the frame is reused.
if frame.get_status() == FrameStatus.Complete:
if not self.frame_queue.full():
frame_cpy = copy.deepcopy(frame)
try_put_frame(self.frame_queue, cam, frame_cpy)
cam.queue_frame(frame)
def stop(self):
self.killswitch.set()
def setup_camera(self):
set_nearest_value(self.cam, 'Height', FRAME_HEIGHT)
set_nearest_value(self.cam, 'Width', FRAME_WIDTH)
# Try to enable automatic exposure time setting
try:
self.cam.ExposureAuto.set('Once')
except (AttributeError, VimbaFeatureError):
self.log.info('Camera {}: Failed to set Feature \'ExposureAuto\'.'.format(
self.cam.get_id()))
self.cam.set_pixel_format(PixelFormat.Mono8)
def run(self):
self.log.info('Thread \'FrameProducer({})\' started.'.format(self.cam.get_id()))
try:
with self.cam:
self.setup_camera()
try:
self.cam.start_streaming(self)
self.killswitch.wait()
finally:
self.cam.stop_streaming()
except VimbaCameraError:
pass
finally:
try_put_frame(self.frame_queue, self.cam, None)
self.log.info('Thread \'FrameProducer({})\' terminated.'.format(self.cam.get_id()))
class FrameConsumer(threading.Thread):
def __init__(self, frame_queue: queue.Queue):
threading.Thread.__init__(self)
self.log = Log.get_instance()
self.frame_queue = frame_queue
def run(self):
IMAGE_CAPTION = 'Multithreading Example: Press <Enter> to exit'
KEY_CODE_ENTER = 13
frames = {}
alive = True
self.log.info('Thread \'FrameConsumer\' started.')
while alive:
# Update current state by dequeuing all currently available frames.
frames_left = self.frame_queue.qsize()
while frames_left:
try:
cam_id, frame = self.frame_queue.get_nowait()
except queue.Empty:
break
# Add/Remove frame from current state.
if frame:
frames[cam_id] = frame
else:
frames.pop(cam_id, None)
frames_left -= 1
# Construct image by stitching frames together.
if frames:
cv_images = [resize_if_required(frames[cam_id]) for cam_id in sorted(frames.keys())]
cv2.imshow(IMAGE_CAPTION, numpy.concatenate(cv_images, axis=1))
# If there are no frames available, show dummy image instead
else:
cv2.imshow(IMAGE_CAPTION, create_dummy_frame())
# Check for shutdown condition
if KEY_CODE_ENTER == cv2.waitKey(10):
cv2.destroyAllWindows()
alive = False
self.log.info('Thread \'FrameConsumer\' terminated.')
class MainThread(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.frame_queue = queue.Queue(maxsize=FRAME_QUEUE_SIZE)
self.producers = {}
self.producers_lock = threading.Lock()
def __call__(self, cam: Camera, event: CameraEvent):
# New camera was detected. Create FrameProducer, add it to active FrameProducers
if event == CameraEvent.Detected:
with self.producers_lock:
self.producers[cam.get_id()] = FrameProducer(cam, self.frame_queue)
self.producers[cam.get_id()].start()
# An existing camera was disconnected, stop associated FrameProducer.
elif event == CameraEvent.Missing:
with self.producers_lock:
producer = self.producers.pop(cam.get_id())
producer.stop()
producer.join()
def run(self):
log = Log.get_instance()
consumer = FrameConsumer(self.frame_queue)
vimba = Vimba.get_instance()
vimba.enable_log(LOG_CONFIG_INFO_CONSOLE_ONLY)
log.info('Thread \'MainThread\' started.')
with vimba:
# Construct FrameProducer threads for all detected cameras
for cam in vimba.get_all_cameras():
self.producers[cam.get_id()] = FrameProducer(cam, self.frame_queue)
# Start FrameProducer threads
with self.producers_lock:
for producer in self.producers.values():
producer.start()
# Start and wait for consumer to terminate
vimba.register_camera_change_handler(self)
consumer.start()
consumer.join()
vimba.unregister_camera_change_handler(self)
# Stop all FrameProducer threads
with self.producers_lock:
# Initiate concurrent shutdown
for producer in self.producers.values():
producer.stop()
# Wait for shutdown to complete
for producer in self.producers.values():
producer.join()
log.info('Thread \'MainThread\' terminated.')
if __name__ == '__main__':
print_preamble()
main = MainThread()
main.start()
main.join()

View File

@@ -0,0 +1,117 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional
from vimba import *
def print_preamble():
print('//////////////////////////////////////////')
print('/// Vimba API Synchronous Grab Example ///')
print('//////////////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python synchronous_grab.py [camera_id]')
print(' python synchronous_grab.py [/h] [-h]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Optional[str]:
args = sys.argv[1:]
argc = len(args)
for arg in args:
if arg in ('/h', '-h'):
print_usage()
sys.exit(0)
if argc > 1:
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
return None if argc == 0 else args[0]
def get_camera(camera_id: Optional[str]) -> Camera:
with Vimba.get_instance() as vimba:
if camera_id:
try:
return vimba.get_camera_by_id(camera_id)
except VimbaCameraError:
abort('Failed to access Camera \'{}\'. Abort.'.format(camera_id))
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Cameras accessible. Abort.')
return cams[0]
def setup_camera(cam: Camera):
with cam:
# Try to adjust GeV packet size. This Feature is only available for GigE - Cameras.
try:
cam.GVSPAdjustPacketSize.run()
while not cam.GVSPAdjustPacketSize.is_done():
pass
except (AttributeError, VimbaFeatureError):
pass
def main():
print_preamble()
cam_id = parse_args()
with Vimba.get_instance():
with get_camera(cam_id) as cam:
setup_camera(cam)
# Acquire 10 frame with a custom timeout (default is 2000ms) per frame acquisition.
for frame in cam.get_frame_generator(limit=10, timeout_ms=3000):
print('Got {}'.format(frame), flush=True)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,328 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import sys
from typing import Optional, Dict, Any
from vimba import *
def print_preamble():
print('//////////////////////////////////')
print('/// Vimba API User Set Example ///')
print('//////////////////////////////////\n')
def print_usage():
print('Usage:')
print(' python user_set.py [camera_id] [/i:Index] [/{h|s|l|i|m|d|or|os|n}]')
print()
print('Parameters:')
print(' camera_id ID of the camera to use (using first camera if not specified)')
print(' /i:index User set index')
print(' /h Print help')
print(' /s Save user set to flash')
print(' /l Load user set from flash (default if not specified)')
print(' /i Get selected user set index')
print(' /m Make user set default')
print(' /d Is user set default')
print(' /or Get user set operation default.')
print(' /os Get user set operation status.')
print(' /n Get user set count')
print()
print('Examples:')
print(' To load user set 0 (factory set) from flash in order to activate it call:')
print(' UserSet /i:0 /l')
print()
print(' To save the current settings to user set 1 call:')
print(' UserSet /i:1 /s')
print()
def abort(reason: str, return_code: int = 1, usage: bool = False):
print(reason + '\n')
if usage:
print_usage()
sys.exit(return_code)
def parse_args() -> Dict[str, Any]:
args = sys.argv[1:]
argc = len(args)
result: Dict[str, Any] = {}
if (argc <= 0) or (4 <= argc):
abort(reason="Invalid number of arguments. Abort.", return_code=2, usage=True)
for arg in args:
if arg in ('/h'):
print_usage()
sys.exit(0)
# Examine command parameters
if arg in ('/s', '/l', '/i', '/m', '/d', '/or', '/os', '/n'):
if result.get('mode') is None:
result['mode'] = arg
else:
abort(reason="Multiple Commands specified. Abort.", return_code=2, usage=True)
# Examine specified index
elif arg.startswith('/i:'):
_, set_id = arg.split(':')
if not set_id:
abort(reason="No index specified after /i:. Abort.", return_code=2, usage=True)
try:
set_id_int = int(set_id)
except ValueError:
abort(reason="Number in /i:<no> is no Integer. Abort.", return_code=2, usage=True)
if set_id_int < 0:
abort(reason="Number in /i:<no> is negative. Abort.", return_code=2, usage=True)
if result.get('set_id') is not None:
abort(reason="Multiple /i:<no> specified. Abort.", return_code=2, usage=True)
result['set_id'] = set_id_int
# Examine camera id
elif result.get('camera_id') is None:
result['camera_id'] = arg
else:
abort(reason="Invalid arguments. Abort.", return_code=2, usage=True)
# Apply defaults
if not result.get('mode'):
result['mode'] = '/l'
return result
def get_camera(cam_id: Optional[str]):
with Vimba.get_instance() as vimba:
# Lookup Camera if it was specified.
if cam_id:
try:
return vimba.get_camera_by_id(cam_id)
except VimbaCameraError:
abort('Failed to access Camera {}. Abort.'.format(cam_id))
# If no camera was specified, use first detected camera.
else:
cams = vimba.get_all_cameras()
if not cams:
abort('No Camera detected. Abort.')
return cams[0]
def select_user_set(camera: Camera, set_id: int):
try:
camera.get_feature_by_name('UserSetSelector').set(set_id)
except VimbaFeatureError:
abort('Failed to select user set with \'{}\'. Abort.'.format(set_id))
def load_from_flash(cam: Camera, set_id: int):
with cam:
print('Loading user set \'{}\' from flash.'.format(set_id))
select_user_set(cam, set_id)
try:
cmd = cam.get_feature_by_name('UserSetLoad')
cmd.run()
while not cmd.is_done():
pass
except VimbaFeatureError:
abort('Failed to load user set \'{}\' from flash. Abort.'.format(set_id))
print('Loaded user set \'{}\' loaded from flash successfully.'.format(set_id))
def save_to_flash(cam: Camera, set_id: int):
with cam:
print('Saving user set \'{}\' to flash.'.format(set_id))
select_user_set(cam, set_id)
try:
cmd = cam.get_feature_by_name('UserSetSave')
cmd.run()
while not cmd.is_done():
pass
except VimbaFeatureError:
abort('Failed to save user set \'{}\' to flash. Abort.'.format(set_id))
print('Saved user set \'{}\' to flash.'.format(set_id))
def get_active_user_set(cam: Camera, _: int):
with cam:
print('Get selected user set id.')
try:
value = cam.get_feature_by_name('UserSetSelector').get()
except VimbaFeatureError:
abort('Failed to get user set id. Abort.')
print('The selected user set id is \'{}\'.'.format(int(value)))
def get_number_of_user_sets(cam: Camera, _: int):
with cam:
print('Get total number of user sets.')
try:
feat = cam.get_feature_by_name('UserSetSelector')
value = len(feat.get_available_entries())
except VimbaFeatureError:
abort('Failed to get total number of user sets. Abort.')
print('The total number of user sets is \'{}\''.format(value))
def set_default_user_set(cam: Camera, set_id: int):
with cam:
print('Set user set \'{}\' as default.'.format(set_id))
# Try to set mode via UserSetDefaultSelector feature
try:
feat = cam.get_feature_by_name('UserSetDefaultSelector')
try:
feat.set(set_id)
except VimbaFeatureError:
abort('Failed to set user set id \'{}\' as default user set'.format(set_id))
except VimbaFeatureError:
# Try to set mode via UserSetMakeDefault command
select_user_set(cam, set_id)
try:
cmd = cam.get_feature_by_name('UserSetMakeDefault')
cmd.run()
while not cmd.is_done():
pass
except VimbaFeatureError:
abort('Failed to set user set id \'{}\' as default user set'.format(set_id))
print('User set \'{}\' is the new default user set.'.format(set_id))
def is_default_user_set(cam: Camera, set_id: int):
with cam:
print('Is user set \'{}\' the default user set?'.format(set_id))
try:
default_id = int(cam.get_feature_by_name('UserSetDefaultSelector').get())
except VimbaFeatureError:
abort('Failed to get default user set id. Abort.')
msg = 'User set \'{}\' {} the default user set.'
print(msg.format(set_id, 'is' if set_id == default_id else 'is not'))
def get_operation_result(cam: Camera, set_id: int):
with cam:
print('Get user set operation result.')
try:
result = cam.get_feature_by_name('UserSetOperationResult').get()
except VimbaFeatureError:
abort('Failed to get user set operation result. Abort.')
print('Operation result was {}.'.format(result))
def get_operation_status(cam: Camera, set_id: int):
with cam:
print('Get user set operation status.')
try:
result = cam.get_feature_by_name('UserSetOperationStatus').get()
except VimbaFeatureError:
abort('Failed to get user set operation status. Abort.')
print('Operation status was {}.'.format(result))
def main():
print_preamble()
args = parse_args()
with Vimba.get_instance():
cam = get_camera(args.get('camera_id'))
print('Using Camera with ID \'{}\''.format(cam.get_id()))
with cam:
mode = args['mode']
try:
set_id = args.get('set_id', int(cam.get_feature_by_name('UserSetSelector').get()))
except VimbaFeatureError:
abort('Failed to get id of current user set. Abort.')
# Mode -> Function Object mapping
mode_to_fn = {
'/l': load_from_flash,
'/s': save_to_flash,
'/i': get_active_user_set,
'/n': get_number_of_user_sets,
'/m': set_default_user_set,
'/d': is_default_user_set,
'/or': get_operation_result,
'/os': get_operation_status
}
fn = mode_to_fn[mode]
fn(cam, set_id)
if __name__ == '__main__':
main()

299
Vimba_6_0/VimbaPython/Install.sh Executable file
View File

@@ -0,0 +1,299 @@
#!/usr/bin/env bash
# BSD 2-Clause License
#
# Copyright (c) 2019, Allied Vision Technologies GmbH
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# global parameters parsed from command line flags
DEBUG=false
while getopts "d" flag; do
case "${flag}" in
d) DEBUG=true ;;
*) ;;
esac
done
function get_bool_input()
{
QUESTION=$1
TRUTHY=$2
FALSY=$3
DEFAULT=$4
ANSWER=""
while [[ "$ANSWER" != "$TRUTHY" ]] && [[ "$ANSWER" != "$FALSY" ]]
do
echo -n "$QUESTION"
read ANSWER
# Use Default value if it was supplied and the input was empty.
if [[ -z "$ANSWER" ]] && [[ ! -z "$DEFAULT" ]]
then
ANSWER=$DEFAULT
fi
# Print message if given input is invalid.
if [[ "$ANSWER" != "$TRUTHY" ]] && [[ "$ANSWER" != "$FALSY" ]]
then
echo " Error: Given Input must be either \"$TRUTHY\" or \"$FALSY\". Try again."
fi
done
# Run test command to set return value for later evaluation.
[[ "$ANSWER" == "$TRUTHY" ]]
}
function inside_virtual_env
{
if [ -z "$VIRTUAL_ENV" ]; then
echo "false"
else
echo "true"
fi
}
function get_python_versions
{
DETECTED_PYTHONS=()
# Check if the script was run from a virtual environment and set search path for binary accordingly
if [ "$(inside_virtual_env)" = true ]; then
if [ "$DEBUG" = true ] ; then
echo "Detected active virtual environment" >&2
fi
SEARCH_PATH="$VIRTUAL_ENV"/bin
else
if [ "$DEBUG" = true ] ; then
echo "No virtual environment detected" >&2
fi
SEARCH_PATH=$(echo "$PATH" | tr ":" " ")
fi
if [ "$DEBUG" = true ] ; then
echo "Searching for python in $SEARCH_PATH" >&2
fi
# iterate over all detected python binaries and check if they are viable installations
for P in $(whereis -b -B $SEARCH_PATH -f python | tr " " "\n" | grep "python[[:digit:]]\.[[:digit:]]\.\?[[:digit:]]\?$" | sort -V)
do
# 1) Remove results that are links (venv executables are often links so we allow those)
if [ -L "$P" ] && [ "$(inside_virtual_env)" = false ]
then
if [ "$DEBUG" = true ] ; then
echo "$P was a link" >&2
fi
continue
fi
# 2) Remove results that are directories
if [ -d "$P" ]
then
if [ "$DEBUG" = true ] ; then
echo "$P was a directory" >&2
fi
continue
fi
# 3) Remove incompatible versions (<3.7)
# patch is ignored but has to be parsed in case the binary name contains it
FILENAME=$(basename -- "$P")
read -r MAJOR MINOR PATCH < <(echo $FILENAME | tr -dc "0-9." | tr "." " ")
if [ $MAJOR -gt 3 ] || { [ $MAJOR -eq 3 ] && [ $MINOR -ge 7 ]; }; then
: # the interperter is compatible
else
if [ "$DEBUG" = true ] ; then
echo "$P is not compatible. VimbaPython requires python >=3.7" >&2
fi
continue
fi
# 4) Remove results that offer no pip support.
$P -m pip > /dev/null 2>&1
if [ $? -ne 0 ]
then
if [ "$DEBUG" = true ] ; then
echo "$P did not have pip support" >&2
fi
continue
fi
DETECTED_PYTHONS+=("$P")
done
echo "${DETECTED_PYTHONS[@]}"
}
echo "###############################"
echo "# VimbaPython install script. #"
echo "###############################"
#########################
# Perform sanity checks #
#########################
# root is only required if we are not installing in a virtual environment
if [ $UID -ne 0 ] && [ "$(inside_virtual_env)" = false ]
then
echo "Error: Installation requires root priviliges. Abort."
exit 1
fi
PWD=$(pwd)
PWD=${PWD##*/}
if [[ "$PWD" != "VimbaPython" ]]
then
echo "Error: Please execute Install.sh within VimbaPython directory."
exit 1
fi
# get path to setup.py file
SOURCEDIR="$(find . -name setup.py -type f -printf '%h' -quit)"
if [ -z "$SOURCEDIR" ]
then
echo "Error: setup.py not found. Abort"
exit 1
fi
PYTHONS=$(get_python_versions)
if [ -z "$PYTHONS" ]
then
echo "Error: No compatible Python version with pip support found. Abort."
exit 1
fi
#################################################
# Determine python installation for VimbaPython #
#################################################
# List all given interpreters and create an Index
echo "The following Python versions with pip support were detected:"
ITER=0
for ITEM in ${PYTHONS[@]}
do
echo " $ITER: $ITEM"
LAST=$ITER
ITER=$(expr $ITER + 1)
done
# Read and verfiy user input
while true
do
echo -n "Enter python version to install VimbaPython (0 - $LAST, default: 0): "
read TMP
if [ -z "$TMP" ]
then
TMP=0
fi
# Check if Input was a number. If so: assign it.
if [ $TMP -eq $TMP ] 2>/dev/null
then
ITER=$TMP
else
echo " Error: Given input was not a number. Try again."
continue
fi
# Verify Input range
if [ 0 -le $ITER ] && [ $ITER -le $LAST ]
then
break
else
echo " Error: Given input is not between 0 and $LAST. Try again."
fi
done
# Search for selected python interpreter
IDX=0
PYTHON=""
for ITEM in ${PYTHONS[@]}
do
if [ $IDX -eq $ITER ]
then
PYTHON=$ITEM
break
else
IDX=$(expr $IDX + 1)
fi
done
echo "Installing VimbaPython for $PYTHON"
echo ""
##################################################
# Determine installation targets from user input #
##################################################
TARGET=""
# Ask for numpy support
get_bool_input "Install VimbaPython with numpy support (yes/no, default: yes):" "yes" "no" "yes"
if [ $? -eq 0 ]
then
TARGET="numpy-export"
echo "Installing VimbaPython with numpy support."
else
echo "Installing VimbaPython without numpy support."
fi
echo ""
# Ask for OpenCV support
get_bool_input "Install VimbaPython with OpenCV support (yes/no, default: yes):" "yes" "no" "yes"
if [ $? -eq 0 ]
then
if [ -z $TARGET ]
then
TARGET="opencv-export"
else
TARGET="$TARGET,opencv-export"
fi
echo "Installing VimbaPython with OpenCV support."
else
echo "Installing VimbaPython without OpenCV support."
fi
echo ""
# Execute installation via pip
if [ -z $TARGET ]
then
TARGET="$SOURCEDIR"
else
TARGET="$SOURCEDIR[$TARGET]"
fi
$PYTHON -m pip install "$TARGET"
if [ $? -eq 0 ]
then
echo "VimbaPython installation successful."
else
echo "Error: VimbaPython installation failed. Please check pip output for details."
fi

View File

@@ -0,0 +1,25 @@
BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,97 @@
Vimba Python API
===============
Prerequisites
===============
To use Vimba Python API, you need Python version 3.7 or higher.
Installing Python - Windows
---------------
If your system requires multiple, coexisting Python versions, consider
using pyenv-win to install and maintain multiple Python installations:
https://github.com/pyenv-win/pyenv-win
We recommend installing Python with admin rights.
1. Install Python: https://www.python.org/downloads/windows/
2. If pip >21.2 is used, read the instructions for all operating systems below.
3. To verify the installation, open the command prompt and enter:
python --version
python -m pip --version
Please ensure that the Python version is 3.7 or higher and pip uses this Python version.
Installing Python - Linux
---------------
On Linux systems, the Python installation process depends heavily on the distribution.
If python3.7 (or higher) is not available for your distribution or your system requires
multiple python versions to coexist, use pyenv:
https://realpython.com/intro-to-pyenv/
1. Install or update python3.7 with the packet manager of your distribution.
2. Install or update pip with the packet manager of your distribution.
3. To verify the installation, open a console and enter:
python --version
python -m pip --version
Installing the Vimba Python API
===============
All operating systems:
Open a terminal and navigate to the VimbaPython installation directory that
you have admin privileges/write permission for,
for example, C:\Users\Public\Documents\Allied Vision\Vimba_5.x\VimbaPython_Source
Users who want to change the API's sources can find them in the Vimba examples
directory, for example:
C:\Users\Public\Documents\Allied Vision\Vimba_5.x\VimbaPython_Source
Please note that Allied Vision can offer only limited support if an application
uses a modified version of the API.
Troubleshooting: If you don't have write permisson for the above-mentioned directories:
Download VimbaPython (in the correct version needed for your Vimba installation) from
https://github.com/alliedvision/VimbaPython and install it from that directory.
Or you can downgrade pip to a version <2.3 with, for example:
python -m pip install --upgrade pip==21.1.2
After the VimbaPython installation is complete, you can upgrade pip again to the latest version.
Basic Installation
---------------
Execute the following command:
python -m pip install .
Installation with optional NumPy and OpenCV export
---------------
Execute the following command:
python -m pip install .[numpy-export,opencv-export]
Helper scripts for Linux
---------------
For Linux systems helper scripts named `Install.sh` and `Uninstall.sh` to install and uninstall
VimbaPython are provided. They will automatically detect if there is a currently active virtual
environment. To install or uninstall VimbaPython for one of the system wide Python installations,
admin rights are required (use `sudo`). To get further details on why the scripts do not offer your
desired Python installation or to troubleshoot problems, a debug flag is provided (for example
`./Install.sh -d`).
ARM users only:
If installation of "opencv-export" fails, pip is not able to install
"opencv-python" for your ARM board. This is a known issue on ARM boards.
If you are affected by this, install VimbaPython without optional dependencies
and try to install OpenCV in a different way (for example, with your operating system's packet manager).
The OpenCV installation can be verified by running the example "Examples/asychronous_grab_opencv.py".

View File

@@ -0,0 +1,346 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import ctypes
from vimba.c_binding import *
class VimbaCommonTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_decode_cstr_behavior(self):
# Expected Behavior:
# c_char_p() == ''
# c_char_p(b'foo') == 'foo'
expected = ''
actual = decode_cstr(ctypes.c_char_p())
self.assertEqual(expected, actual)
expected = 'test'
actual = decode_cstr(ctypes.c_char_p(b'test').value)
self.assertEqual(expected, actual)
def test_decode_flags_zero(self):
# Expected Behavior: In case no bytes are set the
# zero value of the Flag Enum must be returned
expected = (VmbFeatureFlags.None_,)
actual = decode_flags(VmbFeatureFlags, 0)
self.assertEqual(expected, actual)
def test_decode_flags_some(self):
# Expected Behavior: Given Integer must be decided correctly.
# the order of the fields does not matter for this test.
expected = (
VmbFeatureFlags.Write,
VmbFeatureFlags.Read,
VmbFeatureFlags.ModifyWrite
)
input_data = 0
for val in expected:
input_data |= int(val)
actual = decode_flags(VmbFeatureFlags, input_data)
# Convert both collections into a list and sort it.
# That way order doesn't matter. It is only important that values are
# decoded correctly.
self.assertEqual(list(expected).sort(), list(actual).sort())
class CBindingVimbaCTypesTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_enum_vmb_error(self):
self.assertEqual(VmbError.Success, 0)
self.assertEqual(VmbError.InternalFault, -1)
self.assertEqual(VmbError.ApiNotStarted, -2)
self.assertEqual(VmbError.NotFound, -3)
self.assertEqual(VmbError.BadHandle, -4)
self.assertEqual(VmbError.DeviceNotOpen, -5)
self.assertEqual(VmbError.InvalidAccess, -6)
self.assertEqual(VmbError.BadParameter, -7)
self.assertEqual(VmbError.StructSize, -8)
self.assertEqual(VmbError.MoreData, -9)
self.assertEqual(VmbError.WrongType, -10)
self.assertEqual(VmbError.InvalidValue, -11)
self.assertEqual(VmbError.Timeout, -12)
self.assertEqual(VmbError.Other, -13)
self.assertEqual(VmbError.Resources, -14)
self.assertEqual(VmbError.InvalidCall, -15)
self.assertEqual(VmbError.NoTL, -16)
self.assertEqual(VmbError.NotImplemented_, -17)
self.assertEqual(VmbError.NotSupported, -18)
self.assertEqual(VmbError.Incomplete, -19)
self.assertEqual(VmbError.IO, -20)
def test_enum_vmb_pixel_format(self):
self.assertEqual(VmbPixelFormat.Mono8, 0x01080001)
self.assertEqual(VmbPixelFormat.Mono10, 0x01100003)
self.assertEqual(VmbPixelFormat.Mono10p, 0x010A0046)
self.assertEqual(VmbPixelFormat.Mono12, 0x01100005)
self.assertEqual(VmbPixelFormat.Mono12Packed, 0x010C0006)
self.assertEqual(VmbPixelFormat.Mono12p, 0x010C0047)
self.assertEqual(VmbPixelFormat.Mono14, 0x01100025)
self.assertEqual(VmbPixelFormat.Mono16, 0x01100007)
self.assertEqual(VmbPixelFormat.BayerGR8, 0x01080008)
self.assertEqual(VmbPixelFormat.BayerRG8, 0x01080009)
self.assertEqual(VmbPixelFormat.BayerGB8, 0x0108000A)
self.assertEqual(VmbPixelFormat.BayerBG8, 0x0108000B)
self.assertEqual(VmbPixelFormat.BayerGR10, 0x0110000C)
self.assertEqual(VmbPixelFormat.BayerRG10, 0x0110000D)
self.assertEqual(VmbPixelFormat.BayerGB10, 0x0110000E)
self.assertEqual(VmbPixelFormat.BayerBG10, 0x0110000F)
self.assertEqual(VmbPixelFormat.BayerGR12, 0x01100010)
self.assertEqual(VmbPixelFormat.BayerRG12, 0x01100011)
self.assertEqual(VmbPixelFormat.BayerGB12, 0x01100012)
self.assertEqual(VmbPixelFormat.BayerBG12, 0x01100013)
self.assertEqual(VmbPixelFormat.BayerGR12Packed, 0x010C002A)
self.assertEqual(VmbPixelFormat.BayerRG12Packed, 0x010C002B)
self.assertEqual(VmbPixelFormat.BayerGB12Packed, 0x010C002C)
self.assertEqual(VmbPixelFormat.BayerBG12Packed, 0x010C002D)
self.assertEqual(VmbPixelFormat.BayerGR10p, 0x010A0056)
self.assertEqual(VmbPixelFormat.BayerRG10p, 0x010A0058)
self.assertEqual(VmbPixelFormat.BayerGB10p, 0x010A0054)
self.assertEqual(VmbPixelFormat.BayerBG10p, 0x010A0052)
self.assertEqual(VmbPixelFormat.BayerGR12p, 0x010C0057)
self.assertEqual(VmbPixelFormat.BayerRG12p, 0x010C0059)
self.assertEqual(VmbPixelFormat.BayerGB12p, 0x010C0055)
self.assertEqual(VmbPixelFormat.BayerBG12p, 0x010C0053)
self.assertEqual(VmbPixelFormat.BayerGR16, 0x0110002E)
self.assertEqual(VmbPixelFormat.BayerRG16, 0x0110002F)
self.assertEqual(VmbPixelFormat.BayerGB16, 0x01100030)
self.assertEqual(VmbPixelFormat.BayerBG16, 0x01100031)
self.assertEqual(VmbPixelFormat.Rgb8, 0x02180014)
self.assertEqual(VmbPixelFormat.Bgr8, 0x02180015)
self.assertEqual(VmbPixelFormat.Rgb10, 0x02300018)
self.assertEqual(VmbPixelFormat.Bgr10, 0x02300019)
self.assertEqual(VmbPixelFormat.Rgb12, 0x0230001A)
self.assertEqual(VmbPixelFormat.Bgr12, 0x0230001B)
self.assertEqual(VmbPixelFormat.Rgb14, 0x0230005E)
self.assertEqual(VmbPixelFormat.Bgr14, 0x0230004A)
self.assertEqual(VmbPixelFormat.Rgb16, 0x02300033)
self.assertEqual(VmbPixelFormat.Bgr16, 0x0230004B)
self.assertEqual(VmbPixelFormat.Argb8, 0x02200016)
self.assertEqual(VmbPixelFormat.Rgba8, 0x02200016)
self.assertEqual(VmbPixelFormat.Bgra8, 0x02200017)
self.assertEqual(VmbPixelFormat.Rgba10, 0x0240005F)
self.assertEqual(VmbPixelFormat.Bgra10, 0x0240004C)
self.assertEqual(VmbPixelFormat.Rgba12, 0x02400061)
self.assertEqual(VmbPixelFormat.Bgra12, 0x0240004E)
self.assertEqual(VmbPixelFormat.Rgba14, 0x02400063)
self.assertEqual(VmbPixelFormat.Bgra14, 0x02400050)
self.assertEqual(VmbPixelFormat.Rgba16, 0x02400064)
self.assertEqual(VmbPixelFormat.Bgra16, 0x02400051)
self.assertEqual(VmbPixelFormat.Yuv411, 0x020C001E)
self.assertEqual(VmbPixelFormat.Yuv422, 0x0210001F)
self.assertEqual(VmbPixelFormat.Yuv444, 0x02180020)
self.assertEqual(VmbPixelFormat.YCbCr411_8_CbYYCrYY, 0x020C003C)
self.assertEqual(VmbPixelFormat.YCbCr422_8_CbYCrY, 0x02100043)
self.assertEqual(VmbPixelFormat.YCbCr8_CbYCr, 0x0218003A)
def test_enum_vmb_interface(self):
self.assertEqual(VmbInterface.Unknown, 0)
self.assertEqual(VmbInterface.Firewire, 1)
self.assertEqual(VmbInterface.Ethernet, 2)
self.assertEqual(VmbInterface.Usb, 3)
self.assertEqual(VmbInterface.CL, 4)
self.assertEqual(VmbInterface.CSI2, 5)
def test_enum_vmb_access_mode(self):
self.assertEqual(VmbAccessMode.None_, 0)
self.assertEqual(VmbAccessMode.Full, 1)
self.assertEqual(VmbAccessMode.Read, 2)
self.assertEqual(VmbAccessMode.Config, 4)
self.assertEqual(VmbAccessMode.Lite, 8)
def test_enum_vmb_feature_data(self):
self.assertEqual(VmbFeatureData.Unknown, 0)
self.assertEqual(VmbFeatureData.Int, 1)
self.assertEqual(VmbFeatureData.Float, 2)
self.assertEqual(VmbFeatureData.Enum, 3)
self.assertEqual(VmbFeatureData.String, 4)
self.assertEqual(VmbFeatureData.Bool, 5)
self.assertEqual(VmbFeatureData.Command, 6)
self.assertEqual(VmbFeatureData.Raw, 7)
self.assertEqual(VmbFeatureData.None_, 8)
def test_enum_vmb_feature_persist(self):
self.assertEqual(VmbFeaturePersist.All, 0)
self.assertEqual(VmbFeaturePersist.Streamable, 1)
self.assertEqual(VmbFeaturePersist.NoLUT, 2)
def test_enum_vmb_feature_visibility(self):
self.assertEqual(VmbFeatureVisibility.Unknown, 0)
self.assertEqual(VmbFeatureVisibility.Beginner, 1)
self.assertEqual(VmbFeatureVisibility.Expert, 2)
self.assertEqual(VmbFeatureVisibility.Guru, 3)
self.assertEqual(VmbFeatureVisibility.Invisible, 4)
def test_enum_vmb_feature_flags(self):
self.assertEqual(VmbFeatureFlags.None_, 0)
self.assertEqual(VmbFeatureFlags.Read, 1)
self.assertEqual(VmbFeatureFlags.Write, 2)
self.assertEqual(VmbFeatureFlags.Volatile, 8)
self.assertEqual(VmbFeatureFlags.ModifyWrite, 16)
def test_enum_vmb_frame_status(self):
self.assertEqual(VmbFrameStatus.Complete, 0)
self.assertEqual(VmbFrameStatus.Incomplete, -1)
self.assertEqual(VmbFrameStatus.TooSmall, -2)
self.assertEqual(VmbFrameStatus.Invalid, -3)
def test_enum_vmd_frame_flags(self):
self.assertEqual(VmbFrameFlags.None_, 0)
self.assertEqual(VmbFrameFlags.Dimension, 1)
self.assertEqual(VmbFrameFlags.Offset, 2)
self.assertEqual(VmbFrameFlags.FrameID, 4)
self.assertEqual(VmbFrameFlags.Timestamp, 8)
class VimbaCTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_call_vimba_c_valid(self):
# Expectation for valid call: No exceptions, no errors
expected_ver_info = (1, 9, 0)
ver_info = VmbVersionInfo()
call_vimba_c('VmbVersionQuery', byref(ver_info), sizeof(ver_info))
ver_info = (ver_info.major, ver_info.minor, ver_info.patch)
# Not an actual check for compatibility. Just make sure a sensible value was filled
self.assertGreaterEqual(ver_info, expected_ver_info)
def test_call_vimba_c_invalid_func_name(self):
# Expectation: An invalid function name must throw an AttributeError
ver_info = VmbVersionInfo()
self.assertRaises(AttributeError, call_vimba_c, 'VmbVersionQuer', byref(ver_info),
sizeof(ver_info))
def test_call_vimba_c_invalid_arg_number(self):
# Expectation: Invalid number of arguments with sane types.
# must lead to TypeErrors
ver_info = VmbVersionInfo()
self.assertRaises(TypeError, call_vimba_c, 'VmbVersionQuery', byref(ver_info))
def test_call_vimba_c_invalid_arg_type(self):
# Expectation: Arguments with invalid types must lead to TypeErrors
# Call with unexpected base types
self.assertRaises(ctypes.ArgumentError, call_vimba_c, 'VmbVersionQuery', 0, 'hi')
# Call with valid ctypes used wrongly
ver_info = VmbVersionInfo()
self.assertRaises(ctypes.ArgumentError, call_vimba_c, 'VmbVersionQuery', byref(ver_info),
ver_info)
def test_call_vimba_c_exception(self):
# Expectation: Errors returned from the C-Layer must be mapped
# to a special Exception Type call VimbaCError. This error must
# contain the returned Error Code from the failed C-Call.
# VmbVersionQuery has two possible Errors (taken from VimbaC.h):
# - VmbErrorStructSize: The given struct size is not valid for this version of the API
# - VmbErrorBadParameter: If "pVersionInfo" is NULL.
ver_info = VmbVersionInfo()
try:
call_vimba_c('VmbVersionQuery', byref(ver_info), sizeof(ver_info) - 1)
self.fail("Previous call must raise Exception.")
except VimbaCError as e:
self.assertEqual(e.get_error_code(), VmbError.StructSize)
try:
call_vimba_c('VmbVersionQuery', None, sizeof(ver_info))
self.fail("Previous call must raise Exception.")
except VimbaCError as e:
self.assertEqual(e.get_error_code(), VmbError.BadParameter)
class ImageTransformTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_call_vimba_image_transform_valid(self):
# Expectation for valid call: No exceptions, no errors
expected_ver_info = EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION
v = VmbUint32()
call_vimba_image_transform('VmbGetVersion', byref(v))
ver_info = str(v.value >> 24 & 0xff) + '.' + str(v.value >> 16 & 0xff)
self.assertEqual(expected_ver_info, ver_info)
def test_call_vimba_c_invalid_func_name(self):
# Expectation: An invalid function name must throw an AttributeError
v = VmbUint32()
self.assertRaises(AttributeError, call_vimba_image_transform, 'VmbGetVersio', byref(v))
def test_call_vimba_c_invalid_arg_number(self):
# Expectation: Invalid number of arguments with sane types must lead to TypeErrors
self.assertRaises(TypeError, call_vimba_image_transform, 'VmbGetVersion')
def test_call_vimba_c_invalid_arg_type(self):
# Expectation: Arguments with invalid types must lead to TypeErrors
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion',
VmbDouble())
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion', 0)
self.assertRaises(ctypes.ArgumentError, call_vimba_image_transform, 'VmbGetVersion',
'invalid')
def test_call_vimba_c_exception(self):
# Expectation: Failed operations must raise a VimbaCError
self.assertRaises(VimbaCError, call_vimba_image_transform, 'VmbGetVersion', None)

View File

@@ -0,0 +1,176 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba import *
class InterfaceTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
inters = self.vimba.get_all_interfaces()
if not inters:
self.vimba._shutdown()
self.skipTest('No Interface available to test against. Abort.')
def tearDown(self):
self.vimba._shutdown()
def test_interface_decode_id(self):
# Expectation all interface ids can be decoded in something not ''
for i in self.vimba.get_all_interfaces():
self.assertNotEqual(i.get_id(), '')
def test_interface_decode_type(self):
# Expectation all interface types be in interface types
excpected = (
InterfaceType.Firewire,
InterfaceType.Ethernet,
InterfaceType.Usb,
InterfaceType.CL,
InterfaceType.CSI2,
)
for i in self.vimba.get_all_interfaces():
self.assertIn(i.get_type(), excpected)
def test_interface_decode_name(self):
# Expectation all interface names can be decoded in something not ''
for i in self.vimba.get_all_interfaces():
self.assertNotEqual(i.get_name(), '')
def test_interface_decode_serial(self):
# Expectation: Serials can be '' on some interfaces. This test success
# if get serial does not raise
for i in self.vimba.get_all_interfaces():
self.assertNoRaise(i.get_serial)
def test_interface_get_all_features(self):
# Expectation: Call get_all_features raises RuntimeError outside of with
# Inside of with return a non empty set
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNotEqual(inter.get_all_features(), ())
def test_interface_get_features_affected_by(self):
# Expectation: Call get_features_affected_by raises RuntimeError outside of with.
# Inside with it must either return and empty set if the given feature has no affected
# Feature or a set off affected features
with self.vimba.get_all_interfaces()[0] as inter:
try:
affects_feats = inter.get_feature_by_name('DeviceUpdateList')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceUpdateList\'.')
try:
not_affects_feats = inter.get_feature_by_name('DeviceCount')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceCount\'.')
self.assertTrue(affects_feats.has_affected_features())
self.assertNotEquals(inter.get_features_affected_by(affects_feats), ())
self.assertFalse(not_affects_feats.has_affected_features())
self.assertEquals(inter.get_features_affected_by(not_affects_feats), ())
def test_interface_get_features_selected_by(self):
# Expectation: Call get_features_selected_by raises RuntimeError outside of with.
# Inside with it must either return and empty set if the given feature has no selected
# Feature or a set off affected features
with self.vimba.get_all_interfaces()[0] as inter:
try:
selects_feats = inter.get_feature_by_name('DeviceSelector')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceSelector\'.')
try:
not_selects_feats = inter.get_feature_by_name('DeviceCount')
except VimbaFeatureError:
self.skipTest('Test requires Feature \'DeviceCount\'.')
self.assertTrue(selects_feats.has_selected_features())
self.assertNotEquals(inter.get_features_selected_by(selects_feats), ())
self.assertFalse(not_selects_feats.has_selected_features())
self.assertEquals(inter.get_features_selected_by(not_selects_feats), ())
def test_interface_get_features_by_type(self):
# Expectation: Call get_features_by_type raises RuntimeError outside of with
# Inside of with return a non empty set for IntFeature (DeviceCount is IntFeature)
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNotEqual(inter.get_features_by_type(IntFeature), ())
def test_interface_get_features_by_category(self):
# Expectation: Call get_features_by_category raises RuntimeError outside of with
# Inside of with return a non empty set for /DeviceEnumeration)
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNotEqual(inter.get_features_by_category('/DeviceEnumeration'), ())
def test_interface_get_feature_by_name(self):
# Expectation: Call get_feature_by_name raises RuntimeError outside of with
# Inside of with return dont raise VimbaFeatureError for 'DeviceCount'
# A invalid name must raise VimbaFeatureError
with self.vimba.get_all_interfaces()[0] as inter:
self.assertNoRaise(inter.get_feature_by_name, 'DeviceCount')
self.assertRaises(VimbaFeatureError, inter.get_feature_by_name, 'Invalid Name')
def test_interface_context_manager_reentrancy(self):
# Expectation: Implemented Context Manager must be reentrant, not causing
# multiple interface openings (would cause C-Errors)
with self.vimba.get_all_interfaces()[0] as inter:
with inter:
with inter:
pass
def test_interface_api_context_sensitivity_inside_context(self):
# Expectation: Interface has functions that shall only be callable inside the Context,
# calling outside must cause a runtime error. This test check only if the RuntimeErrors
# are triggered then called Outside of the with block.
inter = self.vimba.get_all_interfaces()[0]
self.assertRaises(RuntimeError, inter.read_memory, 0, 0)
self.assertRaises(RuntimeError, inter.write_memory, 0, b'foo')
self.assertRaises(RuntimeError, inter.read_registers, ())
self.assertRaises(RuntimeError, inter.write_registers, {0: 0})
self.assertRaises(RuntimeError, inter.get_all_features)
# Enter scope to get handle on Features as valid parameters for the test:
# Don't to this in production code because the features will be invalid if used.
with inter:
feat = inter.get_all_features()[0]
self.assertRaises(RuntimeError, inter.get_features_affected_by, feat)
self.assertRaises(RuntimeError, inter.get_features_selected_by, feat)
self.assertRaises(RuntimeError, inter.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, inter.get_features_by_category, 'foo')
self.assertRaises(RuntimeError, inter.get_feature_by_name, 'foo')

View File

@@ -0,0 +1,84 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba.util import *
class TestObj:
@LeaveContextOnCall()
def __init__(self):
pass
@EnterContextOnCall()
def __enter__(self):
pass
@LeaveContextOnCall()
def __exit__(self, _1, _2, _3):
pass
@RaiseIfOutsideContext()
def works_inside_context(self):
pass
@RaiseIfInsideContext()
def works_outside_context(self):
pass
class ContextDecoratorTest(unittest.TestCase):
def setUp(self):
self.test_obj = TestObj()
def tearDown(self):
pass
def test_raise_if_inside_context(self):
# Expectation: a decorated method must raise a RuntimeError if a
# Decorated function is called within a with - statement and
# run properly outside of the context.
self.assertNoRaise(self.test_obj.works_outside_context)
with self.test_obj:
self.assertRaises(RuntimeError, self.test_obj.works_outside_context)
self.assertNoRaise(self.test_obj.works_outside_context)
def test_raise_if_outside_context(self):
# Expectation: a decorated method must raise a RuntimeError if a
# Decorated function is called outside a with - statement and
# run properly inside of the context.
self.assertRaises(RuntimeError, self.test_obj.works_inside_context)
with self.test_obj:
self.assertNoRaise(self.test_obj.works_inside_context)
self.assertRaises(RuntimeError, self.test_obj.works_inside_context)

View File

@@ -0,0 +1,256 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from typing import Union, Optional, Tuple, Callable, Dict, Type
from vimba.util import *
class RuntimeTypeCheckTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_func_mixed_args_kwargs_and_defaults(self):
# Expectation: The typecheck must be able to deal with a valid mixture of args, kwargs
# and default values.
@RuntimeTypeCheckEnable()
def test_func(a: int, b: str, c: float = 11.0 / 7.0):
pass
self.assertNoRaise(test_func, 1, '2', 0.0)
self.assertNoRaise(test_func, c=0.0, b='str', a=1)
self.assertNoRaise(test_func, 1, c=0.0, b='str')
self.assertNoRaise(test_func, 1, b='str')
self.assertNoRaise(test_func, 1, 'str')
self.assertRaises(TypeError, test_func, c=0.0, b='str', a=0.0)
self.assertRaises(TypeError, test_func, c='invalid type', b='str', a=0.0)
def test_func_no_hints(self):
# Expectation: Functions without type hints
# should not throw any type errors
@RuntimeTypeCheckEnable()
def test_func(arg1, arg2):
return str()
self.assertNoRaise(test_func, 'str', 0)
def test_func_some_hints(self):
# Expectation: Type checks are only enforced on Arguments with hint.
# Argument without hints should be accepted
@RuntimeTypeCheckEnable()
def test_func(arg1, arg2: int):
return str()
self.assertNoRaise(test_func, 'str', 0)
self.assertNoRaise(test_func, 0.5, 0)
self.assertRaises(TypeError, test_func, 'str', 0.0)
def test_object(self):
# Expectation: The runtime checker must work on Objects just as on
# functions.
class TestObject:
@RuntimeTypeCheckEnable()
def __init__(self, arg1: str, arg2: int):
pass
@RuntimeTypeCheckEnable()
def __call__(self, arg: str) -> str:
return arg
# Invalid construction
self.assertRaises(TypeError, TestObject, 0.0, 0)
obj = TestObject('str', 0)
self.assertNoRaise(obj, 'arg')
self.assertRaises(TypeError, obj, 0.0)
def test_type(self):
# Expectation: types as parameters must be detected like any other values.
@RuntimeTypeCheckEnable()
def func(arg: Type[int]):
pass
self.assertNoRaise(func, int)
self.assertRaises(TypeError, func, str)
self.assertRaises(TypeError, func, 0)
def test_union(self):
# Expectation: int and string are valid parameters. Everything else must throw
@RuntimeTypeCheckEnable()
def func(arg: Union[int, str]) -> Union[int, str]:
return arg
self.assertNoRaise(func, 0)
self.assertNoRaise(func, 'str')
self.assertRaises(TypeError, func, 0.0)
def test_optional(self):
# Expectation: For optionals the check must accept the given type or None.
# Anything else must lead to an TypeError
@RuntimeTypeCheckEnable()
def func(arg: Optional[int]) -> Optional[str]:
return str(arg)
self.assertNoRaise(func, 0)
self.assertNoRaise(func, None)
self.assertRaises(TypeError, func, 'str')
def test_tuple(self):
# Expectation: Fixed size tuples checking must verify that size and type order is
# enforced.
@RuntimeTypeCheckEnable()
def func(arg: Tuple[int, str, float]) -> Tuple[float, int, str]:
i, s, f = arg
return (f, i, s)
self.assertNoRaise(func, (1, 'str', 0.1))
self.assertRaises(TypeError, func, (1, 'str'))
self.assertRaises(TypeError, func, (1, 'str', 0.0, 'extra'))
self.assertRaises(TypeError, func, ('str1', 'str', 0.0))
def test_tuple_var_length(self):
# Expectation: Var length tuples checking must verify that contained type is enforced.
@RuntimeTypeCheckEnable()
def func(arg: Tuple[int, ...]) -> Tuple[str, ...]:
return tuple([str(i) for i in arg])
self.assertNoRaise(func, ())
self.assertNoRaise(func, (1,))
self.assertNoRaise(func, (1, 2, 3, 4, 5, 6))
self.assertRaises(TypeError, func, ('str', ))
self.assertRaises(TypeError, func, (1, 'str'))
def test_tuple_empty(self):
# Empty Tuples must satisfy the requirements to Tuple types as argument and results
@RuntimeTypeCheckEnable()
def func(arg: Tuple[int, ...]) -> Tuple[int, ...]:
return ()
self.assertNoRaise(func, ())
self.assertEqual(func(()), ())
def test_tuple_union(self):
# Tuples of union types must be detected correctly
@RuntimeTypeCheckEnable()
def func(arg: Tuple[Union[int, str], ...]):
return arg
self.assertNoRaise(func, (0,))
self.assertNoRaise(func, ('1',))
self.assertNoRaise(func, (2, 3))
self.assertNoRaise(func, ('4', '5'))
self.assertNoRaise(func, (6, '7'))
self.assertNoRaise(func, ('8', 9))
self.assertRaises(TypeError, func, (2, 0.0))
def test_dict(self):
# Expectation: Dictionaries must be detected correctly.
@RuntimeTypeCheckEnable()
def func(arg: Dict[int, str]):
pass
self.assertNoRaise(func, {0: 'ok'})
self.assertRaises(TypeError, func, None)
self.assertRaises(TypeError, func, 0)
self.assertRaises(TypeError, func, 'No Dict')
self.assertRaises(TypeError, func, {0.0: 'Err'})
self.assertRaises(TypeError, func, {0: b'bytes'})
def test_callable_no_func(self):
# Expectation: The Callable verification shall fail if given Parameter is no callable.
@RuntimeTypeCheckEnable()
def func(fn: Callable[[], None]):
fn()
self.assertRaises(TypeError, func, 'no_callable')
def test_callable_func(self):
# Expectation: A Callable without any hints must comply as long as the number of parameters
# matches to given hints. The Return Type doesn't matter if not given.
@RuntimeTypeCheckEnable()
def func(fn: Callable[[str, float], int], arg1: str, arg2: float) -> int:
return fn(arg1, arg2)
def ok(arg1, arg2):
return 0.0
def err1(arg1):
return 'str'
def err2(arg1, arg2, arg3):
return 23
self.assertNoRaise(func, ok, 'str', 0.0)
self.assertRaises(TypeError, func, err1, 'str', 0.0)
self.assertRaises(TypeError, func, err2, 'str', 0.0)
def test_callable_obj(self):
# Expectation: A Object that is callable must pass the runtime check
@RuntimeTypeCheckEnable()
def func(fn: Callable[[str], None], arg: str) -> str:
return fn(arg)
class Ok:
def __call__(self, arg: str) -> str:
return str
class Err1:
def __call__(self) -> str:
return 'Err1'
class Err2:
def __call__(self, arg1: str, arg2: str) -> str:
return arg1 + arg2
self.assertNoRaise(func, Ok(), 'str')
self.assertRaises(TypeError, func, Err1(), 'str')
self.assertRaises(TypeError, func, Err2(), 'str')
def test_callable_lambda(self):
# Expectation: RuntimeTypeCheck must behave with lambas as with functions
@RuntimeTypeCheckEnable()
def func(fn: Callable[[str, float], int], arg1: str, arg2: float) -> int:
return fn(arg1, arg2)
self.assertNoRaise(func, lambda a1, a2: 0.0, 'str', 0.0)
self.assertRaises(TypeError, func, lambda a1: 'foo', 'str', 0.0)
self.assertRaises(TypeError, func, lambda a1, a2, a3: 23, 'str', 0.0)

View File

@@ -0,0 +1,157 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba.util import *
class TracerTest(unittest.TestCase):
def setUp(self):
# Enable logging and setup hidden buffer
self.log = Log.get_instance()
self.log._test_buffer = []
self.log.enable(LOG_CONFIG_CRITICAL_CONSOLE_ONLY)
def tearDown(self):
# Disable logging and clear hidden buffer
self.log.disable()
self.log._test_buffer = None
def test_trace_inactive(self):
# Expectation: A disabled log must not contain any trace entries.
@TraceEnable()
def test_func(arg):
return str(arg)
self.log.disable()
self.assertEqual(test_func(1), '1')
self.assertFalse(self.log._test_buffer)
self.assertEqual(test_func('test'), 'test')
self.assertFalse(self.log._test_buffer)
self.assertEqual(test_func(2.0), '2.0')
self.assertFalse(self.log._test_buffer)
def test_trace_normal_exit(self):
# Expectation: Must not throw on call normal func.
# Each call traced call must add two Log entries:
@TraceEnable()
def test_func(arg):
return str(arg)
self.assertEqual(test_func(1), '1')
self.assertEqual(len(self.log._test_buffer), 2)
self.assertEqual(test_func('test'), 'test')
self.assertEqual(len(self.log._test_buffer), 4)
self.assertEqual(test_func(2.0), '2.0')
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_raised_exit(self):
# Expectation: Throws internally thrown exception and adds two log entries
# Each call traced call must add two Log entries:
@TraceEnable()
def test_func(arg):
raise TypeError('my error')
self.assertRaises(TypeError, test_func, 1)
self.assertEqual(len(self.log._test_buffer), 2)
self.assertRaises(TypeError, test_func, 'test')
self.assertEqual(len(self.log._test_buffer), 4)
self.assertRaises(TypeError, test_func, 2.0)
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_function(self):
# Expectation: Normal functions must be traceable
@TraceEnable()
def test_func():
pass
test_func()
self.assertEqual(len(self.log._test_buffer), 2)
test_func()
self.assertEqual(len(self.log._test_buffer), 4)
test_func()
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_lambda(self):
# Expectation: Lambdas must be traceable
test_lambda = TraceEnable()(lambda: 0)
test_lambda()
self.assertEqual(len(self.log._test_buffer), 2)
test_lambda()
self.assertEqual(len(self.log._test_buffer), 4)
test_lambda()
self.assertEqual(len(self.log._test_buffer), 6)
def test_trace_object(self):
# Expectation: Objects must be traceable including constructors.
class TestObj:
@TraceEnable()
def __init__(self, arg):
self.arg = arg
@TraceEnable()
def __str__(self):
return 'TestObj({})'.format(str(self.arg))
@TraceEnable()
def __repr__(self):
return 'TestObj({})'.format(repr(self.arg))
@TraceEnable()
def __call__(self):
pass
test_obj = TestObj('test')
self.assertEqual(len(self.log._test_buffer), 2)
str(test_obj)
self.assertEqual(len(self.log._test_buffer), 4)
repr(test_obj)
self.assertEqual(len(self.log._test_buffer), 6)
test_obj()
self.assertEqual(len(self.log._test_buffer), 8)

View File

@@ -0,0 +1,89 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba.c_binding import _select_vimba_home
from vimba.error import VimbaSystemError
class RankVimbaHomeCandidatesTest(unittest.TestCase):
def setUp(self):
pass
def tearDown(self):
pass
def test_empty_gentl_path(self):
candidates = []
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)
def test_empty_string(self):
candidates = ['']
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)
def test_single_bad_vimba_home_candidate(self):
candidates = ['/some/path']
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)
def test_single_good_vimba_home_candidate(self):
candidates = ['/opt/Vimba_3_1']
expected = '/opt/Vimba_3_1'
self.assertEquals(expected, _select_vimba_home(candidates))
def test_presorted_vimba_home_candidates(self):
candidates = ['/home/username/Vimba_4_0', '/opt/some/other/gentl/provider']
expected = '/home/username/Vimba_4_0'
self.assertEqual(expected, _select_vimba_home(candidates))
def test_unsorted_vimba_home_candidates(self):
candidates = ['/opt/some/other/gentl/provider', '/home/username/Vimba_4_0']
expected = '/home/username/Vimba_4_0'
self.assertEqual(expected, _select_vimba_home(candidates))
def test_many_vimba_home_candidates(self):
candidates = ['/some/random/path',
'/opt/some/gentl/provider',
'/opt/Vimba_4_0', # This should be selected
'/opt/another/gentl/provider',
'/another/incorrect/path']
expected = '/opt/Vimba_4_0'
self.assertEqual(expected, _select_vimba_home(candidates))
def test_multiple_vimba_home_directories(self):
# If multiple VIMBA_HOME directories are found an error should be raised
candidates = ['/some/random/path',
'/opt/some/gentl/provider',
'/opt/Vimba_4_0', # first installation
'/home/username/Vimba_4_0', # second installation
'/opt/another/gentl/provider',
'/another/incorrect/path']
with self.assertRaises(VimbaSystemError):
_select_vimba_home(candidates)

View File

@@ -0,0 +1,126 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba import *
class VimbaTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
def tearDown(self):
pass
def test_singleton(self):
# Expected behavior: Multiple calls to Vimba.get_instance() return the same object.
self.assertEqual(self.vimba, Vimba.get_instance())
def test_get_version(self):
# Expectation: Returned Version is not empty and does not raise any exceptions.
self.assertNotEqual(self.vimba.get_version(), "")
def test_get_camera_by_id_failure(self):
# Expected behavior: Lookup of a currently unavailable camera must throw an
# VimbaCameraError
with self.vimba:
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, 'Invalid ID')
def test_get_interface_by_id_failure(self):
# Expected behavior: Lookup of a currently unavailable interface must throw an
# VimbaInterfaceError
with self.vimba:
self.assertRaises(VimbaInterfaceError, self.vimba.get_interface_by_id, 'Invalid ID')
def test_get_feature_by_name_failure(self):
# Expected behavior: Lookup of a currently unavailable feature must throw an
# VimbaFeatureError
with self.vimba:
self.assertRaises(VimbaFeatureError, self.vimba.get_feature_by_name, 'Invalid ID')
def test_runtime_check_failure(self):
self.assertRaises(TypeError, self.vimba.set_network_discovery, 0.0)
with self.vimba:
# All functions with RuntimeTypeCheckEnable must return a TypeError on Failure
self.assertRaises(TypeError, self.vimba.get_camera_by_id, 0)
self.assertRaises(TypeError, self.vimba.get_interface_by_id, 1)
self.assertRaises(TypeError, self.vimba.get_feature_by_name, 0)
self.assertRaises(TypeError, self.vimba.enable_log, '-1')
self.assertRaises(TypeError, self.vimba.get_features_affected_by, '-1')
self.assertRaises(TypeError, self.vimba.get_features_selected_by, '-1')
self.assertRaises(TypeError, self.vimba.get_features_by_type, [])
self.assertRaises(TypeError, self.vimba.register_camera_change_handler, 0)
self.assertRaises(TypeError, self.vimba.unregister_camera_change_handler, 0)
self.assertRaises(TypeError, self.vimba.register_interface_change_handler, 0)
self.assertRaises(TypeError, self.vimba.unregister_interface_change_handler, 0)
def test_vimba_context_manager_reentrancy(self):
# Expectation: Implemented Context Manager must be reentrant, not causing
# multiple starts of the Vimba API (would cause C-Errors)
with self.vimba:
with self.vimba:
with self.vimba:
pass
def test_vimba_api_context_sensitity_outside_context(self):
# Expectation: Vimba has functions that shall only be callable outside the Context and
# calling within the context must cause a runtime error.
self.assertNoRaise(self.vimba.set_network_discovery, True)
with self.vimba:
self.assertRaises(RuntimeError, self.vimba.set_network_discovery, True)
self.assertNoRaise(self.vimba.set_network_discovery, True)
def test_vimba_api_context_sensitity_inside_context(self):
# Expectation: Vimba has functions that shall only be callable inside the Context and
# calling outside must cause a runtime error. This test check only if the RuntimeErrors
# are triggered then called Outside of the with block.
self.assertRaises(RuntimeError, self.vimba.read_memory, 0, 0)
self.assertRaises(RuntimeError, self.vimba.write_memory, 0, b'foo')
self.assertRaises(RuntimeError, self.vimba.read_registers, ())
self.assertRaises(RuntimeError, self.vimba.write_registers, {0: 0})
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
self.assertRaises(RuntimeError, self.vimba.get_interface_by_id, 'id')
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
self.assertRaises(RuntimeError, self.vimba.get_camera_by_id, 'id')
self.assertRaises(RuntimeError, self.vimba.get_all_features)
# Enter scope to get handle on Features as valid parameters for the test:
# Don't to this in production code because the feature will be invalid if use.
with self.vimba:
feat = self.vimba.get_all_features()[0]
self.assertRaises(RuntimeError, self.vimba.get_features_affected_by, feat)
self.assertRaises(RuntimeError, self.vimba.get_features_selected_by, feat)
self.assertRaises(RuntimeError, self.vimba.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, self.vimba.get_features_by_category, 'foo')
self.assertRaises(RuntimeError, self.vimba.get_feature_by_name, 'foo')

View File

@@ -0,0 +1,155 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
from vimba import *
class CamAncillaryDataTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.chunk_mode = self.cam.get_feature_by_name('ChunkModeActive')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'ChunkModeActive\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_ancillary_data_access(self):
# Expectation: Ancillary Data is None if ChunkMode is disable.
# If ChunkMode is enabled Ancillary Data shall not be None.
old_state = self.chunk_mode.get()
try:
# Disable ChunkMode, acquire frame: Ancillary Data must be None
self.chunk_mode.set(False)
self.assertIsNone(self.cam.get_frame().get_ancillary_data())
# Enable ChunkMode, acquire frame: Ancillary Data must not be None
self.chunk_mode.set(True)
self.assertIsNotNone(self.cam.get_frame().get_ancillary_data())
finally:
self.chunk_mode.set(old_state)
def test_ancillary_data_context_manager_reentrancy(self):
# Expectation: Ancillary Data Context Manager must be reentrant.
old_state = self.chunk_mode.get()
try:
self.chunk_mode.set(True)
frame = self.cam.get_frame()
anc_data = frame.get_ancillary_data()
with anc_data:
with anc_data:
with anc_data:
pass
finally:
self.chunk_mode.set(old_state)
def test_ancillary_data_api_context_sensitity(self):
# Expectation: Ancillary Data implements a Context Manager, outside of with-scope
# a runtime error should be raised on all feature related methods accessed outside of the
# context.
old_state = self.chunk_mode.get()
try:
self.chunk_mode.set(True)
frame = self.cam.get_frame()
anc_data = frame.get_ancillary_data()
# Check Access Outside Context
self.assertRaises(RuntimeError, anc_data.get_all_features)
self.assertRaises(RuntimeError, anc_data.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, anc_data.get_features_by_category, '/ChunkData')
self.assertRaises(RuntimeError, anc_data.get_feature_by_name, 'ChunkExposureTime')
with anc_data:
# Check Access after Context entry
self.assertNoRaise(anc_data.get_all_features)
self.assertNoRaise(anc_data.get_features_by_type, IntFeature)
self.assertNoRaise(anc_data.get_features_by_category, '/ChunkData')
self.assertNoRaise(anc_data.get_feature_by_name, 'ChunkExposureTime')
# Check Access after Context leaving
self.assertRaises(RuntimeError, anc_data.get_all_features)
self.assertRaises(RuntimeError, anc_data.get_features_by_type, IntFeature)
self.assertRaises(RuntimeError, anc_data.get_features_by_category, '/ChunkData')
self.assertRaises(RuntimeError, anc_data.get_feature_by_name, 'ChunkExposureTime')
finally:
self.chunk_mode.set(old_state)
def test_ancillary_data_removed_attrs(self):
# Expectation: Ancillary Data are lightweight features. Calling most Feature-Methods that
# call VimbaC Features would cause an internal error. Those error prone methods
# shall raise a RuntimeError on call.
old_state = self.chunk_mode.get()
try:
self.chunk_mode.set(True)
frame = self.cam.get_frame()
anc_data = frame.get_ancillary_data()
with anc_data:
for feat in anc_data.get_all_features():
self.assertRaises(RuntimeError, feat.get_access_mode)
self.assertRaises(RuntimeError, feat.is_readable)
self.assertRaises(RuntimeError, feat.is_writeable)
self.assertRaises(RuntimeError, feat.register_change_handler)
self.assertRaises(RuntimeError, feat.get_range)
self.assertRaises(RuntimeError, feat.get_increment)
self.assertRaises(RuntimeError, feat.set)
finally:
self.chunk_mode.set(old_state)

View File

@@ -0,0 +1,422 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import threading
import os
from vimba import *
from vimba.frame import *
def dummy_frame_handler(cam: Camera, frame: Frame):
pass
class CamCameraTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
self.cam.set_access_mode(AccessMode.Full)
def tearDown(self):
self.cam.set_access_mode(AccessMode.Full)
self.vimba._shutdown()
def test_camera_context_manager_access_mode(self):
# Expectation: Entering Context must not throw in cases where the current access mode is
# within get_permitted_access_modes()
permitted_modes = self.cam.get_permitted_access_modes()
for mode in permitted_modes:
self.cam.set_access_mode(mode)
try:
with self.cam:
pass
except BaseException:
self.fail()
def test_camera_context_manager_feature_discovery(self):
# Expectation: Outside of context, all features must be cleared,
# inside of context all features must be detected.
with self.cam:
self.assertNotEqual(self.cam.get_all_features(), ())
def test_camera_access_mode(self):
# Expectation: set/get access mode
self.cam.set_access_mode(AccessMode.None_)
self.assertEqual(self.cam.get_access_mode(), AccessMode.None_)
self.cam.set_access_mode(AccessMode.Full)
self.assertEqual(self.cam.get_access_mode(), AccessMode.Full)
self.cam.set_access_mode(AccessMode.Read)
self.assertEqual(self.cam.get_access_mode(), AccessMode.Read)
def test_camera_get_id(self):
# Expectation: get decoded camera id
self.assertTrue(self.cam.get_id())
def test_camera_get_name(self):
# Expectation: get decoded camera name
self.assertTrue(self.cam.get_name())
def test_camera_get_model(self):
# Expectation: get decoded camera model
self.assertTrue(self.cam.get_model())
def test_camera_get_serial(self):
# Expectation: get decoded camera serial
self.assertTrue(self.cam.get_serial())
def test_camera_get_permitted_access_modes(self):
# Expectation: get currently permitted access modes
expected = (AccessMode.None_, AccessMode.Full, AccessMode.Read, AccessMode.Config)
for mode in self.cam.get_permitted_access_modes():
self.assertIn(mode, expected)
def test_camera_get_interface_id(self):
# Expectation: get interface Id this camera is connected to
self.assertTrue(self.cam.get_interface_id())
def test_camera_get_features_affected(self):
# Expectation: Features that affect other features shall return a set of affected feature
# Features that don't affect other features shall return (). If a Feature is supplied that
# is not associated with that camera, a TypeError must be raised.
with self.cam:
try:
affect = self.cam.get_feature_by_name('Height')
except VimbaFeatureError as e:
raise unittest.SkipTest('Failed to lookup Feature Height') from e
try:
not_affect = self.cam.get_feature_by_name('AcquisitionFrameCount')
except VimbaFeatureError as e:
raise unittest.SkipTest('Failed to lookup Feature AcquisitionFrameCount') from e
self.assertEqual(self.cam.get_features_affected_by(not_affect), ())
try:
payload_size = self.cam.get_feature_by_name('PayloadSize')
except VimbaFeatureError as e:
raise unittest.SkipTest('Failed to lookup Feature PayloadSize') from e
self.assertIn(payload_size, self.cam.get_features_affected_by(affect))
def test_camera_frame_generator_limit_set(self):
# Expectation: The Frame generator fetches the given number of images.
with self.cam:
self.assertEqual(len([i for i in self.cam.get_frame_generator(0)]), 0)
self.assertEqual(len([i for i in self.cam.get_frame_generator(1)]), 1)
self.assertEqual(len([i for i in self.cam.get_frame_generator(7)]), 7)
self.assertEqual(len([i for i in self.cam.get_frame_generator(11)]), 11)
def test_camera_frame_generator_error(self):
# Expectation: The Frame generator raises a ValueError on a
# negative limit and the camera raises an ValueError
# if the camera is not opened.
# generator execution must throw if streaming is enabled
with self.cam:
# Check limits
self.assertRaises(ValueError, self.cam.get_frame_generator, -1)
self.assertRaises(ValueError, self.cam.get_frame_generator, 1, 0)
self.assertRaises(ValueError, self.cam.get_frame_generator, 1, -1)
self.cam.start_streaming(dummy_frame_handler, 5)
self.assertRaises(VimbaCameraError, self.cam.get_frame)
self.assertRaises(VimbaCameraError, next, self.cam.get_frame_generator(1))
# Stop Streaming: Everything should be fine.
self.cam.stop_streaming()
self.assertNoRaise(self.cam.get_frame)
self.assertNoRaise(next, self.cam.get_frame_generator(1))
def test_camera_get_frame(self):
# Expectation: Gets single Frame without any exception. Image data must be set.
# If a zero or negative timeouts must lead to a ValueError.
with self.cam:
self.assertRaises(ValueError, self.cam.get_frame, 0)
self.assertRaises(ValueError, self.cam.get_frame, -1)
self.assertNoRaise(self.cam.get_frame)
self.assertEqual(type(self.cam.get_frame()), Frame)
def test_camera_capture_error_outside_vimba_scope(self):
# Expectation: Camera access outside of Vimba scope must lead to a RuntimeError
gener = None
with self.cam:
gener = self.cam.get_frame_generator(1)
# Shutdown API
self.vimba._shutdown()
# Access invalid Iterator
self.assertRaises(RuntimeError, next, gener)
def test_camera_capture_error_outside_camera_scope(self):
# Expectation: Camera access outside of Camera scope must lead to a RuntimeError
gener = None
with self.cam:
gener = self.cam.get_frame_generator(1)
self.assertRaises(RuntimeError, next, gener)
def test_camera_capture_timeout(self):
# Expectation: Camera access outside of Camera scope must lead to a VimbaTimeout
with self.cam:
self.assertRaises(VimbaTimeout, self.cam.get_frame, 1)
def test_camera_is_streaming(self):
# Expectation: After start_streaming() is_streaming() must return true. After stop it must
# return false. If the camera context is left without stop_streaming(), leaving
# the context must stop streaming.
# Normal Operation
self.assertEqual(self.cam.is_streaming(), False)
with self.cam:
self.cam.start_streaming(dummy_frame_handler)
self.assertEqual(self.cam.is_streaming(), True)
self.cam.stop_streaming()
self.assertEqual(self.cam.is_streaming(), False)
# Missing the stream stop. Close must stop streaming
with self.cam:
self.cam.start_streaming(dummy_frame_handler, 5)
self.assertEqual(self.cam.is_streaming(), True)
self.assertEqual(self.cam.is_streaming(), False)
def test_camera_streaming_error_frame_count(self):
# Expectation: A negative or zero frame_count must lead to an value error
with self.cam:
self.assertRaises(ValueError, self.cam.start_streaming, dummy_frame_handler, 0)
self.assertRaises(ValueError, self.cam.start_streaming, dummy_frame_handler, -1)
def test_camera_streaming(self):
# Expectation: A given frame_handler must be executed for each buffered frame.
class FrameHandler:
def __init__(self, frame_count):
self.cnt = 0
self.frame_count = frame_count
self.event = threading.Event()
def __call__(self, cam: Camera, frame: Frame):
self.cnt += 1
if self.cnt == self.frame_count:
self.event.set()
timeout = 5.0
frame_count = 10
handler = FrameHandler(frame_count)
with self.cam:
try:
self.cam.start_streaming(handler, frame_count)
# Wait until the FrameHandler has been executed for each queued frame
self.assertTrue(handler.event.wait(timeout))
finally:
self.cam.stop_streaming()
def test_camera_streaming_queue(self):
# Expectation: A given frame must be reused if it is enqueued again.
class FrameHandler:
def __init__(self, frame_count):
self.cnt = 0
self.frame_count = frame_count
self.event = threading.Event()
def __call__(self, cam: Camera, frame: Frame):
self.cnt += 1
if self.cnt == self.frame_count:
self.event.set()
cam.queue_frame(frame)
timeout = 5.0
frame_count = 5
frame_reuse = 2
handler = FrameHandler(frame_count * frame_reuse)
with self.cam:
try:
self.cam.start_streaming(handler, frame_count)
# Wait until the FrameHandler has been executed for each queued frame
self.assertTrue(handler.event.wait(timeout))
finally:
self.cam.stop_streaming()
def test_camera_runtime_type_check(self):
def valid_handler(cam, frame):
pass
def invalid_handler_1(cam):
pass
def invalid_handler_2(cam, frame, extra):
pass
self.assertRaises(TypeError, self.cam.set_access_mode, -1)
with self.cam:
# Expectation: raise TypeError on passing invalid parameters
self.assertRaises(TypeError, self.cam.get_frame, 'hi')
self.assertRaises(TypeError, self.cam.get_features_affected_by, 'No Feature')
self.assertRaises(TypeError, self.cam.get_features_selected_by, 'No Feature')
self.assertRaises(TypeError, self.cam.get_features_by_type, 0.0)
self.assertRaises(TypeError, self.cam.get_feature_by_name, 0)
self.assertRaises(TypeError, self.cam.get_frame_generator, '3')
self.assertRaises(TypeError, self.cam.get_frame_generator, 0, 'foo')
self.assertRaises(TypeError, self.cam.start_streaming, valid_handler, 'no int')
self.assertRaises(TypeError, self.cam.start_streaming, invalid_handler_1)
self.assertRaises(TypeError, self.cam.start_streaming, invalid_handler_2)
self.assertRaises(TypeError, self.cam.save_settings, 0, PersistType.All)
self.assertRaises(TypeError, self.cam.save_settings, 'foo.xml', 'false type')
def test_camera_save_load_settings(self):
# Expectation: After settings export a settings change must be reverted by loading a
# Previously saved configuration.
file_name = 'test_save_load_settings.xml'
with self.cam:
feat_height = self.cam.get_feature_by_name('Height')
old_val = feat_height.get()
self.cam.save_settings(file_name, PersistType.All)
min_, max_ = feat_height.get_range()
inc = feat_height.get_increment()
feat_height.set(max_ - min_ - inc)
self.cam.load_settings(file_name, PersistType.All)
os.remove(file_name)
self.assertEqual(old_val, feat_height.get())
def test_camera_save_settings_verify_path(self):
# Expectation: Valid files end with .xml and can be either a absolute path or relative
# path to the given File. Everything else is a ValueError.
valid_paths = (
'valid1.xml',
os.path.join('.', 'valid2.xml'),
os.path.join('Tests', 'valid3.xml'),
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'valid4.xml'),
)
with self.cam:
self.assertRaises(ValueError, self.cam.save_settings, 'inval.xm', PersistType.All)
for path in valid_paths:
self.assertNoRaise(self.cam.save_settings, path, PersistType.All)
os.remove(path)
def test_camera_load_settings_verify_path(self):
# Expectation: Valid files end with .xml and must exist before before any execution.
valid_paths = (
'valid1.xml',
os.path.join('.', 'valid2.xml'),
os.path.join('Tests', 'valid3.xml'),
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'valid4.xml'),
)
with self.cam:
self.assertRaises(ValueError, self.cam.load_settings, 'inval.xm', PersistType.All)
for path in valid_paths:
self.assertRaises(ValueError, self.cam.load_settings, path, PersistType.All)
for path in valid_paths:
self.cam.save_settings(path, PersistType.All)
self.assertNoRaise(self.cam.load_settings, path, PersistType.All)
os.remove(path)
def test_camera_context_manager_reentrancy(self):
# Expectation: Camera Context Manager must be reentrant. Multiple calls to _open
# must be prevented (would cause VimbaC - Error)
with self.cam:
with self.cam:
with self.cam:
pass
def test_camera_api_context_sensitity_outside_context(self):
# Expectation: Call set_access_mode withing with scope must raise a RuntimeError
with self.cam:
self.assertRaises(RuntimeError, self.cam.set_access_mode)
def test_camera_api_context_sensitity_inside_context(self):
# Expectation: Most Camera related functions are only valid then called within the given
# Context. If called from Outside a runtime error must be raised.
self.assertRaises(RuntimeError, self.cam.read_memory)
self.assertRaises(RuntimeError, self.cam.write_memory)
self.assertRaises(RuntimeError, self.cam.read_registers)
self.assertRaises(RuntimeError, self.cam.write_registers)
self.assertRaises(RuntimeError, self.cam.get_all_features)
self.assertRaises(RuntimeError, self.cam.get_features_affected_by)
self.assertRaises(RuntimeError, self.cam.get_features_selected_by)
self.assertRaises(RuntimeError, self.cam.get_features_by_type)
self.assertRaises(RuntimeError, self.cam.get_features_by_category)
self.assertRaises(RuntimeError, self.cam.get_feature_by_name)
self.assertRaises(RuntimeError, self.cam.get_frame_generator)
self.assertRaises(RuntimeError, self.cam.get_frame)
self.assertRaises(RuntimeError, self.cam.start_streaming)
self.assertRaises(RuntimeError, self.cam.stop_streaming)
self.assertRaises(RuntimeError, self.cam.queue_frame)
self.assertRaises(RuntimeError, self.cam.get_pixel_formats)
self.assertRaises(RuntimeError, self.cam.get_pixel_format)
self.assertRaises(RuntimeError, self.cam.set_pixel_format)
self.assertRaises(RuntimeError, self.cam.save_settings)
self.assertRaises(RuntimeError, self.cam.load_settings)

View File

@@ -0,0 +1,831 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import threading
from vimba import *
from vimba.feature import *
class CamBaseFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.height = self.cam.get_feature_by_name('Height')
except VimbaCameraError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'Height\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_name(self):
# Expectation: Return decoded FeatureName
self.assertEqual(self.height.get_name(), 'Height')
def test_get_flags(self):
# Expectation: Return decoded FeatureFlags
self.assertEqual(self.height.get_flags(), (FeatureFlags.Read, FeatureFlags.Write))
def test_get_category(self):
# Expectation: Return decoded category
self.assertNotEqual(self.height.get_category(), '')
def test_get_display_name(self):
# Expectation: Return decoded category
self.assertEqual(self.height.get_display_name(), 'Height')
def test_get_polling_time(self):
# Expectation: Return polling time. Only volatile features return
# anything other than zero.
self.assertEqual(self.height.get_polling_time(), 0)
def test_get_unit(self):
# Expectation: If Unit exists, return unit else return ''
self.assertEqual(self.height.get_unit(), '')
def test_get_representation(self):
# Expectation: Get numeric representation if existing else ''
self.assertEqual(self.height.get_representation(), '')
def test_get_visibility(self):
# Expectation: Get UI Visibility
self.assertEqual(self.height.get_visibility(), FeatureVisibility.Beginner)
def test_get_tooltip(self):
# Expectation: Shall not raise anything
self.assertNoRaise(self.height.get_tooltip)
def test_get_description(self):
# Expectation: Get decoded description
self.assertNotEqual(self.height.get_description(), '')
def test_get_sfnc_namespace(self):
# Expectation: Get decoded sfnc namespace
self.assertNotEqual(self.height.get_sfnc_namespace(), '')
def test_is_streamable(self):
# Expectation: Streamable features shall return True, others False
self.assertNoRaise(self.height.is_streamable)
def test_has_affected_features(self):
# Expectation:Features that affect features shall return True, others False
self.assertTrue(self.height.has_affected_features())
def test_has_selected_features(self):
# Expectation:Features that select features shall return True, others False
self.assertFalse(self.height.has_selected_features())
def test_get_access_mode(self):
# Expectation: Read/Write Features return (True, True), ReadOnly return (True, False)
self.assertEqual(self.height.get_access_mode(), (True, True))
def test_is_readable(self):
# Expectation: True if feature grant read access else False
self.assertTrue(self.height.is_readable())
def test_is_writeable(self):
# Expectation: True if feature grant write access else False
self.assertTrue(self.height.is_writeable())
def test_change_handler(self):
# Expectation: A given change handler is executed on value change.
# Adding the same handler multiple times shall not lead to multiple executions.
# The same goes for double unregister.
class Handler:
def __init__(self):
self.event = threading.Event()
self.call_cnt = 0
def __call__(self, feat):
self.call_cnt += 1
self.event.set()
handler = Handler()
self.height.register_change_handler(handler)
self.height.register_change_handler(handler)
tmp = self.height.get()
min_, _ = self.height.get_range()
inc = self.height.get_increment()
if min_ <= tmp - inc:
self.height.set(tmp - inc)
else:
self.height.set(tmp + inc)
handler.event.wait()
self.height.unregister_change_handler(handler)
self.height.unregister_change_handler(handler)
self.height.set(tmp)
self.assertEqual(handler.call_cnt, 1)
def test_stringify_features(self):
# Expectation: Each Feature must have a __str__ method. Depending on the Feature
# current Values are queried, this can fail. In those cases, all exceptions are
# fetched -> all features must be strinify able without raising any exception
for feat in self.vimba.get_all_features():
self.assertNoRaise(str, feat)
for feat in self.cam.get_all_features():
self.assertNoRaise(str, feat)
class CamBoolFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.feat = self.vimba.get_feature_by_name('UsbTLIsPresent')
except VimbaFeatureError:
self.vimba._shutdown()
self.skipTest('Required Feature \'UsbTLIsPresent\' not available.')
def tearDown(self):
self.vimba._shutdown()
def test_get_type(self):
# Expectation: BoolFeature must return BoolFeature on get_type
self.assertEqual(self.feat.get_type(), BoolFeature)
def test_get(self):
# Expectation: returns current boolean value.
self.assertNoRaise(self.feat.get)
def test_set(self):
# Expectation: Raises invalid Access on non-writeable features.
self.assertRaises(VimbaFeatureError, self.feat.set, True)
class CamCommandFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.feat = self.vimba.get_feature_by_name('ActionCommand')
except VimbaFeatureError:
self.vimba._shutdown()
self.skipTest('Required Feature \'ActionCommand\' not available.')
def tearDown(self):
self.vimba._shutdown()
def test_get_type(self):
# Expectation: CommandFeature must return CommandFeature on get_type
self.assertEqual(self.feat.get_type(), CommandFeature)
class CamEnumFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.feat_r = self.cam.get_feature_by_name('DeviceScanType')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'DeviceScanType\' not available.')
try:
self.feat_rw = self.cam.get_feature_by_name('AcquisitionMode')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'AcquisitionMode\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: EnumFeature must return EnumFeature on get_type
self.assertEqual(self.feat_r.get_type(), EnumFeature)
self.assertEqual(self.feat_rw.get_type(), EnumFeature)
def test_entry_as_bytes(self):
# Expectation: Get EnumEntry as encoded byte sequence
expected = b'MultiFrame'
entry = self.feat_rw.get_entry('MultiFrame')
self.assertEqual(bytes(entry), expected)
def test_entry_as_tuple(self):
# Expectation: Get EnumEntry as (str, int)
entry = self.feat_rw.get_entry('MultiFrame')
self.assertEqual(entry.as_tuple(), self.feat_rw.get_entry(int(entry)).as_tuple())
def test_get_all_entries(self):
# Expectation: Get all possible enum entries regardless of the availability
expected = (self.feat_r.get_entry('Areascan'),)
for e in expected:
self.assertIn(e, self.feat_r.get_all_entries())
expected = (
self.feat_rw.get_entry('SingleFrame'),
self.feat_rw.get_entry('MultiFrame'),
self.feat_rw.get_entry('Continuous')
)
for e in expected:
self.assertIn(e, self.feat_rw.get_all_entries())
def test_get_avail_entries(self):
# Expectation: All returned enum entries must be available
for e in self.feat_r.get_available_entries():
self.assertTrue(e.is_available())
for e in self.feat_rw.get_available_entries():
self.assertTrue(e.is_available())
def test_get_entry_int(self):
# Expectation: Lookup a given entry by using an int as key.
# Invalid keys must return VimbaFeatureError.
expected = self.feat_r.get_all_entries()[0]
self.assertEqual(self.feat_r.get_entry(int(expected)), expected)
expected = self.feat_rw.get_all_entries()[1]
self.assertEqual(self.feat_rw.get_entry(int(expected)), expected)
self.assertRaises(VimbaFeatureError, self.feat_r.get_entry, -1)
self.assertRaises(VimbaFeatureError, self.feat_rw.get_entry, -1)
def test_get_entry_str(self):
# Expectation: Lookup a given entry by using a str as key.
# Invalid keys must return VimbaFeatureError.
expected = self.feat_r.get_all_entries()[0]
self.assertEqual(self.feat_r.get_entry(str(expected)), expected)
expected = self.feat_rw.get_all_entries()[1]
self.assertEqual(self.feat_rw.get_entry(str(expected)), expected)
self.assertRaises(VimbaFeatureError, self.feat_r.get_entry, 'Should be invalid')
self.assertRaises(VimbaFeatureError, self.feat_rw.get_entry, 'Should be invalid')
def test_get(self):
# Expectation: Get must return the current value.
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_set_entry(self):
# Expectation: Set given enum entry if feature is writable.
# Raises:
# - VimbaFeatureError if enum entry is from other enum feature.
# - VimbaFeatureError if feature is read only
# Read Only Feature
entry = self.feat_r.get_all_entries()[0]
self.assertRaises(VimbaFeatureError, self.feat_r.set, entry)
# Read/Write Feature
old_entry = self.feat_rw.get()
try:
# Normal operation
self.assertNoRaise(self.feat_rw.set, self.feat_rw.get_entry(2))
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
# Provoke FeatureError by setting the feature from the ReadOnly entry.
self.assertRaises(VimbaFeatureError, self.feat_rw.set, entry)
finally:
self.feat_rw.set(old_entry)
def test_set_str(self):
# Expectation: Set given enum entry string value if feature is writable.
# Raises:
# - VimbaFeatureError if given string is not associated with this feature.
# - VimbaFeatureError if feature is read only
# Read Only Feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, str(self.feat_r.get_entry(0)))
# Read/Write Feature
old_entry = self.feat_rw.get()
try:
# Normal operation
self.assertNoRaise(self.feat_rw.set, str(self.feat_rw.get_entry(2)))
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
# Provoke FeatureError by an invalid enum value
self.assertRaises(VimbaFeatureError, self.feat_rw.set, 'Hopefully invalid')
finally:
self.feat_rw.set(old_entry)
def test_set_int(self):
# Expectation: Set given enum entry int value if feature is writable.
# Raises:
# - VimbaFeatureError if given int is not associated with this feature.
# - VimbaFeatureError if feature is read only
# Read Only Feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, int(self.feat_r.get_entry(0)))
# Read/Write Feature
old_entry = self.feat_rw.get()
try:
# Normal operation
self.assertNoRaise(self.feat_rw.set, int(self.feat_rw.get_entry(2)))
self.assertEqual(self.feat_rw.get(), self.feat_rw.get_entry(2))
# Provoke FeatureError by an invalid enum value
self.assertRaises(VimbaFeatureError, self.feat_rw.set, -23)
finally:
self.feat_rw.set(old_entry)
def test_set_in_callback(self):
# Expected behavior: A set operation within a change handler must
# Raise a VimbaFeatureError to prevent an endless handler execution.
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
old_entry = self.feat_rw.get()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
# Trigger change handler and wait for callback execution.
self.feat_rw.set(self.feat_rw.get())
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)
self.feat_rw.set(old_entry)
class CamFloatFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.feat_r = self.vimba.get_feature_by_name('Elapsed')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'Elapsed\' not available.')
try:
self.feat_rw = self.cam.get_feature_by_name('ExposureTime')
except VimbaFeatureError:
# Some Cameras name ExposureTime as ExposureTimeAbs
try:
self.feat_rw = self.cam.get_feature_by_name('ExposureTimeAbs')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'ExposureTime\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: FloatFeature returns FloatFeature on get_type.
self.assertEqual(self.feat_r.get_type(), FloatFeature)
self.assertEqual(self.feat_rw.get_type(), FloatFeature)
def test_get(self):
# Expectation: Get current value.
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_get_range(self):
# Expectation: Get value range. Raise VimbaFeatureError on non-read access.
self.assertNoRaise(self.feat_r.get_range)
self.assertNoRaise(self.feat_rw.get_range)
def test_get_increment(self):
# Expectation: Get value increment if existing. If this Feature has no
# increment, None is returned.
self.assertNoRaise(self.feat_r.get_increment)
self.assertNoRaise(self.feat_rw.get_increment)
def test_set(self):
# Expectation: Set value. Errors:
# VimbaFeatureError if access right are not writable
# VimbaFeatureError if value is out of bounds
# Read only feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, 0.0)
# Read/Write Feature
old_value = self.feat_rw.get()
try:
delta = 0.1
# Range test
min_, max_ = self.feat_rw.get_range()
# Within bounds (no error)
self.assertNoRaise(self.feat_rw.set, min_)
self.assertAlmostEqual(self.feat_rw.get(), min_)
self.assertNoRaise(self.feat_rw.set, max_)
self.assertAlmostEqual(self.feat_rw.get(), max_)
# Out of bounds (must raise)
self.assertRaises(VimbaFeatureError, self.feat_rw.set, min_ - delta)
self.assertRaises(VimbaFeatureError, self.feat_rw.set, max_ + delta)
finally:
self.feat_rw.set(old_value)
def test_set_in_callback(self):
# Expectation: Calling set within change_handler must raise an VimbaFeatureError
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
old_entry = self.feat_rw.get()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
# Trigger change handler and wait for callback execution.
self.feat_rw.set(self.feat_rw.get())
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)
self.feat_rw.set(old_entry)
class CamIntFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
try:
self.feat_r = self.cam.get_feature_by_name('HeightMax')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'HeightMax\' not available.')
try:
self.feat_rw = self.cam.get_feature_by_name('Height')
except VimbaFeatureError:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Required Feature \'Height\' not available.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: IntFeature must return IntFeature on get_type
self.assertEqual(self.feat_r.get_type(), IntFeature)
self.assertEqual(self.feat_rw.get_type(), IntFeature)
def test_get(self):
# Expectation: Get current value
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_get_range(self):
# Expectation: Get range of accepted values
self.assertNoRaise(self.feat_r.get_range)
self.assertNoRaise(self.feat_rw.get_range)
def test_get_increment(self):
# Expectation: Get step between valid values
self.assertNoRaise(self.feat_r.get_increment)
self.assertNoRaise(self.feat_rw.get_increment)
def test_set(self):
# Expectation: Set value or raise VimbaFeatureError under the following conditions.
# 1) Invalid Access Rights
# 2) Misaligned value.
# 3) Out-of-bounds Access
# Read only feature
self.assertRaises(VimbaFeatureError, self.feat_r.set, 0)
# Writable feature
old_value = self.feat_rw.get()
try:
inc = self.feat_rw.get_increment()
min_, max_ = self.feat_rw.get_range()
# Normal usage
self.assertNoRaise(self.feat_rw.set, min_)
self.assertEqual(self.feat_rw.get(), min_)
self.assertNoRaise(self.feat_rw.set, max_)
self.assertEqual(self.feat_rw.get(), max_)
# Out of bounds access.
self.assertRaises(VimbaFeatureError, self.feat_rw.set, min_ - inc)
self.assertRaises(VimbaFeatureError, self.feat_rw.set, max_ + inc)
finally:
self.feat_rw.set(old_value)
def test_set_in_callback(self):
# Expectation: Setting a value within a Callback must raise a VimbaFeatureError
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
old_entry = self.feat_rw.get()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
# Trigger change handler and wait for callback execution.
min_, _ = self.feat_rw.get_range()
inc = self.feat_rw.get_increment()
if min_ <= (old_entry - inc):
self.feat_rw.set(old_entry - inc)
else:
self.feat_rw.set(old_entry + inc)
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)
self.feat_rw.set(old_entry)
class CamStringFeatureTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
try:
self.cam._open()
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to open Camera.') from e
self.feat_r = None
feats = self.cam.get_features_by_type(StringFeature)
for feat in feats:
if feat.get_access_mode() == (True, False):
self.feat_r = feat
if self.feat_r is None:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Test requires read only StringFeature.')
self.feat_rw = None
feats = self.cam.get_features_by_type(StringFeature)
for feat in feats:
if feat.get_access_mode() == (True, True):
self.feat_rw = feat
if self.feat_rw is None:
self.cam._close()
self.vimba._shutdown()
self.skipTest('Test requires read/write StringFeature.')
def tearDown(self):
self.cam._close()
self.vimba._shutdown()
def test_get_type(self):
# Expectation: StringFeature must return StringFeature on get_type
self.assertEqual(self.feat_r.get_type(), StringFeature)
self.assertEqual(self.feat_rw.get_type(), StringFeature)
def test_get(self):
# Expectation: Get current value without raising an exception
self.assertNoRaise(self.feat_r.get)
self.assertNoRaise(self.feat_rw.get)
def test_get_max_length(self):
# Expectation: Get maximum string length
self.assertNoRaise(self.feat_r.get_max_length)
self.assertNoRaise(self.feat_rw.get_max_length)
def test_set(self):
# Expectation:
# 1) Setting a read only feature must raise a VimbaFeatureError
# 2) Setting a read/wrtie must raise VimbaFeatureError if the string is
# longer than max length
# 3) Setting a read/write feature must work if string is long enough
# Ensure Expectation 1
self.assertRaises(VimbaFeatureError, self.feat_r.set, self.feat_r.get())
self.assertNoRaise(self.feat_rw.set, self.feat_rw.get())
# Ensure Expectation 2
old_val = self.feat_rw.get()
try:
invalid = 'a' * self.feat_rw.get_max_length()
self.assertRaises(VimbaFeatureError, self.feat_rw.set, invalid)
finally:
self.feat_rw.set(old_val)
# Ensure Expectation 3
try:
valid = 'a' * (self.feat_rw.get_max_length() - 1)
self.assertNoRaise(self.feat_rw.set, valid)
self.assertEqual(valid, self.feat_rw.get())
finally:
self.feat_rw.set(old_val)
def test_set_in_callback(self):
# Expectation: Setting a value within a Callback must raise a VimbaFeatureError
class Handler:
def __init__(self):
self.raised = False
self.event = threading.Event()
def __call__(self, feat):
try:
feat.set(feat.get())
except VimbaFeatureError:
self.raised = True
self.event.set()
try:
handler = Handler()
self.feat_rw.register_change_handler(handler)
self.feat_rw.set(self.feat_rw.get())
handler.event.wait()
self.assertTrue(handler.raised)
finally:
self.feat_rw.unregister_change_handler(handler)

View File

@@ -0,0 +1,206 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import copy
import ctypes
from vimba import *
from vimba.frame import *
class CamFrameTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
self.vimba._startup()
try:
self.cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
except VimbaCameraError as e:
self.vimba._shutdown()
raise Exception('Failed to lookup Camera.') from e
def tearDown(self):
self.vimba._shutdown()
def test_verify_buffer(self):
# Expectation: A Frame buffer shall have exactly the specified size on construction.
# Allocation is performed by VimbaPython
self.assertEqual(Frame(0, AllocationMode.AnnounceFrame).get_buffer_size(), 0)
self.assertEqual(Frame(1024, AllocationMode.AnnounceFrame).get_buffer_size(), 1024)
self.assertEqual(Frame(1024 * 1024, AllocationMode.AnnounceFrame).get_buffer_size(),
1024 * 1024)
def test_verify_no_copy_empty_buffer_access(self):
# Expectation: Accessing the internal buffer must not create a copy
# frame._buffer is only set on construction if buffer is allocated by VimbaPython
frame = Frame(10, AllocationMode.AnnounceFrame)
self.assertEqual(id(frame._buffer), id(frame.get_buffer()))
def test_verify_no_copy_filled_buffer_access(self):
# Expectation: Accessing the internal buffer must not create a copy
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
self.assertEqual(id(frame._buffer), id(frame.get_buffer()))
def test_get_id(self):
# Expectation: get_id() must return None if Its locally constructed
# else it must return the frame id.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_id())
with self.cam:
self.assertIsNotNone(
self.cam.get_frame(allocation_mode=allocation_mode).get_id())
def test_get_timestamp(self):
# Expectation: get_timestamp() must return None if Its locally constructed
# else it must return the timestamp.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_timestamp())
with self.cam:
self.assertIsNotNone(
self.cam.get_frame(allocation_mode=allocation_mode).get_timestamp())
def test_get_offset(self):
# Expectation: get_offset_x() must return None if Its locally constructed
# else it must return the offset as int. Same goes for get_offset_y()
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_offset_x())
self.assertIsNone(Frame(0, allocation_mode).get_offset_y())
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
self.assertIsNotNone(frame.get_offset_x())
self.assertIsNotNone(frame.get_offset_y())
def test_get_dimension(self):
# Expectation: get_width() must return None if Its locally constructed
# else it must return the offset as int. Same goes for get_height()
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertIsNone(Frame(0, allocation_mode).get_width())
self.assertIsNone(Frame(0, allocation_mode).get_height())
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
self.assertIsNotNone(frame.get_width())
self.assertIsNotNone(frame.get_height())
def test_get_image_size(self):
# Expectation: get_image_size() must return 0 if locally constructed
# else it must return the image_size as int.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
self.assertEquals(Frame(0, allocation_mode).get_image_size(), 0)
with self.cam:
self.assertNotEquals(
self.cam.get_frame(allocation_mode=allocation_mode).get_image_size(), 0)
def test_deepcopy(self):
# Expectation: a deepcopy must clone the frame buffer with its contents an
# update the internally store pointer in VmbFrame struct.
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
frame_cpy = copy.deepcopy(frame)
# Ensure frames and their members are not the same object
self.assertNotEquals(id(frame), id(frame_cpy))
self.assertNotEquals(id(frame._buffer), id(frame_cpy._buffer))
self.assertNotEquals(id(frame._frame), id(frame_cpy._frame))
# Ensure that both buffers have the same size and contain the same data.
self.assertEquals(frame.get_buffer_size(), frame_cpy.get_buffer_size())
self.assertTrue(all(a == b for a, b in zip(frame.get_buffer(),
frame_cpy.get_buffer())))
# Ensure that internal Frame Pointer points to correct buffer.
self.assertEquals(frame._frame.buffer,
ctypes.cast(frame._buffer, ctypes.c_void_p).value)
self.assertEquals(frame_cpy._frame.buffer,
ctypes.cast(frame_cpy._buffer, ctypes.c_void_p).value)
self.assertEquals(frame._frame.bufferSize, frame_cpy._frame.bufferSize)
def test_get_pixel_format(self):
# Expectation: Frames have an image format set after acquisition
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
self.assertNotEquals(
self.cam.get_frame(allocation_mode=allocation_mode).get_pixel_format(), 0)
def test_incompatible_formats_value_error(self):
# Expectation: Conversion into incompatible formats must lead to an value error
for allocation_mode in AllocationMode:
with self.subTest(f'allocation_mode={str(allocation_mode)}'):
with self.cam:
frame = self.cam.get_frame(allocation_mode=allocation_mode)
current_fmt = frame.get_pixel_format()
convertable_fmt = current_fmt.get_convertible_formats()
for fmt in PixelFormat.__members__.values():
if (fmt != current_fmt) and (fmt not in convertable_fmt):
self.assertRaises(ValueError, frame.convert_pixel_format, fmt)
def test_convert_to_all_given_formats(self):
# Expectation: A Series of Frame, each acquired with a different Pixel format
# Must be convertible to all formats the given format claims its convertible to without any
# errors.
test_frames = []
with self.cam:
for fmt in self.cam.get_pixel_formats():
self.cam.set_pixel_format(fmt)
frame = self.cam.get_frame()
self.assertEqual(fmt, frame.get_pixel_format())
test_frames.append(frame)
for frame in test_frames:
# The test shall work on a copy to keep the original Frame untouched
for expected_fmt in frame.get_pixel_format().get_convertible_formats():
cpy_frame = copy.deepcopy(frame)
cpy_frame.convert_pixel_format(expected_fmt)
self.assertEquals(expected_fmt, cpy_frame.get_pixel_format())

View File

@@ -0,0 +1,149 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import ipaddress
import struct
from vimba import *
class CamVimbaTest(unittest.TestCase):
def setUp(self):
self.vimba = Vimba.get_instance()
def tearDown(self):
pass
def test_context_entry_exit(self):
# Expected Behavior:
# On entering the context features, cameras and interfaces shall
# be detected and after leaving the context, everything should be reverted.
self.assertRaises(RuntimeError, self.vimba.get_all_features)
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
with self.vimba:
self.assertNotEqual(self.vimba.get_all_features(), ())
self.assertNotEqual(self.vimba.get_all_interfaces(), ())
self.assertNotEqual(self.vimba.get_all_cameras(), ())
self.assertRaises(RuntimeError, self.vimba.get_all_features)
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
def test_get_all_interfaces(self):
# Expected Behavior: get_all_interfaces() must raise an RuntimeError in closed state and
# be non-empty then opened.
self.assertRaises(RuntimeError, self.vimba.get_all_interfaces)
with self.vimba:
self.assertTrue(self.vimba.get_all_interfaces())
def test_get_interface_by_id(self):
# Expected Behavior: All detected Interfaces must be lookup able by their Id.
# If outside of given scope, an error must be returned
with self.vimba:
ids = [inter.get_id() for inter in self.vimba.get_all_interfaces()]
for id_ in ids:
self.assertNoRaise(self.vimba.get_interface_by_id, id_)
for id_ in ids:
self.assertRaises(RuntimeError, self.vimba.get_interface_by_id, id_)
def test_get_all_cameras(self):
# Expected Behavior: get_all_cameras() must only return camera handles on a open camera.
self.assertRaises(RuntimeError, self.vimba.get_all_cameras)
with self.vimba:
self.assertTrue(self.vimba.get_all_cameras())
def test_get_camera_by_id(self):
# Expected Behavior: Lookup of test camera must not fail after system opening
camera_id = self.get_test_camera_id()
with self.vimba:
self.assertNoRaise(self.vimba.get_camera_by_id, camera_id)
def test_get_camera_by_ip(self):
# Expected Behavior: get_camera_by_id() must work with a valid ipv4 address.
# A with lookup of an invalid ipv4 address (no Camera attached)
# must raise a VimbaCameraError, a lookup with an ipv6 address must raise a
# VimbaCameraError in general (VimbaC doesn't support ipv6)
with self.vimba:
# Verify that the Test Camera is a GigE - Camera
cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
inter = self.vimba.get_interface_by_id(cam.get_interface_id())
if inter.get_type() != InterfaceType.Ethernet:
raise self.skipTest('Test requires GigE - Camera.')
# Lookup test cameras IP address.
with cam:
ip_as_number = cam.get_feature_by_name('GevCurrentIPAddress').get()
# Swap byte order, the raw value does not seem to follow network byte order.
ip_as_number = struct.pack('<L', ip_as_number)
# Verify that lookup with IPv4 Address returns the same Camera Object
ip_addr = str(ipaddress.IPv4Address(ip_as_number))
self.assertEqual(self.vimba.get_camera_by_id(ip_addr), cam)
# Verify that a lookup with an invalid IPv4 Address raises a VimbaCameraError
ip_addr = str(ipaddress.IPv4Address('127.0.0.1'))
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, ip_addr)
# Verify that a lookup with an IPv6 Address raises a VimbaCameraError
ip_addr = str(ipaddress.IPv6Address('FD00::DEAD:BEEF'))
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, ip_addr)
def test_get_camera_by_mac(self):
# Expected Behavior: get_feature_by_id must be usable with a given MAC Address.
with self.vimba:
# Verify that the Test Camera is a GigE - Camera
cam = self.vimba.get_camera_by_id(self.get_test_camera_id())
inter = self.vimba.get_interface_by_id(cam.get_interface_id())
if inter.get_type() != InterfaceType.Ethernet:
raise self.skipTest('Test requires GigE - Camera.')
# Lookup test cameras MAC Address.
with cam:
# Construct MAC Address from raw value.
mac_as_number = cam.get_feature_by_name('GevDeviceMACAddress').get()
mac_as_bytes = mac_as_number.to_bytes(6, byteorder='big')
mac_as_str = ''.join(format(s, '02x') for s in mac_as_bytes).upper()
# Verify that lookup with MAC Address returns the same Camera Object
self.assertEqual(self.vimba.get_camera_by_id(mac_as_str), cam)
# Verify that a lookup with an invalid MAC Address raises a VimbaCameraError
invalid_mac = 'ffffffff'
self.assertRaises(VimbaCameraError, self.vimba.get_camera_by_id, invalid_mac)

View File

@@ -0,0 +1,177 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import unittest
import docopt
import sys
import os
# Add local directory to search path for test module import in this script.
sys.path.insert(0, os.path.abspath(os.path.dirname(__file__)))
# Add vimba module at the start of the search path. The tests should run against the
# local VimbaPython sources regardless of any existing installations.
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
# Inject 'assertNotRaise' to default test module. Tests are derived from this class.
def _assertNoRaise(self, func, *args, **kwargs):
try:
func(*args, **kwargs)
except BaseException as e:
self.fail('Function raised: {}'.format(e))
# Inject shared test camera id into the base TestCase
def _get_test_camera_id(self) -> str:
return unittest.TestCase.test_cam_id
def _set_test_camera_id(test_cam_id) -> str:
unittest.TestCase.test_cam_id = test_cam_id
unittest.TestCase.assertNoRaise = _assertNoRaise
unittest.TestCase.set_test_camera_id = _set_test_camera_id
unittest.TestCase.get_test_camera_id = _get_test_camera_id
def _blacklist_tests(test_suite, blacklist):
for test in test_suite:
# Process TestSuites recursively
if type(test) == unittest.TestSuite:
_blacklist_tests(test, blacklist)
# Test is actually a TestCase. Add skip decorator to test
# function if the test is blacklisted.
else:
name = test._testMethodName
if name in blacklist:
setattr(test, name, unittest.skip('Blacklisted')(getattr(test, name)))
return test_suite
def main():
CLI = """VimbaPython test runner.
Usage:
runner.py -h
runner.py -s basic -o console [BLACKLIST...]
runner.py -s basic -o junit_xml REPORT_DIR [BLACKLIST...]
runner.py -s (real_cam | all) -c CAMERA_ID -o console [BLACKLIST...]
runner.py -s (real_cam | all) -c CAMERA_ID -o junit_xml REPORT_DIR [BLACKLIST...]
Arguments:
CAMERA_ID Camera Id from Camera that shall be used during testing
REPORT_DIR Directory used for junit_export.
BLACKLIST Optional sequence of unittest functions to skip.
Options:
-h Show this screen.
-s Testsuite to execute. real_cam and all require a camera to
run tests against, therefore -c is mandatory.
-c Camera Id used while testing.
-o Test output: Either console or junit_xml.
"""
args = docopt.docopt(CLI)
loader = unittest.TestLoader()
if args['CAMERA_ID']:
unittest.TestCase.set_test_camera_id(args['CAMERA_ID'])
else:
unittest.TestCase.set_test_camera_id(None)
# Select TestRunner
if args['console']:
runner = unittest.TextTestRunner(verbosity=2)
elif args['junit_xml']:
import xmlrunner
runner = xmlrunner.XMLTestRunner(output=args['REPORT_DIR'])
# Import tests cases
import basic_tests.c_binding_test
import basic_tests.util_runtime_type_check_test
import basic_tests.util_tracer_test
import basic_tests.util_context_decorator_test
import basic_tests.vimba_common_test
import basic_tests.vimba_test
import basic_tests.interface_test
import real_cam_tests.vimba_test
import real_cam_tests.feature_test
import real_cam_tests.camera_test
import real_cam_tests.frame_test
import real_cam_tests.ancillary_data_test
# Assign test cases to test suites
BASIC_TEST_MODS = [
basic_tests.c_binding_test,
basic_tests.util_runtime_type_check_test,
basic_tests.util_tracer_test,
basic_tests.util_context_decorator_test,
basic_tests.vimba_common_test,
basic_tests.vimba_test,
basic_tests.interface_test
]
REAL_CAM_TEST_MODS = [
real_cam_tests.vimba_test,
real_cam_tests.feature_test,
real_cam_tests.camera_test,
real_cam_tests.frame_test,
real_cam_tests.ancillary_data_test
]
# Prepare TestSuites
suite_basic = unittest.TestSuite()
suite_cam = unittest.TestSuite()
for mod in BASIC_TEST_MODS:
suite_basic.addTests(_blacklist_tests(loader.loadTestsFromModule(mod), args['BLACKLIST']))
for mod in REAL_CAM_TEST_MODS:
suite_cam.addTests(_blacklist_tests(loader.loadTestsFromModule(mod), args['BLACKLIST']))
# Execute TestSuites
if args['basic']:
runner.run(suite_basic)
elif args['real_cam']:
runner.run(suite_cam)
elif args['all']:
runner.run(suite_basic)
runner.run(suite_cam)
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,172 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import os
import shutil
import subprocess
import docopt
def fprint(line):
print(line, flush=True)
def stringify_list(l):
list_str = ''
for e in l:
list_str += e + ' '
return list_str
def static_test():
fprint('Execute Static Test: flake8')
subprocess.run('flake8 vimba', shell=True)
subprocess.run('flake8 Examples --ignore=F405,F403', shell=True)
subprocess.run('flake8 Tests --ignore=F405,F403', shell=True)
fprint('')
fprint('Execute Static Test: mypy')
subprocess.run('mypy vimba', shell=True, check=True)
fprint('')
def unit_test(testsuite, testcamera, blacklist):
blacklist = " ".join(blacklist)
fprint('Execute Unit tests and measure coverage:')
if testsuite == 'basic':
cmd = 'coverage run Tests/runner.py -s basic -o console {}'.format(blacklist)
else:
cmd = 'coverage run Tests/runner.py -s {} -c {} -o console {}'
cmd = cmd.format(testsuite, testcamera, blacklist)
subprocess.run(cmd, shell=True, check=True)
fprint('')
fprint('Coverage during test execution:')
subprocess.run('coverage report -m', shell=True, check=True)
fprint('')
coverage_file = '.coverage'
if os.path.exists(coverage_file):
os.remove(coverage_file)
def setup_junit(report_dir):
if os.path.exists(report_dir):
shutil.rmtree(report_dir, ignore_errors=True)
os.mkdir(report_dir)
def static_test_junit(report_dir):
fprint('Execute Static Test: flake8')
cmd = 'flake8 vimba --output-file=' + report_dir + '/flake8.txt'
subprocess.run(cmd, shell=True, check=True)
cmd = 'flake8_junit ' + report_dir + '/flake8.txt ' + report_dir + '/flake8_junit.xml'
subprocess.run(cmd, shell=True, check=True)
fprint('')
fprint('Execute Static Test: mypy')
cmd = 'mypy vimba --junit-xml ' + report_dir + '/mypy_junit.xml'
subprocess.run(cmd, shell=True, check=True)
fprint('')
def unit_test_junit(report_dir, testsuite, testcamera, blacklist):
fprint('Execute Unit tests and measure coverage:')
blacklist = " ".join(blacklist)
if testsuite == 'basic':
cmd = 'coverage run --branch Tests/runner.py -s basic -o junit_xml {} {}'
cmd = cmd.format(report_dir, blacklist)
else:
cmd = 'coverage run --branch Tests/runner.py -s {} -c {} -o junit_xml {} {}'
cmd = cmd.format(testsuite, testcamera, report_dir, blacklist)
subprocess.run(cmd, shell=True, check=True)
fprint('')
fprint('Generate Coverage reports:')
subprocess.run('coverage report -m', shell=True, check=True)
subprocess.run('coverage xml -o ' + report_dir + '/coverage.xml', shell=True, check=True)
fprint('')
coverage_file = '.coverage'
if os.path.exists(coverage_file):
os.remove(coverage_file)
def test(testsuite, testcamera, blacklist):
static_test()
unit_test(testsuite, testcamera, blacklist)
def test_junit(report_dir, testsuite, testcamera, blacklist):
setup_junit(report_dir)
static_test_junit(report_dir)
unit_test_junit(report_dir, testsuite, testcamera, blacklist)
def main():
CLI = """VimbaPython tests script.
Usage:
run_tests.py -h
run_tests.py test -s basic [BLACKLIST...]
run_tests.py test -s (real_cam | all) -c CAMERA_ID [BLACKLIST...]
run_tests.py test_junit -s basic [BLACKLIST...]
run_tests.py test_junit -s (real_cam | all) -c CAMERA_ID [BLACKLIST...]
Arguments:
CAMERA_ID Camera Id from Camera that shall be used during testing
BLACKLIST Optional sequence of unittest functions to skip.
Options:
-h Show this screen.
-s Unittestsuite. Can be 'basic', 'real_cam' or 'all'. The last two require a
Camera Id to test against.
-c Camera Id used in testing.
"""
args = docopt.docopt(CLI)
suite = 'basic' if args['basic'] else 'real_cam' if args['real_cam'] else 'all'
if args['test']:
test(suite, args['CAMERA_ID'], args['BLACKLIST'])
elif args['test_junit']:
report_dir = 'Test_Reports'
test_junit(report_dir, suite, args['CAMERA_ID'], args['BLACKLIST'])
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,11 @@
[flake8]
max-line-length = 100
[coverage:run]
source = vimba
[coverage:report]
exclude_lines =
coverage: skip
def __repr__
def __str__

View File

@@ -0,0 +1,82 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import setuptools
import os
import re
def read_file(name):
with open(file=name, mode='r', encoding='utf-8') as file:
return file.read()
def get_version(file_content):
result = re.search(r'{}\s*=\s*[\'"]([^\'"]*)[\'"]'.format('__version__'),
file_content)
return result.group(1)
name = 'VimbaPython'
version = get_version(read_file(os.path.join('.', 'vimba', '__init__.py')))
author = 'Allied Vision Technologies GmbH'
description = 'Python Bindings for Allied Visions VimbaSDK'
long_description = read_file('README.txt')
long_description_type = 'text/markdown'
license = 'BSD-2-Clause'
packages = [
'vimba',
'vimba.c_binding',
'vimba.util'
]
python_requires = '>=3.7'
tests_require = [
'xmlrunner',
'flake8',
'flake8-junit-report',
'mypy',
'coverage',
'docopt'
]
extras_require = {
'numpy-export': ['numpy'],
'opencv-export': ['opencv-python'],
'test': tests_require
}
setuptools.setup(
name=name,
version=version,
author=author,
description=description,
long_description=long_description,
long_description_content_type=long_description_type,
license=license,
packages=packages,
python_requires=python_requires,
extras_require=extras_require
)

View File

@@ -0,0 +1,128 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
# Suppress 'imported but unused' - Error from static style checker.
# flake8: noqa: F401
__version__ = '1.2.1'
__all__ = [
'Vimba',
'Camera',
'CameraChangeHandler',
'CameraEvent',
'AccessMode',
'PersistType',
'Interface',
'InterfaceType',
'InterfaceChangeHandler',
'InterfaceEvent',
'PixelFormat',
'Frame',
'FeatureTypes',
'FrameHandler',
'FrameStatus',
'AllocationMode',
'Debayer',
'intersect_pixel_formats',
'MONO_PIXEL_FORMATS',
'BAYER_PIXEL_FORMATS',
'RGB_PIXEL_FORMATS',
'RGBA_PIXEL_FORMATS',
'BGR_PIXEL_FORMATS',
'BGRA_PIXEL_FORMATS',
'YUV_PIXEL_FORMATS',
'YCBCR_PIXEL_FORMATS',
'COLOR_PIXEL_FORMATS',
'OPENCV_PIXEL_FORMATS',
'VimbaSystemError',
'VimbaCameraError',
'VimbaInterfaceError',
'VimbaFeatureError',
'VimbaFrameError',
'VimbaTimeout',
'IntFeature',
'FloatFeature',
'StringFeature',
'BoolFeature',
'EnumEntry',
'EnumFeature',
'CommandFeature',
'RawFeature',
'LogLevel',
'LogConfig',
'Log',
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
'LOG_CONFIG_TRACE_FILE_ONLY',
'LOG_CONFIG_TRACE',
'LOG_CONFIG_INFO_CONSOLE_ONLY',
'LOG_CONFIG_INFO_FILE_ONLY',
'LOG_CONFIG_INFO',
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
'LOG_CONFIG_WARNING_FILE_ONLY',
'LOG_CONFIG_WARNING',
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
'LOG_CONFIG_ERROR_FILE_ONLY',
'LOG_CONFIG_ERROR',
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
'LOG_CONFIG_CRITICAL_FILE_ONLY',
'LOG_CONFIG_CRITICAL',
'TraceEnable',
'ScopedLogEnable',
'RuntimeTypeCheckEnable'
]
# Import everything exported from the top level module
from .vimba import Vimba
from .camera import AccessMode, PersistType, Camera, CameraChangeHandler, CameraEvent, FrameHandler
from .interface import Interface, InterfaceType, InterfaceChangeHandler, InterfaceEvent
from .frame import PixelFormat, Frame, Debayer, intersect_pixel_formats, MONO_PIXEL_FORMATS, \
BAYER_PIXEL_FORMATS, RGB_PIXEL_FORMATS, RGBA_PIXEL_FORMATS, BGR_PIXEL_FORMATS, \
BGRA_PIXEL_FORMATS, YUV_PIXEL_FORMATS, YCBCR_PIXEL_FORMATS, \
COLOR_PIXEL_FORMATS, OPENCV_PIXEL_FORMATS, FrameStatus, FeatureTypes, \
AllocationMode
from .error import VimbaSystemError, VimbaCameraError, VimbaInterfaceError, VimbaFeatureError, \
VimbaFrameError, VimbaTimeout
from .feature import IntFeature, FloatFeature, StringFeature, BoolFeature, EnumEntry, EnumFeature, \
CommandFeature, RawFeature
from .util import Log, LogLevel, LogConfig, LOG_CONFIG_TRACE_CONSOLE_ONLY, \
LOG_CONFIG_TRACE_FILE_ONLY, LOG_CONFIG_TRACE, LOG_CONFIG_INFO_CONSOLE_ONLY, \
LOG_CONFIG_INFO_FILE_ONLY, LOG_CONFIG_INFO, LOG_CONFIG_WARNING_CONSOLE_ONLY, \
LOG_CONFIG_WARNING_FILE_ONLY, LOG_CONFIG_WARNING, LOG_CONFIG_ERROR_CONSOLE_ONLY, \
LOG_CONFIG_ERROR_FILE_ONLY, LOG_CONFIG_ERROR, LOG_CONFIG_CRITICAL_CONSOLE_ONLY, \
LOG_CONFIG_CRITICAL_FILE_ONLY, LOG_CONFIG_CRITICAL, ScopedLogEnable, \
TraceEnable, RuntimeTypeCheckEnable

View File

@@ -0,0 +1,120 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------
NOTE: Vimba/Vmb naming convention.
VimbaPython is based heavily on VimbaC, this submodule contains all wrapped types and functions
of VimbaC. All VimbaC Types and Functions are prefixed with 'Vmb', this convention is kept for
all python types interfacing with the C - Layer. VimbaC developers should be able to understand
the interface to VimbaC and keeping the name convention helps a lot in that regard.
However prefixing everything with 'Vmb' is not required in VimbaPython, therefore most Types
of the public API have no prefix.
"""
# Suppress 'imported but unused' - Error from static style checker.
# flake8: noqa: F401
__all__ = [
# Exports from vimba_common
'VmbInt8',
'VmbUint8',
'VmbInt16',
'VmbUint16',
'VmbInt32',
'VmbUint32',
'VmbInt64',
'VmbUint64',
'VmbHandle',
'VmbBool',
'VmbUchar',
'VmbDouble',
'VmbError',
'VimbaCError',
'VmbPixelFormat',
'decode_cstr',
'decode_flags',
# Exports from vimba_c
'VmbInterface',
'VmbAccessMode',
'VmbFeatureData',
'VmbFeaturePersist',
'VmbFeatureVisibility',
'VmbFeatureFlags',
'VmbFrameStatus',
'VmbFrameFlags',
'VmbVersionInfo',
'VmbInterfaceInfo',
'VmbCameraInfo',
'VmbFeatureInfo',
'VmbFeatureEnumEntry',
'VmbFrame',
'VmbFeaturePersistSettings',
'G_VIMBA_C_HANDLE',
'VIMBA_C_VERSION',
'EXPECTED_VIMBA_C_VERSION',
'call_vimba_c',
'build_callback_type',
# Exports from vimba_image_transform
'VmbImage',
'VmbImageInfo',
'VmbDebayerMode',
'VmbTransformInfo',
'VIMBA_IMAGE_TRANSFORM_VERSION',
'EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION',
'call_vimba_image_transform',
'PIXEL_FORMAT_TO_LAYOUT',
'LAYOUT_TO_PIXEL_FORMAT',
'PIXEL_FORMAT_CONVERTIBILITY_MAP',
# Exports from ctypes
'byref',
'sizeof',
'create_string_buffer'
]
from .vimba_common import VmbInt8, VmbUint8, VmbInt16, VmbUint16, VmbInt32, VmbUint32, \
VmbInt64, VmbUint64, VmbHandle, VmbBool, VmbUchar, VmbDouble, VmbError, \
VimbaCError, VmbPixelFormat, decode_cstr, decode_flags, \
_select_vimba_home
from .vimba_c import VmbInterface, VmbAccessMode, VmbFeatureData, \
VmbFeaturePersist, VmbFeatureVisibility, VmbFeatureFlags, VmbFrameStatus, \
VmbFrameFlags, VmbVersionInfo, VmbInterfaceInfo, VmbCameraInfo, VmbFeatureInfo, \
VmbFeatureEnumEntry, VmbFrame, VmbFeaturePersistSettings, \
G_VIMBA_C_HANDLE, EXPECTED_VIMBA_C_VERSION, VIMBA_C_VERSION, call_vimba_c, \
build_callback_type
from .vimba_image_transform import VmbImage, VmbImageInfo, VmbDebayerMode, \
VIMBA_IMAGE_TRANSFORM_VERSION, \
EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION, VmbTransformInfo, \
call_vimba_image_transform, PIXEL_FORMAT_TO_LAYOUT, \
LAYOUT_TO_PIXEL_FORMAT, PIXEL_FORMAT_CONVERTIBILITY_MAP
from ctypes import byref, sizeof, create_string_buffer

View File

@@ -0,0 +1,772 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import copy
import ctypes
from typing import Callable, Any, Tuple
from ctypes import c_void_p, c_char_p, byref, sizeof, POINTER as c_ptr, c_char_p as c_str
from ..util import TraceEnable
from ..error import VimbaSystemError
from .vimba_common import Uint32Enum, Int32Enum, VmbInt32, VmbUint32, VmbInt64, VmbUint64, \
VmbHandle, VmbBool, VmbDouble, VmbError, VimbaCError, VmbPixelFormat, \
fmt_enum_repr, fmt_repr, fmt_flags_repr, load_vimba_lib
__version__ = None
__all__ = [
'VmbPixelFormat',
'VmbInterface',
'VmbAccessMode',
'VmbFeatureData',
'VmbFeaturePersist',
'VmbFeatureVisibility',
'VmbFeatureFlags',
'VmbFrameStatus',
'VmbFrameFlags',
'VmbVersionInfo',
'VmbInterfaceInfo',
'VmbCameraInfo',
'VmbFeatureInfo',
'VmbFeatureEnumEntry',
'VmbFrame',
'VmbFeaturePersistSettings',
'G_VIMBA_C_HANDLE',
'VIMBA_C_VERSION',
'EXPECTED_VIMBA_C_VERSION',
'call_vimba_c',
'build_callback_type'
]
# Types
class VmbInterface(Uint32Enum):
"""
Camera Interface Types:
Unknown - Interface is not known to this version of the API
Firewire - 1394
Ethernet - GigE
Usb - USB 3.0
CL - Camera Link
CSI2 - CSI-2
"""
Unknown = 0
Firewire = 1
Ethernet = 2
Usb = 3
CL = 4
CSI2 = 5
def __str__(self):
return self._name_
class VmbAccessMode(Uint32Enum):
"""
Camera Access Mode:
None_ - No access
Full - Read and write access
Read - Read-only access
Config - Configuration access (GeV)
Lite - Read and write access without feature access (only addresses)
"""
None_ = 0
Full = 1
Read = 2
Config = 4
Lite = 8
def __str__(self):
return self._name_
class VmbFeatureData(Uint32Enum):
"""
Feature Data Types
Unknown - Unknown feature type
Int - 64 bit integer feature
Float - 64 bit floating point feature
Enum - Enumeration feature
String - String feature
Bool - Boolean feature
Command - Command feature
Raw - Raw (direct register access) feature
None_ - Feature with no data
"""
Unknown = 0
Int = 1
Float = 2
Enum = 3
String = 4
Bool = 5
Command = 6
Raw = 7
None_ = 8
def __str__(self):
return self._name_
class VmbFeaturePersist(Uint32Enum):
"""
Type of features that are to be saved (persisted) to the XML file
when using VmbCameraSettingsSave
All - Save all features to XML, including look-up tables
Streamable - Save only features marked as streamable, excluding
look-up tables
NoLUT - Save all features except look-up tables (default)
"""
All = 0
Streamable = 1
NoLUT = 2
def __str__(self):
return self._name_
class VmbFeatureVisibility(Uint32Enum):
"""
Feature Visibility
Unknown - Feature visibility is not known
Beginner - Feature is visible in feature list (beginner level)
Expert - Feature is visible in feature list (expert level)
Guru - Feature is visible in feature list (guru level)
Invisible - Feature is not visible in feature list
"""
Unknown = 0
Beginner = 1
Expert = 2
Guru = 3
Invisible = 4
def __str__(self):
return self._name_
class VmbFeatureFlags(Uint32Enum):
"""
Feature Flags
None_ - No additional information is provided
Read - Static info about read access.
Current status depends on access mode, check with
VmbFeatureAccessQuery()
Write - Static info about write access.
Current status depends on access mode, check with
VmbFeatureAccessQuery()
Volatile - Value may change at any time
ModifyWrite - Value may change after a write
"""
None_ = 0
Read = 1
Write = 2
Undocumented = 4
Volatile = 8
ModifyWrite = 16
def __str__(self):
return self._name_
class VmbFrameStatus(Int32Enum):
"""
Frame transfer status
Complete - Frame has been completed without errors
Incomplete - Frame could not be filled to the end
TooSmall - Frame buffer was too small
Invalid - Frame buffer was invalid
"""
Complete = 0
Incomplete = -1
TooSmall = -2
Invalid = -3
def __str__(self):
return self._name_
class VmbFrameFlags(Uint32Enum):
"""
Frame Flags
None_ - No additional information is provided
Dimension - Frame's dimension is provided
Offset - Frame's offset is provided (ROI)
FrameID - Frame's ID is provided
Timestamp - Frame's timestamp is provided
"""
None_ = 0
Dimension = 1
Offset = 2
FrameID = 4
Timestamp = 8
def __str__(self):
return self._name_
class VmbVersionInfo(ctypes.Structure):
"""
Version Information
Fields:
major - Type: VmbUint32, Info: Major version number
minor - Type: VmbUint32, Info: Minor version number
patch - Type: VmbUint32, Info: Patch version number
"""
_fields_ = [
("major", VmbUint32),
("minor", VmbUint32),
("patch", VmbUint32)
]
def __str__(self):
return '{}.{}.{}'.format(self.major, self.minor, self.patch)
def __repr__(self):
rep = 'VmbVersionInfo'
rep += '(major=' + repr(self.major)
rep += ',minor=' + repr(self.minor)
rep += ',patch=' + repr(self.patch)
rep += ')'
return rep
class VmbInterfaceInfo(ctypes.Structure):
"""
Interface information. Holds read-only information about an interface.
Fields:
interfaceIdString - Type: c_char_p
Info: Unique identifier for each interface
interfaceType - Type: VmbInterface (VmbUint32)
Info: Interface type, see VmbInterface
interfaceName - Type: c_char_p
Info: Interface name, given by transport layer
serialString - Type: c_char_p
Info: Serial number
permittedAccess - Type: VmbAccessMode (VmbUint32)
Info: Used access mode, see VmbAccessMode
"""
_fields_ = [
("interfaceIdString", c_char_p),
("interfaceType", VmbUint32),
("interfaceName", c_char_p),
("serialString", c_char_p),
("permittedAccess", VmbUint32)
]
def __repr__(self):
rep = 'VmbInterfaceInfo'
rep += fmt_repr('(interfaceIdString={}', self.interfaceIdString)
rep += fmt_enum_repr(',interfaceType={}', VmbInterface, self.interfaceType)
rep += fmt_repr(',interfaceName={}', self.interfaceName)
rep += fmt_repr(',serialString={}', self.serialString)
rep += fmt_flags_repr(',permittedAccess={}', VmbAccessMode, self.permittedAccess)
rep += ')'
return rep
class VmbCameraInfo(ctypes.Structure):
"""
Camera information. Holds read-only information about a camera.
Fields:
cameraIdString - Type: c_char_p
Info: Unique identifier for each camera
cameraName - Type: c_char_p
Info: Name of the camera
modelName - Type: c_char_p
Info: Model name
serialString - Type: c_char_p
Info: Serial number
permittedAccess - Type: VmbAccessMode (VmbUint32)
Info: Used access mode, see VmbAccessMode
interfaceIdString - Type: c_char_p
Info: Unique value for each interface or bus
"""
_fields_ = [
("cameraIdString", c_char_p),
("cameraName", c_char_p),
("modelName", c_char_p),
("serialString", c_char_p),
("permittedAccess", VmbUint32),
("interfaceIdString", c_char_p)
]
def __repr__(self):
rep = 'VmbCameraInfo'
rep += fmt_repr('(cameraIdString={}', self.cameraIdString)
rep += fmt_repr(',cameraName={}', self.cameraName)
rep += fmt_repr(',modelName={}', self.modelName)
rep += fmt_repr(',serialString={}', self.serialString)
rep += fmt_flags_repr(',permittedAccess={}', VmbAccessMode, self.permittedAccess)
rep += fmt_repr(',interfaceIdString={}', self.interfaceIdString)
rep += ')'
return rep
class VmbFeatureInfo(ctypes.Structure):
"""
Feature information. Holds read-only information about a feature.
Fields:
name - Type: c_char_p
Info: Name used in the API
featureDataType - Type: VmbFeatureData (VmbUint32)
Info: Data type of this feature
featureFlags - Type: VmbFeatureFlags (VmbUint32)
Info: Access flags for this feature
category - Type: c_char_p
Info: Category this feature can be found in
displayName - Type: c_char_p
Info: Feature name to be used in GUIs
pollingTime - Type: VmbUint32
Info: Predefined polling time for volatile
features
unit - Type: c_char_p
Info: Measuring unit as given in the XML file
representation - Type: c_char_p
Info: Representation of a numeric feature
visibility - Type: VmbFeatureVisibility (VmbUint32)
Info: GUI visibility
tooltip - Type: c_char_p
Info: Short description, e.g. for a tooltip
description - Type: c_char_p
Info: Longer description
sfncNamespace - Type: c_char_p
Info: Namespace this feature resides in
isStreamable - Type: VmbBool
Info: Indicates if a feature can be stored
to / loaded from a file
hasAffectedFeatures - Type: VmbBool
Info: Indicates if the feature potentially
affects other features
hasSelectedFeatures - Type: VmbBool
Info: Indicates if the feature selects other
features
"""
_fields_ = [
("name", c_char_p),
("featureDataType", VmbUint32),
("featureFlags", VmbUint32),
("category", c_char_p),
("displayName", c_char_p),
("pollingTime", VmbUint32),
("unit", c_char_p),
("representation", c_char_p),
("visibility", VmbUint32),
("tooltip", c_char_p),
("description", c_char_p),
("sfncNamespace", c_char_p),
("isStreamable", VmbBool),
("hasAffectedFeatures", VmbBool),
("hasSelectedFeatures", VmbBool)
]
def __repr__(self):
rep = 'VmbFeatureInfo'
rep += fmt_repr('(name={}', self.name)
rep += fmt_enum_repr(',featureDataType={}', VmbFeatureData, self.featureDataType)
rep += fmt_flags_repr(',featureFlags={}', VmbFeatureFlags, self.featureFlags)
rep += fmt_repr(',category={}', self.category)
rep += fmt_repr(',displayName={}', self.displayName)
rep += fmt_repr(',pollingTime={}', self.pollingTime)
rep += fmt_repr(',unit={}', self.unit)
rep += fmt_repr(',representation={}', self.representation)
rep += fmt_enum_repr(',visibility={}', VmbFeatureVisibility, self.visibility)
rep += fmt_repr(',tooltip={}', self.tooltip)
rep += fmt_repr(',description={}', self.description)
rep += fmt_repr(',sfncNamespace={}', self.sfncNamespace)
rep += fmt_repr(',isStreamable={}', self.isStreamable)
rep += fmt_repr(',hasAffectedFeatures={}', self.hasAffectedFeatures)
rep += fmt_repr(',hasSelectedFeatures={}', self.hasSelectedFeatures)
rep += ')'
return rep
class VmbFeatureEnumEntry(ctypes.Structure):
"""
Info about possible entries of an enumeration feature:
Fields:
name - Type: c_char_p
Info: Name used in the API
displayName - Type: c_char_p
Info: Enumeration entry name to be used in GUIs
visibility - Type: VmbFeatureVisibility (VmbUint32)
Info: GUI visibility
tooltip - Type: c_char_p
Info: Short description, e.g. for a tooltip
description - Type: c_char_p
Info: Longer description
sfncNamespace - Type: c_char_p
Info: Namespace this feature resides in
intValue - Type: VmbInt64
Info: Integer value of this enumeration entry
"""
_fields_ = [
("name", c_char_p),
("displayName", c_char_p),
("visibility", VmbUint32),
("tooltip", c_char_p),
("description", c_char_p),
("sfncNamespace", c_char_p),
("intValue", VmbInt64)
]
def __repr__(self):
rep = 'VmbFeatureEnumEntry'
rep += fmt_repr('(name={}', self.name)
rep += fmt_repr(',displayName={}', self.displayName)
rep += fmt_enum_repr(',visibility={}', VmbFeatureVisibility, self.visibility)
rep += fmt_repr(',tooltip={}', self.tooltip)
rep += fmt_repr(',description={}', self.description)
rep += fmt_repr(',sfncNamespace={}', self.sfncNamespace)
rep += fmt_repr(',intValue={},', self.intValue)
rep += ')'
return rep
class VmbFrame(ctypes.Structure):
"""
Frame delivered by Camera
Fields (in):
buffer - Type: c_void_p
Info: Comprises image and ancillary data
bufferSize - Type: VmbUint32_t
Info: Size of the data buffer
context - Type: c_void_p[4]
Info: 4 void pointers that can be employed by the user
(e.g. for storing handles)
Fields (out):
receiveStatus - Type: VmbFrameStatus (VmbInt32)
Info: Resulting status of the receive operation
receiveFlags - Type: VmbFrameFlags (VmbUint32)
Info: Flags indicating which additional frame
information is available
imageSize - Type: VmbUint32
Info: Size of the image data inside the data buffer
ancillarySize - Type: VmbUint32
Info: Size of the ancillary data inside the
data buffer
pixelFormat - Type: VmbPixelFormat (VmbUint32)
Info: Pixel format of the image
width - Type: VmbUint32
Info: Width of an image
height - Type: VmbUint32
Info: Height of an image
offsetX - Type: VmbUint32
Info: Horizontal offset of an image
offsetY - Type: VmbUint32
Info: Vertical offset of an image
frameID - Type: VmbUint64
Info: Unique ID of this frame in this stream
timestamp - Type: VmbUint64
Info: Timestamp set by the camera
"""
_fields_ = [
("buffer", c_void_p),
("bufferSize", VmbUint32),
("context", c_void_p * 4),
("receiveStatus", VmbInt32),
("receiveFlags", VmbUint32),
("imageSize", VmbUint32),
("ancillarySize", VmbUint32),
("pixelFormat", VmbUint32),
("width", VmbUint32),
("height", VmbUint32),
("offsetX", VmbUint32),
("offsetY", VmbUint32),
("frameID", VmbUint64),
("timestamp", VmbUint64)
]
def __repr__(self):
rep = 'VmbFrame'
rep += fmt_repr('(buffer={}', self.buffer)
rep += fmt_repr(',bufferSize={}', self.bufferSize)
rep += fmt_repr(',context={}', self.context)
rep += fmt_enum_repr('receiveStatus: {}', VmbFrameStatus, self.receiveStatus)
rep += fmt_flags_repr(',receiveFlags={}', VmbFrameFlags, self.receiveFlags)
rep += fmt_repr(',imageSize={}', self.imageSize)
rep += fmt_repr(',ancillarySize={}', self.ancillarySize)
rep += fmt_enum_repr(',pixelFormat={}', VmbPixelFormat, self.pixelFormat)
rep += fmt_repr(',width={}', self.width)
rep += fmt_repr(',height={}', self.height)
rep += fmt_repr(',offsetX={}', self.offsetX)
rep += fmt_repr(',offsetY={}', self.offsetY)
rep += fmt_repr(',frameID={}', self.frameID)
rep += fmt_repr(',timestamp={}', self.timestamp)
rep += ')'
return rep
def deepcopy_skip_ptr(self, memo):
result = VmbFrame()
memo[id(self)] = result
result.buffer = None
result.bufferSize = 0
result.context = (None, None, None, None)
setattr(result, 'receiveStatus', copy.deepcopy(self.receiveStatus, memo))
setattr(result, 'receiveFlags', copy.deepcopy(self.receiveFlags, memo))
setattr(result, 'imageSize', copy.deepcopy(self.imageSize, memo))
setattr(result, 'ancillarySize', copy.deepcopy(self.ancillarySize, memo))
setattr(result, 'pixelFormat', copy.deepcopy(self.pixelFormat, memo))
setattr(result, 'width', copy.deepcopy(self.width, memo))
setattr(result, 'height', copy.deepcopy(self.height, memo))
setattr(result, 'offsetX', copy.deepcopy(self.offsetX, memo))
setattr(result, 'offsetY', copy.deepcopy(self.offsetY, memo))
setattr(result, 'frameID', copy.deepcopy(self.frameID, memo))
setattr(result, 'timestamp', copy.deepcopy(self.timestamp, memo))
return result
class VmbFeaturePersistSettings(ctypes.Structure):
"""
Parameters determining the operation mode of VmbCameraSettingsSave
and VmbCameraSettingsLoad
Fields:
persistType - Type: VmbFeaturePersist (VmbUint32)
Info: Type of features that are to be saved
maxIterations - Type: VmbUint32
Info: Number of iterations when loading settings
loggingLevel - Type: VmbUint32
Info: Determines level of detail for load/save
settings logging
"""
_fields_ = [
("persistType", VmbUint32),
("maxIterations", VmbUint32),
("loggingLevel", VmbUint32)
]
def __repr__(self):
rep = 'VmbFrame'
rep += fmt_enum_repr('(persistType={}', VmbFeaturePersist, self.persistType)
rep += fmt_repr(',maxIterations={}', self.maxIterations)
rep += fmt_repr(',loggingLevel={}', self.loggingLevel)
rep += ')'
return rep
G_VIMBA_C_HANDLE = VmbHandle(1)
VIMBA_C_VERSION = None
EXPECTED_VIMBA_C_VERSION = '1.9.0'
# For detailed information on the signatures see "VimbaC.h"
# To improve readability, suppress 'E501 line too long (> 100 characters)'
# check of flake8
_SIGNATURES = {
'VmbVersionQuery': (VmbError, [c_ptr(VmbVersionInfo), VmbUint32]),
'VmbStartup': (VmbError, None),
'VmbShutdown': (None, None),
'VmbCamerasList': (VmbError, [c_ptr(VmbCameraInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]),
'VmbCameraInfoQuery': (VmbError, [c_str, c_ptr(VmbCameraInfo), VmbUint32]),
'VmbCameraOpen': (VmbError, [c_str, VmbAccessMode, c_ptr(VmbHandle)]),
'VmbCameraClose': (VmbError, [VmbHandle]),
'VmbFeaturesList': (VmbError, [VmbHandle, c_ptr(VmbFeatureInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
'VmbFeatureInfoQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeatureInfo), VmbUint32]),
'VmbFeatureListAffected': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeatureInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
'VmbFeatureListSelected': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeatureInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
'VmbFeatureAccessQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool), c_ptr(VmbBool)]),
'VmbFeatureIntGet': (VmbError, [VmbHandle, c_str, c_ptr(VmbInt64)]),
'VmbFeatureIntSet': (VmbError, [VmbHandle, c_str, VmbInt64]),
'VmbFeatureIntRangeQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbInt64), c_ptr(VmbInt64)]), # noqa: E501
'VmbFeatureIntIncrementQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbInt64)]),
'VmbFeatureFloatGet': (VmbError, [VmbHandle, c_str, c_ptr(VmbDouble)]),
'VmbFeatureFloatSet': (VmbError, [VmbHandle, c_str, VmbDouble]),
'VmbFeatureFloatRangeQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbDouble), c_ptr(VmbDouble)]),
'VmbFeatureFloatIncrementQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool), c_ptr(VmbDouble)]), # noqa: E501
'VmbFeatureEnumGet': (VmbError, [VmbHandle, c_str, c_ptr(c_str)]),
'VmbFeatureEnumSet': (VmbError, [VmbHandle, c_str, c_str]),
'VmbFeatureEnumRangeQuery': (VmbError, [VmbHandle, c_str, c_ptr(c_str), VmbUint32, c_ptr(VmbUint32)]), # noqa: E501
'VmbFeatureEnumIsAvailable': (VmbError, [VmbHandle, c_str, c_str, c_ptr(VmbBool)]),
'VmbFeatureEnumAsInt': (VmbError, [VmbHandle, c_str, c_str, c_ptr(VmbInt64)]),
'VmbFeatureEnumAsString': (VmbError, [VmbHandle, c_str, VmbInt64, c_ptr(c_str)]),
'VmbFeatureEnumEntryGet': (VmbError, [VmbHandle, c_str, c_str, c_ptr(VmbFeatureEnumEntry), VmbUint32]), # noqa: E501
'VmbFeatureStringGet': (VmbError, [VmbHandle, c_str, c_str, VmbUint32, c_ptr(VmbUint32)]), # noqa: E501
'VmbFeatureStringSet': (VmbError, [VmbHandle, c_str, c_str]),
'VmbFeatureStringMaxlengthQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbUint32)]),
'VmbFeatureBoolGet': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool)]),
'VmbFeatureBoolSet': (VmbError, [VmbHandle, c_str, VmbBool]),
'VmbFeatureCommandRun': (VmbError, [VmbHandle, c_str]),
'VmbFeatureCommandIsDone': (VmbError, [VmbHandle, c_str, c_ptr(VmbBool)]),
'VmbFeatureRawGet': (VmbError, [VmbHandle, c_str, c_str, VmbUint32, c_ptr(VmbUint32)]),
'VmbFeatureRawSet': (VmbError, [VmbHandle, c_str, c_str, VmbUint32]),
'VmbFeatureRawLengthQuery': (VmbError, [VmbHandle, c_str, c_ptr(VmbUint32)]),
'VmbFeatureInvalidationRegister': (VmbError, [VmbHandle, c_str, c_void_p, c_void_p]), # noqa: E501
'VmbFeatureInvalidationUnregister': (VmbError, [VmbHandle, c_str, c_void_p]),
'VmbFrameAnnounce': (VmbError, [VmbHandle, c_ptr(VmbFrame), VmbUint32]),
'VmbFrameRevoke': (VmbError, [VmbHandle, c_ptr(VmbFrame)]),
'VmbFrameRevokeAll': (VmbError, [VmbHandle]),
'VmbCaptureStart': (VmbError, [VmbHandle]),
'VmbCaptureEnd': (VmbError, [VmbHandle]),
'VmbCaptureFrameQueue': (VmbError, [VmbHandle, c_ptr(VmbFrame), c_void_p]),
'VmbCaptureFrameWait': (VmbError, [VmbHandle, c_ptr(VmbFrame), VmbUint32]),
'VmbCaptureQueueFlush': (VmbError, [VmbHandle]),
'VmbInterfacesList': (VmbError, [c_ptr(VmbInterfaceInfo), VmbUint32, c_ptr(VmbUint32), VmbUint32]), # noqa: E501
'VmbInterfaceOpen': (VmbError, [c_str, c_ptr(VmbHandle)]),
'VmbInterfaceClose': (VmbError, [VmbHandle]),
'VmbAncillaryDataOpen': (VmbError, [c_ptr(VmbFrame), c_ptr(VmbHandle)]),
'VmbAncillaryDataClose': (VmbError, [VmbHandle]),
'VmbMemoryRead': (VmbError, [VmbHandle, VmbUint64, VmbUint32, c_str, c_ptr(VmbUint32)]),
'VmbMemoryWrite': (VmbError, [VmbHandle, VmbUint64, VmbUint32, c_str, c_ptr(VmbUint32)]),
'VmbRegistersRead': (VmbError, [VmbHandle, VmbUint32, c_ptr(VmbUint64), c_ptr(VmbUint64), c_ptr(VmbUint32)]), # noqa: E501
'VmbRegistersWrite': (VmbError, [VmbHandle, VmbUint32, c_ptr(VmbUint64), c_ptr(VmbUint64), c_ptr(VmbUint32)]), # noqa: E501
'VmbCameraSettingsSave': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeaturePersistSettings), VmbUint32]), # noqa: E501
'VmbCameraSettingsLoad': (VmbError, [VmbHandle, c_str, c_ptr(VmbFeaturePersistSettings), VmbUint32]) # noqa: E501
}
def _attach_signatures(lib_handle):
global _SIGNATURES
for function_name, signature in _SIGNATURES.items():
fn = getattr(lib_handle, function_name)
fn.restype, fn.argtypes = signature
fn.errcheck = _eval_vmberror
return lib_handle
def _check_version(lib_handle):
global EXPECTED_VIMBA_C_VERSION
global VIMBA_C_VERSION
v = VmbVersionInfo()
lib_handle.VmbVersionQuery(byref(v), sizeof(v))
VIMBA_C_VERSION = str(v)
loaded_version = (v.major, v.minor, v.patch)
expected_version = tuple(map(int, EXPECTED_VIMBA_C_VERSION.split(".")))
# major and minor version must be equal, patch version may be equal or greater
if not(loaded_version[0:2] == expected_version[0:2] and
loaded_version[2] >= expected_version[2]):
msg = 'Invalid VimbaC Version: Expected: {}, Found:{}'
raise VimbaSystemError(msg.format(EXPECTED_VIMBA_C_VERSION, VIMBA_C_VERSION))
return lib_handle
def _eval_vmberror(result: VmbError, func: Callable[..., Any], *args: Tuple[Any, ...]):
if result not in (VmbError.Success, None):
raise VimbaCError(result)
_lib_instance = _check_version(_attach_signatures(load_vimba_lib('VimbaC')))
@TraceEnable()
def call_vimba_c(func_name: str, *args):
"""This function encapsulates the entire VimbaC access.
For Details on valid function signatures see the 'VimbaC.h'.
Arguments:
func_name: The function name from VimbaC to be called.
args: Varargs passed directly to the underlaying C-Function.
Raises:
TypeError if given are do not match the signature of the function.
AttributeError if func with name 'func_name' does not exist.
VimbaCError if the function call is valid but neither None or VmbError.Success was returned.
The following functions of VimbaC can be executed:
VmbVersionQuery
VmbStartup
VmbShutdown
VmbCamerasList
VmbCameraInfoQuery
VmbCameraOpen
VmbCameraClose
VmbFeaturesList
VmbFeatureInfoQuery
VmbFeatureListAffected
VmbFeatureListSelected
VmbFeatureAccessQuery
VmbFeatureIntGet
VmbFeatureIntSet
VmbFeatureIntRangeQuery
VmbFeatureIntIncrementQuery
VmbFeatureFloatGet
VmbFeatureFloatSet
VmbFeatureFloatRangeQuery
VmbFeatureFloatIncrementQuery
VmbFeatureEnumGet
VmbFeatureEnumSet
VmbFeatureEnumRangeQuery
VmbFeatureEnumIsAvailable
VmbFeatureEnumAsInt
VmbFeatureEnumAsString
VmbFeatureEnumEntryGet
VmbFeatureStringGet
VmbFeatureStringSet
VmbFeatureStringMaxlengthQuery
VmbFeatureBoolGet
VmbFeatureBoolSet
VmbFeatureCommandRun
VmbFeatureCommandIsDone
VmbFeatureRawGet
VmbFeatureRawSet
VmbFeatureRawLengthQuery
VmbFeatureInvalidationRegister
VmbFeatureInvalidationUnregister
VmbFrameAnnounce
VmbFrameRevoke
VmbFrameRevokeAll
VmbCaptureStart
VmbCaptureEnd
VmbCaptureFrameQueue
VmbCaptureFrameWait
VmbCaptureQueueFlush
VmbInterfacesList
VmbInterfaceOpen
VmbInterfaceClose
VmbAncillaryDataOpen
VmbAncillaryDataClose
VmbMemoryRead
VmbMemoryWrite
VmbRegistersRead
VmbRegistersWrite
VmbCameraSettingsSave
VmbCameraSettingsLoad
"""
global _lib_instance
getattr(_lib_instance, func_name)(*args)
def build_callback_type(*args):
global _lib_instance
lib_type = type(_lib_instance)
if lib_type == ctypes.CDLL:
return ctypes.CFUNCTYPE(*args)
elif lib_type == ctypes.WinDLL:
return ctypes.WINFUNCTYPE(*args)
else:
raise VimbaSystemError('Unknown Library Type. Abort.')

View File

@@ -0,0 +1,611 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import ctypes
import enum
import os
import sys
import platform
import functools
from typing import Tuple, List
from ..error import VimbaSystemError
__all__ = [
'Int32Enum',
'Uint32Enum',
'VmbInt8',
'VmbUint8',
'VmbInt16',
'VmbUint16',
'VmbInt32',
'VmbUint32',
'VmbInt64',
'VmbUint64',
'VmbHandle',
'VmbBool',
'VmbUchar',
'VmbFloat',
'VmbDouble',
'VmbError',
'VimbaCError',
'VmbPixelFormat',
'decode_cstr',
'decode_flags',
'fmt_repr',
'fmt_enum_repr',
'fmt_flags_repr',
'load_vimba_lib'
]
# Types
class Int32Enum(enum.IntEnum):
@classmethod
def from_param(cls, obj):
return ctypes.c_int(obj)
class Uint32Enum(enum.IntEnum):
@classmethod
def from_param(cls, obj):
return ctypes.c_uint(obj)
# Aliases for vmb base types
VmbInt8 = ctypes.c_byte
VmbUint8 = ctypes.c_ubyte
VmbInt16 = ctypes.c_short
VmbUint16 = ctypes.c_ushort
VmbInt32 = ctypes.c_int
VmbUint32 = ctypes.c_uint
VmbInt64 = ctypes.c_longlong
VmbUint64 = ctypes.c_ulonglong
VmbHandle = ctypes.c_void_p
VmbBool = ctypes.c_bool
VmbUchar = ctypes.c_char
VmbFloat = ctypes.c_float
VmbDouble = ctypes.c_double
class VmbError(Int32Enum):
"""
Enum containing error types returned
Success - No error
InternalFault - Unexpected fault in VimbaC or driver
ApiNotStarted - VmbStartup() was not called before the current
command
NotFound - The designated instance (camera, feature etc.)
cannot be found
BadHandle - The given handle is not valid
DeviceNotOpen - Device was not opened for usage
InvalidAccess - Operation is invalid with the current access mode
BadParameter - One of the parameters is invalid (usually an illegal
pointer)
StructSize - The given struct size is not valid for this version
of the API
MoreData - More data available in a string/list than space is
provided
WrongType - Wrong feature type for this access function
InvalidValue - The value is not valid; Either out of bounds or not
an increment of the minimum
Timeout - Timeout during wait
Other - Other error
Resources - Resources not available (e.g. memory)
InvalidCall - Call is invalid in the current context (callback)
NoTL - No transport layers are found
NotImplemented_ - API feature is not implemented
NotSupported - API feature is not supported
Incomplete - A multiple registers read or write is partially
completed
IO - low level IO error in transport layer
"""
Success = 0
InternalFault = -1
ApiNotStarted = -2
NotFound = -3
BadHandle = -4
DeviceNotOpen = -5
InvalidAccess = -6
BadParameter = -7
StructSize = -8
MoreData = -9
WrongType = -10
InvalidValue = -11
Timeout = -12
Other = -13
Resources = -14
InvalidCall = -15
NoTL = -16
NotImplemented_ = -17
NotSupported = -18
Incomplete = -19
IO = -20
def __str__(self):
return self._name_
class _VmbPixel(Uint32Enum):
Mono = 0x01000000
Color = 0x02000000
class _VmbPixelOccupy(Uint32Enum):
Bit8 = 0x00080000
Bit10 = 0x000A0000
Bit12 = 0x000C0000
Bit14 = 0x000E0000
Bit16 = 0x00100000
Bit24 = 0x00180000
Bit32 = 0x00200000
Bit48 = 0x00300000
Bit64 = 0x00400000
class VmbPixelFormat(Uint32Enum):
"""
Enum containing Pixelformats
Mono formats:
Mono8 - Monochrome, 8 bits (PFNC:Mono8)
Mono10 - Monochrome, 10 bits in 16 bits (PFNC:Mono10)
Mono10p - Monochrome, 4x10 bits continuously packed in 40 bits
(PFNC:Mono10p)
Mono12 - Monochrome, 12 bits in 16 bits (PFNC:Mono12)
Mono12Packed - Monochrome, 2x12 bits in 24 bits (GEV:Mono12Packed)
Mono12p - Monochrome, 2x12 bits continuously packed in 24 bits
(PFNC:Mono12p)
Mono14 - Monochrome, 14 bits in 16 bits (PFNC:Mono14)
Mono16 - Monochrome, 16 bits (PFNC:Mono16)
Bayer formats:
BayerGR8 - Bayer-color, 8 bits, starting with GR line
(PFNC:BayerGR8)
BayerRG8 - Bayer-color, 8 bits, starting with RG line
(PFNC:BayerRG8)
BayerGB8 - Bayer-color, 8 bits, starting with GB line
(PFNC:BayerGB8)
BayerBG8 - Bayer-color, 8 bits, starting with BG line
(PFNC:BayerBG8)
BayerGR10 - Bayer-color, 10 bits in 16 bits, starting with GR
line (PFNC:BayerGR10)
BayerRG10 - Bayer-color, 10 bits in 16 bits, starting with RG
line (PFNC:BayerRG10)
BayerGB10 - Bayer-color, 10 bits in 16 bits, starting with GB
line (PFNC:BayerGB10)
BayerBG10 - Bayer-color, 10 bits in 16 bits, starting with BG
line (PFNC:BayerBG10)
BayerGR12 - Bayer-color, 12 bits in 16 bits, starting with GR
line (PFNC:BayerGR12)
BayerRG12 - Bayer-color, 12 bits in 16 bits, starting with RG
line (PFNC:BayerRG12)
BayerGB12 - Bayer-color, 12 bits in 16 bits, starting with GB
line (PFNC:BayerGB12)
BayerBG12 - Bayer-color, 12 bits in 16 bits, starting with BG
line (PFNC:BayerBG12)
BayerGR12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GR
line (GEV:BayerGR12Packed)
BayerRG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with RG
line (GEV:BayerRG12Packed)
BayerGB12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GB
line (GEV:BayerGB12Packed)
BayerBG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with BG
line (GEV:BayerBG12Packed)
BayerGR10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with GR line (PFNC:BayerGR10p)
BayerRG10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with RG line (PFNC:BayerRG10p)
BayerGB10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with GB line (PFNC:BayerGB10p)
BayerBG10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with BG line (PFNC:BayerBG10p)
BayerGR12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with GR line (PFNC:BayerGR12p)
BayerRG12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with RG line (PFNC:BayerRG12p)
BayerGB12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with GB line (PFNC:BayerGB12p)
BayerBG12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with BG line (PFNC:BayerBG12p)
BayerGR16 - Bayer-color, 16 bits, starting with GR line
(PFNC:BayerGR16)
BayerRG16 - Bayer-color, 16 bits, starting with RG line
(PFNC:BayerRG16)
BayerGB16 - Bayer-color, 16 bits, starting with GB line
(PFNC:BayerGB16)
BayerBG16 - Bayer-color, 16 bits, starting with BG line
(PFNC:BayerBG16)
RGB formats:
Rgb8 - RGB, 8 bits x 3 (PFNC:RGB8)
Bgr8 - BGR, 8 bits x 3 (PFNC:Bgr8)
Rgb10 - RGB, 10 bits in 16 bits x 3 (PFNC:RGB10)
Bgr10 - BGR, 10 bits in 16 bits x 3 (PFNC:BGR10)
Rgb12 - RGB, 12 bits in 16 bits x 3 (PFNC:RGB12)
Bgr12 - BGR, 12 bits in 16 bits x 3 (PFNC:BGR12)
Rgb14 - RGB, 14 bits in 16 bits x 3 (PFNC:RGB14)
Bgr14 - BGR, 14 bits in 16 bits x 3 (PFNC:BGR14)
Rgb16 - RGB, 16 bits x 3 (PFNC:RGB16)
Bgr16 - BGR, 16 bits x 3 (PFNC:BGR16)
RGBA formats:
Argb8 - ARGB, 8 bits x 4 (PFNC:RGBa8)
Rgba8 - RGBA, 8 bits x 4, legacy name
Bgra8 - BGRA, 8 bits x 4 (PFNC:BGRa8)
Rgba10 - RGBA, 10 bits in 16 bits x 4
Bgra10 - BGRA, 10 bits in 16 bits x 4
Rgba12 - RGBA, 12 bits in 16 bits x 4
Bgra12 - BGRA, 12 bits in 16 bits x 4
Rgba14 - RGBA, 14 bits in 16 bits x 4
Bgra14 - BGRA, 14 bits in 16 bits x 4
Rgba16 - RGBA, 16 bits x 4
Bgra16 - BGRA, 16 bits x 4
YUV/YCbCr formats:
Yuv411 - YUV 411 with 8 bits (GEV:YUV411Packed)
Yuv422 - YUV 422 with 8 bits (GEV:YUV422Packed)
Yuv444 - YUV 444 with 8 bits (GEV:YUV444Packed)
YCbCr411_8_CbYYCrYY - Y´CbCr 411 with 8 bits
(PFNC:YCbCr411_8_CbYYCrYY) - identical to Yuv411
YCbCr422_8_CbYCrY - Y´CbCr 422 with 8 bits
(PFNC:YCbCr422_8_CbYCrY) - identical to Yuv422
YCbCr8_CbYCr - Y´CbCr 444 with 8 bits
(PFNC:YCbCr8_CbYCr) - identical to Yuv444
"""
None_ = 0
Mono8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x0001
Mono10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0003
Mono10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0046
Mono12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0005
Mono12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0006
Mono12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0047
Mono14 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0025
Mono16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0007
BayerGR8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x0008
BayerRG8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x0009
BayerGB8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x000A
BayerBG8 = _VmbPixel.Mono | _VmbPixelOccupy.Bit8 | 0x000B
BayerGR10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000C
BayerRG10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000D
BayerGB10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000E
BayerBG10 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x000F
BayerGR12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0010
BayerRG12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0011
BayerGB12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0012
BayerBG12 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0013
BayerGR12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002A
BayerRG12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002B
BayerGB12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002C
BayerBG12Packed = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x002D
BayerGR10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0056
BayerRG10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0058
BayerGB10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0054
BayerBG10p = _VmbPixel.Mono | _VmbPixelOccupy.Bit10 | 0x0052
BayerGR12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0057
BayerRG12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0059
BayerGB12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0055
BayerBG12p = _VmbPixel.Mono | _VmbPixelOccupy.Bit12 | 0x0053
BayerGR16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x002E
BayerRG16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x002F
BayerGB16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0030
BayerBG16 = _VmbPixel.Mono | _VmbPixelOccupy.Bit16 | 0x0031
Rgb8 = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x0014
Bgr8 = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x0015
Rgb10 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x0018
Bgr10 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x0019
Rgb12 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x001A
Bgr12 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x001B
Rgb14 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x005E
Bgr14 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x004A
Rgb16 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x0033
Bgr16 = _VmbPixel.Color | _VmbPixelOccupy.Bit48 | 0x004B
Argb8 = _VmbPixel.Color | _VmbPixelOccupy.Bit32 | 0x0016
Rgba8 = Argb8
Bgra8 = _VmbPixel.Color | _VmbPixelOccupy.Bit32 | 0x0017
Rgba10 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x005F
Bgra10 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x004C
Rgba12 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0061
Bgra12 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x004E
Rgba14 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0063
Bgra14 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0050
Rgba16 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0064
Bgra16 = _VmbPixel.Color | _VmbPixelOccupy.Bit64 | 0x0051
Yuv411 = _VmbPixel.Color | _VmbPixelOccupy.Bit12 | 0x001E
Yuv422 = _VmbPixel.Color | _VmbPixelOccupy.Bit16 | 0x001F
Yuv444 = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x0020
YCbCr411_8_CbYYCrYY = _VmbPixel.Color | _VmbPixelOccupy.Bit12 | 0x003C
YCbCr422_8_CbYCrY = _VmbPixel.Color | _VmbPixelOccupy.Bit16 | 0x0043
YCbCr8_CbYCr = _VmbPixel.Color | _VmbPixelOccupy.Bit24 | 0x003A
def __str__(self):
return self._name_
class VimbaCError(Exception):
"""Error Type containing an error code from the C-Layer. This error code is highly context
sensitive. All wrapped C-Functions that do not return VmbError.Success or None must
raise a VimbaCError and the surrounding code must deal if the Error is possible.
"""
def __init__(self, c_error: VmbError):
super().__init__(repr(c_error))
self.__c_error = c_error
def __str__(self):
return repr(self)
def __repr__(self):
return 'VimbaCError({})'.format(repr(self.__c_error))
def get_error_code(self) -> VmbError:
""" Get contained Error Code """
return self.__c_error
# Utility Functions
def _split_into_powers_of_two(num: int) -> Tuple[int, ...]:
result = []
for mask in [1 << i for i in range(32)]:
if mask & num:
result.append(mask)
if not result:
result.append(0)
return tuple(result)
def _split_flags_into_enum(num: int, enum_type):
return [enum_type(val) for val in _split_into_powers_of_two(num)]
def _repr_flags_list(enum_type, flag_val: int):
values = _split_flags_into_enum(flag_val, enum_type)
if values:
def fold_func(acc, arg):
return '{} {}'.format(acc, repr(arg))
return functools.reduce(fold_func, values, '')
else:
return '{}'.format(repr(enum_type(0)))
def decode_cstr(val: bytes) -> str:
"""Converts c_char_p stored in interface structures to a str.
Arguments:
val - Byte sequence to convert into str.
Returns:
str represented by 'val'
"""
return val.decode() if val else ''
def decode_flags(enum_type, enum_val: int):
"""Splits C-styled bit mask into a set of flags from a given Enumeration.
Arguments:
enum_val - Bit mask to decode.
enum_type - Enum Type represented within 'enum_val'
Returns:
A set of all values of enum_type occurring in enum_val.
Raises:
Attribute error a set value is not within the given 'enum_type'.
"""
return tuple(_split_flags_into_enum(enum_val, enum_type))
def fmt_repr(fmt: str, val):
"""Append repr to a format string."""
return fmt.format(repr(val))
def fmt_enum_repr(fmt: str, enum_type, enum_val):
"""Append repr of a given enum type to a format string.
Arguments:
fmt - Format string
enum_type - Enum Type to construct.
enum_val - Enum value.
Returns:
formatted string
"""
return fmt.format(repr(enum_type(enum_val)))
def fmt_flags_repr(fmt: str, enum_type, enum_val):
"""Append repr of a c-style flag value in the form of a set containing
all bits set from a given enum_type.
Arguments:
fmt - Format string
enum_type - Enum Type to construct.
enum_val - Enum value.
Returns:
formatted string
"""
return fmt.format(_repr_flags_list(enum_type, enum_val))
def load_vimba_lib(vimba_project: str):
""" Load shared library shipped with the Vimba installation
Arguments:
vimba_project - Library name without prefix or extension
Return:
CDLL or WinDLL Handle on loaded library
Raises:
VimbaSystemError if given library could not be loaded.
"""
platform_handlers = {
'linux': _load_under_linux,
'win32': _load_under_windows
}
if sys.platform not in platform_handlers:
msg = 'Abort. Unsupported Platform ({}) detected.'
raise VimbaSystemError(msg.format(sys.platform))
return platform_handlers[sys.platform](vimba_project)
def _load_under_linux(vimba_project: str):
# Construct VimbaHome based on TL installation paths
path_list: List[str] = []
tl32_path = os.environ.get('GENICAM_GENTL32_PATH', "")
if tl32_path:
path_list += tl32_path.split(':')
tl64_path = os.environ.get('GENICAM_GENTL64_PATH', "")
if tl64_path:
path_list += tl64_path.split(':')
# Remove empty strings from path_list if there are any.
# Necessary because the GENICAM_GENTLXX_PATH variable might start with a :
path_list = [path for path in path_list if path]
# Early return if required variables are not set.
if not path_list:
raise VimbaSystemError('No TL detected. Please verify Vimba installation.')
vimba_home_candidates: List[str] = []
for path in path_list:
vimba_home = os.path.dirname(os.path.dirname(os.path.dirname(path)))
if vimba_home not in vimba_home_candidates:
vimba_home_candidates.append(vimba_home)
# Select the most likely directory from the candidates
vimba_home = _select_vimba_home(vimba_home_candidates)
arch = platform.machine()
# Linux x86 64 Bit (Requires additional interpreter version check)
if arch == 'x86_64':
dir_ = 'x86_64bit' if _is_python_64_bit() else 'x86_32bit'
# Linux x86 32 Bit
elif arch in ('i386', 'i686'):
dir_ = 'x86_32bit'
# Linux arm 64 Bit (Requires additional interpreter version check)
elif arch == 'aarch64':
dir_ = 'arm_64bit' if _is_python_64_bit() else 'arm_32bit'
# Linux arm 32 Bit:
elif arch == 'armv7l':
dir_ = 'arm_32bit'
else:
raise VimbaSystemError('Unknown Architecture \'{}\'. Abort'.format(arch))
lib_name = 'lib{}.so'.format(vimba_project)
lib_path = os.path.join(vimba_home, vimba_project, 'DynamicLib', dir_, lib_name)
try:
lib = ctypes.cdll.LoadLibrary(lib_path)
except OSError as e:
msg = 'Failed to load library \'{}\'. Please verify Vimba installation.'
raise VimbaSystemError(msg.format(lib_path)) from e
return lib
def _load_under_windows(vimba_project: str):
vimba_home = os.environ.get('VIMBA_HOME')
if vimba_home is None:
raise VimbaSystemError('Variable VIMBA_HOME not set. Please verify Vimba installation.')
load_64bit = True if (platform.machine() == 'AMD64') and _is_python_64_bit() else False
lib_name = '{}.dll'.format(vimba_project)
lib_path = os.path.join(vimba_home, vimba_project, 'Bin', 'Win64' if load_64bit else 'Win32',
lib_name)
try:
# Load Library with 64 Bit and use cdecl call convention
if load_64bit:
lib = ctypes.cdll.LoadLibrary(lib_path)
# Load Library with 32 Bit and use stdcall call convention
else:
# Tell mypy to ignore this line to allow type checking on both windows and linux as
# windll is not available on linux and would therefore produce an error there
lib = ctypes.windll.LoadLibrary(lib_path) # type: ignore
except OSError as e:
msg = 'Failed to load library \'{}\'. Please verify Vimba installation.'
raise VimbaSystemError(msg.format(lib_path)) from e
return lib
def _select_vimba_home(candidates: List[str]) -> str:
"""
Select the most likely candidate for VIMBA_HOME from the given list of
candidates
Arguments:
candidates - List of strings pointing to possible vimba home directories
Return:
Path that represents the most likely VIMBA_HOME directory
Raises:
VimbaSystemError if multiple VIMBA_HOME directories were found in candidates
"""
most_likely_candidates = []
for candidate in candidates:
if 'vimba' in candidate.lower():
most_likely_candidates.append(candidate)
if len(most_likely_candidates) == 0:
raise VimbaSystemError('No suitable Vimba installation found. The following paths '
'were considered: {}'.format(candidates))
elif len(most_likely_candidates) > 1:
raise VimbaSystemError('Multiple Vimba installations found. Can\'t decide which to select: '
'{}'.format(most_likely_candidates))
return most_likely_candidates[0]
def _is_python_64_bit() -> bool:
# Query if the currently running python interpreter is build as 64 bit binary.
# The default method of getting this information seems to be rather hacky
# (check if maxint > 2^32) but it seems to be the way to do this....
return True if sys.maxsize > 2**32 else False

View File

@@ -0,0 +1,569 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import ctypes
import sys
from ctypes import byref, sizeof, c_char_p, POINTER as c_ptr
from typing import Callable, Any, Tuple, Dict, List
from ..error import VimbaSystemError
from ..util import TraceEnable
from .vimba_common import Uint32Enum, VmbUint32, VmbInt32, VmbError, VmbFloat, VimbaCError, \
VmbPixelFormat, load_vimba_lib, fmt_repr, fmt_enum_repr
__all__ = [
'VmbBayerPattern',
'VmbEndianness',
'VmbAligment',
'VmbAPIInfo',
'VmbPixelLayout',
'VmbDebayerMode',
'VmbImage',
'VmbImageInfo',
'VmbTransformInfo',
'VIMBA_IMAGE_TRANSFORM_VERSION',
'EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION',
'call_vimba_image_transform',
'PIXEL_FORMAT_TO_LAYOUT',
'LAYOUT_TO_PIXEL_FORMAT',
'PIXEL_FORMAT_CONVERTIBILITY_MAP'
]
class VmbBayerPattern(Uint32Enum):
"""Enum defining BayerPatterns
Values:
RGGB - RGGB pattern, red pixel comes first
GBRG - RGGB pattern, green pixel of blue row comes first
GRBG - RGGB pattern, green pixel of red row comes first
BGGR - RGGB pattern, blue pixel comes first
CYGM - CYGM pattern, cyan pixel comes first in the first row, green in the second row
GMCY - CYGM pattern, green pixel comes first in the first row, cyan in the second row
CYMG - CYGM pattern, cyan pixel comes first in the first row, magenta in the second row
MGCY - CYGM pattern, magenta pixel comes first in the first row, cyan in the second row
LAST - Indicator for end of defined range
"""
RGGB = 0
GBRG = 1
GRBG = 2
BGGR = 3
CYGM = 128
GMCY = 129
CYMG = 130
MGCY = 131
LAST = 255
def __str__(self):
return self._name_
class VmbEndianness(Uint32Enum):
"""Enum defining Endian Formats
Values:
LITTLE - Little Endian
BIG - Big Endian
LAST - Indicator for end of defined range
"""
LITTLE = 0
BIG = 1
LAST = 255
def __str__(self):
return self._name_
class VmbAligment(Uint32Enum):
"""Enum defining image alignment
Values:
MSB - Alignment (pppp pppp pppp ....)
LSB - Alignment (.... pppp pppp pppp)
LAST - Indicator for end of defined range
"""
MSB = 0
LSB = 1
LAST = 255
def __str__(self):
return self._name_
class VmbAPIInfo(Uint32Enum):
"""API Info Types
Values:
ALL - All Infos
PLATFORM - Platform the API was built for
BUILD - Build Types (debug or release)
TECHNOLOGY - Special technology info
LAST - Indicator for end of defined range
"""
ALL = 0
PLATFORM = 1
BUILD = 2
TECHNOLOGY = 3
LAST = 4
def __str__(self):
return self._name_
class VmbPixelLayout(Uint32Enum):
"""Image Pixel Layout Information. C Header offers no further documentation."""
Mono = 0
MonoPacked = 1
Raw = 2
RawPacked = 3
RGB = 4
BGR = 5
RGBA = 6
BGRA = 7
YUV411 = 8
YUV422 = 9
YUV444 = 10
MonoP = 11
MonoPl = 12
RawP = 13
RawPl = 14
YYCbYYCr411 = 15
CbYYCrYY411 = YUV411,
YCbYCr422 = 16
CbYCrY422 = YUV422
YCbCr444 = 17
CbYCr444 = YUV444
LAST = 19
def __str__(self):
return self._name_
class VmbColorSpace(Uint32Enum):
"""Image Color space. C Header offers no further documentation."""
Undefined = 0
ITU_BT709 = 1
ITU_BT601 = 2
def __str__(self):
return self._name_
class VmbDebayerMode(Uint32Enum):
"""Debayer Mode. C Header offers no further documentation."""
Mode_2x2 = 0
Mode_3x3 = 1
Mode_LCAA = 2
Mode_LCAAV = 3
Mode_YUV422 = 4
def __str__(self):
return self._name_
class VmbTransformType(Uint32Enum):
"""TransformType Mode. C Header offers no further documentation."""
None_ = 0
DebayerMode = 1
ColorCorrectionMatrix = 2
GammaCorrection = 3
Offset = 4
Gain = 5
def __str__(self):
return self._name_
class VmbPixelInfo(ctypes.Structure):
"""Structure containing pixel information. Sadly c_header contains no more documentation"""
_fields_ = [
('BitsPerPixel', VmbUint32),
('BitsUsed', VmbUint32),
('Alignment', VmbUint32),
('Endianness', VmbUint32),
('PixelLayout', VmbUint32),
('BayerPattern', VmbUint32),
('Reserved', VmbUint32)
]
def __repr__(self):
rep = 'VmbPixelInfo'
rep += fmt_repr('(BitsPerPixel={}', self.BitsPerPixel)
rep += fmt_repr(',BitsUsed={}', self.BitsUsed)
rep += fmt_enum_repr(',Alignment={}', VmbAligment, self.Alignment)
rep += fmt_enum_repr(',Endianness={}', VmbEndianness, self.Endianness)
rep += fmt_enum_repr(',PixelLayout={}', VmbPixelLayout, self.PixelLayout)
rep += fmt_enum_repr(',BayerPattern={}', VmbBayerPattern, self.BayerPattern)
rep += fmt_enum_repr(',Reserved={}', VmbColorSpace, self.Reserved)
rep += ')'
return rep
class VmbImageInfo(ctypes.Structure):
"""Structure containing image information. Sadly c_header contains no more documentation"""
_fields_ = [
('Width', VmbUint32),
('Height', VmbUint32),
('Stride', VmbInt32),
('PixelInfo', VmbPixelInfo)
]
def __repr__(self):
rep = 'VmbImageInfo'
rep += fmt_repr('(Width={}', self.Width)
rep += fmt_repr(',Height={}', self.Height)
rep += fmt_repr(',Stride={}', self.Stride)
rep += fmt_repr(',PixelInfo={}', self.PixelInfo)
rep += ')'
return rep
class VmbImage(ctypes.Structure):
"""Structure containing image. Sadly c_header contains no more documentation"""
_fields_ = [
('Size', VmbUint32),
('Data', ctypes.c_void_p),
('ImageInfo', VmbImageInfo)
]
def __repr__(self):
rep = 'VmbImage'
rep += fmt_repr('(Size={}', self.Size)
rep += fmt_repr(',Data={}', self.Data)
rep += fmt_repr(',ImageInfo={}', self.ImageInfo)
rep += ')'
return rep
class VmbTransformParameterMatrix3x3(ctypes.Structure):
"""Sadly c_header contains no more documentation"""
_fields_ = [
('Matrix', VmbFloat * 9)
]
class VmbTransformParameterGamma(ctypes.Structure):
"""Sadly c_header contains no more documentation"""
_fields_ = [
('Gamma', VmbFloat)
]
class VmbTransformParameterDebayer(ctypes.Structure):
"""Sadly c_header contains no more documentation"""
_fields_ = [
('Method', VmbUint32)
]
class VmbTransformParameterOffset(ctypes.Structure):
"""Sadly c_header contains no more documentation"""
_fields_ = [
('Offset', VmbInt32)
]
class VmbTransformParameterGain(ctypes.Structure):
"""Sadly c_header contains no more documentation"""
_fields_ = [
('Gain', VmbUint32)
]
class VmbTransformParameter(ctypes.Union):
"""Sadly c_header contains no more documentation"""
_fields_ = [
('Matrix3x3', VmbTransformParameterMatrix3x3),
('Debayer', VmbTransformParameterDebayer),
('Gamma', VmbTransformParameterGamma),
('Offset', VmbTransformParameterOffset),
('Gain', VmbTransformParameterGain)
]
class VmbTransformInfo(ctypes.Structure):
"""Struct holding transformation information"""
_fields_ = [
('TransformType', VmbUint32),
('Parameter', VmbTransformParameter)
]
# API
VIMBA_IMAGE_TRANSFORM_VERSION = None
if sys.platform == 'linux':
EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION = '1.0'
else:
EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION = '1.6'
# For detailed information on the signatures see "VimbaImageTransform.h"
# To improve readability, suppress 'E501 line too long (> 100 characters)'
# check of flake8
_SIGNATURES = {
'VmbGetVersion': (VmbError, [c_ptr(VmbUint32)]),
'VmbGetErrorInfo': (VmbError, [VmbError, c_char_p, VmbUint32]),
'VmbGetApiInfoString': (VmbError, [VmbAPIInfo, c_char_p, VmbUint32]),
'VmbSetDebayerMode': (VmbError, [VmbDebayerMode, c_ptr(VmbTransformInfo)]),
'VmbSetColorCorrectionMatrix3x3': (VmbError, [c_ptr(VmbFloat), c_ptr(VmbTransformInfo)]),
'VmbSetGammaCorrection': (VmbError, [VmbFloat, c_ptr(VmbTransformInfo)]),
'VmbSetImageInfoFromPixelFormat': (VmbError, [VmbPixelFormat, VmbUint32, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
'VmbSetImageInfoFromString': (VmbError, [c_char_p, VmbUint32, VmbUint32, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
'VmbSetImageInfoFromInputParameters': (VmbError, [VmbPixelFormat, VmbUint32, VmbUint32, VmbPixelLayout, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
'VmbSetImageInfoFromInputImage': (VmbError, [c_ptr(VmbImage), VmbPixelLayout, VmbUint32, c_ptr(VmbImage)]), # noqa: E501
'VmbImageTransform': (VmbError, [c_ptr(VmbImage), c_ptr(VmbImage), c_ptr(VmbTransformInfo), VmbUint32]) # noqa: E501
}
def _attach_signatures(lib_handle):
global _SIGNATURES
for function_name, signature in _SIGNATURES.items():
fn = getattr(lib_handle, function_name)
fn.restype, fn.argtypes = signature
fn.errcheck = _eval_vmberror
return lib_handle
def _check_version(lib_handle):
global EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION
global VIMBA_IMAGE_TRANSFORM_VERSION
v = VmbUint32()
lib_handle.VmbGetVersion(byref(v))
VIMBA_IMAGE_TRANSFORM_VERSION = '{}.{}'.format((v.value >> 24) & 0xff, (v.value >> 16) & 0xff)
loaded_version = tuple(map(int, VIMBA_IMAGE_TRANSFORM_VERSION.split(".")))
expected_version = tuple(map(int, EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION.split(".")))
# Major version must match. minor version may be equal or greater
if not(loaded_version[0] == expected_version[0] and
loaded_version[1] >= expected_version[1]):
msg = 'Invalid VimbaImageTransform Version: Expected: {}, Found:{}'
raise VimbaSystemError(msg.format(EXPECTED_VIMBA_IMAGE_TRANSFORM_VERSION,
VIMBA_IMAGE_TRANSFORM_VERSION))
return lib_handle
def _eval_vmberror(result: VmbError, func: Callable[..., Any], *args: Tuple[Any, ...]):
if result not in (VmbError.Success, None):
raise VimbaCError(result)
_lib_instance = _check_version(_attach_signatures(load_vimba_lib('VimbaImageTransform')))
@TraceEnable()
def call_vimba_image_transform(func_name: str, *args):
"""This function encapsulates the entire VimbaImageTransform access.
For Details on valid function signatures see the 'VimbaImageTransform.h'.
Arguments:
func_name: The function name from VimbaImageTransform to be called.
args: Varargs passed directly to the underlaying C-Function.
Raises:
TypeError if given are do not match the signature of the function.
AttributeError if func with name 'func_name' does not exist.
VimbaCError if the function call is valid but neither None or VmbError.Success was returned.
The following functions of VimbaImageTransform can be executed:
VmbGetVersion
VmbGetTechnoInfo
VmbGetErrorInfo
VmbGetApiInfoString
VmbSetDebayerMode
VmbSetColorCorrectionMatrix3x3
VmbSetGammaCorrection
VmbSetImageInfoFromPixelFormat
VmbSetImageInfoFromString
VmbSetImageInfoFromInputParameters
VmbSetImageInfoFromInputImage
VmbImageTransform
"""
global _lib_instance
getattr(_lib_instance, func_name)(*args)
PIXEL_FORMAT_TO_LAYOUT: Dict[VmbPixelFormat, Tuple[VmbPixelLayout, int]] = {
VmbPixelFormat.Mono8: (VmbPixelLayout.Mono, 8),
VmbPixelFormat.Mono10: (VmbPixelLayout.Mono, 16),
VmbPixelFormat.Mono12: (VmbPixelLayout.Mono, 16),
VmbPixelFormat.Mono14: (VmbPixelLayout.Mono, 16),
VmbPixelFormat.Mono16: (VmbPixelLayout.Mono, 16),
VmbPixelFormat.BayerGR8: (VmbPixelLayout.Raw, 8),
VmbPixelFormat.BayerRG8: (VmbPixelLayout.Raw, 8),
VmbPixelFormat.BayerGB8: (VmbPixelLayout.Raw, 8),
VmbPixelFormat.BayerBG8: (VmbPixelLayout.Raw, 8),
VmbPixelFormat.BayerGR10: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerRG10: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerGB10: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerBG10: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerGR12: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerRG12: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerGB12: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerBG12: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerGR16: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerRG16: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerGB16: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.BayerBG16: (VmbPixelLayout.Raw, 16),
VmbPixelFormat.Rgb8: (VmbPixelLayout.RGB, 8),
VmbPixelFormat.Rgb10: (VmbPixelLayout.RGB, 16),
VmbPixelFormat.Rgb12: (VmbPixelLayout.RGB, 16),
VmbPixelFormat.Rgb14: (VmbPixelLayout.RGB, 16),
VmbPixelFormat.Rgb16: (VmbPixelLayout.RGB, 16),
VmbPixelFormat.Bgr8: (VmbPixelLayout.BGR, 8),
VmbPixelFormat.Bgr10: (VmbPixelLayout.BGR, 16),
VmbPixelFormat.Bgr12: (VmbPixelLayout.BGR, 16),
VmbPixelFormat.Bgr14: (VmbPixelLayout.BGR, 16),
VmbPixelFormat.Bgr16: (VmbPixelLayout.BGR, 16),
VmbPixelFormat.Rgba8: (VmbPixelLayout.RGBA, 8),
VmbPixelFormat.Rgba10: (VmbPixelLayout.RGBA, 16),
VmbPixelFormat.Rgba12: (VmbPixelLayout.RGBA, 16),
VmbPixelFormat.Rgba14: (VmbPixelLayout.RGBA, 16),
VmbPixelFormat.Rgba16: (VmbPixelLayout.RGBA, 16),
VmbPixelFormat.Bgra8: (VmbPixelLayout.BGRA, 8),
VmbPixelFormat.Bgra10: (VmbPixelLayout.BGRA, 16),
VmbPixelFormat.Bgra12: (VmbPixelLayout.BGRA, 16),
VmbPixelFormat.Bgra14: (VmbPixelLayout.BGRA, 16),
VmbPixelFormat.Bgra16: (VmbPixelLayout.BGRA, 16)
}
LAYOUT_TO_PIXEL_FORMAT = dict([(v, k) for k, v in PIXEL_FORMAT_TO_LAYOUT.items()])
def _query_compatibility(pixel_format: VmbPixelFormat) -> Tuple[VmbPixelFormat, ...]:
global LAYOUT_TO_PIXEL_FORMAT
# Query compatible formats from ImageTransform
output_pixel_layouts = (VmbPixelLayout.Mono, VmbPixelLayout.MonoPacked, VmbPixelLayout.Raw,
VmbPixelLayout.RawPacked, VmbPixelLayout.RGB, VmbPixelLayout.BGR,
VmbPixelLayout.RGBA, VmbPixelLayout.BGRA)
output_bits_per_pixel = (8, 16)
output_layouts = tuple([(layouts, bits)
for layouts in output_pixel_layouts
for bits in output_bits_per_pixel])
result: List[VmbPixelFormat] = []
src_image = VmbImage()
src_image.Size = sizeof(src_image)
call_vimba_image_transform('VmbSetImageInfoFromPixelFormat', pixel_format, 0, 0,
byref(src_image))
dst_image = VmbImage()
dst_image.Size = sizeof(dst_image)
for layout, bits in output_layouts:
try:
call_vimba_image_transform('VmbSetImageInfoFromInputImage', byref(src_image), layout,
bits, byref(dst_image))
fmt = LAYOUT_TO_PIXEL_FORMAT[(layout, bits)]
if fmt not in result:
result.append(fmt)
except VimbaCError as e:
if e.get_error_code() not in (VmbError.NotImplemented_, VmbError.BadParameter):
raise e
return tuple(result)
PIXEL_FORMAT_CONVERTIBILITY_MAP: Dict[VmbPixelFormat, Tuple[VmbPixelFormat, ...]] = {
VmbPixelFormat.Mono8: _query_compatibility(VmbPixelFormat.Mono8),
VmbPixelFormat.Mono10: _query_compatibility(VmbPixelFormat.Mono10),
VmbPixelFormat.Mono10p: _query_compatibility(VmbPixelFormat.Mono10p),
VmbPixelFormat.Mono12: _query_compatibility(VmbPixelFormat.Mono12),
VmbPixelFormat.Mono12Packed: _query_compatibility(VmbPixelFormat.Mono12Packed),
VmbPixelFormat.Mono12p: _query_compatibility(VmbPixelFormat.Mono12p),
VmbPixelFormat.Mono14: _query_compatibility(VmbPixelFormat.Mono14),
VmbPixelFormat.Mono16: _query_compatibility(VmbPixelFormat.Mono16),
VmbPixelFormat.BayerGR8: _query_compatibility(VmbPixelFormat.BayerGR8),
VmbPixelFormat.BayerRG8: _query_compatibility(VmbPixelFormat.BayerRG8),
VmbPixelFormat.BayerGB8: _query_compatibility(VmbPixelFormat.BayerGB8),
VmbPixelFormat.BayerBG8: _query_compatibility(VmbPixelFormat.BayerBG8),
VmbPixelFormat.BayerGR10: _query_compatibility(VmbPixelFormat.BayerGR10),
VmbPixelFormat.BayerRG10: _query_compatibility(VmbPixelFormat.BayerRG10),
VmbPixelFormat.BayerGB10: _query_compatibility(VmbPixelFormat.BayerGB10),
VmbPixelFormat.BayerBG10: _query_compatibility(VmbPixelFormat.BayerBG10),
VmbPixelFormat.BayerGR12: _query_compatibility(VmbPixelFormat.BayerGR12),
VmbPixelFormat.BayerRG12: _query_compatibility(VmbPixelFormat.BayerRG12),
VmbPixelFormat.BayerGB12: _query_compatibility(VmbPixelFormat.BayerGB12),
VmbPixelFormat.BayerBG12: _query_compatibility(VmbPixelFormat.BayerBG12),
VmbPixelFormat.BayerGR12Packed: _query_compatibility(VmbPixelFormat.BayerGR12Packed),
VmbPixelFormat.BayerRG12Packed: _query_compatibility(VmbPixelFormat.BayerRG12Packed),
VmbPixelFormat.BayerGB12Packed: _query_compatibility(VmbPixelFormat.BayerGB12Packed),
VmbPixelFormat.BayerBG12Packed: _query_compatibility(VmbPixelFormat.BayerBG12Packed),
VmbPixelFormat.BayerGR10p: _query_compatibility(VmbPixelFormat.BayerGR10p),
VmbPixelFormat.BayerRG10p: _query_compatibility(VmbPixelFormat.BayerRG10p),
VmbPixelFormat.BayerGB10p: _query_compatibility(VmbPixelFormat.BayerGB10p),
VmbPixelFormat.BayerBG10p: _query_compatibility(VmbPixelFormat.BayerBG10p),
VmbPixelFormat.BayerGR12p: _query_compatibility(VmbPixelFormat.BayerGR12p),
VmbPixelFormat.BayerRG12p: _query_compatibility(VmbPixelFormat.BayerRG12p),
VmbPixelFormat.BayerGB12p: _query_compatibility(VmbPixelFormat.BayerGB12p),
VmbPixelFormat.BayerBG12p: _query_compatibility(VmbPixelFormat.BayerBG12p),
VmbPixelFormat.BayerGR16: _query_compatibility(VmbPixelFormat.BayerGR16),
VmbPixelFormat.BayerRG16: _query_compatibility(VmbPixelFormat.BayerRG16),
VmbPixelFormat.BayerGB16: _query_compatibility(VmbPixelFormat.BayerGB16),
VmbPixelFormat.BayerBG16: _query_compatibility(VmbPixelFormat.BayerBG16),
VmbPixelFormat.Rgb8: _query_compatibility(VmbPixelFormat.Rgb8),
VmbPixelFormat.Bgr8: _query_compatibility(VmbPixelFormat.Bgr8),
VmbPixelFormat.Rgb10: _query_compatibility(VmbPixelFormat.Rgb10),
VmbPixelFormat.Bgr10: _query_compatibility(VmbPixelFormat.Bgr10),
VmbPixelFormat.Rgb12: _query_compatibility(VmbPixelFormat.Rgb12),
VmbPixelFormat.Bgr12: _query_compatibility(VmbPixelFormat.Bgr12),
VmbPixelFormat.Rgb14: _query_compatibility(VmbPixelFormat.Rgb14),
VmbPixelFormat.Bgr14: _query_compatibility(VmbPixelFormat.Bgr14),
VmbPixelFormat.Rgb16: _query_compatibility(VmbPixelFormat.Rgb16),
VmbPixelFormat.Bgr16: _query_compatibility(VmbPixelFormat.Bgr16),
VmbPixelFormat.Argb8: _query_compatibility(VmbPixelFormat.Argb8),
VmbPixelFormat.Rgba8: _query_compatibility(VmbPixelFormat.Rgba8),
VmbPixelFormat.Bgra8: _query_compatibility(VmbPixelFormat.Bgra8),
VmbPixelFormat.Rgba10: _query_compatibility(VmbPixelFormat.Rgba10),
VmbPixelFormat.Bgra10: _query_compatibility(VmbPixelFormat.Bgra10),
VmbPixelFormat.Rgba12: _query_compatibility(VmbPixelFormat.Rgba12),
VmbPixelFormat.Bgra12: _query_compatibility(VmbPixelFormat.Bgra12),
VmbPixelFormat.Rgba14: _query_compatibility(VmbPixelFormat.Rgba14),
VmbPixelFormat.Bgra14: _query_compatibility(VmbPixelFormat.Bgra14),
VmbPixelFormat.Rgba16: _query_compatibility(VmbPixelFormat.Rgba16),
VmbPixelFormat.Bgra16: _query_compatibility(VmbPixelFormat.Bgra16),
VmbPixelFormat.Yuv411: _query_compatibility(VmbPixelFormat.Yuv411),
VmbPixelFormat.Yuv422: _query_compatibility(VmbPixelFormat.Yuv422),
VmbPixelFormat.Yuv444: _query_compatibility(VmbPixelFormat.Yuv444),
VmbPixelFormat.YCbCr411_8_CbYYCrYY: _query_compatibility(VmbPixelFormat.YCbCr411_8_CbYYCrYY),
VmbPixelFormat.YCbCr422_8_CbYCrY: _query_compatibility(VmbPixelFormat.YCbCr422_8_CbYCrY),
VmbPixelFormat.YCbCr8_CbYCr: _query_compatibility(VmbPixelFormat.YCbCr8_CbYCr)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,95 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from .util import Log
__all__ = [
'VimbaSystemError',
'VimbaCameraError',
'VimbaInterfaceError',
'VimbaFeatureError',
'VimbaFrameError',
'VimbaTimeout'
]
class _LoggedError(Exception):
def __init__(self, msg: str):
super().__init__(msg)
Log.get_instance().error(msg)
class VimbaSystemError(_LoggedError):
"""Errors related to the underlying Vimba System
Error type to indicate system-wide errors like:
- Incomplete Vimba installation
- Incompatible version of the underlying C-Layer
- An unsupported OS
"""
pass
class VimbaCameraError(_LoggedError):
"""Errors related to cameras
Error Type to indicated camera-related errors like:
- Access of a disconnected Camera object
- Lookup of non-existing cameras
"""
pass
class VimbaInterfaceError(_LoggedError):
"""Errors related to Interfaces
Error Type to indicated interface-related errors like:
- Access on a disconnected Interface object
- Lookup of a non-existing Interface
"""
pass
class VimbaFeatureError(_LoggedError):
"""Error related to Feature access.
Error type to indicate invalid Feature access like:
- Invalid access mode on Feature access.
- Out of range values upon setting a value.
- Failed lookup of features.
"""
pass
class VimbaFrameError(_LoggedError):
"""Error related to Frame data"""
pass
class VimbaTimeout(_LoggedError):
"""Indicates that an operation timed out."""
pass

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,923 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import enum
import ctypes
import copy
import functools
from typing import Optional, Tuple
from .c_binding import byref, sizeof, decode_flags
from .c_binding import call_vimba_c, call_vimba_image_transform, VmbFrameStatus, VmbFrameFlags, \
VmbFrame, VmbHandle, VmbPixelFormat, VmbImage, VmbDebayerMode, \
VmbTransformInfo, PIXEL_FORMAT_CONVERTIBILITY_MAP, PIXEL_FORMAT_TO_LAYOUT
from .feature import FeaturesTuple, FeatureTypes, FeatureTypeTypes, discover_features
from .shared import filter_features_by_name, filter_features_by_type, filter_features_by_category, \
attach_feature_accessors, remove_feature_accessors
from .util import TraceEnable, RuntimeTypeCheckEnable, EnterContextOnCall, LeaveContextOnCall, \
RaiseIfOutsideContext
from .error import VimbaFrameError, VimbaFeatureError
try:
import numpy # type: ignore
except ModuleNotFoundError:
numpy = None # type: ignore
__all__ = [
'PixelFormat',
'MONO_PIXEL_FORMATS',
'BAYER_PIXEL_FORMATS',
'RGB_PIXEL_FORMATS',
'RGBA_PIXEL_FORMATS',
'BGR_PIXEL_FORMATS',
'BGRA_PIXEL_FORMATS',
'YUV_PIXEL_FORMATS',
'YCBCR_PIXEL_FORMATS',
'COLOR_PIXEL_FORMATS',
'OPENCV_PIXEL_FORMATS',
'FrameStatus',
'Debayer',
'Frame',
'FrameTuple',
'FormatTuple',
'intersect_pixel_formats'
]
# Forward declarations
FrameTuple = Tuple['Frame', ...]
FormatTuple = Tuple['PixelFormat', ...]
class PixelFormat(enum.IntEnum):
"""Enum specifying all PixelFormats. Note: Not all Cameras support all Pixelformats.
Mono formats:
Mono8 - Monochrome, 8 bits (PFNC:Mono8)
Mono10 - Monochrome, 10 bits in 16 bits (PFNC:Mono10)
Mono10p - Monochrome, 4x10 bits continuously packed in 40 bits
(PFNC:Mono10p)
Mono12 - Monochrome, 12 bits in 16 bits (PFNC:Mono12)
Mono12Packed - Monochrome, 2x12 bits in 24 bits (GEV:Mono12Packed)
Mono12p - Monochrome, 2x12 bits continuously packed in 24 bits
(PFNC:Mono12p)
Mono14 - Monochrome, 14 bits in 16 bits (PFNC:Mono14)
Mono16 - Monochrome, 16 bits (PFNC:Mono16)
Bayer formats:
BayerGR8 - Bayer-color, 8 bits, starting with GR line
(PFNC:BayerGR8)
BayerRG8 - Bayer-color, 8 bits, starting with RG line
(PFNC:BayerRG8)
BayerGB8 - Bayer-color, 8 bits, starting with GB line
(PFNC:BayerGB8)
BayerBG8 - Bayer-color, 8 bits, starting with BG line
(PFNC:BayerBG8)
BayerGR10 - Bayer-color, 10 bits in 16 bits, starting with GR
line (PFNC:BayerGR10)
BayerRG10 - Bayer-color, 10 bits in 16 bits, starting with RG
line (PFNC:BayerRG10)
BayerGB10 - Bayer-color, 10 bits in 16 bits, starting with GB
line (PFNC:BayerGB10)
BayerBG10 - Bayer-color, 10 bits in 16 bits, starting with BG
line (PFNC:BayerBG10)
BayerGR12 - Bayer-color, 12 bits in 16 bits, starting with GR
line (PFNC:BayerGR12)
BayerRG12 - Bayer-color, 12 bits in 16 bits, starting with RG
line (PFNC:BayerRG12)
BayerGB12 - Bayer-color, 12 bits in 16 bits, starting with GB
line (PFNC:BayerGB12)
BayerBG12 - Bayer-color, 12 bits in 16 bits, starting with BG
line (PFNC:BayerBG12)
BayerGR12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GR
line (GEV:BayerGR12Packed)
BayerRG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with RG
line (GEV:BayerRG12Packed)
BayerGB12Packed - Bayer-color, 2x12 bits in 24 bits, starting with GB
line (GEV:BayerGB12Packed)
BayerBG12Packed - Bayer-color, 2x12 bits in 24 bits, starting with BG
line (GEV:BayerBG12Packed)
BayerGR10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with GR line (PFNC:BayerGR10p)
BayerRG10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with RG line (PFNC:BayerRG10p)
BayerGB10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with GB line (PFNC:BayerGB10p)
BayerBG10p - Bayer-color, 4x10 bits continuously packed in 40
bits, starting with BG line (PFNC:BayerBG10p)
BayerGR12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with GR line (PFNC:BayerGR12p)
BayerRG12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with RG line (PFNC:BayerRG12p)
BayerGB12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with GB line (PFNC:BayerGB12p)
BayerBG12p - Bayer-color, 2x12 bits continuously packed in 24
bits, starting with BG line (PFNC:BayerBG12p)
BayerGR16 - Bayer-color, 16 bits, starting with GR line
(PFNC:BayerGR16)
BayerRG16 - Bayer-color, 16 bits, starting with RG line
(PFNC:BayerRG16)
BayerGB16 - Bayer-color, 16 bits, starting with GB line
(PFNC:BayerGB16)
BayerBG16 - Bayer-color, 16 bits, starting with BG line
(PFNC:BayerBG16)
RGB formats:
Rgb8 - RGB, 8 bits x 3 (PFNC:RGB8)
Bgr8 - BGR, 8 bits x 3 (PFNC:Bgr8)
Rgb10 - RGB, 10 bits in 16 bits x 3 (PFNC:RGB10)
Bgr10 - BGR, 10 bits in 16 bits x 3 (PFNC:BGR10)
Rgb12 - RGB, 12 bits in 16 bits x 3 (PFNC:RGB12)
Bgr12 - BGR, 12 bits in 16 bits x 3 (PFNC:BGR12)
Rgb14 - RGB, 14 bits in 16 bits x 3 (PFNC:RGB14)
Bgr14 - BGR, 14 bits in 16 bits x 3 (PFNC:BGR14)
Rgb16 - RGB, 16 bits x 3 (PFNC:RGB16)
Bgr16 - BGR, 16 bits x 3 (PFNC:BGR16)
RGBA formats:
Argb8 - ARGB, 8 bits x 4 (PFNC:RGBa8)
Rgba8 - RGBA, 8 bits x 4, legacy name
Bgra8 - BGRA, 8 bits x 4 (PFNC:BGRa8)
Rgba10 - RGBA, 10 bits in 16 bits x 4
Bgra10 - BGRA, 10 bits in 16 bits x 4
Rgba12 - RGBA, 12 bits in 16 bits x 4
Bgra12 - BGRA, 12 bits in 16 bits x 4
Rgba14 - RGBA, 14 bits in 16 bits x 4
Bgra14 - BGRA, 14 bits in 16 bits x 4
Rgba16 - RGBA, 16 bits x 4
Bgra16 - BGRA, 16 bits x 4
YUV/YCbCr formats:
Yuv411 - YUV 411 with 8 bits (GEV:YUV411Packed)
Yuv422 - YUV 422 with 8 bits (GEV:YUV422Packed)
Yuv444 - YUV 444 with 8 bits (GEV:YUV444Packed)
YCbCr411_8_CbYYCrYY - Y´CbCr 411 with 8 bits
(PFNC:YCbCr411_8_CbYYCrYY) - identical to Yuv411
YCbCr422_8_CbYCrY - Y´CbCr 422 with 8 bits
(PFNC:YCbCr422_8_CbYCrY) - identical to Yuv422
YCbCr8_CbYCr - Y´CbCr 444 with 8 bits
(PFNC:YCbCr8_CbYCr) - identical to Yuv444
"""
# Mono Formats
Mono8 = VmbPixelFormat.Mono8
Mono10 = VmbPixelFormat.Mono10
Mono10p = VmbPixelFormat.Mono10p
Mono12 = VmbPixelFormat.Mono12
Mono12Packed = VmbPixelFormat.Mono12Packed
Mono12p = VmbPixelFormat.Mono12p
Mono14 = VmbPixelFormat.Mono14
Mono16 = VmbPixelFormat.Mono16
# Bayer Formats
BayerGR8 = VmbPixelFormat.BayerGR8
BayerRG8 = VmbPixelFormat.BayerRG8
BayerGB8 = VmbPixelFormat.BayerGB8
BayerBG8 = VmbPixelFormat.BayerBG8
BayerGR10 = VmbPixelFormat.BayerGR10
BayerRG10 = VmbPixelFormat.BayerRG10
BayerGB10 = VmbPixelFormat.BayerGB10
BayerBG10 = VmbPixelFormat.BayerBG10
BayerGR12 = VmbPixelFormat.BayerGR12
BayerRG12 = VmbPixelFormat.BayerRG12
BayerGB12 = VmbPixelFormat.BayerGB12
BayerBG12 = VmbPixelFormat.BayerBG12
BayerGR12Packed = VmbPixelFormat.BayerGR12Packed
BayerRG12Packed = VmbPixelFormat.BayerRG12Packed
BayerGB12Packed = VmbPixelFormat.BayerGB12Packed
BayerBG12Packed = VmbPixelFormat.BayerBG12Packed
BayerGR10p = VmbPixelFormat.BayerGR10p
BayerRG10p = VmbPixelFormat.BayerRG10p
BayerGB10p = VmbPixelFormat.BayerGB10p
BayerBG10p = VmbPixelFormat.BayerBG10p
BayerGR12p = VmbPixelFormat.BayerGR12p
BayerRG12p = VmbPixelFormat.BayerRG12p
BayerGB12p = VmbPixelFormat.BayerGB12p
BayerBG12p = VmbPixelFormat.BayerBG12p
BayerGR16 = VmbPixelFormat.BayerGR16
BayerRG16 = VmbPixelFormat.BayerRG16
BayerGB16 = VmbPixelFormat.BayerGB16
BayerBG16 = VmbPixelFormat.BayerBG16
# RGB Formats
Rgb8 = VmbPixelFormat.Rgb8
Bgr8 = VmbPixelFormat.Bgr8
Rgb10 = VmbPixelFormat.Rgb10
Bgr10 = VmbPixelFormat.Bgr10
Rgb12 = VmbPixelFormat.Rgb12
Bgr12 = VmbPixelFormat.Bgr12
Rgb14 = VmbPixelFormat.Rgb14
Bgr14 = VmbPixelFormat.Bgr14
Rgb16 = VmbPixelFormat.Rgb16
Bgr16 = VmbPixelFormat.Bgr16
# RGBA Formats
Rgba8 = VmbPixelFormat.Rgba8
Bgra8 = VmbPixelFormat.Bgra8
Argb8 = VmbPixelFormat.Argb8
Rgba10 = VmbPixelFormat.Rgba10
Bgra10 = VmbPixelFormat.Bgra10
Rgba12 = VmbPixelFormat.Rgba12
Bgra12 = VmbPixelFormat.Bgra12
Rgba14 = VmbPixelFormat.Rgba14
Bgra14 = VmbPixelFormat.Bgra14
Rgba16 = VmbPixelFormat.Rgba16
Bgra16 = VmbPixelFormat.Bgra16
Yuv411 = VmbPixelFormat.Yuv411
Yuv422 = VmbPixelFormat.Yuv422
Yuv444 = VmbPixelFormat.Yuv444
# YCbCr Formats
YCbCr411_8_CbYYCrYY = VmbPixelFormat.YCbCr411_8_CbYYCrYY
YCbCr422_8_CbYCrY = VmbPixelFormat.YCbCr422_8_CbYCrY
YCbCr8_CbYCr = VmbPixelFormat.YCbCr8_CbYCr
def __str__(self):
return self._name_
def __repr__(self):
return 'PixelFormat.{}'.format(str(self))
def get_convertible_formats(self) -> Tuple['PixelFormat', ...]:
formats = PIXEL_FORMAT_CONVERTIBILITY_MAP[VmbPixelFormat(self)]
return tuple([PixelFormat(fmt) for fmt in formats])
MONO_PIXEL_FORMATS = (
PixelFormat.Mono8,
PixelFormat.Mono10,
PixelFormat.Mono10p,
PixelFormat.Mono12,
PixelFormat.Mono12Packed,
PixelFormat.Mono12p,
PixelFormat.Mono14,
PixelFormat.Mono16
)
BAYER_PIXEL_FORMATS = (
PixelFormat.BayerGR8,
PixelFormat.BayerRG8,
PixelFormat.BayerGB8,
PixelFormat.BayerBG8,
PixelFormat.BayerGR10,
PixelFormat.BayerRG10,
PixelFormat.BayerGB10,
PixelFormat.BayerBG10,
PixelFormat.BayerGR12,
PixelFormat.BayerRG12,
PixelFormat.BayerGB12,
PixelFormat.BayerBG12,
PixelFormat.BayerGR12Packed,
PixelFormat.BayerRG12Packed,
PixelFormat.BayerGB12Packed,
PixelFormat.BayerBG12Packed,
PixelFormat.BayerGR10p,
PixelFormat.BayerRG10p,
PixelFormat.BayerGB10p,
PixelFormat.BayerBG10p,
PixelFormat.BayerGR12p,
PixelFormat.BayerRG12p,
PixelFormat.BayerGB12p,
PixelFormat.BayerBG12p,
PixelFormat.BayerGR16,
PixelFormat.BayerRG16,
PixelFormat.BayerGB16,
PixelFormat.BayerBG16
)
RGB_PIXEL_FORMATS = (
PixelFormat.Rgb8,
PixelFormat.Rgb10,
PixelFormat.Rgb12,
PixelFormat.Rgb14,
PixelFormat.Rgb16
)
RGBA_PIXEL_FORMATS = (
PixelFormat.Rgba8,
PixelFormat.Argb8,
PixelFormat.Rgba10,
PixelFormat.Rgba12,
PixelFormat.Rgba14,
PixelFormat.Rgba16
)
BGR_PIXEL_FORMATS = (
PixelFormat.Bgr8,
PixelFormat.Bgr10,
PixelFormat.Bgr12,
PixelFormat.Bgr14,
PixelFormat.Bgr16
)
BGRA_PIXEL_FORMATS = (
PixelFormat.Bgra8,
PixelFormat.Bgra10,
PixelFormat.Bgra12,
PixelFormat.Bgra14,
PixelFormat.Bgra16
)
YUV_PIXEL_FORMATS = (
PixelFormat.Yuv411,
PixelFormat.Yuv422,
PixelFormat.Yuv444
)
YCBCR_PIXEL_FORMATS = (
PixelFormat.YCbCr411_8_CbYYCrYY,
PixelFormat.YCbCr422_8_CbYCrY,
PixelFormat.YCbCr8_CbYCr
)
COLOR_PIXEL_FORMATS = BAYER_PIXEL_FORMATS + RGB_PIXEL_FORMATS + RGBA_PIXEL_FORMATS + \
BGR_PIXEL_FORMATS + BGRA_PIXEL_FORMATS + YUV_PIXEL_FORMATS + \
YCBCR_PIXEL_FORMATS
OPENCV_PIXEL_FORMATS = (
PixelFormat.Mono8,
PixelFormat.Bgr8,
PixelFormat.Bgra8,
PixelFormat.Mono16,
PixelFormat.Bgr16,
PixelFormat.Bgra16
)
class Debayer(enum.IntEnum):
"""Enum specifying debayer modes.
Enum values:
Mode2x2 - 2x2 with green averaging (this is the default if no debayering algorithm
is added as transformation option).
Mode3x3 - 3x3 with equal green weighting per line (8-bit images only).
ModeLCAA - Debayering with horizontal local color anti-aliasing (8-bit images only).
ModeLCAAV - Debayering with horizontal and vertical local color anti-aliasing
( 8-bit images only).
ModeYuv422 - Debayering with YUV422-alike sub-sampling (8-bit images only).
"""
Mode2x2 = VmbDebayerMode.Mode_2x2
Mode3x3 = VmbDebayerMode.Mode_3x3
ModeLCAA = VmbDebayerMode.Mode_LCAA
ModeLCAAV = VmbDebayerMode.Mode_LCAAV
ModeYuv422 = VmbDebayerMode.Mode_YUV422
def __str__(self):
return 'DebayerMode.{}'.format(self._name_)
def __repr__(self):
return str(self)
class FrameStatus(enum.IntEnum):
"""Enum specifying the current status of internal Frame data.
Enum values:
Complete - Frame data is complete without errors.
Incomplete - Frame could not be filled to the end.
TooSmall - Frame buffer was too small.
Invalid - Frame buffer was invalid.
"""
Complete = VmbFrameStatus.Complete
Incomplete = VmbFrameStatus.Incomplete
TooSmall = VmbFrameStatus.TooSmall
Invalid = VmbFrameStatus.Invalid
class AllocationMode(enum.IntEnum):
"""Enum specifying the supported frame allocation modes.
Enum values:
AnnounceFrame - The buffer is allocated by VimbaPython
AllocAndAnnounceFrame - The buffer is allocated by the Transport Layer
"""
AnnounceFrame = 0
AllocAndAnnounceFrame = 1
class AncillaryData:
"""Ancillary Data are created after enabling a Cameras 'ChunkModeActive' Feature.
Ancillary Data are Features stored within a Frame.
"""
@TraceEnable()
@LeaveContextOnCall()
def __init__(self, handle: VmbFrame):
"""Do not call directly. Get Object via Frame access method"""
self.__handle: VmbFrame = handle
self.__data_handle: VmbHandle = VmbHandle()
self.__feats: FeaturesTuple = ()
self.__context_cnt: int = 0
@TraceEnable()
def __enter__(self):
if not self.__context_cnt:
self._open()
self.__context_cnt += 1
return self
@TraceEnable()
def __exit__(self, exc_type, exc_value, exc_traceback):
self.__context_cnt -= 1
if not self.__context_cnt:
self._close()
@RaiseIfOutsideContext()
def get_all_features(self) -> FeaturesTuple:
"""Get all features in ancillary data.
Returns:
A set of all currently features stored in Ancillary Data.
Raises:
RuntimeError then called outside of "with" - statement.
"""
return self.__feats
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_by_type(self, feat_type: FeatureTypeTypes) -> FeaturesTuple:
"""Get all features in ancillary data of a specific type.
Valid FeatureTypes are: IntFeature, FloatFeature, StringFeature, BoolFeature,
EnumFeature, CommandFeature, RawFeature
Arguments:
feat_type - FeatureType used find features of that type.
Returns:
A all features of type 'feat_type'.
Raises:
RuntimeError then called outside of "with" - statement.
TypeError if parameters do not match their type hint.
"""
return filter_features_by_type(self.__feats, feat_type)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_by_category(self, category: str) -> FeaturesTuple:
"""Get all features in ancillary data of a specific category.
Arguments:
category - Category that should be used for filtering.
Returns:
A all features of category 'category'.
Raises:
RuntimeError then called outside of "with" - statement.
TypeError if parameters do not match their type hint.
"""
return filter_features_by_category(self.__feats, category)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_feature_by_name(self, feat_name: str) -> FeatureTypes:
"""Get a features in ancillary data by its name.
Arguments:
feat_name - Name used to find a feature.
Returns:
Feature with the associated name.
Raises:
RuntimeError then called outside of "with" - statement.
TypeError if parameters do not match their type hint.
VimbaFeatureError if no feature is associated with 'feat_name'.
"""
feat = filter_features_by_name(self.__feats, feat_name)
if not feat:
raise VimbaFeatureError('Feature \'{}\' not found.'.format(feat_name))
return feat
@TraceEnable()
@EnterContextOnCall()
def _open(self):
call_vimba_c('VmbAncillaryDataOpen', byref(self.__handle), byref(self.__data_handle))
self.__feats = _replace_invalid_feature_calls(discover_features(self.__data_handle))
attach_feature_accessors(self, self.__feats)
@TraceEnable()
@LeaveContextOnCall()
def _close(self):
remove_feature_accessors(self, self.__feats)
self.__feats = ()
call_vimba_c('VmbAncillaryDataClose', self.__data_handle)
self.__data_handle = VmbHandle()
def _replace_invalid_feature_calls(feats: FeaturesTuple) -> FeaturesTuple:
# AncillaryData are basically "lightweight" features. Calling most feature related
# Functions with a AncillaryData - Handle leads to VimbaC Errors. This method decorates
# all Methods that are unsafe to call with a decorator raising a RuntimeError.
to_wrap = [
'get_access_mode',
'is_readable',
'is_writeable',
'register_change_handler',
'get_increment',
'get_range',
'set'
]
# Decorator raising a RuntimeError instead of delegating call to inner function.
def invalid_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
msg = 'Calling \'{}\' is invalid for AncillaryData Features.'
raise RuntimeError(msg.format(func.__name__))
return wrapper
# Replace original implementation by injecting a surrounding decorator and
# binding the resulting function as a method to the Feature instance.
for f, a in [(f, a) for f in feats for a in to_wrap]:
try:
fn = invalid_call(getattr(f, a))
setattr(f, a, fn.__get__(f))
except AttributeError:
pass
return feats
class Frame:
"""This class allows access to Frames acquired by a camera. The Frame is basically
a buffer that wraps image data and some metadata.
"""
def __init__(self, buffer_size: int, allocation_mode: AllocationMode):
"""Do not call directly. Create Frames via Camera methods instead."""
self._allocation_mode = allocation_mode
# Allocation is not necessary for the AllocAndAnnounce case. In that case the Transport
# Layer will take care of buffer allocation. The self._buffer variable will be updated after
# the frame is announced and memory has been allocated.
if self._allocation_mode == AllocationMode.AnnounceFrame:
self._buffer = (ctypes.c_ubyte * buffer_size)()
self._frame: VmbFrame = VmbFrame()
# Setup underlaying Frame
if self._allocation_mode == AllocationMode.AnnounceFrame:
self._frame.buffer = ctypes.cast(self._buffer, ctypes.c_void_p)
self._frame.bufferSize = sizeof(self._buffer)
elif self._allocation_mode == AllocationMode.AllocAndAnnounceFrame:
# Set buffer pointer to NULL and inform Transport Layer of size it should allocate
self._frame.buffer = None
self._frame.bufferSize = buffer_size
def __str__(self):
msg = 'Frame(id={}, status={}, buffer={})'
return msg.format(self._frame.frameID, str(FrameStatus(self._frame.receiveStatus)),
hex(self._frame.buffer))
def __deepcopy__(self, memo):
cls = self.__class__
result = cls.__new__(cls)
memo[id(self)] = result
# VmbFrame contains Pointers and ctypes.Structure with Pointers can't be copied.
# As a workaround VmbFrame contains a deepcopy-like Method performing deep copy of all
# Attributes except PointerTypes. Those must be set manually after the copy operation.
setattr(result, '_buffer', copy.deepcopy(self._buffer, memo))
setattr(result, '_frame', self._frame.deepcopy_skip_ptr(memo))
result._frame.buffer = ctypes.cast(result._buffer, ctypes.c_void_p)
result._frame.bufferSize = sizeof(result._buffer)
return result
def _set_buffer(self, buffer: ctypes.c_void_p):
"""Set self._buffer to memory pointed to by passed buffer pointer
Useful if frames were allocated with AllocationMode.AllocAndAnnounce
"""
self._buffer = ctypes.cast(buffer,
ctypes.POINTER(ctypes.c_ubyte * self._frame.bufferSize)).contents
def get_buffer(self) -> ctypes.Array:
"""Get internal buffer object containing image data."""
return self._buffer
def get_buffer_size(self) -> int:
"""Get byte size of internal buffer."""
return self._frame.bufferSize
def get_image_size(self) -> int:
"""Get byte size of image data stored in buffer."""
return self._frame.imageSize
def get_ancillary_data(self) -> Optional[AncillaryData]:
"""Get AncillaryData.
Frames acquired with cameras where Feature ChunkModeActive is enabled can contain
ancillary data within the image data.
Returns:
None if Frame contains no ancillary data.
AncillaryData if Frame contains ancillary data.
"""
if not self._frame.ancillarySize:
return None
return AncillaryData(self._frame)
def get_status(self) -> FrameStatus:
"""Returns current frame status."""
return FrameStatus(self._frame.receiveStatus)
def get_pixel_format(self) -> PixelFormat:
"""Get format of the acquired image data"""
return PixelFormat(self._frame.pixelFormat)
def get_height(self) -> Optional[int]:
"""Get image height in pixels.
Returns:
Image height in pixels if dimension data is provided by the camera.
None if dimension data is not provided by the camera.
"""
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
if VmbFrameFlags.Dimension not in flags:
return None
return self._frame.height
def get_width(self) -> Optional[int]:
"""Get image width in pixels.
Returns:
Image width in pixels if dimension data is provided by the camera.
None if dimension data is not provided by the camera.
"""
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
if VmbFrameFlags.Dimension not in flags:
return None
return self._frame.width
def get_offset_x(self) -> Optional[int]:
"""Get horizontal offset in pixels.
Returns:
Horizontal offset in pixel if offset data is provided by the camera.
None if offset data is not provided by the camera.
"""
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
if VmbFrameFlags.Offset not in flags:
return None
return self._frame.offsetX
def get_offset_y(self) -> Optional[int]:
"""Get vertical offset in pixels.
Returns:
Vertical offset in pixels if offset data is provided by the camera.
None if offset data is not provided by the camera.
"""
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
if VmbFrameFlags.Offset not in flags:
return None
return self._frame.offsetY
def get_id(self) -> Optional[int]:
"""Get Frame ID.
Returns:
Frame ID if the id is provided by the camera.
None if frame id is not provided by the camera.
"""
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
if VmbFrameFlags.FrameID not in flags:
return None
return self._frame.frameID
def get_timestamp(self) -> Optional[int]:
"""Get Frame timestamp.
Returns:
Timestamp if provided by the camera.
None if timestamp is not provided by the camera.
"""
flags = decode_flags(VmbFrameFlags, self._frame.receiveFlags)
if VmbFrameFlags.Timestamp not in flags:
return None
return self._frame.timestamp
@RuntimeTypeCheckEnable()
def convert_pixel_format(self, target_fmt: PixelFormat,
debayer_mode: Optional[Debayer] = None):
"""Convert internal pixel format to given format.
Note: This method allocates a new buffer for internal image data leading to some
runtime overhead. For performance reasons, it might be better to set the value
of the camera's 'PixelFormat' feature instead. In addition, a non-default debayer mode
can be specified.
Arguments:
target_fmt - PixelFormat to convert to.
debayer_mode - Non-default algorithm used to debayer images in Bayer Formats. If
no mode is specified, default debayering mode 'Mode2x2' is applied. If
the current format is no Bayer format, this parameter is silently
ignored.
Raises:
TypeError if parameters do not match their type hint.
ValueError if the current format can't be converted into 'target_fmt'. Convertible
Formats can be queried via get_convertible_formats() of PixelFormat.
AssertionError if image width or height can't be determined.
"""
global BAYER_PIXEL_FORMATS
# 1) Perform sanity checking
fmt = self.get_pixel_format()
if fmt == target_fmt:
return
if target_fmt not in fmt.get_convertible_formats():
raise ValueError('Current PixelFormat can\'t be converted into given format.')
# 2) Specify Transformation Input Image
height = self._frame.height
width = self._frame.width
c_src_image = VmbImage()
c_src_image.Size = sizeof(c_src_image)
c_src_image.Data = ctypes.cast(self._buffer, ctypes.c_void_p)
call_vimba_image_transform('VmbSetImageInfoFromPixelFormat', fmt, width, height,
byref(c_src_image))
# 3) Specify Transformation Output Image
c_dst_image = VmbImage()
c_dst_image.Size = sizeof(c_dst_image)
layout, bits = PIXEL_FORMAT_TO_LAYOUT[VmbPixelFormat(target_fmt)]
call_vimba_image_transform('VmbSetImageInfoFromInputImage', byref(c_src_image), layout,
bits, byref(c_dst_image))
# 4) Allocate Buffer and perform transformation
img_size = int(height * width * c_dst_image.ImageInfo.PixelInfo.BitsPerPixel / 8)
anc_size = self._frame.ancillarySize
buf = (ctypes.c_ubyte * (img_size + anc_size))()
c_dst_image.Data = ctypes.cast(buf, ctypes.c_void_p)
# 5) Setup Debayering mode if given.
transform_info = VmbTransformInfo()
if debayer_mode and (fmt in BAYER_PIXEL_FORMATS):
call_vimba_image_transform('VmbSetDebayerMode', VmbDebayerMode(debayer_mode),
byref(transform_info))
# 6) Perform Transformation
call_vimba_image_transform('VmbImageTransform', byref(c_src_image), byref(c_dst_image),
byref(transform_info), 1)
# 7) Copy ancillary data if existing
if anc_size:
src = ctypes.addressof(self._buffer) + self._frame.imageSize
dst = ctypes.addressof(buf) + img_size
ctypes.memmove(dst, src, anc_size)
# 8) Update frame metadata
self._buffer = buf
self._frame.buffer = ctypes.cast(self._buffer, ctypes.c_void_p)
self._frame.bufferSize = sizeof(self._buffer)
self._frame.imageSize = img_size
self._frame.pixelFormat = target_fmt
def as_numpy_ndarray(self) -> 'numpy.ndarray':
"""Construct numpy.ndarray view on VimbaFrame.
Returns:
numpy.ndarray on internal image buffer.
Raises:
ImportError if numpy is not installed.
VimbaFrameError if current PixelFormat can't be converted to a numpy.ndarray.
"""
if numpy is None:
raise ImportError('\'Frame.as_opencv_image()\' requires module \'numpy\'.')
# Construct numpy overlay on underlaying image buffer
height = self._frame.height
width = self._frame.width
fmt = self._frame.pixelFormat
c_image = VmbImage()
c_image.Size = sizeof(c_image)
call_vimba_image_transform('VmbSetImageInfoFromPixelFormat', fmt, width, height,
byref(c_image))
layout = PIXEL_FORMAT_TO_LAYOUT.get(fmt)
if not layout:
msg = 'Can\'t construct numpy.ndarray for Pixelformat {}. ' \
'Use \'frame.convert_pixel_format()\' to convert to a different Pixelformat.'
raise VimbaFrameError(msg.format(str(self.get_pixel_format())))
bits_per_channel = layout[1]
channels_per_pixel = c_image.ImageInfo.PixelInfo.BitsPerPixel // bits_per_channel
return numpy.ndarray(shape=(height, width, channels_per_pixel),
buffer=self._buffer, # type: ignore
dtype=numpy.uint8 if bits_per_channel == 8 else numpy.uint16)
def as_opencv_image(self) -> 'numpy.ndarray':
"""Construct OpenCV compatible view on VimbaFrame.
Returns:
OpenCV compatible numpy.ndarray
Raises:
ImportError if numpy is not installed.
ValueError if current pixel format is not compatible with opencv. Compatible
formats are in OPENCV_PIXEL_FORMATS.
"""
global OPENCV_PIXEL_FORMATS
if numpy is None:
raise ImportError('\'Frame.as_opencv_image()\' requires module \'numpy\'.')
fmt = self._frame.pixelFormat
if fmt not in OPENCV_PIXEL_FORMATS:
raise ValueError('Current Format \'{}\' is not in OPENCV_PIXEL_FORMATS'.format(
str(PixelFormat(self._frame.pixelFormat))))
return self.as_numpy_ndarray()
@TraceEnable()
@RuntimeTypeCheckEnable()
def intersect_pixel_formats(fmts1: FormatTuple, fmts2: FormatTuple) -> FormatTuple:
"""Build intersection of two sets containing PixelFormat.
Arguments:
fmts1 - PixelFormats to intersect with fmts2
fmts2 - PixelFormats to intersect with fmts1
Returns:
Set of PixelFormats that occur in fmts1 and fmts2
Raises:
TypeError if parameters do not match their type hint.
"""
return tuple(set(fmts1).intersection(set(fmts2)))

View File

@@ -0,0 +1,391 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import enum
from typing import Tuple, List, Callable, Dict
from .c_binding import call_vimba_c, byref, sizeof, decode_cstr
from .c_binding import VmbInterface, VmbInterfaceInfo, VmbHandle, VmbUint32
from .feature import discover_features, FeatureTypes, FeaturesTuple, FeatureTypeTypes
from .shared import filter_features_by_name, filter_features_by_type, filter_affected_features, \
filter_selected_features, filter_features_by_category, \
attach_feature_accessors, remove_feature_accessors, read_memory, \
write_memory, read_registers, write_registers
from .util import TraceEnable, RuntimeTypeCheckEnable, EnterContextOnCall, LeaveContextOnCall, \
RaiseIfOutsideContext
from .error import VimbaFeatureError
__all__ = [
'InterfaceType',
'Interface',
'InterfaceEvent',
'InterfaceChangeHandler',
'InterfacesTuple',
'InterfacesList',
'discover_interfaces',
'discover_interface'
]
# Forward declarations
InterfaceChangeHandler = Callable[['Interface', 'InterfaceEvent'], None]
InterfacesTuple = Tuple['Interface', ...]
InterfacesList = List['Interface']
class InterfaceType(enum.IntEnum):
"""Enum specifying all interface types.
Enum values:
Unknown - Interface is not known to this VimbaPython version.
Firewire - 1394
Ethernet - Gigabit Ethernet
Usb - USB 3.0
CL - Camera Link
CSI2 - CSI-2
"""
Unknown = VmbInterface.Unknown
Firewire = VmbInterface.Firewire
Ethernet = VmbInterface.Ethernet
Usb = VmbInterface.Usb
CL = VmbInterface.CL
CSI2 = VmbInterface.CSI2
class InterfaceEvent(enum.IntEnum):
"""Enum specifying an Interface Event
Enum values:
Missing - A known interface disappeared from the bus
Detected - A new interface was discovered
Reachable - A known interface can be accessed
Unreachable - A known interface cannot be accessed anymore
"""
Missing = 0
Detected = 1
Reachable = 2
Unreachable = 3
class Interface:
"""This class allows access to an interface such as USB detected by Vimba.
Interface is meant to be used in conjunction with the "with" - statement. On entering a context,
all Interface features are detected and can be accessed within the context. Static Interface
properties like Name can be accessed outside the context.
"""
@TraceEnable()
@LeaveContextOnCall()
def __init__(self, info: VmbInterfaceInfo):
"""Do not call directly. Access Interfaces via vimba.Vimba instead."""
self.__handle: VmbHandle = VmbHandle(0)
self.__info: VmbInterfaceInfo = info
self.__feats: FeaturesTuple = ()
self.__context_cnt: int = 0
@TraceEnable()
def __enter__(self):
if not self.__context_cnt:
self._open()
self.__context_cnt += 1
return self
@TraceEnable()
def __exit__(self, exc_type, exc_value, exc_traceback):
self.__context_cnt -= 1
if not self.__context_cnt:
self._close()
def __str__(self):
return 'Interface(id={})'.format(self.get_id())
def __repr__(self):
rep = 'Interface'
rep += '(__handle=' + repr(self.__handle)
rep += ',__info=' + repr(self.__info)
rep += ')'
return rep
def get_id(self) -> str:
"""Get Interface Id such as VimbaUSBInterface_0x0."""
return decode_cstr(self.__info.interfaceIdString)
def get_type(self) -> InterfaceType:
"""Get Interface Type such as InterfaceType.Usb."""
return InterfaceType(self.__info.interfaceType)
def get_name(self) -> str:
"""Get Interface Name such as Vimba USB Interface."""
return decode_cstr(self.__info.interfaceName)
def get_serial(self) -> str:
"""Get Interface Serial or '' if not set."""
return decode_cstr(self.__info.serialString)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def read_memory(self, addr: int, max_bytes: int) -> bytes: # coverage: skip
"""Read a byte sequence from a given memory address.
Arguments:
addr: Starting address to read from.
max_bytes: Maximum number of bytes to read from addr.
Returns:
Read memory contents as bytes.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
ValueError if addr is negative.
ValueError if max_bytes is negative.
ValueError if the memory access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return read_memory(self.__handle, addr, max_bytes)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def write_memory(self, addr: int, data: bytes): # coverage: skip
"""Write a byte sequence to a given memory address.
Arguments:
addr: Address to write the content of 'data' to.
data: Byte sequence to write at address 'addr'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
ValueError if addr is negative.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return write_memory(self.__handle, addr, data)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def read_registers(self, addrs: Tuple[int, ...]) -> Dict[int, int]: # coverage: skip
"""Read contents of multiple registers.
Arguments:
addrs: Sequence of addresses that should be read iteratively.
Returns:
Dictionary containing a mapping from given address to the read register values.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
ValueError if any address in addrs is negative.
ValueError if the register access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return read_registers(self.__handle, addrs)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def write_registers(self, addrs_values: Dict[int, int]): # coverage: skip
"""Write data to multiple registers.
Arguments:
addrs_values: Mapping between register addresses and the data to write.
Raises:
TypeError if parameters do not match their type hint.
ValueError if any address in addrs_values is negative.
ValueError if the register access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return write_registers(self.__handle, addrs_values)
@RaiseIfOutsideContext()
def get_all_features(self) -> FeaturesTuple:
"""Get access to all discovered features of this Interface.
Returns:
A set of all currently detected features.
Raises:
RuntimeError if called outside "with" - statement.
"""
return self.__feats
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_affected_by(self, feat: FeatureTypes) -> FeaturesTuple:
"""Get all features affected by a specific interface feature.
Arguments:
feat - Feature to find features that are affected by 'feat'.
Returns:
A set of features affected by changes on 'feat'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
VimbaFeatureError if 'feat' is not a feature of this interface.
"""
return filter_affected_features(self.__feats, feat)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_selected_by(self, feat: FeatureTypes) -> FeaturesTuple:
"""Get all features selected by a specific interface feature.
Arguments:
feat - Feature to find features that are selected by 'feat'.
Returns:
A set of features selected by changes on 'feat'.
Raises:
TypeError if 'feat' is not of any feature type.
RuntimeError if called outside "with" - statement.
VimbaFeatureError if 'feat' is not a feature of this interface.
"""
return filter_selected_features(self.__feats, feat)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_by_type(self, feat_type: FeatureTypeTypes) -> FeaturesTuple:
"""Get all interface features of a specific feature type.
Valid FeatureTypes are: IntFeature, FloatFeature, StringFeature, BoolFeature,
EnumFeature, CommandFeature, RawFeature
Arguments:
feat_type - FeatureType used find features of that type.
Returns:
A set of features of type 'feat_type'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
"""
return filter_features_by_type(self.__feats, feat_type)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_by_category(self, category: str) -> FeaturesTuple:
"""Get all interface features of a specific category.
Arguments:
category - category for filtering.
Returns:
A set of features of category 'category'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
"""
return filter_features_by_category(self.__feats, category)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_feature_by_name(self, feat_name: str) -> FeatureTypes:
"""Get an interface feature by its name.
Arguments:
feat_name - Name to find a feature.
Returns:
Feature with the associated name.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called outside "with" - statement.
VimbaFeatureError if no feature is associated with 'feat_name'.
"""
feat = filter_features_by_name(self.__feats, feat_name)
if not feat:
raise VimbaFeatureError('Feature \'{}\' not found.'.format(feat_name))
return feat
@TraceEnable()
@EnterContextOnCall()
def _open(self):
call_vimba_c('VmbInterfaceOpen', self.__info.interfaceIdString, byref(self.__handle))
self.__feats = discover_features(self.__handle)
attach_feature_accessors(self, self.__feats)
@TraceEnable()
@LeaveContextOnCall()
def _close(self):
for feat in self.__feats:
feat.unregister_all_change_handlers()
remove_feature_accessors(self, self.__feats)
self.__feats = ()
call_vimba_c('VmbInterfaceClose', self.__handle)
self.__handle = VmbHandle(0)
@TraceEnable()
def discover_interfaces() -> InterfacesList:
"""Do not call directly. Access Interfaces via vimba.System instead."""
result = []
inters_count = VmbUint32(0)
call_vimba_c('VmbInterfacesList', None, 0, byref(inters_count), sizeof(VmbInterfaceInfo))
if inters_count:
inters_found = VmbUint32(0)
inters_infos = (VmbInterfaceInfo * inters_count.value)()
call_vimba_c('VmbInterfacesList', inters_infos, inters_count, byref(inters_found),
sizeof(VmbInterfaceInfo))
for info in inters_infos[:inters_found.value]:
result.append(Interface(info))
return result
@TraceEnable()
def discover_interface(id_: str) -> Interface:
"""Do not call directly. Access Interfaces via vimba.System instead."""
# Since there is no function to query a single interface, discover all interfaces and
# extract the Interface with the matching ID.
inters = discover_interfaces()
return [i for i in inters if id_ == i.get_id()].pop()

View File

@@ -0,0 +1,357 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import itertools
from typing import Dict, Tuple
from .c_binding import VmbUint32, VmbUint64, VmbHandle, VmbFeatureInfo
from .c_binding import call_vimba_c, byref, sizeof, create_string_buffer, VimbaCError
from .feature import FeaturesTuple, FeatureTypes, FeatureTypeTypes
from .error import VimbaFeatureError
from .util import TraceEnable
__all__ = [
'filter_affected_features',
'filter_selected_features',
'filter_features_by_name',
'filter_features_by_type',
'filter_features_by_category',
'attach_feature_accessors',
'remove_feature_accessors',
'read_memory',
'write_memory',
'read_registers',
'write_registers'
]
@TraceEnable()
def filter_affected_features(feats: FeaturesTuple, feat: FeatureTypes) -> FeaturesTuple:
"""Search for all Features affected by a given feature within a feature set.
Arguments:
feats: Feature set to search in.
feat: Feature that might affect Features within 'feats'.
Returns:
A set of all features that are affected by 'feat'.
Raises:
VimbaFeatureError if 'feat' is not stored within 'feats'.
"""
if feat not in feats:
raise VimbaFeatureError('Feature \'{}\' not in given Features'.format(feat.get_name()))
result = []
if feat.has_affected_features():
feats_count = VmbUint32()
feats_handle = feat._handle
feats_name = feat._info.name
# Query affected features from given Feature
call_vimba_c('VmbFeatureListAffected', feats_handle, feats_name, None, 0,
byref(feats_count), sizeof(VmbFeatureInfo))
feats_found = VmbUint32(0)
feats_infos = (VmbFeatureInfo * feats_count.value)()
call_vimba_c('VmbFeatureListAffected', feats_handle, feats_name, feats_infos, feats_count,
byref(feats_found), sizeof(VmbFeatureInfo))
# Search affected features in given feature set
for info, feature in itertools.product(feats_infos[:feats_found.value], feats):
if info.name == feature._info.name:
result.append(feature)
return tuple(result)
@TraceEnable()
def filter_selected_features(feats: FeaturesTuple, feat: FeatureTypes) -> FeaturesTuple:
"""Search for all Features selected by a given feature within a feature set.
Arguments:
feats: Feature set to search in.
feat: Feature that might select Features within 'feats'.
Returns:
A set of all features that are selected by 'feat'.
Raises:
VimbaFeatureError if 'feat' is not stored within 'feats'.
"""
if feat not in feats:
raise VimbaFeatureError('Feature \'{}\' not in given Features'.format(feat.get_name()))
result = []
if feat.has_selected_features():
feats_count = VmbUint32()
feats_handle = feat._handle
feats_name = feat._info.name
# Query selected features from given feature
call_vimba_c('VmbFeatureListSelected', feats_handle, feats_name, None, 0,
byref(feats_count), sizeof(VmbFeatureInfo))
feats_found = VmbUint32(0)
feats_infos = (VmbFeatureInfo * feats_count.value)()
call_vimba_c('VmbFeatureListSelected', feats_handle, feats_name, feats_infos, feats_count,
byref(feats_found), sizeof(VmbFeatureInfo))
# Search selected features in given feature set
for info, feature in itertools.product(feats_infos[:feats_found.value], feats):
if info.name == feature._info.name:
result.append(feature)
return tuple(result)
@TraceEnable()
def filter_features_by_name(feats: FeaturesTuple, feat_name: str):
"""Search for a feature with a specific name within a feature set.
Arguments:
feats: Feature set to search in.
feat_name: Feature name to look for.
Returns:
The Feature with the name 'feat_name' or None if lookup failed
"""
filtered = [feat for feat in feats if feat_name == feat.get_name()]
return filtered.pop() if filtered else None
@TraceEnable()
def filter_features_by_type(feats: FeaturesTuple, feat_type: FeatureTypeTypes) -> FeaturesTuple:
"""Search for all features with a specific type within a given feature set.
Arguments:
feats: Feature set to search in.
feat_type: Feature Type to search for
Returns:
A set of all features of type 'feat_type' in 'feats'. If no matching type is found an
empty set is returned.
"""
return tuple([feat for feat in feats if type(feat) == feat_type])
@TraceEnable()
def filter_features_by_category(feats: FeaturesTuple, category: str) -> FeaturesTuple:
"""Search for all features of a given category.
Arguments:
feats: Feature set to search in.
category: Category to filter for
Returns:
A set of all features of category 'category' in 'feats'. If no matching type is found an
empty set is returned.
"""
return tuple([feat for feat in feats if feat.get_category() == category])
@TraceEnable()
def attach_feature_accessors(obj, feats: FeaturesTuple):
"""Attach all Features in feats to obj under the feature name.
Arguments:
obj: Object feats should be attached on.
feats: Features to attach.
"""
BLACKLIST = (
'PixelFormat', # PixelFormats have special access methods.
)
for feat in feats:
feat_name = feat.get_name()
if feat_name not in BLACKLIST:
setattr(obj, feat_name, feat)
@TraceEnable()
def remove_feature_accessors(obj, feats: FeaturesTuple):
"""Remove all Features in feats from obj.
Arguments:
obj: Object, feats should be removed from.
feats: Features to remove.
"""
for feat in feats:
try:
delattr(obj, feat.get_name())
except AttributeError:
pass
@TraceEnable()
def read_memory(handle: VmbHandle, addr: int, max_bytes: int) -> bytes: # coverage: skip
"""Read a byte sequence from a given memory address.
Arguments:
handle: Handle on entity that allows raw memory access.
addr: Starting address to read from.
max_bytes: Maximum number of bytes to read from addr.
Returns:
Read memory contents as bytes.
Raises:
ValueError if addr is negative
ValueError if max_bytes is negative.
ValueError if the memory access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
_verify_addr(addr)
_verify_size(max_bytes)
buf = create_string_buffer(max_bytes)
bytesRead = VmbUint32()
try:
call_vimba_c('VmbMemoryRead', handle, addr, max_bytes, buf, byref(bytesRead))
except VimbaCError as e:
msg = 'Memory read access at {} failed with C-Error: {}.'
raise ValueError(msg.format(hex(addr), repr(e.get_error_code()))) from e
return buf.raw[:bytesRead.value]
@TraceEnable()
def write_memory(handle: VmbHandle, addr: int, data: bytes): # coverage: skip
""" Write a byte sequence to a given memory address.
Arguments:
handle: Handle on entity that allows raw memory access.
addr: Address to write the content of 'data' too.
data: Byte sequence to write at address 'addr'.
Raises:
ValueError if addr is negative.
ValueError if the memory access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
_verify_addr(addr)
bytesWrite = VmbUint32()
try:
call_vimba_c('VmbMemoryWrite', handle, addr, len(data), data, byref(bytesWrite))
except VimbaCError as e:
msg = 'Memory write access at {} failed with C-Error: {}.'
raise ValueError(msg.format(hex(addr), repr(e.get_error_code()))) from e
@TraceEnable()
def read_registers(handle: VmbHandle, addrs: Tuple[int, ...]) -> Dict[int, int]: # coverage: skip
"""Read contents of multiple registers.
Arguments:
handle: Handle on entity providing registers to access.
addrs: Sequence of addresses that should be read iteratively.
Return:
Dictionary containing a mapping from given address to the read register values.
Raises:
ValueError if any address in addrs is negative.
ValueError if the register access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
for addr in addrs:
_verify_addr(addr)
size = len(addrs)
valid_reads = VmbUint32()
c_addrs = (VmbUint64 * size)()
c_values = (VmbUint64 * size)()
for i, addr in enumerate(addrs):
c_addrs[i] = addr
try:
call_vimba_c('VmbRegistersRead', handle, size, c_addrs, c_values, byref(valid_reads))
except VimbaCError as e:
msg = 'Register read access failed with C-Error: {}.'
raise ValueError(msg.format(repr(e.get_error_code()))) from e
return dict(zip(c_addrs, c_values))
@TraceEnable()
def write_registers(handle: VmbHandle, addrs_values: Dict[int, int]): # coverage: skip
"""Write data to multiple Registers.
Arguments:
handle: Handle on entity providing registers to access.
addrs_values: Mapping between Register addresses and the data to write.
Raises:
ValueError if any address in addrs_values is negative.
ValueError if the register access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
for addr in addrs_values:
_verify_addr(addr)
size = len(addrs_values)
valid_writes = VmbUint32()
addrs = (VmbUint64 * size)()
values = (VmbUint64 * size)()
for i, addr in enumerate(addrs_values):
addrs[i] = addr
values[i] = addrs_values[addr]
try:
call_vimba_c('VmbRegistersWrite', handle, size, addrs, values, byref(valid_writes))
except VimbaCError as e:
msg = 'Register write access failed with C-Error: {}.'
raise ValueError(msg.format(repr(e.get_error_code()))) from e
def _verify_addr(addr: int): # coverage: skip
# Note: Coverage is skipped. Function is untestable in a generic way.
if addr < 0:
raise ValueError('Given Address {} is negative'.format(addr))
def _verify_size(size: int): # coverage: skip
# Note: Coverage is skipped. Function is untestable in a generic way.
if size < 0:
raise ValueError('Given size {} is negative'.format(size))

View File

@@ -0,0 +1,72 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
# Suppress 'imported but unused' - Error from static style checker.
# flake8: noqa: F401
__all__ = [
'LogLevel',
'LogConfig',
'Log',
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
'LOG_CONFIG_TRACE_FILE_ONLY',
'LOG_CONFIG_TRACE',
'LOG_CONFIG_INFO_CONSOLE_ONLY',
'LOG_CONFIG_INFO_FILE_ONLY',
'LOG_CONFIG_INFO',
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
'LOG_CONFIG_WARNING_FILE_ONLY',
'LOG_CONFIG_WARNING',
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
'LOG_CONFIG_ERROR_FILE_ONLY',
'LOG_CONFIG_ERROR',
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
'LOG_CONFIG_CRITICAL_FILE_ONLY',
'LOG_CONFIG_CRITICAL',
# Decorators
'TraceEnable',
'ScopedLogEnable',
'RuntimeTypeCheckEnable',
'EnterContextOnCall',
'LeaveContextOnCall',
'RaiseIfInsideContext',
'RaiseIfOutsideContext'
]
from .log import Log, LogLevel, LogConfig, LOG_CONFIG_TRACE_CONSOLE_ONLY, \
LOG_CONFIG_TRACE_FILE_ONLY, LOG_CONFIG_TRACE, LOG_CONFIG_INFO_CONSOLE_ONLY, \
LOG_CONFIG_INFO_FILE_ONLY, LOG_CONFIG_INFO, LOG_CONFIG_WARNING_CONSOLE_ONLY, \
LOG_CONFIG_WARNING_FILE_ONLY, LOG_CONFIG_WARNING, LOG_CONFIG_ERROR_CONSOLE_ONLY, \
LOG_CONFIG_ERROR_FILE_ONLY, LOG_CONFIG_ERROR, LOG_CONFIG_CRITICAL_CONSOLE_ONLY, \
LOG_CONFIG_CRITICAL_FILE_ONLY, LOG_CONFIG_CRITICAL
from .tracer import TraceEnable
from .scoped_log import ScopedLogEnable
from .runtime_type_check import RuntimeTypeCheckEnable
from .context_decorator import EnterContextOnCall, LeaveContextOnCall, RaiseIfInsideContext, \
RaiseIfOutsideContext

View File

@@ -0,0 +1,96 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import functools
__all__ = [
'EnterContextOnCall',
'LeaveContextOnCall',
'RaiseIfInsideContext',
'RaiseIfOutsideContext'
]
class EnterContextOnCall:
"""Decorator setting/injecting flag used for checking the context."""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
args[0]._context_entered = True
return func(*args, **kwargs)
return wrapper
class LeaveContextOnCall:
"""Decorator clearing/injecting flag used for checking the context."""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
args[0]._context_entered = False
return result
return wrapper
class RaiseIfInsideContext:
"""Raising RuntimeError is decorated Method is called inside with-statement.
Note This Decorator shall work only on Object implementing a Context Manger.
For this to work object must offer a boolean attribute called _context_entered
"""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if args[0]._context_entered:
msg = 'Called \'{}()\' inside of \'with\' - statement scope.'
msg = msg.format('{}'.format(func.__qualname__))
raise RuntimeError(msg)
return func(*args, **kwargs)
return wrapper
class RaiseIfOutsideContext:
"""Raising RuntimeError is decorated Method is called outside with-statement.
Note This Decorator shall work only on Object implementing a Context Manger.
For this to work object must offer a boolean attribute called __context_entered
"""
def __call__(self, func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
if not args[0]._context_entered:
msg = 'Called \'{}()\' outside of \'with\' - statement scope.'
msg = msg.format('{}'.format(func.__qualname__))
raise RuntimeError(msg)
return func(*args, **kwargs)
return wrapper

View File

@@ -0,0 +1,295 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import os
import enum
import datetime
import logging
from typing import List, Optional
__all__ = [
'LogLevel',
'LogConfig',
'Log',
'LOG_CONFIG_TRACE_CONSOLE_ONLY',
'LOG_CONFIG_TRACE_FILE_ONLY',
'LOG_CONFIG_TRACE',
'LOG_CONFIG_INFO_CONSOLE_ONLY',
'LOG_CONFIG_INFO_FILE_ONLY',
'LOG_CONFIG_INFO',
'LOG_CONFIG_WARNING_CONSOLE_ONLY',
'LOG_CONFIG_WARNING_FILE_ONLY',
'LOG_CONFIG_WARNING',
'LOG_CONFIG_ERROR_CONSOLE_ONLY',
'LOG_CONFIG_ERROR_FILE_ONLY',
'LOG_CONFIG_ERROR',
'LOG_CONFIG_CRITICAL_CONSOLE_ONLY',
'LOG_CONFIG_CRITICAL_FILE_ONLY',
'LOG_CONFIG_CRITICAL'
]
class LogLevel(enum.IntEnum):
"""Enum containing all LogLevels.
Enum values are:
Trace - Show Tracing information. Show all messages.
Info - Show Informational, Warning, Error, and Critical Events.
Warning - Show Warning, Error, and Critical Events.
Error - Show Errors and Critical Events.
Critical - Show Critical Events only.
"""
Trace = logging.DEBUG
Info = logging.INFO
Warning = logging.WARNING
Error = logging.ERROR
Critical = logging.CRITICAL
def __str__(self):
return self._name_
def as_equal_len_str(self) -> str:
return _LEVEL_TO_EQUAL_LEN_STR[self]
_LEVEL_TO_EQUAL_LEN_STR = {
LogLevel.Trace: 'Trace ',
LogLevel.Info: 'Info ',
LogLevel.Warning: 'Warning ',
LogLevel.Error: 'Error ',
LogLevel.Critical: 'Critical'
}
class LogConfig:
"""The LogConfig is a builder to configure various specialized logging configurations.
The constructed LogConfig must set via vimba.Vimba or the ScopedLogEnable Decorator
to start logging.
"""
__ENTRY_FORMAT = logging.Formatter('%(asctime)s | %(message)s')
def __init__(self):
self.__handlers: List[logging.Handler] = []
self.__max_msg_length: Optional[int] = None
def add_file_log(self, level: LogLevel) -> 'LogConfig':
"""Add a new Log file to the Config Builder.
Arguments:
level: LogLevel of the added log file.
Returns:
Reference to the LogConfig instance (builder pattern).
"""
log_ts = datetime.datetime.today().strftime('%Y-%m-%d_%H-%M-%S')
log_file = 'VimbaPython_{}_{}.log'.format(log_ts, str(level))
log_file = os.path.join(os.getcwd(), log_file)
handler = logging.FileHandler(log_file, delay=True)
handler.setLevel(level)
handler.setFormatter(LogConfig.__ENTRY_FORMAT)
self.__handlers.append(handler)
return self
def add_console_log(self, level: LogLevel) -> 'LogConfig':
"""Add a new Console Log to the Config Builder.
Arguments:
level: LogLevel of the added console log file.
Returns:
Reference to the LogConfig instance (builder pattern).
"""
handler = logging.StreamHandler()
handler.setLevel(level)
handler.setFormatter(LogConfig.__ENTRY_FORMAT)
self.__handlers.append(handler)
return self
def set_max_msg_length(self, max_msg_length: int):
"""Set max length of a log entry. Messages longer than this entry will be cut off."""
self.__max_msg_length = max_msg_length
def get_max_msg_length(self) -> Optional[int]:
"""Get configured max message length"""
return self.__max_msg_length
def get_handlers(self) -> List[logging.Handler]:
"""Get all configured log handlers"""
return self.__handlers
class Log:
class __Impl:
"""This class is wraps the logging Facility. Since this is as Singleton
Use Log.get_instace(), to access the log.
"""
def __init__(self):
"""Do not call directly. Use Log.get_instance() instead."""
self.__logger: Optional[logging.Logger] = None
self.__config: Optional[LogConfig] = None
self._test_buffer: Optional[List[str]] = None
def __bool__(self):
return bool(self.__logger)
def enable(self, config: LogConfig):
"""Enable global VimbaPython logging mechanism.
Arguments:
config: The configuration to apply.
"""
self.disable()
logger = logging.getLogger('VimbaPythonLog')
logger.setLevel(logging.DEBUG)
for handler in config.get_handlers():
logger.addHandler(handler)
self.__config = config
self.__logger = logger
def disable(self):
"""Disable global VimbaPython logging mechanism."""
if self.__logger and self.__config:
for handler in self.__config.get_handlers():
handler.close()
self.__logger.removeHandler(handler)
self.__logger = None
self.__config = None
def get_config(self) -> Optional[LogConfig]:
""" Get log configuration
Returns:
Configuration if the log is enabled. In case the log is disabled return None.
"""
return self.__config
def trace(self, msg: str):
"""Add an entry of LogLevel.Trace to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.debug(self.__build_msg(LogLevel.Trace, msg))
def info(self, msg: str):
"""Add an entry of LogLevel.Info to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.info(self.__build_msg(LogLevel.Info, msg))
def warning(self, msg: str):
"""Add an entry of LogLevel.Warning to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.warning(self.__build_msg(LogLevel.Warning, msg))
def error(self, msg: str):
"""Add an entry of LogLevel.Error to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.error(self.__build_msg(LogLevel.Error, msg))
def critical(self, msg: str):
"""Add an entry of LogLevel.Critical to the log. Does nothing is the log is disabled.
Arguments:
msg - The message that should be added to the Log.
"""
if self.__logger:
self.__logger.critical(self.__build_msg(LogLevel.Critical, msg))
def __build_msg(self, loglevel: LogLevel, msg: str) -> str:
msg = '{} | {}'.format(loglevel.as_equal_len_str(), msg)
max_len = self.__config.get_max_msg_length() if self.__config else None
if max_len and (max_len < len(msg)):
suffix = ' ...'
msg = msg[:max_len - len(suffix)] + suffix
if self._test_buffer is not None:
self._test_buffer.append(msg)
return msg
__instance = __Impl()
@staticmethod
def get_instance() -> '__Impl':
"""Get Log instance."""
return Log.__instance
def _build_cfg(console_level: Optional[LogLevel], file_level: Optional[LogLevel]) -> LogConfig:
cfg = LogConfig()
cfg.set_max_msg_length(200)
if console_level:
cfg.add_console_log(console_level)
if file_level:
cfg.add_file_log(file_level)
return cfg
# Exported Default Log configurations.
LOG_CONFIG_TRACE_CONSOLE_ONLY = _build_cfg(LogLevel.Trace, None)
LOG_CONFIG_TRACE_FILE_ONLY = _build_cfg(None, LogLevel.Trace)
LOG_CONFIG_TRACE = _build_cfg(LogLevel.Trace, LogLevel.Trace)
LOG_CONFIG_INFO_CONSOLE_ONLY = _build_cfg(LogLevel.Info, None)
LOG_CONFIG_INFO_FILE_ONLY = _build_cfg(None, LogLevel.Info)
LOG_CONFIG_INFO = _build_cfg(LogLevel.Info, LogLevel.Info)
LOG_CONFIG_WARNING_CONSOLE_ONLY = _build_cfg(LogLevel.Warning, None)
LOG_CONFIG_WARNING_FILE_ONLY = _build_cfg(None, LogLevel.Warning)
LOG_CONFIG_WARNING = _build_cfg(LogLevel.Warning, LogLevel.Warning)
LOG_CONFIG_ERROR_CONSOLE_ONLY = _build_cfg(LogLevel.Error, None)
LOG_CONFIG_ERROR_FILE_ONLY = _build_cfg(None, LogLevel.Error)
LOG_CONFIG_ERROR = _build_cfg(LogLevel.Error, LogLevel.Error)
LOG_CONFIG_CRITICAL_CONSOLE_ONLY = _build_cfg(LogLevel.Critical, None)
LOG_CONFIG_CRITICAL_FILE_ONLY = _build_cfg(None, LogLevel.Critical)
LOG_CONFIG_CRITICAL = _build_cfg(LogLevel.Critical, LogLevel.Critical)

View File

@@ -0,0 +1,223 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import collections.abc
from inspect import isfunction, ismethod, signature
from functools import wraps
from typing import get_type_hints, Union
from .log import Log
__all__ = [
'RuntimeTypeCheckEnable'
]
class RuntimeTypeCheckEnable:
"""Decorator adding runtime type checking to the wrapped callable.
Each time the callable is executed, all arguments are checked if they match with the given
type hints. If all checks are passed, the wrapped function is executed, if the given
arguments to not match a TypeError is raised.
Note: This decorator is no replacement for a feature complete TypeChecker. It supports only
a subset of all types expressible by type hints.
"""
_log = Log.get_instance()
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
full_args, hints = self.__dismantle_sig(func, *args, **kwargs)
for arg_name in hints:
self.__verify_arg(func, hints[arg_name], (arg_name, full_args[arg_name]))
return func(*args, **kwargs)
return wrapper
def __dismantle_sig(self, func, *args, **kwargs):
# Get merge args, kwargs and defaults to complete argument list.
full_args = signature(func).bind(*args, **kwargs)
full_args.apply_defaults()
# Get available type hints, remove return value.
hints = get_type_hints(func)
hints.pop('return', None)
return (full_args.arguments, hints)
def __verify_arg(self, func, type_hint, arg_spec):
arg_name, arg = arg_spec
if (self.__matches(type_hint, arg)):
return
msg = '\'{}\' called with unexpected argument type. Argument\'{}\'. Expected type: {}.'
msg = msg.format(func.__qualname__, arg_name, type_hint)
RuntimeTypeCheckEnable._log.error(msg)
raise TypeError(msg)
def __matches(self, type_hint, arg) -> bool:
if self.__matches_base_types(type_hint, arg):
return True
elif self.__matches_type_types(type_hint, arg):
return True
elif self.__matches_union_types(type_hint, arg):
return True
elif self.__matches_tuple_types(type_hint, arg):
return True
elif self.__matches_dict_types(type_hint, arg):
return True
else:
return self.__matches_callable(type_hint, arg)
def __matches_base_types(self, type_hint, arg) -> bool:
return type_hint == type(arg)
def __matches_type_types(self, type_hint, arg) -> bool:
try:
if not type_hint.__origin__ == type:
return False
hint_args = type_hint.__args__
except AttributeError:
return False
return arg in hint_args
def __matches_union_types(self, type_hint, arg) -> bool:
try:
if not type_hint.__origin__ == Union:
return False
except AttributeError:
return False
# If Matches if true for an Union hint:
for hint in type_hint.__args__:
if self.__matches(hint, arg):
return True
return False
def __matches_tuple_types(self, type_hint, arg) -> bool:
try:
if not (type_hint.__origin__ == tuple and type(arg) == tuple):
return False
except AttributeError:
return False
if arg == ():
return True
if Ellipsis in type_hint.__args__:
fn = self.__matches_var_length_tuple
else:
fn = self.__matches_fixed_size_tuple
return fn(type_hint, arg)
def __matches_fixed_size_tuple(self, type_hint, arg) -> bool:
# To pass, the entire tuple must match in length and all types
expand_hint = type_hint.__args__
if len(expand_hint) != len(arg):
return False
for hint, value in zip(expand_hint, arg):
if not self.__matches(hint, value):
return False
return True
def __matches_var_length_tuple(self, type_hint, arg) -> bool:
# To pass a tuple can be empty or all contents must match the given type.
hint, _ = type_hint.__args__
for value in arg:
if not self.__matches(hint, value):
return False
return True
def __matches_dict_types(self, type_hint, arg) -> bool:
# To pass the hint must be a Dictionary and arg must match the given types.
try:
if not (type_hint.__origin__ == dict and type(arg) == dict):
return False
except AttributeError:
return False
key_type, val_type = type_hint.__args__
for k, v in arg.items():
if type(k) != key_type or type(v) != val_type:
return False
return True
def __matches_callable(self, type_hint, arg) -> bool:
# Return if the given hint is no callable
try:
if not type_hint.__origin__ == collections.abc.Callable:
return False
except AttributeError:
return False
# Verify that are is some form of callable.:
# 1) Check if it is either a function or a method
# 2) If it is an object, check if it has a __call__ method. If so use call for checks.
if not (isfunction(arg) or ismethod(arg)):
try:
arg = getattr(arg, '__call__')
except AttributeError:
return False
# Examine signature of given callable
sig_args = signature(arg).parameters
hint_args = type_hint.__args__
# Verify Parameter list length
if len(sig_args) != len(hint_args[:-1]):
return False
return True

View File

@@ -0,0 +1,80 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from functools import wraps
from typing import Any, Callable, Tuple, Optional
from .log import LogConfig, Log
__all__ = [
'ScopedLogEnable'
]
class _ScopedLog:
__log = Log.get_instance()
def __init__(self, config: LogConfig):
self.__config: LogConfig = config
self.__old_config: Optional[LogConfig] = None
def __enter__(self):
self.__old_config = _ScopedLog.__log.get_config()
_ScopedLog.__log.enable(self.__config)
return self
def __exit__(self, exc_type, exc_value, exc_traceback):
if self.__old_config:
_ScopedLog.__log.enable(self.__old_config)
else:
_ScopedLog.__log.disable()
class ScopedLogEnable:
"""Decorator: Enables logging facility before execution of the wrapped function
and disables logging after exiting the wrapped function. This allows more specific
logging of a code section compared to enabling or disabling the global logging mechanism.
Arguments:
config: The configuration the log should be enabled with.
"""
def __init__(self, config: LogConfig):
"""Add scoped logging to a Callable.
Arguments:
config: The configuration the log should be enabled with.
"""
self.__config = config
def __call__(self, func: Callable[..., Any]):
@wraps(func)
def wrapper(*args: Tuple[Any, ...]):
with _ScopedLog(self.__config):
return func(*args)
return wrapper

View File

@@ -0,0 +1,136 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
from functools import reduce, wraps
from inspect import signature
from .log import Log
__all__ = [
'TraceEnable'
]
_FMT_MSG_ENTRY: str = 'Enter | {}'
_FMT_MSG_LEAVE: str = 'Leave | {}'
_FMT_MSG_RAISE: str = 'Raise | {}, {}'
_FMT_ERROR: str = 'ErrorType: {}, ErrorValue: {}'
_INDENT_PER_LEVEL: str = ' '
def _args_to_str(func, *args, **kwargs) -> str:
# Expand function signature
sig = signature(func).bind(*args, **kwargs)
sig.apply_defaults()
full_args = sig.arguments
# Early return if there is nothing to print
if not full_args:
return '(None)'
def fold(args_as_str: str, arg):
name, value = arg
if name == 'self':
arg_str = 'self'
else:
arg_str = str(value)
return '{}{}, '.format(args_as_str, arg_str)
return '({})'.format(reduce(fold, full_args.items(), '')[:-2])
def _get_indent(level: int) -> str:
return _INDENT_PER_LEVEL * level
def _create_enter_msg(name: str, level: int, args_str: str) -> str:
msg = '{}{}{}'.format(_get_indent(level), name, args_str)
return _FMT_MSG_ENTRY.format(msg)
def _create_leave_msg(name: str, level: int, ) -> str:
msg = '{}{}'.format(_get_indent(level), name)
return _FMT_MSG_LEAVE.format(msg)
def _create_raise_msg(name: str, level: int, exc_type: Exception, exc_value: str) -> str:
msg = '{}{}'.format(_get_indent(level), name)
exc = _FMT_ERROR.format(exc_type, exc_value)
return _FMT_MSG_RAISE.format(msg, exc)
class _Tracer:
__log = Log.get_instance()
__level: int = 0
@staticmethod
def is_log_enabled() -> bool:
return bool(_Tracer.__log)
def __init__(self, func, *args, **kwargs):
self.__full_name: str = '{}.{}'.format(func.__module__, func.__qualname__)
self.__full_args: str = _args_to_str(func, *args, **kwargs)
def __enter__(self):
msg = _create_enter_msg(self.__full_name, _Tracer.__level, self.__full_args)
_Tracer.__log.trace(msg)
_Tracer.__level += 1
def __exit__(self, exc_type, exc_value, exc_traceback):
_Tracer.__level -= 1
if exc_type:
msg = _create_raise_msg(self.__full_name, _Tracer.__level, exc_type, exc_value)
else:
msg = _create_leave_msg(self.__full_name, _Tracer.__level)
_Tracer.__log.trace(msg)
class TraceEnable:
"""Decorator: Adds an entry of LogLevel. Trace on entry and exit of the wrapped function.
On exit, the log entry contains information if the function was left normally or with an
exception.
"""
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
if _Tracer.is_log_enabled():
with _Tracer(func, *args, **kwargs):
result = func(*args, **kwargs)
return result
else:
return func(*args, **kwargs)
return wrapper

View File

@@ -0,0 +1,600 @@
"""BSD 2-Clause License
Copyright (c) 2019, Allied Vision Technologies GmbH
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import threading
from typing import List, Dict, Tuple
from .c_binding import call_vimba_c, VIMBA_C_VERSION, VIMBA_IMAGE_TRANSFORM_VERSION, \
G_VIMBA_C_HANDLE
from .feature import discover_features, FeatureTypes, FeaturesTuple, FeatureTypeTypes, EnumFeature
from .shared import filter_features_by_name, filter_features_by_type, filter_affected_features, \
filter_selected_features, filter_features_by_category, \
attach_feature_accessors, remove_feature_accessors, read_memory, \
write_memory, read_registers, write_registers
from .interface import Interface, InterfaceChangeHandler, InterfaceEvent, InterfacesTuple, \
InterfacesList, discover_interfaces, discover_interface
from .camera import Camera, CamerasList, CameraChangeHandler, CameraEvent, CamerasTuple, \
discover_cameras, discover_camera
from .util import Log, LogConfig, TraceEnable, RuntimeTypeCheckEnable, EnterContextOnCall, \
LeaveContextOnCall, RaiseIfInsideContext, RaiseIfOutsideContext
from .error import VimbaCameraError, VimbaInterfaceError, VimbaFeatureError
from . import __version__ as VIMBA_PYTHON_VERSION
__all__ = [
'Vimba',
]
class Vimba:
class __Impl:
"""This class allows access to the entire Vimba System.
Vimba is meant be used in conjunction with the "with" - Statement, upon
entering the context, all system features, connected cameras and interfaces are detected
and can be used.
"""
@TraceEnable()
@LeaveContextOnCall()
def __init__(self):
"""Do not call directly. Use Vimba.get_instance() instead."""
self.__feats: FeaturesTuple = ()
self.__inters: InterfacesList = ()
self.__inters_lock: threading.Lock = threading.Lock()
self.__inters_handlers: List[InterfaceChangeHandler] = []
self.__inters_handlers_lock: threading.Lock = threading.Lock()
self.__cams: CamerasList = ()
self.__cams_lock: threading.Lock = threading.Lock()
self.__cams_handlers: List[CameraChangeHandler] = []
self.__cams_handlers_lock: threading.Lock = threading.Lock()
self.__nw_discover: bool = True
self.__context_cnt: int = 0
@TraceEnable()
def __enter__(self):
if not self.__context_cnt:
self._startup()
self.__context_cnt += 1
return self
@TraceEnable()
def __exit__(self, exc_type, exc_value, exc_traceback):
self.__context_cnt -= 1
if not self.__context_cnt:
self._shutdown()
def get_version(self) -> str:
""" Returns version string of VimbaPython and underlaying dependencies."""
msg = 'VimbaPython: {} (using VimbaC: {}, VimbaImageTransform: {})'
return msg.format(VIMBA_PYTHON_VERSION, VIMBA_C_VERSION, VIMBA_IMAGE_TRANSFORM_VERSION)
@RaiseIfInsideContext()
@RuntimeTypeCheckEnable()
def set_network_discovery(self, enable: bool):
"""Enable/Disable network camera discovery.
Arguments:
enable - If 'True' VimbaPython tries to detect cameras connected via Ethernet
on entering the 'with' statement. If set to 'False', no network
discover occurs.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError if called inside with-statement.
"""
self.__nw_discover = enable
@RuntimeTypeCheckEnable()
def enable_log(self, config: LogConfig):
"""Enable VimbaPython's logging mechanism.
Arguments:
config - Configuration for the logging mechanism.
Raises:
TypeError if parameters do not match their type hint.
"""
Log.get_instance().enable(config)
def disable_log(self):
"""Disable VimbaPython's logging mechanism."""
Log.get_instance().disable()
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def read_memory(self, addr: int, max_bytes: int) -> bytes: # coverage: skip
"""Read a byte sequence from a given memory address.
Arguments:
addr: Starting address to read from.
max_bytes: Maximum number of bytes to read from addr.
Returns:
Read memory contents as bytes.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
ValueError if addr is negative
ValueError if max_bytes is negative.
ValueError if the memory access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return read_memory(G_VIMBA_C_HANDLE, addr, max_bytes)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def write_memory(self, addr: int, data: bytes): # coverage: skip
""" Write a byte sequence to a given memory address.
Arguments:
addr: Address to write the content of 'data' too.
data: Byte sequence to write at address 'addr'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
ValueError if addr is negative.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return write_memory(G_VIMBA_C_HANDLE, addr, data)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def read_registers(self, addrs: Tuple[int, ...]) -> Dict[int, int]: # coverage: skip
"""Read contents of multiple registers.
Arguments:
addrs: Sequence of addresses that should be read iteratively.
Return:
Dictionary containing a mapping from given address to the read register values.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
ValueError if any address in addrs_values is negative.
ValueError if the register access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return read_registers(G_VIMBA_C_HANDLE, addrs)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def write_registers(self, addrs_values: Dict[int, int]): # coverage: skip
"""Write data to multiple Registers.
Arguments:
addrs_values: Mapping between Register addresses and the data to write.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
ValueError if any address in addrs is negative.
ValueError if the register access was invalid.
"""
# Note: Coverage is skipped. Function is untestable in a generic way.
return write_registers(G_VIMBA_C_HANDLE, addrs_values)
@RaiseIfOutsideContext()
def get_all_interfaces(self) -> InterfacesTuple:
"""Get access to all discovered Interfaces:
Returns:
A set of all currently detected Interfaces.
Raises:
RuntimeError then called outside of "with" - statement.
"""
with self.__inters_lock:
return tuple(self.__inters)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_interface_by_id(self, id_: str) -> Interface:
"""Lookup Interface with given ID.
Arguments:
id_ - Interface Id to search for.
Returns:
Interface associated with given Id.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
VimbaInterfaceError if interface with id_ can't be found.
"""
with self.__inters_lock:
inter = [inter for inter in self.__inters if id_ == inter.get_id()]
if not inter:
raise VimbaInterfaceError('Interface with ID \'{}\' not found.'.format(id_))
return inter.pop()
@RaiseIfOutsideContext()
def get_all_cameras(self) -> CamerasTuple:
"""Get access to all discovered Cameras.
Returns:
A set of all currently detected Cameras.
Raises:
RuntimeError then called outside of "with" - statement.
"""
with self.__cams_lock:
return tuple(self.__cams)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_camera_by_id(self, id_: str) -> Camera:
"""Lookup Camera with given ID.
Arguments:
id_ - Camera Id to search for. For GigE - Cameras, the IP and MAC-Address
can be used to Camera lookup
Returns:
Camera associated with given Id.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
VimbaCameraError if camera with id_ can't be found.
"""
with self.__cams_lock:
# Search for given Camera Id in all currently detected cameras.
for cam in self.__cams:
if id_ == cam.get_id():
return cam
# If a search by ID fails, the given id_ is almost certain an IP or MAC - Address.
# Try to query this Camera.
try:
cam_info = discover_camera(id_)
# Since cam_info is newly constructed, search in existing cameras for a Camera
for cam in self.__cams:
if cam_info.get_id() == cam.get_id():
return cam
except VimbaCameraError:
pass
raise VimbaCameraError('No Camera with Id \'{}\' available.'.format(id_))
@RaiseIfOutsideContext()
def get_all_features(self) -> FeaturesTuple:
"""Get access to all discovered system features:
Returns:
A set of all currently detected Features.
Raises:
RuntimeError then called outside of "with" - statement.
"""
return self.__feats
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_affected_by(self, feat: FeatureTypes) -> FeaturesTuple:
"""Get all system features affected by a specific system feature.
Arguments:
feat - Feature used find features that are affected by feat.
Returns:
A set of features affected by changes on 'feat'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
VimbaFeatureError if 'feat' is not a system feature.
"""
return filter_affected_features(self.__feats, feat)
@TraceEnable()
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_selected_by(self, feat: FeatureTypes) -> FeaturesTuple:
"""Get all system features selected by a specific system feature.
Arguments:
feat - Feature used find features that are selected by feat.
Returns:
A set of features selected by 'feat'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
VimbaFeatureError if 'feat' is not a system feature.
"""
return filter_selected_features(self.__feats, feat)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_by_type(self, feat_type: FeatureTypeTypes) -> FeaturesTuple:
"""Get all system features of a specific feature type.
Valid FeatureTypes are: IntFeature, FloatFeature, StringFeature, BoolFeature,
EnumFeature, CommandFeature, RawFeature
Arguments:
feat_type - FeatureType used find features of that type.
Returns:
A set of features of type 'feat_type'.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
"""
return filter_features_by_type(self.__feats, feat_type)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_features_by_category(self, category: str) -> FeaturesTuple:
"""Get all system features of a specific category.
Arguments:
category - Category that should be used for filtering.
Returns:
A set of features of category 'category'.
Returns:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
"""
return filter_features_by_category(self.__feats, category)
@RaiseIfOutsideContext()
@RuntimeTypeCheckEnable()
def get_feature_by_name(self, feat_name: str) -> FeatureTypes:
"""Get a system feature by its name.
Arguments:
feat_name - Name used to find a feature.
Returns:
Feature with the associated name.
Raises:
TypeError if parameters do not match their type hint.
RuntimeError then called outside of "with" - statement.
VimbaFeatureError if no feature is associated with 'feat_name'.
"""
feat = filter_features_by_name(self.__feats, feat_name)
if not feat:
raise VimbaFeatureError('Feature \'{}\' not found.'.format(feat_name))
return feat
@RuntimeTypeCheckEnable()
def register_camera_change_handler(self, handler: CameraChangeHandler):
"""Add Callable what is executed on camera connect/disconnect
Arguments:
handler - The change handler that shall be added.
Raises:
TypeError if parameters do not match their type hint.
"""
with self.__cams_handlers_lock:
if handler not in self.__cams_handlers:
self.__cams_handlers.append(handler)
def unregister_all_camera_change_handlers(self):
"""Remove all currently registered camera change handlers"""
with self.__cams_handlers_lock:
if self.__cams_handlers:
self.__cams_handlers.clear()
@RuntimeTypeCheckEnable()
def unregister_camera_change_handler(self, handler: CameraChangeHandler):
"""Remove previously registered camera change handler
Arguments:
handler - The change handler that shall be removed.
Raises:
TypeError if parameters do not match their type hint.
"""
with self.__cams_handlers_lock:
if handler in self.__cams_handlers:
self.__cams_handlers.remove(handler)
@RuntimeTypeCheckEnable()
def register_interface_change_handler(self, handler: InterfaceChangeHandler):
"""Add Callable what is executed on interface connect/disconnect
Arguments:
handler - The change handler that shall be added.
Raises:
TypeError if parameters do not match their type hint.
"""
with self.__inters_handlers_lock:
if handler not in self.__inters_handlers:
self.__inters_handlers.append(handler)
def unregister_all_interface_change_handlers(self):
"""Remove all currently registered interface change handlers"""
with self.__inters_handlers_lock:
if self.__inters_handlers:
self.__inters_handlers.clear()
@RuntimeTypeCheckEnable()
def unregister_interface_change_handler(self, handler: InterfaceChangeHandler):
"""Remove previously registered interface change handler
Arguments:
handler - The change handler that shall be removed.
Raises:
TypeError if parameters do not match their type hint.
"""
with self.__inters_handlers_lock:
if handler in self.__inters_handlers:
self.__inters_handlers.remove(handler)
@TraceEnable()
@EnterContextOnCall()
def _startup(self):
Log.get_instance().info('Starting {}'.format(self.get_version()))
call_vimba_c('VmbStartup')
self.__inters = discover_interfaces()
self.__cams = discover_cameras(self.__nw_discover)
self.__feats = discover_features(G_VIMBA_C_HANDLE)
attach_feature_accessors(self, self.__feats)
feat = self.get_feature_by_name('DiscoveryInterfaceEvent')
feat.register_change_handler(self.__inter_cb_wrapper)
feat = self.get_feature_by_name('DiscoveryCameraEvent')
feat.register_change_handler(self.__cam_cb_wrapper)
@TraceEnable()
@LeaveContextOnCall()
def _shutdown(self):
self.unregister_all_camera_change_handlers()
self.unregister_all_interface_change_handlers()
for feat in self.__feats:
feat.unregister_all_change_handlers()
remove_feature_accessors(self, self.__feats)
self.__feats = ()
self.__cams_handlers = []
self.__cams = ()
self.__inters_handlers = []
self.__inters = ()
call_vimba_c('VmbShutdown')
def __cam_cb_wrapper(self, cam_event: EnumFeature): # coverage: skip
# Skip coverage because it can't be measured. This is called from C-Context
event = CameraEvent(int(cam_event.get()))
cam = None
cam_id = self.get_feature_by_name('DiscoveryCameraIdent').get()
log = Log.get_instance()
# New camera found: Add it to camera list
if event == CameraEvent.Detected:
cam = discover_camera(cam_id)
with self.__cams_lock:
self.__cams.append(cam)
log.info('Added camera \"{}\" to active cameras'.format(cam_id))
# Existing camera lost. Remove it from active cameras
elif event == CameraEvent.Missing:
with self.__cams_lock:
cam = [c for c in self.__cams if cam_id == c.get_id()].pop()
cam._disconnected = True
self.__cams.remove(cam)
log.info('Removed camera \"{}\" from active cameras'.format(cam_id))
else:
cam = self.get_camera_by_id(cam_id)
with self.__cams_handlers_lock:
for handler in self.__cams_handlers:
try:
handler(cam, event)
except Exception as e:
msg = 'Caught Exception in handler: '
msg += 'Type: {}, '.format(type(e))
msg += 'Value: {}, '.format(e)
msg += 'raised by: {}'.format(handler)
Log.get_instance().error(msg)
raise e
def __inter_cb_wrapper(self, inter_event: EnumFeature): # coverage: skip
# Skip coverage because it can't be measured. This is called from C-Context
event = InterfaceEvent(int(inter_event.get()))
inter = None
inter_id = self.get_feature_by_name('DiscoveryInterfaceIdent').get()
log = Log.get_instance()
# New interface found: Add it to interface list
if event == InterfaceEvent.Detected:
inter = discover_interface(inter_id)
with self.__inters_lock:
self.__inters.append(inter)
log.info('Added interface \"{}\" to active interfaces'.format(inter_id))
# Existing interface lost. Remove it from active interfaces
elif event == InterfaceEvent.Missing:
with self.__inters_lock:
inter = [i for i in self.__inters if inter_id == i.get_id()].pop()
self.__inters.remove(inter)
log.info('Removed interface \"{}\" from active interfaces'.format(inter_id))
else:
inter = self.get_interface_by_id(inter_id)
with self.__inters_handlers_lock:
for handler in self.__inters_handlers:
try:
handler(inter, event)
except Exception as e:
msg = 'Caught Exception in handler: '
msg += 'Type: {}, '.format(type(e))
msg += 'Value: {}, '.format(e)
msg += 'raised by: {}'.format(handler)
Log.get_instance().error(msg)
raise e
__instance = __Impl()
@staticmethod
@TraceEnable()
def get_instance() -> '__Impl':
"""Get VimbaSystem Singleton."""
return Vimba.__instance

View File

@@ -0,0 +1,236 @@
#!/bin/bash
# BSD 2-Clause License
#
# Copyright (c) 2019, Allied Vision Technologies GmbH
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# 1. Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# global parameters parsed from command line flags
DEBUG=false
while getopts "d" flag; do
case "${flag}" in
d) DEBUG=true ;;
*) ;;
esac
done
function inside_virtual_env
{
if [ -z "$VIRTUAL_ENV" ]; then
echo "false"
else
echo "true"
fi
}
function get_python_versions
{
DETECTED_PYTHONS=()
# Check if the script was run from a virtual environment and set search path for binary accordingly
if [ "$(inside_virtual_env)" = true ]; then
if [ "$DEBUG" = true ] ; then
echo "Detected active virtual environment" >&2
fi
SEARCH_PATH="$VIRTUAL_ENV"/bin
else
if [ "$DEBUG" = true ] ; then
echo "No virtual environment detected" >&2
fi
SEARCH_PATH=$(echo "$PATH" | tr ":" " ")
fi
if [ "$DEBUG" = true ] ; then
echo "Searching for python in $SEARCH_PATH" >&2
fi
# iterate over all detected python binaries and check if they are viable installations
for P in $(whereis -b -B $SEARCH_PATH -f python | tr " " "\n" | grep "python[[:digit:]]\.[[:digit:]]\.\?[[:digit:]]\?$" | sort -V)
do
# 1) Remove results that are links (venv executables are often links so we allow those)
if [ -L "$P" ] && [ "$(inside_virtual_env)" = false ]
then
if [ "$DEBUG" = true ] ; then
echo "$P was a link" >&2
fi
continue
fi
# 2) Remove results that are directories
if [ -d "$P" ]
then
if [ "$DEBUG" = true ] ; then
echo "$P was a directory" >&2
fi
continue
fi
# 3) Remove results that offer no pip support.
$P -m pip > /dev/null 2>&1
if [ $? -ne 0 ]
then
if [ "$DEBUG" = true ] ; then
echo "$P did not have pip support" >&2
fi
continue
fi
# 4) Remove results where VimbaPython is not installed
if [ $($P -m pip list --format=columns | grep "VimbaPython" | wc -l) -ne 1 ]
then
if [ "$DEBUG" = true ] ; then
echo "$P did not have VimbaPython installed" >&2
fi
continue
fi
DETECTED_PYTHONS+=("$P")
done
echo "${DETECTED_PYTHONS[@]}"
}
echo "#################################"
echo "# VimbaPython uninstall script. #"
echo "#################################"
#########################
# Perform sanity checks #
#########################
if [ $UID -ne 0 ] && [ "$(inside_virtual_env)" = false ]
then
echo "Error: Uninstallation requires root privileges. Abort."
exit 1
fi
PWD=$(pwd)
PWD=${PWD##*/}
if [[ "$PWD" != "VimbaPython" ]]
then
echo "Error: Please execute Uninstall.sh within VimbaPython directory."
exit 1
fi
PYTHONS=$(get_python_versions)
if [ -z "$PYTHONS" ]
then
echo "Can't remove VimbaPython. No installation was found."
exit 0
fi
#############################################
# Determine python to uninstall VimbaPython #
#############################################
# List all given interpreters and create an Index
echo "VimbaPython is installed for the following interpreters:"
ITER=0
for ITEM in ${PYTHONS[@]}
do
echo " $ITER: $ITEM"
LAST=$ITER
ITER=$(expr $ITER + 1)
done
# Read and verfiy user input
while true
do
echo -n "Enter python version to uninstall VimbaPython (0 - $LAST, all: a, default: a): "
read TMP
# Set TMP to default value if nothing was entered.
if [ -z $TMP ]
then
TMP="a"
fi
# Check if Input was "a". If so skip further Input verification.
if [ "$TMP" == "a" ]
then
echo " Removing all installations of VimbaPython."
ITER=$TMP
break
else
# Check if Input was a number. If so: assign it.
if [ $TMP -eq $TMP ] 2>/dev/null
then
ITER=$TMP
else
echo " Error: Given input was not a number. Try again."
continue
fi
# Verify Input range
if [ 0 -le $ITER -a $ITER -le $LAST ]
then
break
else
echo " Error: Given input is not between 0 and $LAST. Try again."
fi
fi
done
# Search for selected python interpreter
IDX=0
PYTHON=""
for ITEM in ${PYTHONS[@]}
do
if [ "$ITER" == "a" ]
then
PYTHON=$PYTHONS
break
elif [ $IDX -eq $ITER ]
then
PYTHON=$ITEM
break
else
IDX=$(expr $IDX + 1)
fi
done
# Remove VimbaPython via pip
for P in ${PYTHON[@]}
do
echo ""
echo "Remove VimbaPython for $P"
$P -m pip uninstall --yes VimbaPython
if [ $? -eq 0 ]
then
echo "VimbaPython removal for $P was successful."
else
echo "Error: VimbaPython removal for $P failed. Please check pip output for details."
fi
done