皆さん、こんにちは!
みやしんです。
今回は写真に写っているモノまでの距離を推定する方法をご紹介します!
ある程度大きさの分かっている物体であれば、その寸法情報を元に距離を推定することが出来ます!
自動車とか標識など、大きさが決まているモノや分かっているモノを基準にすると良いにゃ!!
では、早速始めていきましょー!
この記事で行うこと
こちらの写真に写っている自動車までの距離を推定します!スマホの広角レンズで撮っていますのでカメラキャリブレーション(OpenCV)をして写真の歪みをとった上で距離推定をします。写真上の車の大きさを調べるために物体検出(yolov5)も行います。この写真は自動車から5m離れたところから僕が撮影しました。距離推定後に5mと推定できたか推定精度の確認をしたいと思います。
メジャーを持って行って5mをピッタリ測って撮影したにゃ!!
対象物とカメラの情報を事前準備
下記の情報を事前に準備しておきましょう!
イメージセンサーって何??ってなる人も多いかもしれません。イメージセンサーとは、アナログカメラで言うとフィルム部分のことです。今回のカメラで言うと、縦3mm×横4mmのイメージセンサーを使っていて、その中に800万画素分の微小な半導体センサーが付いた四角い板になります。
イメージセンサーはこんな感じのモノです。
距離推定の原理_ピンホールカメラモデル
ピンホールカメラという極小な穴を開けたカメラモデルを用いて、下記のように距離を推定します。
- h:実物の大きさ
- d:カメラから実物までの距離
- h’:イメージセンサ上に写った大きさ
- d’:焦点距離
上記のように、d, h と d’ h’の間に相似関係が成り立ちます。
今回は自動車(VOXY)を写していますのでこんな感じです。
上の図でいくと、今回はカメラと自動車間の距離である x を推定します。
h は車の大きさ(全高)ですので既知の値です。
カメラキャリブレーションや物体検出により d’ と h’ を求めて x を算出する流れになります。
カメラキャリブレーション OpenCV
広角カメラで撮影すると画像が歪みますので、カメラキャリブレーションをして歪みを補正します。チェスボード柄を色々な角度から40枚撮影して歪み補正を行いました。
カメラキャリブレーションに関しては、下記の記事に詳しく記載してありますので是非ご参考にしてみてください。今回は画像サイズは縮小しないで行っています。(800万画素のままキャリブレーションを実施しています)
カメラキャリブレーションによって得られた各パラメータが以下になります。
この値を用いて写真の歪み補正を行います。
フォルダー構成はこんな感じです。
ImageUndistortion.py
import cv2
import numpy as np
import os
import glob
# チェスボード画像から算出したカメラパラメータを設定
fx = 1520.34696
fy = 1533.06074
Cx = 1637.74266
Cy = 1239.28645
mtx = np.array([[fx, 0, Cx],[0, fy, Cy],[0, 0, 1]])
# チェスボード画像から算出した歪係数を設定
k1 = 0.03117193
k2 = -0.0282226
p1 = 0.00190641
p2 = 0.00157755
k3 = 0.01867061
dist = np.array([[k1, k2, p1, p2, k3]])
# img_resizedフォルダー内のjpg画像を読み込む
images = glob.glob('*.jpg')
# Using the derived camera parameters to undistort the image
for filepath in images:
img = cv2.imread(filepath)
h,w = img.shape[:2]
# Refining the camera matrix using parameters obtained by calibration
# ROI:Region Of Interest(対象領域)
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w,h), 1, (w,h))
# Method 1 to undistort the image
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
# undistort関数と同じ結果が返されるので、今回はコメントアウト(initUndistortRectifyMap()関数)
# Method 2 to undistort the image
# mapx,mapy=cv2.initUndistortRectifyMap(mtx,dist,None,newcameramtx,(w,h),5)
# dst = cv2.remap(img,mapx,mapy,cv2.INTER_LINEAR)
# 歪み補正した画像をimg_undistortフォルダへ保存
cv2.imwrite('./img_undistort/undistort_' + str(filepath), dst)
cv2.waitKey(0)
結果はこんな感じです。
歪み補正前
歪み補正後
物体検出による車体の画素数を算出 YOLOv5
歪み補正をした写真を使って、車部分を物体検出します。物体検出でバウンディングBOXで囲まれて、そのBOXの縦と横の画素数が分かれば、1pixelが何メートルか(何ミリメートルか)が分かってきます。
こんなイメージです。
もちろん、車との距離が変わるとバウンディングBOXの大きさも変わりますので、1pixelあたりの長さも変わってきます。
では、実際にやってみます!
こちらの記事に物体検出の詳しいやり方をまとめていますので、是非ご参考にしてみてください。
動作環境設定
pip install -qr https://raw.githubusercontent.com/ultralytics/yolov5/master/requirements.txt
フォルダー構成
yolov5.py
import torch
import csv
# PyTorch Hubから学習済みモデルをダウンロード
model = torch.hub.load("ultralytics/yolov5", "yolov5s", pretrained=True)
# 検出できる物体を表示する(80種類)
print(model.names)
results = model("undistort_MyCar.jpg") # 画像パスを設定し、物体検出を行う
objects = results.pandas().xyxy[0] # 検出結果を取得してobjectに格納
# objectに格納したデータ
# => バウンディングBOX左上のx座標とy座標、信頼度、クラスラベル、物体名
# 物体検出結果を出力するためのcsvファイルを作成
with open('detection_Result.csv', 'w') as f:
print("ID,種類,x座標,y座標,幅,高さ", file=f) # print()の第2引数で出力先を指定可能
for i in range(len(objects)):
name = objects.name[i]
xmin = objects.xmin[i]
ymin = objects.ymin[i]
width = objects.xmax[i] - objects.xmin[i]
height = objects.ymax[i] - objects.ymin[i]
# print(f"{i}, 種類:{name}, 座標x:{xmin}, 座標y:{ymin}, 幅:{width}, 高さ:{height}")
# csvファイルにバウンディングBOX情報を出力
print(f"{i},{name},{xmin},{ymin},{width},{height}", file=f)
results.show() # 検出した物体の表示
results.crop() # 検出した物体の切り取り
検出結果
detection_Result.csv
ID | 種類 | x座標 | y座標 | 幅 | 高さ |
0 | car | 887.1334 | 1002.408 | 1435.308 | 573.3729 |
各パラメータも出力できました!
車までの距離を推定、精度検証をする
これまでに得られた情報をまとめるとこんな感じです。
続いて、pixel単位のものをmmに単位変換していきます。
具体的には、4番:焦点距離fy、5番:写真内の車の全高 です。この変換のために2番:解像度(縦) 2448pixel と 3番:イメージセンササイズ(縦) 3mm の情報を使います。
1pixelあたり何mmか?の情報を使って単位変換すると、
すると、こんな感じで必要な情報を全て「mm」の単位に統一できます。
ここまで来たら、あとは相似関係の計算をしてFinishです。
おおおおおお!
実際の距離は5,000mmだから、誤差は2%にゃ!!
ちゃんとやれば、良い精度で距離推定できるんだにゃ!!
思ったより精度が良くてビックリにゃ!
みんなもやってみてね!!!
コメント