"""
overlay
画像処理の結果をカメラ映像に重ねて描画するモジュール
チェックボックスで個別に ON/OFF できる
"""
from dataclasses import dataclass
import cv2
import numpy as np
from common import config
from pc.vision import line_detector
from pc.vision.line_detector import LineDetectResult
# 描画色の定義 (BGR)
COLOR_NEAR: tuple = (0, 255, 0)
COLOR_FAR: tuple = (255, 0, 0)
COLOR_CENTER: tuple = (0, 255, 255)
COLOR_TEXT: tuple = (255, 255, 255)
# 二値化オーバーレイの不透明度
BINARY_OPACITY: float = 0.4
@dataclass
class OverlayFlags:
"""オーバーレイ表示項目のフラグ
Attributes:
binary: 二値化画像の半透明表示
near_region: 近方領域の枠
far_region: 遠方領域の枠
centroids: 重心マーカー
center_line: 画像中心線
error_text: 偏差の数値表示
"""
binary: bool = False
near_region: bool = False
far_region: bool = False
centroids: bool = False
center_line: bool = False
error_text: bool = False
def draw_overlay(
frame: np.ndarray,
result: LineDetectResult | None,
flags: OverlayFlags,
) -> np.ndarray:
"""カメラ映像にオーバーレイを描画する
Args:
frame: 元の BGR カメラ画像
result: 線検出の結果(None の場合はオーバーレイなし)
flags: 表示項目のフラグ
Returns:
オーバーレイ描画済みの画像
"""
display = frame.copy()
if result is None:
return display
# 二値化画像の半透明オーバーレイ
if flags.binary and result.binary_image is not None:
display = _draw_binary_overlay(
display, result.binary_image,
)
# 近方領域の枠
if flags.near_region:
cv2.rectangle(
display,
(0, line_detector.NEAR_Y_START),
(
config.FRAME_WIDTH - 1,
line_detector.NEAR_Y_END - 1,
),
COLOR_NEAR, 1,
)
# 遠方領域の枠
if flags.far_region:
cv2.rectangle(
display,
(0, line_detector.FAR_Y_START),
(
config.FRAME_WIDTH - 1,
line_detector.FAR_Y_END - 1,
),
COLOR_FAR, 1,
)
# 画像中心線
if flags.center_line:
center_x = config.FRAME_WIDTH // 2
cv2.line(
display,
(center_x, 0),
(center_x, config.FRAME_HEIGHT),
COLOR_CENTER, 1,
)
# 重心マーカー
if flags.centroids:
if result.near_x is not None:
near_y = (
line_detector.NEAR_Y_START
+ line_detector.NEAR_Y_END
) // 2
cv2.circle(
display,
(int(result.near_x), near_y),
6, COLOR_NEAR, -1,
)
if result.far_x is not None:
far_y = (
line_detector.FAR_Y_START
+ line_detector.FAR_Y_END
) // 2
cv2.circle(
display,
(int(result.far_x), far_y),
6, COLOR_FAR, -1,
)
# 偏差の数値表示
if flags.error_text:
_draw_error_text(display, result)
return display
def _draw_binary_overlay(
frame: np.ndarray,
binary: np.ndarray,
) -> np.ndarray:
"""二値化画像を半透明で重ねる
Args:
frame: 元の BGR 画像
binary: 二値化画像(グレースケール)
Returns:
合成された画像
"""
# 二値化画像を赤色の BGR に変換
binary_bgr = np.zeros_like(frame)
binary_bgr[:, :, 2] = binary
# 半透明合成
return cv2.addWeighted(
frame, 1.0 - BINARY_OPACITY,
binary_bgr, BINARY_OPACITY, 0,
)
def _draw_error_text(
frame: np.ndarray,
result: LineDetectResult,
) -> None:
"""偏差の数値を画像に描画する
Args:
frame: 描画先の画像
result: 線検出の結果
"""
near_str = (
f"near: {result.near_error:+.3f}"
if result.near_x is not None
else "near: N/A"
)
far_str = (
f"far: {result.far_error:+.3f}"
if result.far_x is not None
else "far: N/A"
)
cv2.putText(
frame, near_str, (5, 15),
cv2.FONT_HERSHEY_SIMPLEX, 0.4,
COLOR_TEXT, 1,
)
cv2.putText(
frame, far_str, (5, 30),
cv2.FONT_HERSHEY_SIMPLEX, 0.4,
COLOR_TEXT, 1,
)