"""line_detector モジュールのテスト"""
import numpy as np
import pytest
from common import config
from pc.vision.line_detector import (
ImageParams,
LineDetectResult,
build_result,
detect_line,
fit_row_centers,
no_detection,
)
class TestNoDetection:
"""no_detection のテスト"""
def test_returns_not_detected(
self, blank_image: np.ndarray,
) -> None:
"""detected=False で全フィールドがデフォルト値"""
result = no_detection(blank_image)
assert result.detected is False
assert result.position_error == 0.0
assert result.heading == 0.0
assert result.curvature == 0.0
assert result.poly_coeffs is None
assert result.row_centers is None
assert result.binary_image is not None
class TestBuildResult:
"""build_result のテスト"""
def test_straight_center_line(self) -> None:
"""画像中央の直線は position_error ≈ 0"""
h = config.FRAME_HEIGHT
w = config.FRAME_WIDTH
center_x = w / 2.0
# x = center_x (定数) → coeffs = [0, 0, center_x]
coeffs = np.array([0.0, 0.0, center_x])
binary = np.zeros((h, w), dtype=np.uint8)
result = build_result(coeffs, binary)
assert result.detected is True
assert result.position_error == pytest.approx(
0.0, abs=0.01,
)
assert result.heading == pytest.approx(
0.0, abs=0.01,
)
assert result.curvature == pytest.approx(
0.0, abs=0.01,
)
def test_offset_line(self) -> None:
"""左にオフセットした直線は position_error > 0"""
h = config.FRAME_HEIGHT
w = config.FRAME_WIDTH
# 左寄りの直線
offset_x = w / 4.0
coeffs = np.array([0.0, 0.0, offset_x])
binary = np.zeros((h, w), dtype=np.uint8)
result = build_result(coeffs, binary)
assert result.position_error > 0 # 中心より左
class TestDetectLine:
"""detect_line のテスト"""
def test_current_detects_straight_line(
self, straight_line_image: np.ndarray,
) -> None:
"""現行手法で中央の直線を検出できる"""
# 小さいテスト画像用にパラメータを調整
params = ImageParams(
method="current",
clahe_grid=2, blur_size=3,
open_size=1, close_width=3,
close_height=1,
)
result = detect_line(
straight_line_image, params,
)
assert result.detected is True
assert abs(result.position_error) < 0.5
def test_current_no_line(
self, blank_image: np.ndarray,
) -> None:
"""均一画像では線を検出しない"""
params = ImageParams(method="current")
result = detect_line(blank_image, params)
assert result.detected is False
def test_blackhat_detects_straight_line(
self, straight_line_image: np.ndarray,
) -> None:
"""案A で中央の直線を検出できる"""
params = ImageParams(
method="blackhat",
blackhat_ksize=15,
binary_thresh=30,
blur_size=3,
iso_close_size=1,
dist_thresh=0.0,
min_line_width=1,
median_ksize=0,
neighbor_thresh=0.0,
residual_thresh=0.0,
)
result = detect_line(
straight_line_image, params,
)
assert result.detected is True
def test_dual_norm_detects_straight_line(
self, straight_line_image: np.ndarray,
) -> None:
"""案B で中央の直線を検出できる"""
params = ImageParams(
method="dual_norm",
bg_blur_ksize=21,
adaptive_block=11, adaptive_c=5,
iso_close_size=1,
dist_thresh=0.0,
min_line_width=1,
median_ksize=0,
neighbor_thresh=0.0,
residual_thresh=0.0,
)
result = detect_line(
straight_line_image, params,
)
assert result.detected is True
def test_robust_detects_straight_line(
self, straight_line_image: np.ndarray,
) -> None:
"""案C で中央の直線を検出できる"""
params = ImageParams(
method="robust",
blackhat_ksize=15,
adaptive_block=11, adaptive_c=5,
iso_close_size=1,
dist_thresh=0.0,
min_line_width=1,
median_ksize=0,
neighbor_thresh=0.0,
residual_thresh=0.0,
)
result = detect_line(
straight_line_image, params,
)
assert result.detected is True
def test_valley_detects_straight_line(
self, straight_line_image: np.ndarray,
) -> None:
"""案D で中央の直線を検出できる"""
params = ImageParams(method="valley")
result = detect_line(
straight_line_image, params,
)
assert result.detected is True
def test_default_params(
self, straight_line_image: np.ndarray,
) -> None:
"""params=None でもデフォルトで動作する"""
result = detect_line(straight_line_image)
assert isinstance(result, LineDetectResult)
def test_result_has_binary_image(
self, straight_line_image: np.ndarray,
) -> None:
"""結果に二値化画像が含まれる"""
result = detect_line(straight_line_image)
assert result.binary_image is not None
assert result.binary_image.shape == (
config.FRAME_HEIGHT, config.FRAME_WIDTH,
)
class TestFitRowCenters:
"""fit_row_centers のテスト"""
def test_detects_binary_line(
self, binary_line: np.ndarray,
) -> None:
"""二値画像の白線から中心をフィッティングできる"""
result = fit_row_centers(
binary_line, min_width=1,
)
assert result.detected is True
assert abs(result.position_error) < 0.3
def test_empty_binary(self) -> None:
"""白ピクセルがない二値画像では検出しない"""
h, w = config.FRAME_HEIGHT, config.FRAME_WIDTH
empty = np.zeros((h, w), dtype=np.uint8)
result = fit_row_centers(empty, min_width=1)
assert result.detected is False