AR/VR/画像処理【画像処理】前方画像から真上写真を作る(パース変換、幾何補正)

【画像処理】前方画像から真上写真を作る(パース変換、幾何補正)

今回のお題は"台形の幾何補正"です。
「台形って何?」
はい、車や列車で前方を撮影した映像から、真上写真を得ること(=台形を長方形に)です。

ということで、やりたいことは
「前方映像として取得した道路や軌道の映像から、真上写真を作ろう」なのでした。

まずこちらをご覧ください。

01.png

線路が縦に並行に伸び、枕木が綺麗に直行していますね。

■前方映像(動画)からダイレクトに真上写真に変換する

やりたかったのは、静止画保存したものを(DxOなどを使って)バッチで処理することではなく、動画からダイレクトに幾何変形して、静止画ビューアで見せること。
結果から先に言えば、PythonでQGISプラグインとして実装できました。

02.png

元の動画はこういう画角です。

03.png

この赤線で囲ったところの台形を長方形に変形します。

04.png
05.png

■パース変換(幾何補正)の肝

水平線、垂直線で画像を回転する手法の、更に発展形で、パース変換という処理があります。
奥行きのある写真の投影変換ですね。台形を起こして長方形にします。

私が大変重用しているDxOの"PhotoLab"というのがあります。
パース変換を自動でしてくれたり、台形を指定して長方形に強要することもできます。

DxO PhotoLabによる変換の例

dxo_01.png
dxo_02.png
dxo_03.png

一枚設定して、補正情報をコピーペーストすることで、一括処理にも対応しています。

dxo_04.png
dxo_05.png

台形補正(Keystoning)の設定パラメータ


DxOで処理をすると、*.dopファイルというのが生成されて、そこに処理時のパラメータがテキスト形式で記録されています。

この.dxoファイルの中に、KeystoningPointというパラメータがあり、これがまさに「この台形を長方形にしたよ」の証です。
座標値は正規化されており、縦横画素数を掛けると指定したX,Y座標になってます。

Overrides = {
KeystoningActive = true,
KeystoningPoints = {
0.4344940185546875,
-0.0019125227117910981,
0.35461607575416565,
0.97519111633300781,
0.68816280364990234,
1.0101222991943359,
0.5928160548210144,
-0.0090487487614154816,
}
,
KeystoningMode = "Parallel",
CropRect = {
0.33949017524719238,
0.029555149376392365,
0.35562554001808167,
0.54165977239608765,
}
,
CropActive = true,
CropRatio = -1,
}
,

このパラメータを使って台形を長方形に変形すれば良い・・・と分かったところで、実際にPythonのプログラムです。

■台形変換をする幾何補正プログラム:Keystoning.py

DxOをパラメータ生成器として使って、DxOが書き出してくれたパラメータを流用するという"コバンザメ戦略"で行きます。
自作で画像エディタみたいなものを作るのが面倒臭いし、「どこをどう写像する」の情報が欲しいだけなので。

とはいえ、撮影の度、カメラの画角も対象物との図形的な関係も毎回変わってくるので、ここはプログラム内に固定値ではなく、外部jsonから供給します。

keystoning.json
{
"mode": "parallel",
"keystoning_points": [
[0.42649209499359131,0.13081993162631989],
[0.60012799501419067, 0.13193754851818085],
[0.64911401271820068,0.65973210334777832],
[0.37617066502571106,0.65972703695297241]
],
"crop_rect": [0.3010, 0.0885, 0.4106, 0.5733],
"output_size": [800, 600]
}

・機能
指定したディレクトリのjpegを読み込む load_images
パース変換 keystoning_from_json
不要削除 trim_bottom

・変換処理の詳細

# 補正対象の台形4点(元画像座標系)
# 台形の寸法とスケール調整
# 元画像の四隅を変形後の座標に変換
# 平行移動を加えて左上が (0, 0) になるよう補正
# 補正画像を生成
# 台形の中心を補正後座標系で取得
アフィン変換で画像全体を中央に移動
# ✅ ステップ1:縦方向にスケーリング(器の寸法を再構成)
# 台形の底辺と高さを補正後+平行移動後の座標で取得
# スケーリング後の座標に合わせて変換
# 下端から上に向かって、黒以外のピクセルが現れる位置を探す
# 黒い余白を切り落とす

・keystoning.pyの使い方

私がQGISプラグインのビューア表示時に使用している例です。
サンプルとしてkeystoning.pyとkeystoning.jsonを添付しますが、余分なメソッドや作りかけの部品(残骸)も多く残っています。

ここで、frameというのは、動画をOpenCVで開いてキャプチャした静止画です。

宣言部
from . import keystoning as ks

処理部
# 1. 映像取得
index = max(0, min(index, self.total_frames - 1))
self.current_index = index
print(f"🎯 cap.set → index={index}")
self.cap.set(cv2.CAP_PROP_POS_FRAMES, index)
ret, frame = self.cap.read()
print(f"📽️ cap.read → ret={ret}, frame={'OK' if frame is not None else 'None'}")
if not ret or frame is None:
self.image_label.setText("❌ フレーム取得失敗")
return
target_width = frame.shape[1]

try:
keystoning =ks.keystoning_from_json(index,frame, "keystoning")
angle = ks.detect_rail_angle_with_pairing(keystoning)
if (angle is not None) and (80 <= angle <= 100) and (abs(90-angle) >= 1.0):
print(f"🚃 レールの角度を検出しました: {angle:.2f}度、垂直に補正します")
keystoning = ks.rotate_and_crop(keystoning, angle,target_width) # ← 回転によって生じる黒い部分をトリミングする
frame= ks.trim_bottom(keystoning,0.01)
except Exception as e:
print("幾何変形失敗:",e)

・keystoning.pyの処理時間

2998ファイル@8フォルダ 1.05GB 11分 →4.5fps

0045_45000-45999_keystoning 343ファイル 1分
0046_46000-46999_keystoning 579ファイル 2分
0047_47000-47999_keystoning 460ファイル 2分
0048_48000-48999_keystoning 249ファイル 1分
0049_49000-49999_keystoning 391ファイル 2分
0050_50000-50999_keystoning 256ファイル 1分
0051_51000-51999_keystoning 350ファイル 1分
0052_52000-52999_keystoning 362ファイル 1分

※計測はExplorerのタイムスタンプ(分)での計測なので正確さは無い!

 

 

▼この記事を書いたひと

001@2x.png

R&Dセンター 松井 良行

R&Dセンター 技術戦略担当部長。コンピュータと共に35年。そしてこれからも!

 

 

AR/VR/画像処理の最新記事


お問い合わせ

ご意見・ご質問などお気軽にお問い合わせ下さい。
ナカシャクリエイテブ株式会社

●富士見事務所 TEL : 052-228-8733 FAX : 052-323-3337
〒460-0014 愛知県名古屋市中区富士見町13−22 ファミール富士見711  地図
交通部 R&Dセンター

Email:メールでのお問い合わせ