読者です 読者をやめる 読者になる 読者になる

Kinect距離画像を8bitに変換してopenCVSで使う

Kinect(V2じゃないです)で取得した距離画像は16bit(正確にはプレイヤーインデックスを含んでいたりしますが...)なので、こんな感じになります。
f:id:katosunmemo:20150617224604j:plain

でもopenCVSで処理するためにIplimageに入れるとこうなっちゃう。
f:id:katosunmemo:20150617224454j:plain


今回は16bitの画像を8bitに落としてIplimageに格納するメモです。
(変換に次ぐ変換で、ほんとにこれでいいの?って感じはしますが...)


1.KinectからDepthImageFrame型で入力
2.short型配列にピクセルデータをコピー(この段階ではまだ16bit)
3.先ほどのピクセル配列をBitmapSource型の画像化する(ここでピクセルフォーマットを8bitにする)
4.再びbyte型配列にピクセルとして格納
5.BitmapData画像をメモリにロック
6.4のピクセルを Marshal.Copyを使ってBitmapData型画像にコピー
7.先ほどのBitmapData画像のロックを解除してBitmap画像に格納している?(ゴメンナサイ、ここについてはよくわかりません)
8.Iplimageに変換

と言った流れになっています。
最初のDepthImageFrame型の入力は保存しておいて、openCVSで処理した座標の値をオリジナルから読み出すという風にしておけば分解能が下がってしまうというのも防げるのではないかなと思います。

        //距離画像取得イベント
        void kinect_DepthFrameReady(object sender, DepthImageFrameReadyEventArgs e)
        {
            using (DepthImageFrame frame = e.OpenDepthImageFrame())
            {
                if (frame == null)
                {
                    return;
                }
                
                short[] depthPixel = new short[frame.PixelDataLength];
                frame.CopyPixelDataTo(depthPixel);
                

                // DepthImagePixel で取得すると、16bitの距離データ+プライヤーインデックス
                // が取得できる(480x640)
                DepthImagePixel[] depth = new DepthImagePixel[frame.PixelDataLength];

                //距離画像をピクセルにバラす(中身は距離情報)
                //原点から右にナンバリング
                //距離を表示する時にアクセスする
                frame.CopyDepthImagePixelDataTo(depth);

                //距離画像を8bitに変換してIplImageにする
                BitmapSource convertedFrame_BS = BitmapSource.Create(frame.Width, frame.Height, 96, 96,
                    PixelFormats.Gray8, null, ConvertDepthFrame(depthPixel, ((KinectSensor)sender).DepthStream),
                    frame.Width * PixelFormats.Gray8.BitsPerPixel / 8);
                int width = (int)convertedFrame_BS.Width;
                int height = (int)convertedFrame_BS.Height;
                int stride = width;
                byte[] datas = new byte[stride * height];
                convertedFrame_BS.CopyPixels(datas, stride, 0);

                Bitmap convertedData_BM = new Bitmap(
                    width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

                System.Drawing.Imaging.BitmapData convertedBits_BD = convertedData_BM.LockBits(
                    new System.Drawing.Rectangle(0, 0, width, height), System.Drawing.Imaging.ImageLockMode.WriteOnly,
                    System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

                Marshal.Copy(datas, 0, convertedBits_BD.Scan0, datas.Length);

                convertedData_BM.UnlockBits(convertedBits_BD);

                depthImageforCV_8bit = convertedData_BM.ToIplImage();

                Cv.ShowImage("処理画像", depthImageforCV_8bit);

                
            }
        }

RaspberryPi2からつぶやく その1

せっかく部室にはるばる足を伸ばしたのに誰もいなくてがっかり、そんなことが頻発する最近...

raspbianをインストールしたRaspberryPi2から部室の状態をつぶやいてみようと思います。

(Raspbian上で動くTwitterクライアントというか、botというか、そんな感じです)

作業日記としてやったことを書いていきます。

仕様

・防犯上の都合から鍵アカウントとする
・リプライを受けたら部室の様子を撮影して画像付きのリプライで返す


アカウント側の用意

使用するTwitterアカウントの設定からモバイルを選んで携帯電話の番号を登録しておきます。


TwitterのApplication ManagementのCreateNewAppをクリックし、必要事項を入力します。apps.twitter.com

Name アプリの名前
Description 説明
Website ブログとかのURL

新しいアプリの作成ができるとAPIkeyなど、開発のために必要な情報が与えられます。

「Permissions」タブをクリックし、権限が『Read and Write(読み書き)』になっていることを確認。

以上の手続きをすると連携アプリにはこんな感じに表示されます。
f:id:katosunmemo:20150524014922j:plain



RaspberryPi2側の用意

次を実行

$ sudo apt-get update
$ sudo apt-get install python-setuptools
$ sudo easy_install pip
$ sudo pip install twython

Pythonスクリプトの作成

アカウント認証の部分はApplication Managementで取得したものを使用する。

import os
from twython import Twython

#Account Check
CONSUMER_KEY = '‘Consumer key'
CONSUMER_SECRET = 'Consumer secret'
ACCESS_KEY = 'Access token'
ACCESS_SECRET = 'Access token secret'

api = Twython(CONSUMER_KEY, CONSUMER_SECRET, ACCESS_KEY, ACCESS_SECRET)

api.update_status(status='test')

スクリプトを実行するとつぶやきが無事送信されました!
f:id:katosunmemo:20150524015230j:plain


あとはリプライをもらってから撮影して画像貼ってツイート出来るようにしないと...


【めも】RaspberryPi2をリモートでセットアップする

いろいろあってRaspberryPi2が手に入ったので、何かしらしてみようと思います。

今回は手許に使えるHDMI接続のモニタが無かったので、イロイロ調べてPCからリモートでOS(Raspbian)のセットアップをして、リモートデスクトップ接続の準備もしたので、今後のためにメモしておきます。

その1 OSの書き込み

RaspberryPi2は複数のOSに対応しており、NOOBSを使えばいろいろハッピーなのですが、今回やるリモートでセットアップだと使えないとのことなので、SDに直接Raspbianを書き込みます。

まず、Raspberry財団公式ページからzipに圧縮されたRaspbianをDL
Downloads | Raspberry Pi

解凍して、ImageWriterを使ってSDに書き込みます。
Win32 Disk Imager | SourceForge.net

起動して、ImageFileに先ほどのRaspbianのイメージファイルを、Deviceに書き込み先を指定して、「Write」をクリック
f:id:katosunmemo:20150328233038j:plain

以上で書き込みは終了です。

その2 ターミナルで接続する

HDMI接続のモニタが無い前提だし、初期のままでは何も操作ができないので、PCのターミナルから操作できるようにします。

まず、コマンドプロンプトで使っているPCのIPアドレスを調べます。
ipconfigを入力して表示される「イーサネットアダプターローカルエリア接続」の「IPv4アドレス」がPCのIPアドレスです。
f:id:katosunmemo:20150329025652j:plain

この場合、アドレスは192.168.0.19で、RaspberryPi2には192.168.0.xが割り当てられています。

xに何が入るかを調べるためにpingを打って返信があったものを候補とします。

for /l %i in (0,1,255) do ping -w 1 -n 1 192.168.0.%i >> result.txt

結果をメモ帳で開きます。

アドレスの先に何もない場合は要求がタイムアウトしましたと出ますが、何かしらが接続されている場合は応答にかかった時間などが記録されているので一目瞭然です。
今回は196.168.0.55でした。
f:id:katosunmemo:20150329030440j:plain

ターミナルでこのIPアドレスにアクセスします。
ターミナルには「TeraTerm」を使用しました。

ホストに先ほどのIPアドレスのいずれかを入力します。
f:id:katosunmemo:20150329030740j:plain

接続先がRaspberryPi2でない場合は接続が拒否されるので、その場合は別のIPアドレスの候補で試してください。

接続するとセキュリティ警告が出るが「続行」を選択。
SSH認証は
ユーザ名:pi
パスフレーズ:raspberry
f:id:katosunmemo:20150329033119j:plain

こうしてターミナルからRaspberryPi2を動かすことが出来るようになりました。
f:id:katosunmemo:20150329031220j:plain

その3 GUIを導入する

ターミナルからではGUI環境が使用できません。

しかし、Windowsからなら標準で付属しているリモートデスクトップ接続を利用すると超簡単です。

先ほどのターミナルから
sudo apt-get update
sudo apt-get install xrdp
とタイプして指示通りインストールします。

準備はこれで終わりで、PC側からリモートデスクトップ接続の画面で先ほど突き止めたRaspberryPi2のIPアドレスを入力します。
f:id:katosunmemo:20150329031725j:plain

するとユーザ認証を求められますが、SSHの時と同じものを入力。
f:id:katosunmemo:20150329031808j:plain

こうして無事GUIまでたどり着きました。
f:id:katosunmemo:20150329031918j:plain

その4 固定IPアドレスを振る

今はルータからIPアドレスを振ってもらっていますが、起動のたびに毎回IPアドレスが変わってしまうのは面倒。
毎回おなじになるみたい?
ネットワークわからんちんでも申し訳ない...
困ってる方がいたら参考程度に見てください...
自分の場合はなぜか外部にアクセスできなくなりました...
結局Avahiデーモンを使うことに(2015/3/29)

そこで、IPアドレスを固定します。

ターミナルから設定する方法は検索して出てきましたが、せっかくなので一部を除いてここではGUIでやっちゃおうと思います。

一部というのは、設定ファイルをいじるにはrootでログインしなければいけないが、rootユーザにはパスワードが設定されていないのでGUIからログイン出来ないためです。

そこで、まずはターミナルからrootのパスワードを設定します。
sudo passwd root
を入力すればあとは指示に従ってパスワードを設定できます。

先ほど立ち上げたGUIから一度ログアウトして、rootユーザーでログインし直します。

「FileManager」から
f:id:katosunmemo:20150329032513j:plain

/etc/networkにある「interfaces」を次のように編集します。
f:id:katosunmemo:20150329032613j:plain

192.168.0.55は好きなもので読み替えてください。
これでIPアドレスは固定されました。


以上で、RaspberryPi2にモニタその他を直接接続せず、PCからリモートでセットアップ完了しました。

【めも】PLAYSTATIONeyeドライバ導入

前回の記事で紹介したPSeyeのPC用ドライバの導入についてのメモ書き.

Webカメラの比較 - #かとすんめも

PSeyeのドライバを調べてもなぜか4,5年前の記事ばかりしか出てこない...
もうオワコンなのかな?


とりあえず,ドライバはAlexP氏が公開してくださっているものを使いました.
CL > Downloads

見つかった4,5年前の記事を読むと昔は無料だったようですが,今は有料になっている模様...
といっても,1.99ドル,日本円で260円(2014年11月30日)だったので,まぁ缶ジュース2本分我慢と思って買いました.
f:id:katosunmemo:20141130233954j:plain

メールアドレスを入力して,支払いを済ませればDLが始まります.

(実は無料で公開している別のサイトもありましたが,調べたらそのサイトは危険なことで有名だったみたいなのでそちらからのDLは止めました...まぁお布施ということで!)



同梱されているCL-eyeTestはWebカメラからの動画を表示してくれるソフトで,複数のカメラをつなげていても切り替えられるし,FPSも変更できるので便利!

右にSDKがあったりしますが,自作のWebカメラ用のソフトでそのまま使えたので,必要ないかも?

Webカメラの比較

夏から画像解析に手を出し始め,借り物も合わせて4個のWebカメラが手元にあるのでそれぞれ比較してみようと思う.
というか,PLAYSTATIPNeyeすげぇ!の記事です.

今回のカメラ
130万画素一発接続Webカメラ - UCAM-DLU130Hシリーズ

ガラスレンズ搭載130万画素Webカメラ - UCAM-DLK130Tシリーズ

マイク内蔵120万画素Webカメラ HD720p対応モデル|株式会社バッファロー BUFFALO

PlayStation®Eye | プレイステーション® オフィシャルサイト

PCでの表示にはAlexP様のPSeye用のドライバに同梱されているCL-eye Testを使用.
CL - Downloads
撮影には石井 隆様のAGデスクトップレコーダーを使用.
AG-デスクトップレコーダーの詳細情報 : Vector ソフトを探す!

カメラを比較する

カメラ比較 - YouTube



先ほどの4種類のカメラと,PSeyeについてはデフォルトと,75FPSのものについてまとめた.
動く物体についてと,静止した時のホワイトバランスの調整速度の比較.


見て分かる通り,PSeyeが圧倒的に反応が速く,はっきり映る.
そして,主観的には画像が綺麗に見える.
(これについては,他のWebカメラはマニュアルフォーカスのものが混じっていて,ピントが合っていなくてボケていた可能性がある)

特筆すべきはPEeyeの75FPSの時の映像.
先ほどの動画ではわかりにくいが,映像を見ながら手に持ってみると気持ち悪いくらいぬるぬる動く.
まるでデジカメの液晶を見ているかのよう.

すみません、上記の内容は訂正します。
そもそもモニタが60FPSなのに、カメラをいじって変化があるわけ無いですよね。
プラシーボ怖い...(2014/12/18)

他のWebカメラではかなり遅れるので,PSeye恐るべし,と言った感じ.

【めも】GY521を使う

Amazonで買える超安価なジャイロモジュールを使ってみたのでメモ.

f:id:katosunmemo:20141002232853j:plain

価格としては400円から500円ちょいくらいの価格帯を推移してるっぽい.
ロボットの回転角度をセンシングしたかったため使ってみた.
そのため,回転角度を計測することをメインにメモをする.
GY521にはいくつかのモードがあるが,今回はデフォルトを使用.


  • 駆動電圧

駆動電圧としては3-5Vとなっているので,3.3V系でも5V系でもそのままつなげて使用できる.
ちなみに,3.3Vでも5Vでもレベル変換しなくても大丈夫だった.

  • センサの読み出し

読み出しにはI2Cを使用しており,
3軸の加速度センサの数値(x→y→z)

温度

3軸のジャイロセンサの数値(x→y→z)
の順に2byte,合計14byteのデータを送信してくる.

ここで注意が必要なのが,受信の順番では上位8ビットと下位8ビットが逆になっているので,交換してやる必要がある.

また,I2Cに必要なプルアップは基板上で行われている.

I2CアドレスはAD0ピンで設定することが出来る.
AD0ピンは基板上でプルダウンされている.
LOW→0x68
HIGH→0x69

処理のアウトラインとしては
GY521にコマンド(0x6B)を送信(初回のみ)
↓(以下ループ)
GY521にコマンド(0x3B)を送信

一度の受信で14byte分を受け取る

変換式を使って変換

  • 生データ変換式

加速度 (生データ)/16384(g)
傾斜角度 (生データ)*360/2.0/3.14
回転速度 (生データ)/131(°/s)

ソースコードについては特に縛りなく様を参考に,0.1sの割り込みで受信し,回転角度を積分で求めるプログラムを作った.
特に縛りなく様に載っているソースコードで加速度,傾斜,回転速度を求めることは可能.
特に縛りなく: 加速度+ジャイロのGY-521(MPU-6050)を使ってみた -1-


  • 問題点

第一の目標としての回転角度の計測は前の項目で述べたとおり,0.1sの割り込みで受信して積分する.
(0.1sという値については今後変更する可能性があり,とりあえずでこの割り込み間隔にしてある)
ところで,下がジャイロの静止状態での生データである.
f:id:katosunmemo:20141003003604j:plain
常に角速度が検出され,これを積分すると発散してしまう.

カルマンフィルタの実装かなぁ...

【めも】Raspberry PiとArduinoでシリアル通信【Python】

あるプロジェクトに使う用にRaspberry PiとArduinoの通信をしなきゃいけなくて,動作確認程度ながら出来たのでメモ.


f:id:katosunmemo:20140919023955j:plain

テストなので,Arduino側からはひたすら振動センサの値を送るプログラムを書き込んでおき,Raspberry Pi側ではそれを表示し続けるプログラムを実行する.



まずPythonのシリアルポートライブラリのインストール
sudo apt-get install python-serial


Arduino側】

/*
ピン配置がUNOとMegaで違うのに注意
UNO→A0から14
Mega→A0から54
*/

#define __AVR_ATmega1280__
#define __AVR_ATmega2560__


 enum{
  VIB_PORT1,
  VIB_PORT2
};

int vib_1, vib_2;//振動センサの値

void setup(){
  Serial.begin(9600);
 
  //デバッグランプ(引き込み)
  pinMode(A3, OUTPUT);
  pinMode(A4, OUTPUT);
  pinMode(A5, OUTPUT);
}

void loop(){
  vib_1 = analogRead(VIB_PORT1);
  vib_2 = analogRead(VIB_PORT2);
 
  Serial.print(vib_1, DEC);
  Serial.print('\t');
  Serial.print(vib_2, DEC);
  Serial.print('\n');
 
  //デバッグランプ||<
  digitalWrite(A3, HIGH);
  digitalWrite(A4, LOW);
  digitalWrite(A5, HIGH);
 
 
}



【Raspberry Pi側(Giga Hacks さまより引用)】

import serial
 
ser=serial.Serial("/dev/ttyACM0",9600)
 
while True:
    val=ser.readline()
    print(val)

【実行結果】


f:id:katosunmemo:20140919024017j:plain

二軸の振動センサなので,2つの情報を並べている.