覚え書きブログ

3次元計測の覚え書き(ハミングカラーコードのマッチング編)

以下を参考にして、プロジェクターとカメラを用いた3次元計測の前準備として、ハミングカラーコードを用いてプロジェクターとカメラの位置関係のキャリブレーションを行ってみた。
qiita.com
github.com

ハミングカラーコードをプロジェクターから投影し、カメラで撮影

f:id:hirotaka_hachiya:20190630103417p:plain
f:id:hirotaka_hachiya:20190630103406p:plain

ちなみに、ハミングカラーコードは、以下のプログラムで設定されているH4の変数として設定されている。
https://github.com/kibekibe/structured_light/blob/master/hamming_color_code/hamming_decode.py

H4 = '6767645454676232326754576757646751323132645754645131315451546231576267323767375757313751573732626464626237315151373764'

H4の1~7の数値は、以下のRGB値で設定された色のID(arrayのインデックス+1)に対応していて、H4の隣り合うID間のRGB値は255進数でいうと、1bitずれるように設定されている(ハミング距離が1)。

(Pdb) color
array([[  0,   0, 255],
       [  0, 255,   0],
       [  0, 255, 255],
       [255,   0,   0],
       [255,   0, 255],
       [255, 255,   0],
       [255, 255, 255]], dtype=uint8)


平面の板は、マスの角の座標を用いたカメラのキャリブレーションも同時に行うため、以下のような黒いマス部分を薄いグレーにしたA3のチェッカーボードを用いる。
f:id:hirotaka_hachiya:20190630103941p:plain
カメラのキャリブレーションについては、以下を参照。
https://kamino.hatenablog.com/entry/opencv_calibrate_camera
http://labs.eecs.tottori-u.ac.jp/sd/Member/oyamada/OpenCV/html/py_tutorials/py_calib3d/py_calibration/py_calibration.html
http://ishidate.my.coocan.jp/opencv_17/opencv_17.htm


全体像としては以下のようになっている。
f:id:hirotaka_hachiya:20190630104319j:plain

プロジェクターを以下のように設定し、実際に撮影された画像は以下のようになった。
f:id:hirotaka_hachiya:20190630112052j:plain
f:id:hirotaka_hachiya:20190630112158p:plain
f:id:hirotaka_hachiya:20190630112303p:plain

マスク画像を用いたカラーコードが投影された板の場所を絞り込み

カラーコードを投影していない素のチェッカーボードの状態で撮影し、RGB画像(各チャネル256階調)の各ピクセル値を、ある所定の閾値で0か255に置き換えた以下のようなマスク画像を作成する。
f:id:hirotaka_hachiya:20190630110238p:plain

カメラで撮影したカラーコード画像の各ピクセルを色IDに分類

ここが一番重要なところであるが、プロジェクターから投影したパターンの元画像と、カメラで撮影した画像をピクセルレベルでマッチングを行う。
上記のマスクをかけた後のカメラ画像では、基本的にはカラーコードしか映っていないはずなので、各ピクセルのRGB値を頼りに1~7のカラーIDに対応付ければ、所望のマッチングができると期待される。

そこで、kibekibeさんのhamming_decode.pyでは、RGB値の絶対誤差に基づく、最近傍カラーの選択し、4色セットでカメラ画像の各ピクセルがカラーコードH4の何番目(つまり、縦縞のカラーコードの場合、横のx軸のの座標、横縞のカラーコードの場合、縦のy軸の座標)を見ているのかを判定している。
https://github.com/kibekibe/structured_light/blob/master/hamming_color_code/hamming_decode.py

具体的には、以下の部分である。

# get_segment_imageから呼ばれる
def get_seg(val, color_array):
    dif = color_array - val / (val.max()+1) * 255.0
    dif = abs(dif)
    dif = dif.sum(axis=1) # 0:行数が1行になる。, 1:列数が1になる
    id = np.argmin(dif)
    return id

def get_seg_str_from_seg_id(seg_seq):
    corres = []
    for i in xrange(len(seg_seq)-3):
        pattern = '%d%d%d%d'%(seg_seq[i], seg_seq[i+1], seg_seq[i+2], seg_seq[i+3])
        find = H4.find(pattern)# ミスったら-1を返しているはず。
        corres.append(find)   
    return corres

以下のように、実行してみたところ、以下のような斑な結果が得られた。。。

> python .\hamming_decode.py --input_dir=data/input --lit_thr=100 --output_dir=data/output --output_8bit_dir=data/output_8bit --output_mask_dir=data/output_mask

f:id:hirotaka_hachiya:20190630113035p:plain
f:id:hirotaka_hachiya:20190630113111p:plain

本当はなだらかにグレーの濃淡が、濃い色から薄い色に変化していくはずだが、濃くなったり薄くなったりとマッチングが不安定になっているのがわかる。

以下のように、カメラ画像(original)と選択された色の画像(selected color)を見比べてみると、黄色と緑の部分が、間違って白にマッチングしているように見える。
f:id:hirotaka_hachiya:20190630113723p:plain

もう少し具体的に数値レベルで見てみると、以下のように緑のRGB値は[139, 254, 153」となっており、RとBの成分も高い値を出している。これで最近傍カラー選択をすると、白の[255, 255, 255]が意図せず選ばれてしまうのがわかる。
f:id:hirotaka_hachiya:20190701111340p:plain

カラーマッチングの改良

そこで、単純だが、カメラ画像のRGB値にある閾値rgb_thrを基準に、0か255に置き換えてから、最近傍カラー選択をするように変更した。

        #-----------------
        # rgb_threを基準に、RGB値を0か255に置き換える
        cap_x[cap_x<=args.rgb_thr] = 0
        cap_x[cap_x>args.rgb_thr] = 255
        cap_y[cap_y<=args.rgb_thr] = 0
        cap_y[cap_y>args.rgb_thr] = 255
        #-----------------

そうすると以下のように、ところどころミスはあるものの、大部分でマッチングが成功する(滑らかにindexが大きくなっている、グレーの濃淡が変化している)。
f:id:hirotaka_hachiya:20190630114847p:plain
f:id:hirotaka_hachiya:20190630114851p:plain

githubレポジトリ

上記のカラーマッチングを変更したバージョンは以下のgithubに置いた。
github.com

実行例は以下通り。

> python .\hamming_decode.py --input_dir=data/input --li
t_thr=100 --output_dir=data/output --output_8bit_dir=data/output_8bit --output_mask_dir=data/output_mask  --rgb_thr=160