はじめに
ここでは、Remo購入者向けに、Remoで非圧縮データをPCへ転送する方法を紹介します。
※このページで紹介する内容はあくまでも一例です。個別の作成のご相談ご質問はお答えできませんのでご了承下さい。
※以下の情報は2009年11月現在のものです。ご注意ください。
Remoついてはこちらを参照願います。
YUV画像について
コンピュータで色を表す場合はいろいろな方法があり、よく使われるものにRGBとYUV(YCrCbという場合もある)があります。RGBは赤、緑、青を数値で表し、YUVは輝度情報と色差情報で表します。
PC上で画像を表示させる場合はモニタがRGBを使用しているので、RGBがわかりやすいですが、色認識などの画像認識の場合は輝度情報と色差情報の方が計算しやすいので、RemoではYUVで画像データをやりとりします。
人間の目は輝度情報には敏感だが、色情報には鈍感なので、画像転送などの場合は色情報を間引くことが多いです。間引き方にもいろいろあり、Remoでは「YUV422」という方式になっています。
※以下、Remoのページの命令128のところと同じ内容です。
YUVは輝度情報と色差情報を分けた表し方です。YUVのYは輝度、Uは青色差、Vは赤色差のデータです。UはCb、VはCrとも表します。YもCrもCbも各8ビットで表します。
Yデータだけを扱うと白黒画像になります。
PCなどで画像を扱う場合、RGBが直感的にわかりやすく、よく使われますが、本製品はイメージセンサとしての機能を重視したために色体系はYUVを使用しています。色を判定する場合などはRGBそのままでは明暗があるものを判定できないので、結局YUVに変換して計算したりします。もともとYUVなら変換する手間がなく、高速に画像処理するプログラムを組むことができます。
YUV”422”は色データ量を減らして16ビットで1ピクセルを表す方法です。”422”の意味ですが、これは8バイトのうち、4バイトをY、2バイトをCb、2バイトをCrに割り当てて4ピクセルを表します。こうすることで輝度Yは劣化がなくなります。各ピクセルと出力の関係は次のようになります。(4バイトで2ピクセルではなく、8(4+2+2)バイトで4ピクセルなので422)
人間の目は輝度情報には敏感ですが、色についてはあまり解像度がよくありませんので、色情報を間引いてもあまり劣化したようには感じられません。
四角はそれぞれ1画素に相当します。
各データの横の番号はたとえばY0の場合は輝度0ピクセル目、Y1は1ピクセル目...という意味です。4ピクセルをCb0,Y0,Cr0,Y1,Cb2,Y2,Cr2,Y3の順番にの8バイトで出力しますので1ピクセル辺り2バイト換算のデータ量になります。但し、上の図のようにカラー情報は2ピクセルごとに省略されています。
上の図ではY1とY3のカラー情報がありませんが、計算時や画像表示時にはとなりのピクセルの色情報で計算します。
YUVのイメージは次のようになります。
この画像は違う製品のもの(S-EYEで撮影)なので、画面サイズはRemoとは異なりますのでご注意願います。
この例のように、Yデータは劣化が無いものです。Cr(V)とCb(U)は本当は横方向に半分になった画像を横に2倍に伸ばしたものです。CrとCbの2つの色差情報しかありませんが、合成するとちゃんとしたカラー画像になります。
R=1.164x(Y-16)+1.569x(Cr-128)
G=1.164x(Y-16)-00.391x(Cb-128)-0.813x(Cr-128)
B=1.164x(Y-16)+2.018x(Cb-128)
ただし、計算中に整数にまるめられ値がオーバーフロー(8bit値より繰り上がる)やアンダーフロー(0より下回る)になる可能性がありますので、PC上では倍精度符号付変数(WindowsXPの場合double)で計算し、255以上の場合は255、0以下の場合は0に修正しています。
実際の画像フォーマット
一般的なYUV画像の表し方は理解できたかと思います。ここでは実際にRemoからどのようにデータが送られてくるかを説明します。
まず、Remoに対して画像を送信するように命令を送ります。命令についてはRemoのページに書いてあります。たとえば、左目の画像を非圧縮で送る命令は
[255] [97] [1] [128]
この4つの数値データを送ります。(カッコ内は10進数で表した数値データです)
Remoでは、一秒間に30回の速度で画像を撮影し、Remo内のバッファに画像を更新しつづけています。上の4バイトの数値を受け取ると、まず同じ数値を返し、その後、160x56サイズのYUVデータを送ってきます。
※「白黒は1画素1バイト」とありますが、この図は別のものの流用です。Remoは白黒画像モードはありません。
2画素につき4バイト(1画素につき2バイト)のデータ量なので、画像本体は160x56x2バイト=17920バイト、ヘッダのバイトを合わせて17924バイトのデータが送られてくることになります。
プログラミングについて
プログラミングのご質問については浅草ギ研では回答できません。書籍や、Microsoftの開発ページ「MSDN」を参照願います。MSDNに初心者用質問掲示板などがあります。
プログラムの説明
実際に作ったプログラムはこちらになります。
RemoView1.lzh
これはプロジェクトファイルをそのまま圧縮したものです。解凍し、RemoView1.csproj を開いてください。
Form1.csのコードを表示するとソースが見れます。
では解説です。
<ディレクティブの追加>
WindowsFormでプロジェクトを作ると、ある程度のライブラリが組み込まれますが、ここではそれ以外のライブラリも使ってますので、行10−12の3行を追加します。Threadingはデータ受信待ちでSleepメソッド(メソッドはCで言う関数のようなもの)を使っているので必要です。その他2つは画像を高速に表示するのに使います。
<コンストラクタ部>
コンストラクタはフォームが開くときに行われる処理です。あらかじめてきとうに貼り付けたSerialPortコンポーネントと、PictureBoxコンポーネントのプロパティを設定しています。
<命令の送信>
まず、Remoに対して画像の送信要求を送るところです。バイトサイズのsndData配列をつくり、データを入れて、行46で送信しています。行44と45は、送信に先立ち、PC側の送受信バッファを空にしているところです。この程度のプログラムでは不要かもしれません。
<オーバーフローチェックメソッド>
YUVの説明のところでも書きましたが、画像をPCに表示するにはYUVからRGBに変換する必要があります。このとき、計算結果によっては8ビットの値(0〜255)を超える場合がありますのでそれをまるめる処理をしています。
<画像の表示>
さて、ここからが中心部分です。とりあえず、表示部分のプログラム全体はこのようになります。下でそれぞれについて説明します。
Form上で画像を表示させるコンポーネントにPictureBoxがあります。これにはSetPixelというメソッドがあり、簡単にデータを表示させることができます。C#を含む.NET Freamworkの初級本などにはこの方法が説明されています。しかし、このSetPixcelは動作が非常に遅いので、画像認識などに使う場合は実用的なプログラムは書けません。
ということで、ここではビットマップ上で画像を展開してPictureBoxに渡す、という方法を取っています。
この前処理部分が行67〜72の部分になります。ここではこれらについては詳しく説明しませんので、MSDNなどで勉強してみてください。
行73:送られてくる画像データは160x56x2バイトですが、表示するのはRGBの3つの値になるので160x56x3になります。
行74,75:計算用の一時変数です。念のため必要なサイズよりも多めに作っています。
行76:データが全部送られるであろう時間分待っています。送られてくるデータ量が17924バイトと決まっているので、受信バッファのサイズを見る、という手もありますが、途中で通信が途絶えたり、データが抜けたりするとそこで泊まってしまいますので、とりあえずSleepで待つ、という簡単な方法をとっています。ここはもっと上手いやり方があると思います。
非圧縮データの場合、実測で約750mSぐらいかかることがわかっています。
行77〜84:時間が来たら、来ている分のデータだけ読み込んでいます。
行86〜112:ここでYUVをRGBに変換し、rgbValuesという配列に格納しています。YUV422なので、Yの輝度値は1ピクセルにつき1つありますが、色差情報は2ピクセルで1つを共有しますので、2ピクセル分づつ処理しています。
pictureBoxの場合、RGBのデータはB->G->Rの順番に格納しますので注意してください。行97〜99の部分です。
尚、ヘッダに4バイトありますので、配列の位置は[i + 4]のように4バイトズレた位置を指定しています。
行114〜116:ビットマップに展開した画像をPictureBoxに渡し、PictureBoxを再描画(行116)して高速に表示しています。
<リセット命令>
リセット命令は、単に4バイトのデータを送信するだけです。
<AWBオフ命令>
AWBオフも同じ。
プログラムの実行
ではプログラムを動かしてみましょう。
まず、Remo側の電源を入れます。ペアリングが終わっているのでLED_SのLEDが一秒ごとに点滅しています。LED_Tは消えています。
PCのプログラムでCOMをオープンすると接続され、LED_Tが点灯します。このプログラムでCOMをオープンするのはコンストラクタ内、つまり起動時になります。
ではF5キーを押してプログラムを動かしてみます。Remo側を見ていると、少し時間が経過した後にLED_Tが点灯し、接続されたのがわかります。
LeftEyeボタンを押したら無事画像が表示されました。(ボタンを押すためにクリックしてる画像)
このようにして画像が取得できます。表示の部分は少し難解なので、ビギナーの方は単純にdispPictureメソッド(関数)をコピーして使ってもよいかと思います。引数はPictureBoxの実体を渡します(行47)。
2009年11月26日
|