Radiation-resistantCamera/Vimba_6_0/VimbaPython/Source/vimba/frame.py
2025-04-30 09:26:04 +08:00

924 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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)))