यह guide केवल जापानी में उपलब्ध है (यह मनुष्य द्वारा लिखा गद्य है, इसलिए इसमें कुछ भी machine से बदला नहीं गया है)।
位置は分かっているが向きが分からない固定カメラ(道路・気象・観光の公共カメラ、工場の定点カメラ)の (yaw, pitch, roll) を、写真そのものから、学習なしで決める層です。向きが決まると、写真を地図・DEM・3D 都市に置ける(逆に、向きが無いと置けない —— 公開されているカメラ一覧の多くは位置だけで、向きは道路の増減方向や手校正しかありません)。
手掛かりは 2 つで、どちらも閉形式に近い:
• 太陽: 太陽の見かけの位置は時刻と場所の関数(NOAA の太陽計算、Meeus 1998)。時刻つきの写真で太陽の画素を 2 点以上拾えば、回転は Wahba 問題の SVD 解(Kabsch 1976)で一意に決まる。先行 = Lalonde・Narasimhan・Efros, IJCV 2010(webcam 22 台で 3° 以内)、Jacobs ら WACV 2008。
• スカイライン: カメラ位置から DEM で描いた 360° の稜線と、写真から動的計画法(Lie・Lin・Hsu 2005)で抜いた空と地形の境界を照合する。先行 = Baatz・Saurer・Köser・Pollefeys, ECCV 2012(位置未知の大規模版)。この族は位置既知・1 台に絞り、代わりに曖昧さ(yaw の谷が複数)を返す。
7 op / 3 カテゴリ(numpy + scipy。台帳は opsgeocam.py、実体は geocam.py):
• sun(3) — sun_position / sun_pixel_position / camera_orientation_from_sun: 時刻 → 太陽の方位・仰角(屈折込み)、写真 → 太陽の画素、太陽の画素 ≥ 2 点 → 姿勢。
• skyline(3) — dem_skyline / skyline_extract / render_skyline_view: DEM の 1 点 × 全方位の地平線仰角、写真 → 列ごとの境界の行、姿勢 → その向きの空のマスク(重ね描き・合成)。
• orientation(1) — camera_orientation_from_skyline: 境界 + DEM スカイライン → 姿勢 + yaw の残差曲線 + 曖昧さ。
世界 = ENU(東・北・上)、方位は北 0° 時計回り、仰角は水平 0°。カメラ = OpenCV(右・下・前)、K = (fx, fy, cx, cy)。yaw = 光軸の方位、pitch = 光軸の仰角(上が正)、roll = 光軸まわり(正でカメラが右に傾く)。R_wc = Rz(−yaw)·Rx(pitch)·Ry(roll)·B。DEM は行 0 が北端。太陽の方位・仰角も同じ規約なので、2 経路の答えはそのまま比べられます。
Mermaid diagram (source):
flowchart LR
T[時刻 + 緯度経度] --> SP[sun_position]
I[写真] --> SPX[sun_pixel_position]
SPX --> OS[camera_orientation_from_sun]
SP --> OS
D[DEM] --> DS[dem_skyline]
I --> SE[skyline_extract]
DS --> OK[camera_orientation_from_skyline]
SE --> OK
OK --> RV[render_skyline_view]
OS --> RV
RV --> OV[写真に重ねて目で確かめる]
import numpy as np import fullseye as fs dem = ... # (H, W) 標高 [m]、行 0 が北端 K = (280.0, 280.0, 160.0, 120.0) # fx, fy, cx, cy sky = fs.ledger.dem_skyline(dem, 30.0, (150, 150), eye_height=5.0, az_step_deg=0.5) rows = fs.ledger.skyline_extract(photo) # (W,) 列ごとの空と地形の境界の行 est = fs.ledger.camera_orientation_from_skyline(rows, K, sky) print(est["yaw_deg"], est["pitch_deg"], est["roll_deg"], est["ambiguous"], est["margin_deg"]) # 同じカメラを太陽で検算(同じ日の朝と夕の 2 枚があれば足りる) uv = np.vstack([fs.ledger.sun_pixel_position(morning), fs.ledger.sun_pixel_position(evening)]) est2 = fs.ledger.camera_orientation_from_sun(uv, [t_morning, t_evening], lat, lon, K) sky_mask = fs.ledger.render_skyline_view(sky, K, photo.shape, est["yaw_deg"], est["pitch_deg"], est["roll_deg"])
• sun_position: 春分の正午に仰角 = 90 − |緯度|、夏至の正午に赤道で 90 − 23.44°、方位が東 → 南 → 西と単調、赤緯は ±23.44° 以内。
• 姿勢の往復: _rotation → _pose_from_rotation が 1e−9 で戻る。画面の上の画素は仰角が高く、yaw 90° で光軸は東。
• 描いて → 抜いて → 当てる: 合成 DEM で render_skyline_view した空のマスクに雑音を足し、skyline_extract → camera_orientation_from_skyline で yaw / pitch / roll が 0.1° 以内に戻る。
• 太陽: 真の姿勢で投影した太陽の画素(0.5 px の雑音)から camera_orientation_from_sun が 0.05° 以内に戻る。1 点・共線(同じ時刻)は ValueError。
• 平地の DEM ではスカイラインが全方位で同じ → ambiguous=True(黙って 1 つを返さない)。
• スカイラインは山があってこそ。平地・海・都市の建物(DEM に無い)では谷が複数、margin が小さい。ambiguous を必ず読む。
• 太陽は写っていてこそ。曇天・夜・太陽が画角外の向きでは sun_pixel_position が ValueError。太陽より明るい人工光源がある夜景では使えない。
• DEM の分解能より細かい稜線は描けない(30 m DEM で仰角 ≈ 0.1° の粗さ)。遠い山は地球の丸みと屈折で沈む(earth_curvature=True が既定)。
• 内部行列 K は要る(EXIF の焦点距離か、既知の画角から fx = W / (2 tan(fov/2)))。K の誤りは pitch と roll に化ける。
• 生の公開カメラ画像は repo に入れない(ライセンスと人物・車両)。この族は幾何だけを扱い、写っているものの認識はしない。
合成では sun_pixel_position で足りたが、実写の道路カメラでは局名の白い文字・標識・白い車が飽和して見た目では太陽と区別できない(300 局の探針で 24/24 が誤検出)。太陽は円盤でなく大きなブルームで、画像の上端や文字の帯で切れる。そこで 2 op を足した:
• sun_bloom_fit —— 最大の飽和塊の切れていない縁に円を当てて中心を返す(重心は切れた側の反対へ偏る)。太陽とは決めない。
• camera_orientation_from_sun_candidates —— フレームごとの候補(太陽も車も混在)から、時刻どおりに動く 1 本を RANSAC で選び、道路カメラの事前知識で非物理な仮説を捨て、焦点距離も探索する。1 時間の低仰角の弧では yaw は強く決まり、roll と焦点距離は弱い(loo_px と n_inliers を見る)。