メガネ売り場でサングラスを見ていたら、偏光レンズというのがありました。晴れている時の建物の反射や、雲の白さを緩和してくれるなど、車の運転で周囲がまぶしく感じられる人には都合がいいようです。あと、釣りをする人で水面のキラキラする反射をカットするのを利用して水中を良く見るためにも使われているらしいです。まぁ、水中の魚が見えているのにつれなかったらストレスが溜まりそうですけどね。
偏光カメラとは
こんにちは。仕事で試したものの、ボツになったモノの供養としてGitHubにソース上げたりしています。せっかくなのでココにもまとめることにしました。使わないと忘れてしまうので。
仕事場に偏光カメラ FLIRの BFS-U3-51S5P-C がありました。こんなやつ(FLIRのページに飛びます) です。適当な絵が見つからなかったので今回利用させてもらったツールの説明に載っていたイメージを参考に載せておきます。
一番左のRAWイメージにあるように、4つの画素が1セットになっていて、その中を0,45,90,135度の方向で偏光フィルタした光を受光するような仕組みになています。
偏光の仕組みは詳しくないのであまり説明できませんが、電磁波の一種である光は波の振幅の方向を持っています。偏光フィルタはこの方向に制限を掛けるもので、先に記した方向以外をカットするようなフィルタです。赤色のフィルタが赤色しか通さないというのは周波数(もしくは、波長)をフィルタするものですが、方向って?みたいな感じですね。
料理します
さて、画素の並びが先の参照イメージのように規則的ではありますが、同じ方向に偏光されたものを集めると歯抜けになってしまいますが、うまく抜き出してそろえてやると、各方向ごとの画像が得られます。
また、偏光方向の情報が得られるので、方向と強さを可視化するような処理もできます。
準備
内容を勉強して自力で式を作るのがスジですが、そこまでできなかったのでこちらのツールを利用させていただきました。
polanalyser(GitHub)
おそらくFLIRのSpinnaker SDKを探せば同様の処理をするAPIがあると思います。
それから、単に画像をえるだけだと面白くないので今回streamlitを利用してまとめました。(ソースはこちら)
その他、あらかじめカメラで撮影した画像を用意しておきます。カメラのビューアSpinViewをFLIRのサイトからダウンロードして利用できます。カメラはUSB接続ですが、専用のビューアなどを利用しないと画像を取得できないのは産業用カメラあるあるです。
全体の構成
左にサイドバーを用意し、画像をアップロードできるようにします。
処理ごとにページを用意しました。方向別の画像、DoLP、AoLPの3つです。
画像のアップロード
streamlitに用意されている file_uploaderを利用します。
uploaded_file = st.sidebar.file_uploader("upload image")
ファイルオブジェクトが得られるので、これを利用して画像として読み込みます。これにはPILを利用しました。さらに、numpy形式にします。処理用のページにファイルの中身を渡す必要があるので、streamlit のセッションに登録します。
st.session_state["image_file"] = uploaded_file.name
st.write(st.session_state.image_file)
image = Image.open(uploaded_file)
org_img = np.array(image)
st.session_state["org_image"] = org_img
st.image(org_img)
4方向の画像
まずは一番わかりやすい4つの方向の画像取得です。polanalyserのおかげで一発です。
import polanalyser as pa
img_000, img_045, img_090, img_135 = pa.demosaicing(org_img, pa.COLOR_PolarMono)
streamlitで普通に表示できる形式です。
st.image(img_000, caption="000")
DoLP, AoLP
それぞれ、偏光の割合、偏光の角度、だそうです。これも前準備がいろいろありますが、polanalyserのサンプルコード calculate_stokes.py をまるごと頂きました。
その他
これでだいたい動くのですが、streamlitならではとして、ページを切り替えるごとにすべての処理が走るようです。軽い処理なら問題ありませんが、そこそこ重いようで画像の表示に時間がかかります。毎回待たされるのは問題なので、streamlit のキャッシュの仕組みを使いました。引数が変更されていなければ再処理しないというものです。
これを必要なだけ各関数に付けました。
@st.cache_data
def draw_aolp_images(org_img):
これで、ページを切り替えても高速に描画されます。こんな感じでいいのかちょっと自信ないのですが。
おわりに
pythonでサクッとどうにかしました。
カメラで撮影しながら処理結果を見るというような使い方をするには、C++くらいでSDK使って、それなりのものを作る必要があるんだろうなと想像しています。仕事の都合でそこまで至らなかったので、今回はSDKも覗かず終わらせました。