""" Android Hardware Camera2 Params 组件 — RggbChannelVector, BlackLevelPattern, StreamConfigurationMap 等 方法签名完全对齐 Android SDK (android.hardware.camera2.params 包) """ from __future__ import annotations import math from typing import Any, Dict, List, Optional, Sequence, Set, Tuple from .._core import Stub, AndroidOnlyError from ..java.lang import RuntimeException # ═══════════════════════════════════════════════════════════════ # RggbChannelVector # ═══════════════════════════════════════════════════════════════ class RggbChannelVector(Stub): """RggbChannelVector — RGGB 通道向量""" COUNT = 4 def __init__(self, red: float, green_even: float, green_odd: float, blue: float): super().__init__() self._red = red self._green_even = green_even self._green_odd = green_odd self._blue = blue def getRed(self) -> float: return self._red def getGreenEven(self) -> float: return self._green_even def getGreenOdd(self) -> float: return self._green_odd def getBlue(self) -> float: return self._blue def getComponent(self, i: int) -> float: components = [self._red, self._green_even, self._green_odd, self._blue] return components[i] def toFloatArray(self) -> List[float]: return [self._red, self._green_even, self._green_odd, self._blue] def copyTo(self, destination: List[float], offset: int = 0): for i, v in enumerate([self._red, self._green_even, self._green_odd, self._blue]): destination[offset + i] = v def equals(self, other: Any) -> bool: if isinstance(other, RggbChannelVector): return (abs(self._red - other._red) < 1e-6 and abs(self._green_even - other._green_even) < 1e-6 and abs(self._green_odd - other._green_odd) < 1e-6 and abs(self._blue - other._blue) < 1e-6) return False def hashCode(self) -> int: return hash((self._red, self._green_even, self._green_odd, self._blue)) & 0x7FFFFFFF def toString(self) -> str: return f"RggbChannelVector(R={self._red:.3f}, Ge={self._green_even:.3f}, Go={self._green_odd:.3f}, B={self._blue:.3f})" # ═══════════════════════════════════════════════════════════════ # BlackLevelPattern # ═══════════════════════════════════════════════════════════════ class BlackLevelPattern(Stub): """BlackLevelPattern — 黑电平模式""" COUNT = 4 def __init__(self, red: int = 0, green_even: int = 0, green_odd: int = 0, blue: int = 0): super().__init__() self._offsets = [red, green_even, green_odd, blue] def getOffsetForIndex(self, column: int, row: int) -> int: idx = (row % 2) * 2 + (column % 2) return self._offsets[idx] def copyTo(self, destination: List[int], offset: int = 0): for i, v in enumerate(self._offsets): destination[offset + i] = v def getRed(self) -> int: return self._offsets[0] def getGreenEven(self) -> int: return self._offsets[1] def getGreenOdd(self) -> int: return self._offsets[2] def getBlue(self) -> int: return self._offsets[3] def equals(self, other: Any) -> bool: if isinstance(other, BlackLevelPattern): return self._offsets == other._offsets return False def hashCode(self) -> int: return hash(tuple(self._offsets)) & 0x7FFFFFFF def toString(self) -> str: return f"BlackLevelPattern(R={self._offsets[0]}, Ge={self._offsets[1]}, Go={self._offsets[2]}, B={self._offsets[3]})" # ═══════════════════════════════════════════════════════════════ # ColorSpaceTransform # ═══════════════════════════════════════════════════════════════ class ColorSpaceTransform(Stub): """ColorSpaceTransform — 色彩空间变换 (3x3 矩阵)""" COUNT = 9 ELEMENTS_PER_ROW = 3 ROWS = 3 COLUMNS = 3 def __init__(self, elements: Optional[List[Rational]] = None): super().__init__() if elements is not None and len(elements) == 9: self._elements = list(elements) else: self._elements = [Rational(1, 1) if i % 4 == 0 else Rational(0, 1) for i in range(9)] def getElement(self, column: int, row: int) -> Rational: return self._elements[row * 3 + column] def setElement(self, column: int, row: int, element: Rational): self._elements[row * 3 + column] = element def copyTo(self, destination: List[Rational], offset: int = 0): for i, v in enumerate(self._elements): destination[offset + i] = v def getElements(self) -> List[Rational]: return list(self._elements) def transform(self, r: float, g: float, b: float) -> Tuple[float, float, float]: e = self._elements r_out = e[0].toFloat() * r + e[1].toFloat() * g + e[2].toFloat() * b g_out = e[3].toFloat() * r + e[4].toFloat() * g + e[5].toFloat() * b b_out = e[6].toFloat() * r + e[7].toFloat() * g + e[8].toFloat() * b return (r_out, g_out, b_out) def equals(self, other: Any) -> bool: if isinstance(other, ColorSpaceTransform): return all(abs(a.toFloat() - b.toFloat()) < 1e-6 for a, b in zip(self._elements, other._elements)) return False def hashCode(self) -> int: return hash(tuple(self._elements)) & 0x7FFFFFFF def toString(self) -> str: return (f"ColorSpaceTransform([{self._elements[0]}, {self._elements[1]}, {self._elements[2]}," f" {self._elements[3]}, {self._elements[4]}, {self._elements[5]}," f" {self._elements[6]}, {self._elements[7]}, {self._elements[8]}])") # ═══════════════════════════════════════════════════════════════ # Rational # ═══════════════════════════════════════════════════════════════ class Rational(Stub): """Rational — 有理数""" def __init__(self, numerator: int = 0, denominator: int = 1): super().__init__() if denominator < 0: numerator = -numerator denominator = -denominator gcd = math.gcd(abs(numerator), abs(denominator)) if denominator != 0 else 1 self._numerator = numerator // gcd self._denominator = denominator // gcd def getNumerator(self) -> int: return self._numerator def getDenominator(self) -> int: return self._denominator def toFloat(self) -> float: if self._denominator == 0: return float('inf') if self._numerator >= 0 else float('-inf') return self._numerator / self._denominator def toDouble(self) -> float: return self.toFloat() def equals(self, other: Any) -> bool: if isinstance(other, Rational): return (self._numerator * other._denominator == other._numerator * self._denominator) return False def hashCode(self) -> int: return hash((self._numerator, self._denominator)) & 0x7FFFFFFF def toString(self) -> str: return f"{self._numerator}/{self._denominator}" # ═══════════════════════════════════════════════════════════════ # StreamConfigurationMap # ═══════════════════════════════════════════════════════════════ class StreamConfigurationMap(Stub): """StreamConfigurationMap — 流配置映射""" def __init__(self): super().__init__() self._output_formats: Dict[int, List[Tuple[int, int]]] = {} self._input_formats: Dict[int, List[Tuple[int, int]]] = {} self._high_speed_video_sizes: Dict[int, List[Tuple[int, int]]] = {} self._output_min_frame_durations: Dict[Tuple[int, int, int], int] = {} self._output_stall_durations: Dict[Tuple[int, int, int], int] = {} self._input_min_frame_durations: Dict[Tuple[int, int, int], int] = {} self._input_stall_durations: Dict[Tuple[int, int, int], int] = {} self._mutable: bool = True def getOutputFormats(self) -> List[int]: return list(self._output_formats.keys()) def getInputFormats(self) -> List[int]: return list(self._input_formats.keys()) def getOutputSizes(self, format: int) -> List[Tuple[int, int]]: return list(self._output_formats.get(format, [])) def getInputSizes(self, format: int) -> List[Tuple[int, int]]: return list(self._input_formats.get(format, [])) def getHighSpeedVideoSizes(self) -> List[Tuple[int, int]]: result = set() for sizes in self._high_speed_video_sizes.values(): result.update(sizes) return list(result) def getHighSpeedVideoFpsRangeFor(self, size: Tuple[int, int]) -> List[Tuple[int, int]]: return [(30, 30), (60, 60), (120, 120)] def getHighSpeedVideoFpsRanges(self) -> List[Tuple[int, int]]: return [(30, 30), (60, 60), (120, 120), (240, 240)] def getOutputMinFrameDuration(self, format: int, size: Tuple[int, int]) -> int: return self._output_min_frame_durations.get((format, size[0], size[1]), 33333333) def getOutputStallDuration(self, format: int, size: Tuple[int, int]) -> int: return self._output_stall_durations.get((format, size[0], size[1]), 0) def getInputMinFrameDuration(self, format: int, size: Tuple[int, int]) -> int: return self._input_min_frame_durations.get((format, size[0], size[1]), 33333333) def getInputStallDuration(self, format: int, size: Tuple[int, int]) -> int: return self._input_stall_durations.get((format, size[0], size[1]), 0) def isOutputSupportedFor(self, size: Tuple[int, int]) -> bool: for sizes in self._output_formats.values(): if size in sizes: return True return False def isValid(self) -> bool: return len(self._output_formats) > 0 def setMutable(self, mutable: bool): self._mutable = mutable def isMutable(self) -> bool: return self._mutable def addOutputFormat(self, format: int, sizes: List[Tuple[int, int]]): self._output_formats[format] = list(sizes) def addInputFormat(self, format: int, sizes: List[Tuple[int, int]]): self._input_formats[format] = list(sizes) def toString(self) -> str: return f"StreamConfigurationMap(outputFormats={len(self._output_formats)})" # ═══════════════════════════════════════════════════════════════ # TonemapCurve # ═══════════════════════════════════════════════════════════════ class TonemapCurve(Stub): """TonemapCurve — 色调映射曲线""" LEVEL_BLACK = 0 LEVEL_WHITE = 1 CHANNEL_RED = 0 CHANNEL_GREEN = 1 CHANNEL_BLUE = 2 POINT_SIZE = 2 def __init__(self, red_curve: Optional[List[float]] = None, green_curve: Optional[List[float]] = None, blue_curve: Optional[List[float]] = None): super().__init__() self._curves = { TonemapCurve.CHANNEL_RED: list(red_curve or [0.0, 0.0, 1.0, 1.0]), TonemapCurve.CHANNEL_GREEN: list(green_curve or [0.0, 0.0, 1.0, 1.0]), TonemapCurve.CHANNEL_BLUE: list(blue_curve or [0.0, 0.0, 1.0, 1.0]), } def getPointCount(self, channel: int) -> int: return len(self._curves.get(channel, [])) // 2 def getPoint(self, channel: int, index: int, out_point: List[float] = None) -> Tuple[float, float]: curve = self._curves.get(channel, []) if out_point is None: out_point = [0.0, 0.0] out_point[0] = curve[index * 2] out_point[1] = curve[index * 2 + 1] return (out_point[0], out_point[1]) def setPoint(self, channel: int, index: int, x: float, y: float): curve = self._curves[channel] while len(curve) < (index * 2 + 2): curve.extend([0.0, 0.0]) curve[index * 2] = x curve[index * 2 + 1] = y def getCurve(self, channel: int) -> Optional[List[float]]: return list(self._curves.get(channel, [])) def estimateCurve(self, channel: int, x: float) -> float: curve = self._curves.get(channel, []) if not curve or len(curve) < 4: return x for i in range(0, len(curve) - 2, 2): x0, y0 = curve[i], curve[i + 1] x1, y1 = curve[i + 2], curve[i + 3] if x0 <= x <= x1 or (x1 <= x <= x0): if abs(x1 - x0) < 1e-10: return y0 t = (x - x0) / (x1 - x0) return y0 + t * (y1 - y0) return curve[-1] if curve else x def equals(self, other: Any) -> bool: if isinstance(other, TonemapCurve): return all(abs(self._curves[c][i] - other._curves[c][i]) < 1e-6 for c in range(3) for i in range(len(self._curves[c]))) return False def hashCode(self) -> int: return hash(tuple(tuple(v) for v in self._curves.values())) & 0x7FFFFFFF def toString(self) -> str: return f"TonemapCurve(points={self.getPointCount(0)}, {self.getPointCount(1)}, {self.getPointCount(2)})" # ═══════════════════════════════════════════════════════════════ # LensShadingMap # ═══════════════════════════════════════════════════════════════ class LensShadingMap(Stub): """LensShadingMap — 镜头阴影映射""" def __init__(self, columns: int = 1, rows: int = 1): super().__init__() self._columns = columns self._rows = rows self._gain_map: Dict[Tuple[int, int, int], float] = {} def getGainFactor(self, channel: int, column: int, row: int) -> float: return self._gain_map.get((channel, column, row), 1.0) def setGainFactor(self, channel: int, column: int, row: int, gain: float): self._gain_map[(channel, column, row)] = gain def getGainFactorVector(self, column: int, row: int) -> RggbChannelVector: r = self.getGainFactor(0, column, row) ge = self.getGainFactor(1, column, row) go = self.getGainFactor(2, column, row) b = self.getGainFactor(3, column, row) return RggbChannelVector(r, ge, go, b) def getColumnCount(self) -> int: return self._columns def getRowCount(self) -> int: return self._rows def copyGainFactors(self, destination: List[float], offset: int = 0): for (ch, col, row), gain in self._gain_map.items(): idx = offset + (row * self._columns + col) * 4 + ch if idx < len(destination): destination[idx] = gain def equals(self, other: Any) -> bool: if isinstance(other, LensShadingMap): return (self._columns == other._columns and self._rows == other._rows and self._gain_map == other._gain_map) return False def hashCode(self) -> int: return hash((self._columns, self._rows, tuple(sorted(self._gain_map.items())))) & 0x7FFFFFFF def toString(self) -> str: return f"LensShadingMap({self._columns}x{self._rows})" # ═══════════════════════════════════════════════════════════════ # MeteringRectangle # ═══════════════════════════════════════════════════════════════ class MeteringRectangle(Stub): """MeteringRectangle — 测光矩形""" def __init__(self, x: int = 0, y: int = 0, width: int = 0, height: int = 0, metering_weight: int = 1): super().__init__() self._x = x self._y = y self._width = width self._height = height self._metering_weight = metering_weight def getX(self) -> int: return self._x def getY(self) -> int: return self._y def getWidth(self) -> int: return self._width def getHeight(self) -> int: return self._height def getMeteringWeight(self) -> int: return self._metering_weight def getRect(self) -> Tuple[int, int, int, int]: return (self._x, self._y, self._x + self._width, self._y + self._height) def equals(self, other: Any) -> bool: if isinstance(other, MeteringRectangle): return (self._x == other._x and self._y == other._y and self._width == other._width and self._height == other._height and self._metering_weight == other._metering_weight) return False def hashCode(self) -> int: return hash((self._x, self._y, self._width, self._height, self._metering_weight)) & 0x7FFFFFFF def toString(self) -> str: return f"MeteringRectangle({self._x},{self._y} {self._width}x{self._height} w={self._metering_weight})" # ═══════════════════════════════════════════════════════════════ # OutputConfiguration # ═══════════════════════════════════════════════════════════════ class OutputConfiguration(Stub): """OutputConfiguration — 输出配置""" SURFACE_TYPE_UNKNOWN = -1 SURFACE_TYPE_SURFACE_VIEW = 0 SURFACE_TYPE_SURFACE_TEXTURE = 1 SURFACE_TYPE_IMAGE_READER = 2 SURFACE_TYPE_MEDIA_CODEC = 3 SURFACE_TYPE_MEDIA_RECORDER = 4 ROTATION_0 = 0 ROTATION_90 = 1 ROTATION_180 = 2 ROTATION_270 = 3 def __init__(self, surface: Any = None, surface_type: int = SURFACE_TYPE_UNKNOWN): super().__init__() self._surface = surface self._surface_type = surface_type self._rotation: int = OutputConfiguration.ROTATION_0 self._width: int = 0 self._height: int = 0 self._format: int = 0 self._is_deferred: bool = False self._physical_camera_id: str = "" self._surface_set: List[Any] = [] self._shared_surfaces: List[Any] = [] self._capture_stage: int = 0 def getSurface(self) -> Any: return self._surface def setSurface(self, surface: Any): self._surface = surface def getSurfaceType(self) -> int: return self._surface_type def setSurfaceType(self, type_: int): self._surface_type = type_ def getRotation(self) -> int: return self._rotation def setRotation(self, rotation: int): self._rotation = rotation def getWidth(self) -> int: return self._width def getHeight(self) -> int: return self._height def getFormat(self) -> int: return self._format def setDimensions(self, width: int, height: int): self._width = width self._height = height def setFormat(self, format: int): self._format = format def isDeferred(self) -> bool: return self._is_deferred def setDeferred(self, deferred: bool): self._is_deferred = deferred def getPhysicalCameraId(self) -> str: return self._physical_camera_id def setPhysicalCameraId(self, camera_id: str): self._physical_camera_id = camera_id def addSurface(self, surface: Any): self._surface_set.append(surface) def removeSurface(self, surface: Any): self._surface_set = [s for s in self._surface_set if s is not surface] def getSurfaces(self) -> List[Any]: return list(self._surface_set) def toString(self) -> str: return f"OutputConfiguration({self._width}x{self._height}, rot={self._rotation})" # ═══════════════════════════════════════════════════════════════ # SessionConfiguration # ═══════════════════════════════════════════════════════════════ class SessionConfiguration(Stub): """SessionConfiguration — 会话配置""" SESSION_TYPE_REGULAR = 0 SESSION_TYPE_VENDOR = 1 SESSION_TYPE_HIGH_SPEED = 2 SESSION_TYPE_LOW_LATENCY = 3 SESSION_TYPE_OFFLINE_PROCESSING = 4 def __init__(self, session_type: int = SESSION_TYPE_REGULAR, outputs: Optional[List[OutputConfiguration]] = None, callback: Any = None): super().__init__() self._session_type = session_type self._outputs = list(outputs or []) self._callback = callback self._input_config: Optional[OutputConfiguration] = None self._session_params: Any = None self._implementation: Any = None self._color_space: int = 0 self._is_global_session: bool = False def getSessionType(self) -> int: return self._session_type def getOutputConfigurations(self) -> List[OutputConfiguration]: return list(self._outputs) def addOutputConfiguration(self, output: OutputConfiguration): self._outputs.append(output) def removeOutputConfiguration(self, output: OutputConfiguration): self._outputs = [o for o in self._outputs if o is not output] def setInputConfiguration(self, input_config: OutputConfiguration): self._input_config = input_config def getInputConfiguration(self) -> Optional[OutputConfiguration]: return self._input_config def setSessionParameters(self, params: Any): self._session_params = params def getSessionParameters(self) -> Any: return self._session_params def setColorSpace(self, color_space: int): self._color_space = color_space def getColorSpace(self) -> int: return self._color_space def getCallback(self) -> Any: return self._callback def toString(self) -> str: return f"SessionConfiguration(type={self._session_type}, outputs={len(self._outputs)})" # ═══════════════════════════════════════════════════════════════ # MultiResolutionStreamConfigurationMap # ═══════════════════════════════════════════════════════════════ class MultiResolutionStreamConfigurationMap(Stub): """MultiResolutionStreamConfigurationMap — 多分辨率流配置映射""" def __init__(self): super().__init__() self._configs: Dict[int, List[OutputConfiguration]] = {} def getOutputConfigurations(self, format: int) -> List[OutputConfiguration]: return list(self._configs.get(format, [])) def getFormats(self) -> List[int]: return list(self._configs.keys()) def addConfiguration(self, format: int, config: OutputConfiguration): if format not in self._configs: self._configs[format] = [] self._configs[format].append(config) def removeConfiguration(self, format: int, config: OutputConfiguration): if format in self._configs: self._configs[format] = [c for c in self._configs[format] if c is not config] def getMaxResolution(self, format: int) -> Optional[OutputConfiguration]: configs = self._configs.get(format, []) if not configs: return None return max(configs, key=lambda c: c.getWidth() * c.getHeight()) def toString(self) -> str: return f"MultiResolutionStreamConfigurationMap(formats={len(self._configs)})" # ═══════════════════════════════════════════════════════════════ # OisSample # ═══════════════════════════════════════════════════════════════ class OisSample(Stub): """OisSample — OIS 样本数据""" def __init__(self, timestamp_ns: int = 0, x_shift: float = 0.0, y_shift: float = 0.0): super().__init__() self._timestamp_ns = timestamp_ns self._x_shift = x_shift self._y_shift = y_shift def getTimestampNanos(self) -> int: return self._timestamp_ns def getXshift(self) -> float: return self._x_shift def getYshift(self) -> float: return self._y_shift def equals(self, other: Any) -> bool: if isinstance(other, OisSample): return (self._timestamp_ns == other._timestamp_ns and abs(self._x_shift - other._x_shift) < 1e-6 and abs(self._y_shift - other._y_shift) < 1e-6) return False def hashCode(self) -> int: return hash((self._timestamp_ns, self._x_shift, self._y_shift)) & 0x7FFFFFFF def toString(self) -> str: return f"OisSample(ts={self._timestamp_ns}, x={self._x_shift:.3f}, y={self._y_shift:.3f})" # ═══════════════════════════════════════════════════════════════ # RecommendedStreamConfiguration # ═══════════════════════════════════════════════════════════════ class RecommendedStreamConfiguration(Stub): """RecommendedStreamConfiguration — 推荐流配置""" USECASE_PREVIEW = 0 USECASE_RECORD = 1 USECASE_STILL_CAPTURE = 2 USECASE_VIDEO_SNAPSHOT = 3 USECASE_ZSL = 4 USECASE_RAW = 5 USECASE_LOW_LATENCY_SNAPSHOT = 6 USECASE_10BIT_OUTPUT = 7 USECASE_ANALYSIS = 8 USECASE_CUSTOM = 9 USECASE_VENDOR_START = 0x1000 def __init__(self, width: int = 0, height: int = 0, format: int = 0, input_output: bool = False, usecase: int = USECASE_PREVIEW): super().__init__() self._width = width self._height = height self._format = format self._input_output = input_output self._usecase = usecase self._fps_range: Tuple[int, int] = (30, 30) self._is_high_speed: bool = False def getWidth(self) -> int: return self._width def getHeight(self) -> int: return self._height def getFormat(self) -> int: return self._format def isInput(self) -> bool: return self._input_output def isOutput(self) -> bool: return not self._input_output def getUsecase(self) -> int: return self._usecase def getFpsRange(self) -> Tuple[int, int]: return self._fps_range def setFpsRange(self, min_fps: int, max_fps: int): self._fps_range = (min_fps, max_fps) def isHighSpeed(self) -> bool: return self._is_high_speed def setHighSpeed(self, high_speed: bool): self._is_high_speed = high_speed def toString(self) -> str: return f"RecommendedStreamConfiguration({self._width}x{self._height}, fmt={self._format})" # ═══════════════════════════════════════════════════════════════ # RecommendedStreamConfigurationMap # ═══════════════════════════════════════════════════════════════ class RecommendedStreamConfigurationMap(Stub): """RecommendedStreamConfigurationMap — 推荐流配置映射""" def __init__(self): super().__init__() self._recommendations: Dict[int, List[RecommendedStreamConfiguration]] = {} self._max_resolutions: Dict[int, Tuple[int, int]] = {} def getRecommendedStreamConfigurations(self, usecase: int) -> List[RecommendedStreamConfiguration]: return list(self._recommendations.get(usecase, [])) def getAllUsecases(self) -> List[int]: return list(self._recommendations.keys()) def addRecommendation(self, usecase: int, config: RecommendedStreamConfiguration): if usecase not in self._recommendations: self._recommendations[usecase] = [] self._recommendations[usecase].append(config) def removeRecommendation(self, usecase: int, config: RecommendedStreamConfiguration): if usecase in self._recommendations: self._recommendations[usecase] = [ c for c in self._recommendations[usecase] if c is not config ] def getMaxResolution(self, usecase: int) -> Optional[Tuple[int, int]]: return self._max_resolutions.get(usecase) def setMaxResolution(self, usecase: int, width: int, height: int): self._max_resolutions[usecase] = (width, height) def toString(self) -> str: return f"RecommendedStreamConfigurationMap(usecases={len(self._recommendations)})" # ═══════════════════════════════════════════════════════════════ # TotalCaptureResultExt — TotalCaptureResult 的扩展 # ═══════════════════════════════════════════════════════════════ class TotalCaptureResultExt(Stub): """TotalCaptureResultExt — TotalCaptureResult 的 params 扩展""" def __init__(self): super().__init__() self._results: Dict[str, Any] = {} self._partial_results: List[Any] = [] self._session_id: int = 0 self._frame_number: int = 0 self._sequence_id: int = 0 self._af_trigger_id: int = 0 self._ae_trigger_id: int = 0 self._sensor_timestamp: int = 0 self._physical_camera_results: Dict[str, Any] = {} def getResult(self, key: str) -> Optional[Any]: return self._results.get(key) def setResult(self, key: str, value: Any): self._results[key] = value def getAllResults(self) -> Dict[str, Any]: return dict(self._results) def getPartialResults(self) -> List[Any]: return list(self._partial_results) def addPartialResult(self, result: Any): self._partial_results.append(result) def getSessionId(self) -> int: return self._session_id def setSessionId(self, session_id: int): self._session_id = session_id def getFrameNumber(self) -> int: return self._frame_number def setFrameNumber(self, frame: int): self._frame_number = frame def getSequenceId(self) -> int: return self._sequence_id def setSequenceId(self, seq: int): self._sequence_id = seq def getPhysicalCameraResult(self, camera_id: str) -> Optional[Any]: return self._physical_camera_results.get(camera_id) def setPhysicalCameraResult(self, camera_id: str, result: Any): self._physical_camera_results[camera_id] = result def toString(self) -> str: return f"TotalCaptureResultExt(frame={self._frame_number}, seq={self._sequence_id})"