Coverage for graphica/core/cvd_simulation.py: 100.0%
16 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-12 16:38 +0000
1"""色覚のシミュレーション。GUI には依存しない。
3sRGB に直接掛ける近似の行列(Brettel / Viénot を簡略化したもの)で、プレビュー用。各行の和が 1 なので白・黒・灰は変わらない。
4"""
5import numpy as np
7# 1型(赤)・2型(緑)・3型(青)
8CVD_MATRICES = {
9 'protanopia': np.array([
10 [0.567, 0.433, 0.000],
11 [0.558, 0.442, 0.000],
12 [0.000, 0.242, 0.758],
13 ]),
14 'deuteranopia': np.array([
15 [0.625, 0.375, 0.000],
16 [0.700, 0.300, 0.000],
17 [0.000, 0.300, 0.700],
18 ]),
19 'tritanopia': np.array([
20 [0.950, 0.050, 0.000],
21 [0.000, 0.433, 0.567],
22 [0.000, 0.475, 0.525],
23 ]),
24}
26CVD_TYPE_LABELS = {
27 'protanopia': '1型(P型、赤色覚異常)',
28 'deuteranopia': '2型(D型、緑色覚異常)',
29 'tritanopia': '3型(T型、青色覚異常)',
30}
33def simulate_rgb_array(rgb_array: np.ndarray, cvd_type: str) -> np.ndarray:
34 """形が (..., 3) の RGB に行列を掛ける。0〜255 のままでよい(スケールは保たれる)。クリップと型の変換は呼び出し側。"""
35 if cvd_type not in CVD_MATRICES:
36 raise ValueError(f"未知の色覚タイプです: {cvd_type}")
37 matrix = CVD_MATRICES[cvd_type]
38 original_shape = rgb_array.shape
39 flat = np.asarray(rgb_array, dtype=np.float64).reshape(-1, 3)
40 simulated = flat @ matrix.T
41 return simulated.reshape(original_shape)
44def simulate_hex_color(hex_color: str, cvd_type: str) -> str:
45 """#RRGGBB を1つ変換する(画像を介さない単色用)。"""
46 hex_color = hex_color.lstrip('#')
47 rgb = np.array([int(hex_color[i:i + 2], 16) for i in (0, 2, 4)], dtype=np.float64)
48 simulated = np.clip(simulate_rgb_array(rgb, cvd_type), 0, 255).astype(np.uint8)
49 return '#' + ''.join(f'{v:02x}' for v in simulated)