201 lines
11 KiB
Python
201 lines
11 KiB
Python
"""Dispatch logic for decoded frames (MFD/SAR/generic).
|
|
|
|
Provides `dispatch_frame(module, frame, leader)` which encapsulates
|
|
recording, saving previews, and invoking registered callbacks.
|
|
"""
|
|
from typing import Any, Optional
|
|
import logging
|
|
import os
|
|
import time
|
|
import pathlib
|
|
|
|
try:
|
|
from PIL import Image, ImageOps, ImageEnhance
|
|
except Exception:
|
|
Image = None
|
|
ImageOps = None
|
|
ImageEnhance = None
|
|
|
|
|
|
def dispatch_frame(module: Any, frame: Any, leader: Optional[Any]) -> None:
|
|
"""Handle a decoded frame: start/ write video, save previews, and call callbacks.
|
|
|
|
`module` is the `SfpConnectorModule` instance.
|
|
"""
|
|
try:
|
|
# Determine image type
|
|
image_type_id = None
|
|
try:
|
|
if leader is not None:
|
|
image_type_id = int(leader.HEADER_DATA.TYPE)
|
|
except Exception:
|
|
image_type_id = None
|
|
|
|
# SAR path
|
|
if image_type_id == getattr(module, 'IMAGE_TYPE_SAR', 2):
|
|
try:
|
|
if Image is not None and hasattr(frame, 'copy'):
|
|
sar_img = frame.copy()
|
|
if getattr(module, '_sar_autocontrast', False) and ImageOps is not None:
|
|
try:
|
|
sar_img = ImageOps.autocontrast(sar_img)
|
|
except Exception:
|
|
pass
|
|
try:
|
|
bf = max(0.0, 1.0 + (float(getattr(module, '_sar_brightness', 0)) / 100.0))
|
|
cf = max(0.0, 1.0 + (float(getattr(module, '_sar_contrast', 0)) / 100.0))
|
|
if bf != 1.0 and ImageEnhance is not None:
|
|
sar_img = ImageEnhance.Brightness(sar_img).enhance(bf)
|
|
if cf != 1.0 and ImageEnhance is not None:
|
|
sar_img = ImageEnhance.Contrast(sar_img).enhance(cf)
|
|
except Exception:
|
|
pass
|
|
|
|
# save sar png
|
|
if getattr(module, '_sar_save_png', False):
|
|
try:
|
|
if getattr(module, '_dump_manager', None) is not None:
|
|
saved = module._dump_manager.save_preview_from_pil(sar_img, category='sar', fmt='png')
|
|
if saved:
|
|
logging.getLogger().info("VideoReceiverSFP: saved sar png %s", saved)
|
|
else:
|
|
ts = time.strftime('%Y%m%d_%H%M%S') + (f"_{int(time.time() * 1000) % 1000:03d}")
|
|
dumps_dir = getattr(module, '_dumps_dir', os.path.join(os.getcwd(), 'dumps'))
|
|
os.makedirs(dumps_dir, exist_ok=True)
|
|
pngpath = os.path.join(dumps_dir, f'VideoReceiverSFP_sar_{ts}.png')
|
|
sar_img.save(pngpath)
|
|
module._saved_sar_pngs.append(pngpath)
|
|
while len(module._saved_sar_pngs) > module._dump_keep:
|
|
old = module._saved_sar_pngs.popleft()
|
|
try:
|
|
pathlib.Path(old).unlink()
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
logging.getLogger().exception("VideoReceiverSFP: failed saving sar png frame")
|
|
|
|
# sar video
|
|
try:
|
|
if getattr(module, '_record_sar_video', False) and getattr(module, '_dump_manager', None) is not None:
|
|
if not getattr(module, '_sar_video_active', False):
|
|
try:
|
|
w, h = None, None
|
|
if Image is not None and hasattr(sar_img, 'size'):
|
|
w, h = sar_img.size
|
|
else:
|
|
try:
|
|
import numpy as _np
|
|
arr = _np.asarray(sar_img)
|
|
if arr is not None and arr.ndim >= 2:
|
|
h, w = arr.shape[0], arr.shape[1]
|
|
except Exception:
|
|
pass
|
|
if w and h:
|
|
started = module._dump_manager.start_video_record('sar', w, h, fps=getattr(module, '_video_fps', 20))
|
|
if started:
|
|
module._sar_video_active = True
|
|
try:
|
|
logging.getLogger().info("VideoReceiverSFP: started SAR video writer (%dx%d @ %s FPS)", w, h, getattr(module, '_video_fps', 20))
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
logging.getLogger().exception("VideoReceiverSFP: failed to start sar video writer")
|
|
if getattr(module, '_sar_video_active', False):
|
|
try:
|
|
module._dump_manager.write_video_frame(sar_img, 'sar')
|
|
except Exception:
|
|
logging.getLogger().exception("VideoReceiverSFP: failed writing sar frame to video")
|
|
except Exception:
|
|
pass
|
|
|
|
# deliver to callbacks
|
|
for scb in list(getattr(module, '_sar_callbacks', [])):
|
|
try:
|
|
scb(sar_img)
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
logging.getLogger().exception("VideoReceiverSFP: error processing SAR image for SAR callbacks")
|
|
|
|
# MFD path
|
|
if image_type_id == getattr(module, 'IMAGE_TYPE_MFD', 1):
|
|
try:
|
|
try:
|
|
if getattr(module, '_record_mfd_video', False) and getattr(module, '_dump_manager', None) is not None:
|
|
if not getattr(module, '_mfd_video_active', False):
|
|
try:
|
|
w, h = None, None
|
|
if Image is not None and hasattr(frame, 'size'):
|
|
w, h = frame.size
|
|
else:
|
|
try:
|
|
import numpy as _np
|
|
arr = _np.asarray(frame)
|
|
if arr is not None and arr.ndim >= 2:
|
|
h, w = arr.shape[0], arr.shape[1]
|
|
except Exception:
|
|
pass
|
|
if w and h:
|
|
started = module._dump_manager.start_video_record('mfd', w, h, fps=getattr(module, '_video_fps', 20))
|
|
if started:
|
|
module._mfd_video_active = True
|
|
try:
|
|
logging.getLogger().info("VideoReceiverSFP: started MFD video writer (%dx%d @ %s FPS)", w, h, getattr(module, '_video_fps', 20))
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
logging.getLogger().exception('VideoReceiverSFP: failed to start mfd video writer')
|
|
if getattr(module, '_mfd_video_active', False):
|
|
try:
|
|
module._dump_manager.write_video_frame(frame, 'mfd')
|
|
except Exception:
|
|
logging.getLogger().exception('VideoReceiverSFP: failed writing mfd frame to video')
|
|
try:
|
|
if not getattr(module, '_mfd_callbacks', False):
|
|
logging.getLogger().debug('VideoReceiverSFP: mfd frames are being recorded but no MFD callbacks are registered')
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
for mcb in list(getattr(module, '_mfd_callbacks', [])):
|
|
try:
|
|
mcb(frame)
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
|
|
# generic callbacks and saving
|
|
for cb in list(getattr(module, '_callbacks', [])):
|
|
try:
|
|
if getattr(module, '_save_png', False) and Image is not None and hasattr(frame, 'save'):
|
|
try:
|
|
if getattr(module, '_dump_manager', None) is not None:
|
|
saved = module._dump_manager.save_preview_from_pil(frame, category='frame', fmt='png')
|
|
if saved:
|
|
logging.getLogger().info("VideoReceiverSFP: saved frame to %s", saved)
|
|
else:
|
|
ts = time.strftime('%Y%m%d_%H%M%S') + (f"_{int(time.time() * 1000) % 1000:03d}")
|
|
dumps_dir = getattr(module, '_dumps_dir', os.path.join(os.getcwd(), 'dumps'))
|
|
os.makedirs(dumps_dir, exist_ok=True)
|
|
pngpath = os.path.join(dumps_dir, f'VideoReceiverSFP_frame_{ts}.png')
|
|
frame.save(pngpath)
|
|
module._saved_pngs.append(pngpath)
|
|
while len(module._saved_pngs) > module._dump_keep:
|
|
old = module._saved_pngs.popleft()
|
|
try:
|
|
pathlib.Path(old).unlink()
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
logging.getLogger().exception('VideoReceiverSFP: failed to save preview png')
|
|
try:
|
|
cb(frame)
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
except Exception:
|
|
logging.getLogger().exception('VideoReceiverSFP: dispatch_frame unexpected error')
|