924 lines
33 KiB
Python
924 lines
33 KiB
Python
![]() |
"""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)))
|