101 lines
4.7 KiB
Python
101 lines
4.7 KiB
Python
"""MFD parameters state management for VideoReceiverSFP.
|
|
|
|
Self-contained state manager for MFD category parameters matching ControlPanel behavior.
|
|
"""
|
|
import logging
|
|
import numpy as np
|
|
from typing import Dict, Any
|
|
|
|
|
|
class MfdState:
|
|
"""Manages MFD parameters (categories, intensities, colors, raw_map) and LUT generation."""
|
|
|
|
def __init__(self):
|
|
self.mfd_params: Dict[str, Any] = self._initialize_mfd_params()
|
|
self.mfd_lut: np.ndarray = np.zeros((256, 3), dtype=np.uint8)
|
|
self._rebuild_lut()
|
|
|
|
def _initialize_mfd_params(self) -> Dict[str, Any]:
|
|
"""Initialize MFD parameters matching ControlPanel defaults."""
|
|
params = {
|
|
"categories": {
|
|
"Occlusion": {"color": (0, 0, 0), "intensity": 255, "pixels": [0, 1]},
|
|
"Cat A": {"color": (255, 255, 255), "intensity": 255, "pixels": [2]},
|
|
"Cat B": {"color": (255, 255, 255), "intensity": 255, "pixels": [3, 18]},
|
|
"Cat C": {"color": (255, 255, 255), "intensity": 255, "pixels": [4, 5, 6, 16]},
|
|
"Cat C1": {"color": (255, 255, 255), "intensity": 255, "pixels": [7, 8, 9]},
|
|
"Cat C2": {"color": (255, 255, 255), "intensity": 255, "pixels": [10, 11, 12]},
|
|
"Cat C3": {"color": (255, 255, 255), "intensity": 255, "pixels": [13, 14, 15]},
|
|
"Reserved": {"color": (0, 0, 0), "intensity": 255, "pixels": list(range(17, 32))},
|
|
},
|
|
"raw_map_intensity": 255,
|
|
}
|
|
# Create reverse mapping pixel_to_category
|
|
params["pixel_to_category"] = {
|
|
p: cat for cat, data in params["categories"].items() for p in data["pixels"]
|
|
}
|
|
return params
|
|
|
|
def update_category_intensity(self, category_name: str, intensity_value: int) -> bool:
|
|
"""Update intensity for a category and rebuild LUT. Returns True if updated."""
|
|
if category_name not in self.mfd_params["categories"]:
|
|
logging.warning(f"[MfdState] Unknown category: {category_name}")
|
|
return False
|
|
clamped = int(np.clip(intensity_value, 0, 255))
|
|
self.mfd_params["categories"][category_name]["intensity"] = clamped
|
|
self._rebuild_lut()
|
|
return True
|
|
|
|
def update_category_color(self, category_name: str, bgr_tuple: tuple) -> bool:
|
|
"""Update color (BGR) for a category and rebuild LUT. Returns True if updated."""
|
|
if category_name not in self.mfd_params["categories"]:
|
|
logging.warning(f"[MfdState] Unknown category: {category_name}")
|
|
return False
|
|
# ensure BGR tuple with clamping
|
|
new_bgr = tuple(int(np.clip(c, 0, 255)) for c in bgr_tuple)
|
|
self.mfd_params["categories"][category_name]["color"] = new_bgr
|
|
self._rebuild_lut()
|
|
return True
|
|
|
|
def update_raw_map_intensity(self, intensity_value: int) -> bool:
|
|
"""Update raw_map_intensity and rebuild LUT. Returns True if updated."""
|
|
clamped = int(np.clip(intensity_value, 0, 255))
|
|
self.mfd_params["raw_map_intensity"] = clamped
|
|
self._rebuild_lut()
|
|
return True
|
|
|
|
def _rebuild_lut(self):
|
|
"""Rebuild the MFD LUT from current parameters (matches ControlPanel logic)."""
|
|
try:
|
|
params = self.mfd_params
|
|
raw_map_factor = params["raw_map_intensity"] / 255.0
|
|
pixel_map = params["pixel_to_category"]
|
|
categories = params["categories"]
|
|
|
|
# Build in BGR order (ControlPanel convention)
|
|
bgr_lut = np.zeros((256, 3), dtype=np.uint8)
|
|
|
|
for idx in range(256):
|
|
cat_name = pixel_map.get(idx)
|
|
if cat_name:
|
|
cat_data = categories[cat_name]
|
|
bgr = cat_data["color"]
|
|
intensity_factor = cat_data["intensity"] / 255.0
|
|
bgr_lut[idx, 0] = int(np.clip(int(round(float(bgr[0]) * intensity_factor)), 0, 255))
|
|
bgr_lut[idx, 1] = int(np.clip(int(round(float(bgr[1]) * intensity_factor)), 0, 255))
|
|
bgr_lut[idx, 2] = int(np.clip(int(round(float(bgr[2]) * intensity_factor)), 0, 255))
|
|
elif 32 <= idx <= 255:
|
|
raw_intensity = (float(idx) - 32.0) * (255.0 / 223.0)
|
|
final_gray = int(round(np.clip(raw_intensity * raw_map_factor, 0, 255)))
|
|
bgr_lut[idx, :] = final_gray
|
|
|
|
# Convert BGR -> RGB for PIL consumers
|
|
self.mfd_lut = bgr_lut[:, ::-1].copy()
|
|
except Exception:
|
|
logging.exception("[MfdState] Error rebuilding LUT")
|
|
self.mfd_lut = np.zeros((256, 3), dtype=np.uint8)
|
|
|
|
def get_lut(self) -> np.ndarray:
|
|
"""Return current MFD LUT (256x3 RGB uint8)."""
|
|
return self.mfd_lut
|