2013年12月29日日曜日

3Dスキャナ作り方

前回、パターン投影法を採用することが決まったが、具体的なマイルストーンがわかったのでメモしておこう。

  1. カメラ、プロジェクタの校正
    • 昔は面倒だったようだが、今ではステレオカメラと同様の手法を適応できる
  2. 縞画像の生成→投影→画像取得
    • カメラ、プロジェクタをどのように制御するかの問題。RaspberryPiの扱いに苦戦しそうな予感。
  3. 取得した画像より位相マップを作成
    • Phase Unwrappingによって位相情報を取り出す。とりあえずは一番シンプルなIto's methodでいく。
  4. 位相マップから3Dデータを生成
    • ここは三角測量の応用。生成したデータはPCL(Point Cloud Library)に取り込んで表示させる。
実行手順は1から4の順番だが、実装では校正にプロジェクタを使うので2→1の順番で進める。

2013年12月28日土曜日

3Dスキャナはじめました。

3Dスキャナの勉強を始めた。

1.モチベーション

光学設計者としてレンズ設計だけでなくシステム設計も理解できるようになりたいから。
かの有名な物理学者ファイマンもおっしゃられました。
What I cannot create, I do not understand
ちょっとレベルが違いますが、やっぱり作れないというのはエンジニアとしてあまりおもしろくない。ということで、自ら3Dスキャナを作ってみたい。

2.作成コンセプト

  • ポータブル
  • 安い(お金は掛けたくないおー)

3.計測手法の決定

3D計測で有名どころといえば最近はKinectだ。ToF(Time of Flight)という手法を使っているらしい。最近iPad用に出たStructure Sensor(http://structure.io/)というのも多分ToFだ。

この他にも3D計測を行う手法はいくつもある。
以下のサイトによくまとまっている。
http://www.visetec.co.jp/wordpress/?p=5


まずはどの手法でとりかかるかだが、簡単そうなのは光切断法。でもこれはいろいろな人がやっていて先日もRaspberryPiをつかって安価に3Dスキャナつくりましたよーって人が海外にいた。しかも結構よく見る。
ということで今回は「パターン投影法」を試すことにした。
パターン投影法の中でも位相シフト法やフーリエ変換法が実装が簡単なのでこれらを中心に勉強をしていく。三角測量の応用なので原理自体は光切断法と大差なさそうだ。原理についてはまた後日掲載予定。

ざっと調べた感じ光切断法と比べたパターン投影法のメリットは
  • 測定時間が短い(研究ではリアルタイム計測もやられている)
  • 精度が良い(研究ではμmの精度も出せてるようだ)
逆にデメリットは
  • 値段高い…
  • プロジェクタの明るさが必要(明るいと値段も上がる)

今回はRaspberryPiを使うのでポータブルなシステムにしたい。
プロジェクタはポータブルタイプで80ルーメンあたりが主要なようだ。値段は3万円程度(RaspberryPi 10個分…)。しかし、精度のよい3Dスキャナは何十万円とするみたいなので、ポータブルで精度のよい物ができればコストメリットは十分にある。
ということで「パターン投影法」をやる意味は見い出せた。

4.カメラとプロジェクタの選定

カメラはRaspberryPiのカメラモジュールを使う。画素数は5Mしかないけど、多分十分だし何より安い。必要になったときにいいやつにグレードアップさせよう。

プロジェクタは悩んだ。明るいやつにすれば大きくて高くなる。それだとコンセプトに合わない。正直最低限の明るさがどれくらいかわからないが、最悪部屋を真っ暗にすれば試作はできそうなのでポータブルでなるべく明るいサンワサプライのを選んだ。これはバッテリー搭載でポータブルなのはもちろんmicroUSBでRaspberryPiにも給電できそうだ。

ちなみに余談だが、最近はプロジェクタの素子にはDLPを使うことが多いそうだ。LCD(液晶)と比べて、透過率が良いのが利点のようだ。

今後の展開

今後は理論を勉強して実装を試みたいと思う。
理論は簡単な説明はあるけど、3Dスキャナを自作できるほどの包括的な情報がないのでできれば公開していきたい。







2013年12月26日木曜日

位相アンラッピング(Phase unwrapping)

[1D phase unwrapping]
1D Phase Unwappingをサクッと行う
1.元データの作成
まず元データを作成。適当なデータを作る。
In [104]:
from numpy import *
N = 500
In [105]:
x = linspace(0, 2*pi, N)
In [106]:
import matplotlib.pyplot as plt
%matplotlib inline
y = 6*sin(x)*x
plt.plot(x, y)
plt.show()
2.関数をWrapする 
ラッピングされたデータを作る
In [107]:
yw = arctan2(sin(y), cos(y))
plt.plot(x, yw)
Out[107]:
[<matplotlib.lines.Line2D at 0x10bd8cc50>]
[-pi, pi]の範囲で折り返された(wrapされた)データが出来た。
3.Unwrap
ここまででデータ作成終了。連続だったデータもラップ(wrap)されて位相のジャンプが現れる。 これをUnwrapする。
In [108]:
def mywrap(y):
    for i in range(1, len(y)):
        div = y[i] - y[i-1]
        if div < -pi:
            y[i:] += 2*pi
        elif div > pi:
            y[i:] -= 2*pi
        else:
            pass
    
    return y
mywrap(yw)
plt.plot(x, yw)
plt.show()
元のデータが再現された。 でもこれはサンプリングが十分でかつノイズがない場合に限られる。
実はnumpyにはunwrapが準備されている。wrapメソッドはないようだ。
In [109]:
yw = unwrap(yw)
plt.plot(x, yw)
plt.show()
と同じ結果になる。
5.ノイズののったデータの場合
In [112]:
y = 6*sin(x)*x + random.normal(0, 1, len(x))
plt.plot(x,y)
plt.show()
In [113]:
yw = arctan2(sin(y), cos(y))
yw = unwrap(yw)
plt.plot(x, yw)
plt.show()
というふうに関数の値自体に対しては小さいノイズにも関わらず位相ジャンプが発生してしまう。
次にサンプリングを少なくしてみる。
In [127]:
N=40
x = linspace(0, 2*pi, N)
y = 6*sin(x)*x 
plt.plot(x, y, "o-")
plt.show()
In [132]:
yw = arctan2(sin(y), cos(y))
plt.plot(x, yw, "-o")
Out[132]:
[<matplotlib.lines.Line2D at 0x10bdbd190>]
In [133]:
yw = unwrap(yw)
plt.plot(x, yw, "o-")
plt.show()
序盤は再現できているが変化率の大きいところではまったく反対方向に増加してしまっている。 このようにノイズやサンプリング数によってunwrapは影響を受けてしまう。
以上、ちょっとした実験でした。