OpenCVでステレオマッチング
かなりの今更感はさておき、OpenCV でステレオ画像による深度計算をやっています。
環境
- LMDE2 64bit
- OpenCV2
- Python2.7
スクリプト
stereo.py (Gist)
#!/usr/bin/python
# coding: utf-8
import numpy as np
import cv2
from matplotlib import pyplot as plt
img_left = cv2.imread("l.jpg",0)
img_right = cv2.imread("r.jpg",0)
img_left = cv2.GaussianBlur(cv2.equalizeHist(img_left), (3,3), 0)
img_right = cv2.GaussianBlur(cv2.equalizeHist(img_right) ,(3,3), 0)
window_size = 1
stereo = cv2.StereoSGBM(
minDisparity = 0, # 視差の下限
numDisparities = 32, # 最大の上限
SADWindowSize = window_size,# SADの窓サイズ
uniquenessRatio = 2, # パーセント単位で表現されるマージン
speckleWindowSize = 2, # 視差領域の最大サイズ
speckleRange = 16, # それぞれの連結成分における最大視差値
disp12MaxDiff = 2, # left-right 視差チェックにおけて許容される最大の差
P1 = 8*3*window_size**2, # 視差のなめらかさを制御するパラメータ1
P2 = 32*3*window_size**2, # 視差のなめらかさを制御するパラメータ2
fullDP = True # 完全な2パス動的計画法を使うならTrue
)
disp = stereo.compute(img_left, img_right)
disp = cv2.normalize(disp, alpha = 0, beta = 255, norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_8U)
cv2.imshow("disp", disp)
cv2.waitKey(0)
cv2.destroyAllWindows()
入力画像と深度計算の結果
cv2.StereoSGBM() のパラメータをいじくり回したり、単眼のスマホカメラで撮影した入力画像の位置合わせをしたりしていると、それなりに深度が出るようになりました。
PCD 化
PCL(Point Cloud Library)のフォーマットである PCD 形式に変換し、pcl_viewer で確認します。
スクリプトのPCD化部分次のとおり。
depthmap_to_pcd.py (Gist)
q = np.array([
[1, 0, 0, -width/2],
[0, 1, 0, -height/2],
[0, 0, 0, focal_length],
[0, 0, -1/tx, 0]
])
_points, _colors = calc_point_cloud(image, disp, q)
points = list(_points)
colors = list(_colors)
size = len(points)
f = open('points.pcd', 'w')
header = """# .PCD v.7 - Point Cloud Data file format
VERSION .7
FIELDS x y z rgb
SIZE 4 4 4 4
TYPE F F F F
COUNT 1 1 1 1
WIDTH %(size)d
HEIGHT 1
VIEWPOINT 0 0 0 1 0 0 0
POINTS %(size)d
DATA ascii
""" % { "size" : size }
f.write(header)
x = 0
for i in range(size):
color = 0
color += colors[i][0] << 16
color += colors[i][1] << 8
color += colors[i][2] << 0
f.write("%f %f %f %f\n" % ( points[i][0], points[i][1], points[i][2], float(color) ))
x += 1
f.close
func.py (Gist)
def remove_invalid(disp_arr, points, colors):
mask = (
(disp_arr > disp_arr.min()) &
np.all(~np.isnan(points), axis=1) &
np.all(~np.isinf(points), axis=1)
)
return points[mask], colors[mask]
def calc_point_cloud(image, disp, q):
points = cv2.reprojectImageTo3D(disp, q).reshape(-1,3)
colors = image.reshape(-1,3)
return remove_invalid(disp.reshape(-1), points, colors)
pcl_viewer で開いたところ。
数百万円するレーザー計測器には到底及ばないものの、それなりにカラーボックスやその上に置いている箱の形状が出てますね! もっとフワフワな点群になるかと思っていたので、ちょっと予想外でした。
参考
- OpenCV: Camera Calibration and 3D Reconstruction
- 3D reconstruction from stereo images in Python
- Producing 3D point clouds with a stereo camera in OpenCV | Stackable
- OpenCV Stereo Matching の性能比較 1 – Daily Tech Blog
- Python+OpenCVでのカメラキャリブレーション有りステレオビジョンのサンプルコードなど – 蟹者
- Stereo Matching/Calibration Help – OpenCV Q&A Forum
- OpenCV、画像処理、コンピュータビジョン、3D点群処理、動いたやつをまとめていく。 – Qiita
- OpenCVのバージョン3が正式リリースされたぞ
コメント(4件)
過去にいただいたコメントです。現在、新しいコメントは受け付けていません。
OpenCVでステレオマッチング(ステレオカメラ) – dyama's page (ピンバック)
[…] テレオ画像による深度計算をやっています。 昨晩、スマホの単眼カメラで撮影した2枚のステレオ画像の深度計算で割と良さそうな結果が出たので、Amazonで安いWebカメラを二つ注文。早速 […]
3/26メモ – dyama's page (ピンバック)
[…] 前々回と前回の OpenCV まわりに関連して、とうとう Kinect を購入しました。Kinect for Windows じゃなく 360 でもとりあえずは研究に使えるらしいし、ドライバや SDK が厳格そうな Windows で開発 […]
Linux で Microsoft Xbox One Kinect センサーを使ってみました – dyama's page (ピンバック)
[…] 先週からスマホカメラ、USBカメラ×2台、Kinect 360 と深度マップ撮影をやっていて、とうとう Kinect One の購入に至ったので書いておきます。 […]
OpenCVでステレオマッチング(ステレオカメラ) – dyama's page (ピンバック)
[…] 前日より引き続き、OpenCV でステレオ画像による深度計算をやっています。 昨晩、スマホの単眼カメラで撮影した2枚のステレオ画像の深度計算で割と…ので、Amazonで安いWebカメラを二つ注文。早速届いたので遊んでみます。 […]