pythonでインターフェース作ったり、Arduinoのコントロールしながら、
C++での画像処理結果(前に何があるか、とか)をpythonに送りたくなったのでソケット通信でやって見ました。
pythonだとopencvで特徴量記述とかが出来ないし(そもそも遅いし)、C++でインターフェースとかシリアル通信なんかやってられないので、双方の弱点をカバーする方法としてプロセス間通信が最も横着な方法であると判断。
参考文献:
1、Pythonメモ 様の記事 ttp://yoshi-python.blogspot.jp/2009/10/blog-post_19.html
2、Geekなぺーじ 様の記事 ttp://www.geekpage.jp/programming/linux-network/tcp-1.php
3、信州大学HP 様 ttp://intuniv.cs.shinshu-u.ac.jp/Lecture/NetProg/chapter3/struct.html
4、pythonのリファレンス
■ 今回はpythonをサーバとしてC++側と双方向通信させました。 ちなみにサーバとクライアントの違いについてはよくわかってません。接続を待つ方がサーバぐらいの認識でいいのだろうか。
以下、参考文献のページのコードをほぼ引用した自分用のメモ
python側:
import socket
def server(host, port):
s = socket.socket() ←ソケットのインスタンスを作る
s.bind((host, port)) ←バインド(IPとポートを設定する)
s.listen(1) ←接続待ちに必要。引数は接続できるノードの数。
print "waiting"
conn, addr = s.accept() # ←クライアントの接続を待つ。connはつながってきたソケット
addrはノードのアドレス(使い所は不明)
while True: ←ずっとループ
data = conn.recv(1024) # クライアントからのデータを受信する(ここでは1024byte)
↑ここでのdataの形式がよくわからないが多分char(C++側ではconst charで送ってる)
print "server: receive '%s'" % data
datalist=str(data) ←とりあえずデータをstring化
datasp=datalist.split(",") ←データをカンマで区切る。数字をカンマ区切りで送ることを考えている。
print datasp
object.set_value(float(datasp[0]))
↑任意の変数に受信データの値を入力。これでArduinoでもなんでも動かせる(pythonと繋がってれば)
conn.send("sendfrompython") # クライアントにデータを送信する。ここでpythonからの命令(右に曲がるから右に注目しろ、とか)をC++に送ることが出来る。
conn.close() ←クライアントのソケットを閉じる。
s.close() ←こっちのソケットを閉じる。
閉じてもすぐに切断されるわけではないらしい。連続で同じポート使ったりできないことがあるので時間開けるかポート番号変えるのが吉。いまのところpython-C++ではこの事象は起こってないがpythonのプログラム間(同ソース別スレッド)でこの事象を確認。
class Server(threading.Thread):######### C++との通信スレッド
def run(self):
server(HOST, PORT) ←上記関数。HOST(127.0.0.1とか)とPORT(12345とか)はグローバル変数か何かにしとく
C++側:参考資料2からほぼ引用、難しそうな構造体については参考文献3を参照
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
↑必要なヘッダの皆さん。特に意識せずに使えたので最初から入ってると思われる。
void* thread(void* pParam) //pythonへの情報送信用スレッド
{
////ソケット通信による通信
int sockmode=1; ////1ならソケット通信ON ←通信をONOFFできて便利
if(sockmode==1){
struct sockaddr_in server; ←アドレスなんかを入れておく構造体インスタンスを定義
int sock; ←ソケットをとりあえず定義
char buf[64]; ←バッファを定義[]内はバイト数。多すぎず少なすぎずで。(少ないと即死)
int n; ←データ読み込み用
/* ソケットの作成 */
sock = socket(AF_INET, SOCK_STREAM, 0);
↑AF_INETはIPv4であること、SOCK_STREAMはTCPであることを表している。多分どの言語でも共通の定数。ここらを変えるとIPv6にしたりUDPにもできる。サーバと共通じゃないとだめ。
/* 接続先指定用構造体の準備 */↓python側の設定を書く。つまり宛先の設定
server.sin_family = AF_INET;
server.sin_port = htons(12347); ←ポート番号。htons()はバイトオーダを変換する関数
(詳細不明。接続のための何らかの規格化だろう)
server.sin_addr.s_addr = inet_addr("127.0.0.1");←このIPは自分自身(PC)を示す。変えると別の機器(PC他)にももちろんつながる。はず。関数は文字列をちゃんとアドレスの形式にする関数
/* サーバに接続 */↓詳細は「C++ connect socket」でぐぐってね
connect(sock, (struct sockaddr *)&server, sizeof(server));
while(1){ /* サーバからデータを受信。 送信も */
memset(buf, 0, sizeof(buf)); ←バッファのリセット
stringstream sts; ←とっても便利なstringstream(以下ss)オブジェクトを定義
string str="0,0,end"; ←送信文字列の初期化。文字は適当に
最後の「,end」はとても大事。python側でsplitするときとか特に
↓ 別のところで「進んでOK」とか「止まれ」とかのフラグ出すようにしてる。それに応じて送信値変更。明らかに改善の余地あり。
if(forwardflag ==1){str="100,100,end";}
if(stopflag==1){str="0,0,end";}
cout << "send:" <<str <<endl;
char sdata[64]; ←送信用バッファの定義
sts << str; ←事前に作った文字列をssへ
sts >> sdata; ←ssから送信用charへ自動変換&代入。このツールまじ便利
const char* sdataconst= sdata; ←charをconst charへ送信関数の引数がconst〜なので(このへんの変換でちょっと詰まった。)
write(sock, sdataconst, strlen(sdataconst));←データをあっちに送信。第3引数は送信データの文字数にする。strlenは全角文字入ると正確じゃなくなるので注意
cout<<"senddata"<<sdataconst <<endl;
int n = read(sock, buf, sizeof(buf)); ←pythonからbufへ読み込み。nは読み込めたかどうかとかを確認するための数字(読み込め無かったら-1とか)。読み込みデータじゃないので注意
cout <<"resdata" <<buf << endl;
sleep(1);←1秒に1回通信。もっと少なくてもいいけどスリープ入れないと多分ろくなことにならない
sts.clear(); ←ssの中身をクリア
}
/* socketの終了 */
close(sock); ←ソケット閉じる
}
return 0;
以上。
これでとりあえず通信ができることを確認。強制終了前提のコードなのであくまで参考で。
↓左がC++で右がpython(eclipseのコンソール)。なんとか通信している。
2012年12月9日日曜日
2012年12月1日土曜日
ここのところ覚えた小技のメモ
いろいろアイデアを考えるのはいいけど、結構実装の面で詰まることも多かったので詰まったところをメモ。主にC++の基本的なところとちょっとした小技(?)、忘れると1〜数日潰す可能性あり。
参考資料 : ググって出てきた様々なページ。ヤフー知恵袋、stackoverflow
1.vectorのvectorの作り方とvectorのきほん
vector<vector<double> > ←最後の「>」の手前に半角スペース入れないとだめ(環境に因るかも)
2.vectorの要素の追加
(vector).push_back(入れたい値) でvectorの最後尾に新しい要素を入れる。
カラのvectorの定義だけして vector[n]=値 とかで代入しようとするとエラーになる。
まずvector.push_back=値 またはassign()とかresize()適当なサイズと初期値を設定する。
vectorのvectorならvector.push_back=2層目vector<〜〜> みたいな感じで使う 。
pythonのリストなら適当でなんとかなるのにね。不便だね。
3.opencvのROIの使い方
Rect roirect=Rect(0,0,150,150);// まず四角を定義してROIの設定
Mat imgroi=img;// ←ここでimg(roirect) にすると定義した四角がROIになったimgをimgroiとして定義できる。
ここまではいいがROIのオフセット(ROIの四角の起点になる座標)が(0,0)じゃないときオフセットを考慮する必要がある。 やらないと例えばROIが画面中心らへんだった時検出したキーポイントが画面中心じゃなくて左上とかにずれて表示される。
↓こんな感じ
for(int i=0;i<keypoint.size();i++){
keypoint.pt.x += roirect.x; ←検出したキーポイントの座標をオフセット分だけずらす
keypoint.pt.y += roirect.y; ←キーポイント構造体の座標取り出し方法
(〜.pt.x, 〜.pt.y)も意外に忘れやすいので注意
}
↑キーポイントはROIの中だけで検出されるけどキーポイント座標(pt.x pt.y)もROIのローカル座標でが決定されるのでオフセット分ずらす必要がある(オフセットが(0,0)なら別にずらさなくていい)。
4.文字扱いの数字を数値として取り出す方法
ファイルとかから読み込んだ数値をちゃんとintなりdoubleで読み込む(変換する)コツ。stringstream を使うだけ。これもpythonなら意識する必要すらない部分。C++万歳!(皮肉)
↓以下やり方。
stringstream ss; ←stringstreamのインスタンスをつくる
string str; ←stringと任意の形式(この例ではdouble)を定義
double dbl;
ss << str;// ←stringstreamにstringをぶち込む
ss >>dbl; ←stringstreamからdoubleに取り出す。これで変換完了。
ss.clear(); ←ssの中身をクリア、しないとろくなことにならないよ。ループで使う時とか。
5.opencv:任意の座標の色情報の取り出し方
意外に手こずった部分。Vec3bという型を作って以下の書式で取り出す。
Vec3b color = img.at<Vec3b>(Y座標,X座標); ←.at<Vec3b>はおそらくY座標、X座標の順で定義されている。普通と違うので注意(環境やimgの種類によってちがうかも)。
また、取り出した色情報((B,G,R),0〜256の数字)はデフォルトだと多分charで入ってるので
intへのキャストが必要↓
(int)ccolor[0]+100 ; ←例えば取り出した色の青色成分に100足すとき
今日のところはとりあえず終了。
参考資料 : ググって出てきた様々なページ。ヤフー知恵袋、stackoverflow
1.vectorのvectorの作り方とvectorのきほん
vector<vector<double> > ←最後の「>」の手前に半角スペース入れないとだめ(環境に因るかも)
2.vectorの要素の追加
(vector).push_back(入れたい値) でvectorの最後尾に新しい要素を入れる。
カラのvectorの定義だけして vector[n]=値 とかで代入しようとするとエラーになる。
まずvector.push_back=値 またはassign()とかresize()適当なサイズと初期値を設定する。
vectorのvectorならvector.push_back=2層目vector<〜〜> みたいな感じで使う 。
pythonのリストなら適当でなんとかなるのにね。不便だね。
3.opencvのROIの使い方
Rect roirect=Rect(0,0,150,150);// まず四角を定義してROIの設定
Mat imgroi=img;// ←ここでimg(roirect) にすると定義した四角がROIになったimgをimgroiとして定義できる。
ここまではいいがROIのオフセット(ROIの四角の起点になる座標)が(0,0)じゃないときオフセットを考慮する必要がある。 やらないと例えばROIが画面中心らへんだった時検出したキーポイントが画面中心じゃなくて左上とかにずれて表示される。
↓こんな感じ
for(int i=0;i<keypoint.size();i++){
keypoint.pt.x += roirect.x; ←検出したキーポイントの座標をオフセット分だけずらす
keypoint.pt.y += roirect.y; ←キーポイント構造体の座標取り出し方法
(〜.pt.x, 〜.pt.y)も意外に忘れやすいので注意
}
↑キーポイントはROIの中だけで検出されるけどキーポイント座標(pt.x pt.y)もROIのローカル座標でが決定されるのでオフセット分ずらす必要がある(オフセットが(0,0)なら別にずらさなくていい)。
4.文字扱いの数字を数値として取り出す方法
ファイルとかから読み込んだ数値をちゃんとintなりdoubleで読み込む(変換する)コツ。stringstream を使うだけ。これもpythonなら意識する必要すらない部分。C++万歳!(皮肉)
↓以下やり方。
stringstream ss; ←stringstreamのインスタンスをつくる
string str; ←stringと任意の形式(この例ではdouble)を定義
double dbl;
ss << str;// ←stringstreamにstringをぶち込む
ss >>dbl; ←stringstreamからdoubleに取り出す。これで変換完了。
ss.clear(); ←ssの中身をクリア、しないとろくなことにならないよ。ループで使う時とか。
5.opencv:任意の座標の色情報の取り出し方
意外に手こずった部分。Vec3bという型を作って以下の書式で取り出す。
Vec3b color = img.at<Vec3b>(Y座標,X座標); ←.at<Vec3b>はおそらくY座標、X座標の順で定義されている。普通と違うので注意(環境やimgの種類によってちがうかも)。
また、取り出した色情報((B,G,R),0〜256の数字)はデフォルトだと多分charで入ってるので
intへのキャストが必要↓
(int)ccolor[0]+100 ; ←例えば取り出した色の青色成分に100足すとき
今日のところはとりあえず終了。
2012年11月18日日曜日
しょぼいけど特定物体認識できたよ〜(´∀`)〜
苦節数カ月、やっとここまで来たよ・・・(つд`)
考えうるもっともしょぼい方法だけど、FAST(キーポイント検出)、FREAK(特徴量記述)を使った特定物体認識(というほどのものでもないかもしれないが)に成功したのでメモ。
・・・と思ったけど、今日はとても疲れたので実行結果だけ↓
特徴量を記述、指標化して(参考資料:FREAKの論文)総当り比較(画像が増えてくると間違いなく遅くなる)で、ひげ剃りとかカロリーメイトとか本棚(上段、下段)をまあだいたい識別できるようになった。ちなみに左上の数字はFPS。その横が似ている画像ファイル名。
↓比較用ひげ剃り画像、こんな感じで写真をフォルダに入れまくることで、どれに一番近いかを判別してくれる。
まだ課題も多いけど、これをラジコンと接続して目的の物まで向かわせるとかは頑張れば出来そう。
〜おわり〜
考えうるもっともしょぼい方法だけど、FAST(キーポイント検出)、FREAK(特徴量記述)を使った特定物体認識(というほどのものでもないかもしれないが)に成功したのでメモ。
・・・と思ったけど、今日はとても疲れたので実行結果だけ↓
特徴量を記述、指標化して(参考資料:FREAKの論文)総当り比較(画像が増えてくると間違いなく遅くなる)で、ひげ剃りとかカロリーメイトとか本棚(上段、下段)をまあだいたい識別できるようになった。ちなみに左上の数字はFPS。その横が似ている画像ファイル名。
↓比較用ひげ剃り画像、こんな感じで写真をフォルダに入れまくることで、どれに一番近いかを判別してくれる。
まだ課題も多いけど、これをラジコンと接続して目的の物まで向かわせるとかは頑張れば出来そう。
〜おわり〜
2012年11月17日土曜日
opencvでAi-Ball(小型WiFiカメラ)を使う
わかってしまえば簡単だが、わからないと一生わからない気がするのでメモ
※重要※
今回のコードによって発生した問題につきましては一切の責任を負いかねますのでご注意の上ご利用ください(トリセツとかに載っていない使い方の為)。
今回(3週間くらい前だが)は、Ai-Ball(公式サイト?)をopwncvで普通に使う方法をメモっておきます。
主な参考資料
・Ai-Ballの取説
・海外の質問掲示板(ここの情報がメインだが、場所を失念)
・九州工業大学 宮本研究室HP様(ttp://www.brain.kyutech.ac.jp/~miyamo/index.php/)
要約
c++:
VideoCapture cap("http://(Ai-BallのIPアドレス)/?action=stream.mjpeg");
python:
capture=cv.CaptureFromFile("http://(Ai-BallのIPアドレス)/?action=stream.mjpeg")
↑ちなみに普通のwebカメラだと
capture = cv.CaptureFromCAM(0) pythonでは微妙に使う関数が違うので注意
それぞれのカメラ動画使うサンプル見ればわかると思いますけど、変化点は↑だけです。
なかなか分からなかったのがアドレスの最後の「.mjpeg」の部分(どこかの海外掲示板の丸パクリ、もうどうやって検索したのか思い出せない)。
こうすることでリアルタイム(うちのPCでは18fpsくらいしか出ないが)で普通のウェブカメラとして使えます。
ちなみに宮本研究室さんの公開されているプログラムは動画(mjpeg)としてではなくスナップショット(jpg)として画像を取り込んでいるためfpsが落ちます(環境により異なると思いますが筆者環境では4fpsくらい)
今現在(2012年11月)、使いやすそうな小型無線カメラもあんまりないし、もしかしたらこの情報が役に立つ人がいるといいな・・・と思いつつ終了。
※重要※
今回のコードによって発生した問題につきましては一切の責任を負いかねますのでご注意の上ご利用ください(トリセツとかに載っていない使い方の為)。
今回(3週間くらい前だが)は、Ai-Ball(公式サイト?)をopwncvで普通に使う方法をメモっておきます。
主な参考資料
・Ai-Ballの取説
・海外の質問掲示板(ここの情報がメインだが、場所を失念)
・九州工業大学 宮本研究室HP様(ttp://www.brain.kyutech.ac.jp/~miyamo/index.php/)
要約
c++:
VideoCapture cap("http://(Ai-BallのIPアドレス)/?action=stream.mjpeg");
python:
capture=cv.CaptureFromFile("http://(Ai-BallのIPアドレス)/?action=stream.mjpeg")
↑ちなみに普通のwebカメラだと
capture = cv.CaptureFromCAM(0) pythonでは微妙に使う関数が違うので注意
それぞれのカメラ動画使うサンプル見ればわかると思いますけど、変化点は↑だけです。
なかなか分からなかったのがアドレスの最後の「.mjpeg」の部分(どこかの海外掲示板の丸パクリ、もうどうやって検索したのか思い出せない)。
こうすることでリアルタイム(うちのPCでは18fpsくらいしか出ないが)で普通のウェブカメラとして使えます。
ちなみに宮本研究室さんの公開されているプログラムは動画(mjpeg)としてではなくスナップショット(jpg)として画像を取り込んでいるためfpsが落ちます(環境により異なると思いますが筆者環境では4fpsくらい)
今現在(2012年11月)、使いやすそうな小型無線カメラもあんまりないし、もしかしたらこの情報が役に立つ人がいるといいな・・・と思いつつ終了。
2012年10月28日日曜日
ハンドジェスチャによるラジコン操縦システムの開発
無駄に格調高いタイトルだけど、ただこれまでのプログラムを全混ぜしただけ
まずラジコンの話↓
先週どうにかxbeeが使えるようになったのでラジコンでも作ってみた(有給2日取って)。
同僚「ラジコン作ったんだって?」
筆者「おう!テンション上って動画まで撮ってきたぜ!!」
同僚「マジかよ見せて」
筆者「もちろん!」
↓
同僚「え・・・ああ、なんか微妙だな・・・」
筆者「そうか、まあこんなもんだよ実際・・・(´・ω・)」
しょうがないよね。単三電池4本でArduinoとxbeeとDCモータ2つ動かしてるからね(´・ω・)
〜材料〜
タミヤのキット(これ)の下半分
Arduino uno
xbee ZBのやつ
モータドライバ(TA7291P)✕2
ブレッドボード
電池ボックス、単三✕4
100均の使い捨てお弁当箱
ジャンプワイヤ✕多分14本くらい
〜操作インターフェース(上の動画はこれで操作してた)〜
2本のスライダで前後と左右(回転)を制御する極めて洗練された(必要な機能まで削ぎ落した)インターフェース。
・一旦止まらないと曲がれない酷い仕様。
・PWMで速度は無駄に細かく(最大1024段階)調整できる。
上半分はテスト用のGUIなので今回の話とは無関係
ラジコンの話 〜おわり〜
過去に作ったopencvのプログラム(過去記事)にラジコン操作プログラムをぶち込んで、
特定の指の状態のとき上のインターフェースのスライダの値を変化するようにしてみた。
手の形:動作
パー:前進
親指曲げたパー:後退
グー:停止
チョキ:左回転(回転は人差し指基準に左右の指を立てるイメージ)
指でっぽう:右回転
でやってみた結果、当然ながら成功。ラジコンの動きは上の動画と同じで、手の認識は過去記事と同じ。気分は超能力者 (´д`)ノシ ミ凸
これでとりあえず、カメラ→PCで画像処理、判断→無線→Arduino→モータの流れが実装出来た。
しばらくはコードの最適化、高速化(一部をboost.pythonでC++にしただけでフレームレートが5fps増えた) でもやりながら、機械学習やら制御やらを勉強する予定。
〜おわり〜
まずラジコンの話↓
先週どうにかxbeeが使えるようになったのでラジコンでも作ってみた(有給2日取って)。
同僚「ラジコン作ったんだって?」
筆者「おう!テンション上って動画まで撮ってきたぜ!!」
同僚「マジかよ見せて」
筆者「もちろん!」
↓
同僚「え・・・ああ、なんか微妙だな・・・」
筆者「そうか、まあこんなもんだよ実際・・・(´・ω・)」
しょうがないよね。単三電池4本でArduinoとxbeeとDCモータ2つ動かしてるからね(´・ω・)
〜材料〜
タミヤのキット(これ)の下半分
Arduino uno
xbee ZBのやつ
モータドライバ(TA7291P)✕2
ブレッドボード
電池ボックス、単三✕4
100均の使い捨てお弁当箱
ジャンプワイヤ✕多分14本くらい
〜操作インターフェース(上の動画はこれで操作してた)〜
2本のスライダで前後と左右(回転)を制御する極めて洗練された(必要な機能まで削ぎ落した)インターフェース。
・一旦止まらないと曲がれない酷い仕様。
・PWMで速度は無駄に細かく(最大1024段階)調整できる。
上半分はテスト用のGUIなので今回の話とは無関係
ラジコンの話 〜おわり〜
過去に作ったopencvのプログラム(過去記事)にラジコン操作プログラムをぶち込んで、
特定の指の状態のとき上のインターフェースのスライダの値を変化するようにしてみた。
手の形:動作
パー:前進
親指曲げたパー:後退
グー:停止
チョキ:左回転(回転は人差し指基準に左右の指を立てるイメージ)
指でっぽう:右回転
でやってみた結果、当然ながら成功。ラジコンの動きは上の動画と同じで、手の認識は過去記事と同じ。気分は超能力者 (´д`)ノシ ミ凸
これでとりあえず、カメラ→PCで画像処理、判断→無線→Arduino→モータの流れが実装出来た。
しばらくはコードの最適化、高速化(一部をboost.pythonでC++にしただけでフレームレートが5fps増えた) でもやりながら、機械学習やら制御やらを勉強する予定。
〜おわり〜
2012年10月20日土曜日
arduino-xbee-firmata-python連携キター(´∀`)ー
いろいろ参考にしまくってどうにかxbee(簡単に無線でシリアル通信できるユニット)でいつも通りにarduino動かせるようになったので要点をメモ(忘れると1日潰れる箇所あり。注意)
筆者環境:ubuntu 12.04 (32bit) pythonはeclipse(PyDev) 他はふつうのはず
主な参考文献、ソフトなどの入手先
1、ubuntuでのxbeeのソフト設定方法を参考にさせて頂きました。
ttp://d.hatena.ne.jp/abikounso/20100506/1273106924
2、オライリーの「XBeeで作るワイヤレスセンサネットワーク」、今回の話の基盤になる書籍。
3、FTDI(PCとxbeeをつなぐUSBコネクタのドライバ)
ttp://www.ftdichip.com/FTDrivers.htm(目次的なページ)
http://www.ftdichip.com/Drivers/VCP.htm(ubuntuはこっち)
ページにあるTN-101という資料に具体的な入れ方が書いてあるから見ながら入れる(英語)
■xbeeを使えるようにする
上記3のドライバを入れる → xbeeをエクスプローラ(xbee-USBコネクタ)に装着
→ PCのUSBに挿し込む
→ 参考資料1や2を参考にwineでX-CTUを起動 、ファームウェア(〜ATのバージョン最新のやつ、バージョンは16進数なので注意)をxbeeに書き込む
→picocom(コマンドラインでxbeeの通信設定できるソフト) を導入
apt-get でも入れられるがバージョンが古い(1.4)ため参考資料のやり方でできない。
最新のtar玉落としてきて 「cd (最新版のフォルダ) ./picocom」で起動
これを使ってATコマンドでネットワークIDなどの登録、設定を行う。無いと詰む
→ 順序は変わるが、ここでボーレートをデフォルトの9600から57600に変更する。
やらないとfirmata使えない(解決に半日)やり方は以下
ファームウェアの設定画面のSerial〜 のBaud Rateを57600にする。
メイン画面の右上のBaudも57600にしておく
2個目のxbeeも同様に設定する(USB引っこ抜いて、アダプタのxbeeを載せ替え)。ファームウェアは片方はコーディネータ(AT)、片方はルータ(AT)になるようにしておく。あと、ATDH、ATDLのシリアルは相手側の番号を入力する。
コーディネータをPC(USBコネクタで)に繋ぎ、ルータの方をarduino(xbeeシールド)に載せる。
図は配線後の写真(サンプルスケッチでLEDを光らせてみた)
シールドを使うときの 注意点として、arduinoにスケッチ(arduinoに書き込むプログラムのおしゃれな言い方)を書き込むときはシールド上「serial select」スイッチをUSBに、書き込み終わって実際に無線で動かすときはMICROにすること。間違えると動かないし、スケッチ書き込みでバグる。
arduinoにstandardfirmataのスケッチ(サンプルの中にある)を書き込む。
pythonでpyfirmata(ググる→githubらへんから入手→setup.pyでインストール→pythonのインクルードパスにフォルダ登録しとく)を使って普通にプログラム書く。使い方はpyfirmataのサンプル参照。そのうち自分でまとめるかもしれないけど。
→以前に作ったfirmata用テストプログラムの接続ポートだけ変えてサーボモータを動かすことに成功。スライドバーの動きに応じてサーボが動く、楽しすぎワロタ
上がテストGUI(作りが雑)、下が実機の構成(arduino、電池、サーボ、ブレッドボード )。
ブレッドボードに今回の話と関係ないセンサが刺さってるのはご愛嬌
明日と来週で適当にラジコンでも作ってみるか。
・・・書き忘れがあったら嫌だな(;´д`)。ああ、無線モノは認識に時間かかる(X-CTUとか)から気長に待つべし。連打するとろくなことにならない。
筆者環境:ubuntu 12.04 (32bit) pythonはeclipse(PyDev) 他はふつうのはず
主な参考文献、ソフトなどの入手先
1、ubuntuでのxbeeのソフト設定方法を参考にさせて頂きました。
ttp://d.hatena.ne.jp/abikounso/20100506/1273106924
2、オライリーの「XBeeで作るワイヤレスセンサネットワーク」、今回の話の基盤になる書籍。
3、FTDI(PCとxbeeをつなぐUSBコネクタのドライバ)
ttp://www.ftdichip.com/FTDrivers.htm(目次的なページ)
http://www.ftdichip.com/Drivers/VCP.htm(ubuntuはこっち)
ページにあるTN-101という資料に具体的な入れ方が書いてあるから見ながら入れる(英語)
■xbeeを使えるようにする
上記3のドライバを入れる → xbeeをエクスプローラ(xbee-USBコネクタ)に装着
→ PCのUSBに挿し込む
→ 参考資料1や2を参考にwineでX-CTUを起動 、ファームウェア(〜ATのバージョン最新のやつ、バージョンは16進数なので注意)をxbeeに書き込む
→picocom(コマンドラインでxbeeの通信設定できるソフト) を導入
apt-get でも入れられるがバージョンが古い(1.4)ため参考資料のやり方でできない。
最新のtar玉落としてきて 「cd (最新版のフォルダ) ./picocom」で起動
これを使ってATコマンドでネットワークIDなどの登録、設定を行う。無いと詰む
→ 順序は変わるが、ここでボーレートをデフォルトの9600から57600に変更する。
やらないとfirmata使えない(解決に半日)やり方は以下
ファームウェアの設定画面のSerial〜 のBaud Rateを57600にする。
メイン画面の右上のBaudも57600にしておく
2個目のxbeeも同様に設定する(USB引っこ抜いて、アダプタのxbeeを載せ替え)。ファームウェアは片方はコーディネータ(AT)、片方はルータ(AT)になるようにしておく。あと、ATDH、ATDLのシリアルは相手側の番号を入力する。
コーディネータをPC(USBコネクタで)に繋ぎ、ルータの方をarduino(xbeeシールド)に載せる。
図は配線後の写真(サンプルスケッチでLEDを光らせてみた)
シールドを使うときの 注意点として、arduinoにスケッチ(arduinoに書き込むプログラムのおしゃれな言い方)を書き込むときはシールド上「serial select」スイッチをUSBに、書き込み終わって実際に無線で動かすときはMICROにすること。間違えると動かないし、スケッチ書き込みでバグる。
arduinoにstandardfirmataのスケッチ(サンプルの中にある)を書き込む。
pythonでpyfirmata(ググる→githubらへんから入手→setup.pyでインストール→pythonのインクルードパスにフォルダ登録しとく)を使って普通にプログラム書く。使い方はpyfirmataのサンプル参照。そのうち自分でまとめるかもしれないけど。
→以前に作ったfirmata用テストプログラムの接続ポートだけ変えてサーボモータを動かすことに成功。スライドバーの動きに応じてサーボが動く、楽しすぎワロタ
上がテストGUI(作りが雑)、下が実機の構成(arduino、電池、サーボ、ブレッドボード )。
ブレッドボードに今回の話と関係ないセンサが刺さってるのはご愛嬌
明日と来週で適当にラジコンでも作ってみるか。
・・・書き忘れがあったら嫌だな(;´д`)。ああ、無線モノは認識に時間かかる(X-CTUとか)から気長に待つべし。連打するとろくなことにならない。
2012年10月14日日曜日
boost python の導入
C/C++でglade使ってGTKのUI作ってOPENCVも使って、同時にArduinoも(firmataで)動かせればいいな〜と思いつつここ2週間ほど足掻いてきたけど
「もうむりぽ・・・(´ω`)、いまのぼくではpythonでしかopencvとarduinoを繋げないお・・」
な感じになってきたので、やっぱりpythonで諸問題の解決に当たることにしました。
pythonにおける実行速度の問題解決方法はだいたい以下の路線になります。
1,cpython
2,pypy
3,boost python
4,そもそものアルゴリズムを変更
今回は3の「boost python」とかいうのに(C++勉強したのがもったいなかったので)挑戦したのでメモ
boost python・・・C++のコードを普通にpythonにimportできる神ライブラリ(boostっていうのはC++の準標準ライブラリ群のことらしい)
これでやたら計算時間がかかるベクトルの角度計算のとこだけ高速化できればめでたくHappyENDを迎えることができる。
で、Boostのインストールから(ubuntu 12.04 32bit)
synapticで「boost」で検索→libboost-python1.46.1(2012年10月現在)を インストール(依存関係にあるものも全部)。ほかの方法(apt-getとかtar玉から手動でインストールとかはなぜかうまく行かなかった(インストールはうまく行ったがその後が・・・)。ふつうはうまく行くはずなのでそちらで行けるならどうぞ)
おわり
使い方:
参考文献:
1.「[c++][python][boost]boost::pythonを使ってC++のPythonラッパーを作る」: ttp://d.hatena.ne.jp/sleepy_yoshi/20111120/p1
2.Boost.Python の機能をざっと紹介してみる :
ttp://d.hatena.ne.jp/moriyoshi/20091214/1260779899
参考文献を参考にさせていただきながら、どうにかhello world的な行為に成功。
□まずc++のソースを書く(ほぼ参考文献2からの引用、コードの解説はそちらでどうぞ)
#define BOOST_PYTHON_STATIC_LIB
#include <string>
#include <boost/python.hpp>
using namespace std;
string add_hello( string s )
{
return "Hello, " + s;
}
int square( int n )
{
return n * n;
}
BOOST_PYTHON_MODULE(kakudo)
{
// C++のadd_hello関数を、greetという名前でpython用に公開
boost::python::def( "greet", add_hello );
// C++のsquare関数を、squareという名前でpython用に公開
boost::python::def( "square", square );
}
流れとしては、ヘッダ書く→関数書く(クラスもいけるらしい)→pythonの関数名との対応関係を記入という感じ。これだけ見るとわかりやすくて簡単そうだが・・・
□これをpythonで動かす
g++でコンパイル(コンパイルのオプションなんかは参考文献1からのパクr引用)
g++ -I`python -c 'from distutils.sysconfig import *; print get_python_inc()'` -DPIC -shared -fPIC -o kakudo.so kakudo.cpp -lboost_python
↑何がなんだかわからない。ちょっと調べて簡略化(太字部分)
g++ -I/usr/include/python2.7 -DPIC -shared -fPIC -o kakudo.so kakudo.cpp -lboost_python
どうやらここはこの前の`pkg-config 〜`みたいな感じで「python.h」のおいてあるフォルダの位置(だいたいpythonのインストールフォルダ)と思われる(他のページでpython.hがいるって書いてたきがする) 。
次に、 -DPIC -shared -fPIC の部分。-DPICはよく分からなかった(削除してもとりあえず問題なかった) -shared -fPIC は共用ファイル(〜.so)を作るのにセットで必要なオプションっぽい(-fpicはなくても機能したけど直感的に入れといたほうが良い気がする)
その次は -o (出力ファイル名) (入力ファイル名)
最後に -lboost_python は-l(ライブラリの検索) とboost_pythonなのでインクルードパス(どこだかしらないけどboostを入れた時にg++のデフォルト探索範囲に設定されてるはず)からboost_pythonを探してくるコマンド。もし無いと
ImportError:〜.so: undefined symbol: _ZN5boost6python6detail11init_moduleEPKcPFvvE
↑python側でこんなエラーが出る(知らずにこんなエラーが出たら泣くね。ライブラリ忘れには注意したいところ)。
〜python側の処理(筆者のオリジナルコード)〜
pythonのインクルードパスにkakudo.so (名前は↑のコマンドで自由に変更できる)のあるフォルダを入れてから以下の高度なpythonプログラムを記述し、実行する。
import kakudo
a=kakudo.greet("hello")
print a
〜出力〜
Hello, hello
以上、おわり。
来週はベクトルの角度求める関数でも書いて見る予定。これで高速化出来なかったら精神的ダメージを受けそうだわ・・・わりとその可能性もあるし
「もうむりぽ・・・(´ω`)、いまのぼくではpythonでしかopencvとarduinoを繋げないお・・」
な感じになってきたので、やっぱりpythonで諸問題の解決に当たることにしました。
pythonにおける実行速度の問題解決方法はだいたい以下の路線になります。
1,cpython
2,pypy
3,boost python
4,そもそものアルゴリズムを変更
今回は3の「boost python」とかいうのに(C++勉強したのがもったいなかったので)挑戦したのでメモ
boost python・・・C++のコードを普通にpythonにimportできる神ライブラリ(boostっていうのはC++の準標準ライブラリ群のことらしい)
これでやたら計算時間がかかるベクトルの角度計算のとこだけ高速化できればめでたくHappyENDを迎えることができる。
で、Boostのインストールから(ubuntu 12.04 32bit)
synapticで「boost」で検索→libboost-python1.46.1(2012年10月現在)を インストール(依存関係にあるものも全部)。ほかの方法(apt-getとかtar玉から手動でインストールとかはなぜかうまく行かなかった(インストールはうまく行ったがその後が・・・)。ふつうはうまく行くはずなのでそちらで行けるならどうぞ)
おわり
使い方:
参考文献:
1.「[c++][python][boost]boost::pythonを使ってC++のPythonラッパーを作る」: ttp://d.hatena.ne.jp/sleepy_yoshi/20111120/p1
2.Boost.Python の機能をざっと紹介してみる :
ttp://d.hatena.ne.jp/moriyoshi/20091214/1260779899
参考文献を参考にさせていただきながら、どうにかhello world的な行為に成功。
□まずc++のソースを書く(ほぼ参考文献2からの引用、コードの解説はそちらでどうぞ)
#define BOOST_PYTHON_STATIC_LIB
#include <string>
#include <boost/python.hpp>
using namespace std;
string add_hello( string s )
{
return "Hello, " + s;
}
int square( int n )
{
return n * n;
}
BOOST_PYTHON_MODULE(kakudo)
{
// C++のadd_hello関数を、greetという名前でpython用に公開
boost::python::def( "greet", add_hello );
// C++のsquare関数を、squareという名前でpython用に公開
boost::python::def( "square", square );
}
流れとしては、ヘッダ書く→関数書く(クラスもいけるらしい)→pythonの関数名との対応関係を記入という感じ。これだけ見るとわかりやすくて簡単そうだが・・・
□これをpythonで動かす
g++でコンパイル(コンパイルのオプションなんかは参考文献1からのパクr引用)
g++ -I`python -c 'from distutils.sysconfig import *; print get_python_inc()'` -DPIC -shared -fPIC -o kakudo.so kakudo.cpp -lboost_python
↑何がなんだかわからない。ちょっと調べて簡略化(太字部分)
g++ -I/usr/include/python2.7 -DPIC -shared -fPIC -o kakudo.so kakudo.cpp -lboost_python
どうやらここはこの前の`pkg-config 〜`みたいな感じで「python.h」のおいてあるフォルダの位置(だいたいpythonのインストールフォルダ)と思われる(他のページでpython.hがいるって書いてたきがする) 。
次に、 -DPIC -shared -fPIC の部分。-DPICはよく分からなかった(削除してもとりあえず問題なかった) -shared -fPIC は共用ファイル(〜.so)を作るのにセットで必要なオプションっぽい(-fpicはなくても機能したけど直感的に入れといたほうが良い気がする)
その次は -o (出力ファイル名) (入力ファイル名)
最後に -lboost_python は-l(ライブラリの検索) とboost_pythonなのでインクルードパス(どこだかしらないけどboostを入れた時にg++のデフォルト探索範囲に設定されてるはず)からboost_pythonを探してくるコマンド。もし無いと
ImportError:〜.so: undefined symbol: _ZN5boost6python6detail11init_moduleEPKcPFvvE
↑python側でこんなエラーが出る(知らずにこんなエラーが出たら泣くね。ライブラリ忘れには注意したいところ)。
〜python側の処理(筆者のオリジナルコード)〜
pythonのインクルードパスにkakudo.so (名前は↑のコマンドで自由に変更できる)のあるフォルダを入れてから以下の高度なpythonプログラムを記述し、実行する。
import kakudo
a=kakudo.greet("hello")
print a
〜出力〜
Hello, hello
以上、おわり。
来週はベクトルの角度求める関数でも書いて見る予定。これで高速化出来なかったら精神的ダメージを受けそうだわ・・・わりとその可能性もあるし
2012年10月10日水曜日
eclipseでC++でGTKで動かす(その1)
先月末ごろ
筆者「じゃんけんマシンの基礎キターーー(略」
筆者「・・・でもどう頑張っても20fpsくらいしか出ない。
しゃーなしC++やってみるか。死ぬほど嫌だけど。」
ということでC++の 勉強を始めました。今回はそのメモ
eclipseだとCDT(C Develop tool(?))というのをいれるとC/C++が使えるようになる。
CDT入れるとこまではググってどうにか・・・・
で、問題はここからだよ。先週の土日を消費しても間に合わず今日(いまさっき)どうにか到達
1,gtkmm(pythonでいうところのpygtkにあたるもの)の導入
筆者「じゃんけんマシンの基礎キターーー(略」
筆者「・・・でもどう頑張っても20fpsくらいしか出ない。
しゃーなしC++やってみるか。死ぬほど嫌だけど。」
ということでC++の 勉強を始めました。今回はそのメモ
eclipseだとCDT(C Develop tool(?))というのをいれるとC/C++が使えるようになる。
CDT入れるとこまではググってどうにか・・・・
で、問題はここからだよ。先週の土日を消費しても間に合わず今日(いまさっき)どうにか到達
1,gtkmm(pythonでいうところのpygtkにあたるもの)の導入
sudo apt-get install
libgtkmm-3.0-dev
sudo apt-get install
libgstreamermm-0.10-dev
sudo apt-get install
libgdamm4.0-dev
という感じでインストールするだけ(ubuntu12.04)。
2,gtkmmの動作を確認
ようつべにて
「Creating a simple GUI programm with C++ and GTKMM with a Button」
という偉大な動画からテスト用のコードを拝借
#include <gtkmm.h>
int main( int argc, char *argv[] )
{
Gtk::Main kit( argc, argv );
Gtk::Window window;
Gtk::Main::run( window );
return 0;
}
これでウィンドウが表示できるぜ!!ってなればよかったんだけどね・・・
当然コンパイラが<gtkmm.h>とかいうのをどここから探して
持ってこないといけないわけでして・・・
ここでeclipseの設定をいじらないといけなくなりました。
まあ当然と言われるとそうなんだが。
以下設定のしかた。
1,プロジェクトのプロパティから下のような設定画面を出す。
ここでC/C++ビルドの「設定」をクリック
2,この中の 「〜C++ Compiler」の「その他」をクリック
で、「その他のフラグ」に`pkg-config --libs --cflags gtkmm-3.0`と書いておく
(かいせつ)
「`」はshift+@キーで出る(shift+7キーじゃないよ)
pkg-config はいつの間にか入ってたがアプリっぽい。もし無いなら入れるべし。
--libs --cflags は
pkg-configのオプション。C++(多分Cも)にインクルードする関連の
ファイルのパスを全部表示してくれる便利な奴。
これでgtkmm使うに当って必要なファイルは全部リンク出来る感じになる。多分他のツール
(opencvとか)使うときにも活用できる。
その他の3つほど上に「インクルード」とかいうのがあってそれで
参照するパスを入れることもできるが(多分そっちが普通か?)
gtkmmは参照ファイルが多いので止めた方がいい。 日が暮れるので。
これで使えたらいいんだけどまだやることがある。
今まで触ってた項目の親である「GCC C++ Compiler」をクリックして「エキスパート設定
コマンド行パターン」とかいう難しそうなところに注目。
どうやらここでコンパイル時のコマンドラインの入力順を規定しているっぽい。
eclipseも所詮はコマンドライン入力を自動でやってくれるだけの存在でしかない。
ここで「g++ (ファイル名)(オプション)(出力ファイル名)(なんとか〜)」の()の
順番をいろいろ入れ替えられるようになってる(はず)。
デフォルトだとgtkmm込のコンパイルが通らないのでここの順番を
${COMMAND} ${FLAGS} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS}
から
${COMMAND} ${OUTPUT_FLAG} ${OUTPUT_PREFIX}${OUTPUT} ${INPUTS} ${FLAGS}
に変更(ここの丸写し。いいのだろうか:スタックオーバーフロー)
でビルド→実行→窓出現
ビルドで警告出るし、実行で「必要なプロジェクトにエラーがあります」とか出るけど
無視して実行(そのうち解決できるといいね)。
結果↓
pythonなら10分くらいで出来そうな話だったけどさすがC++。そらみんな別の言語使うわ。
気軽にGUI作れるgladeっていうソフトで作れるようになるとこまで持っていくので次回へ
続く(諦めてpythonに戻ったら続かない)
2012年9月30日日曜日
じゃんけんマシンの基礎キター(´∀`)ー
ついにここまできたぜ(これを目指してたわけじゃないけど)・・・
手領域の中心からの指の位置を正規化したりとかして指の状態把握が出来るようになっターーーーーーー(・∀・)ーーーー
これで一人でもじゃんけん遊びができるぜ!!!
以下スクリーンショット
右画面のリスト([]の中身)は左から近似指標(0〜100%)、手の状態、認識してる指の本数です。
とりあえずグーチョキパーとじゃんけんで出したら友達が減るやつ(指鉄砲)だけ定義して認識出来るようにしました。
つらい道のりだった・・・ソースとか考え方の解説とかは来週にはなんとかできたらいいな(飲み会があったような気がするから無理かもしれないが)
以上、おわり
手領域の中心からの指の位置を正規化したりとかして指の状態把握が出来るようになっターーーーーーー(・∀・)ーーーー
これで一人でもじゃんけん遊びができるぜ!!!
以下スクリーンショット
右画面のリスト([]の中身)は左から近似指標(0〜100%)、手の状態、認識してる指の本数です。
とりあえずグーチョキパーとじゃんけんで出したら友達が減るやつ(指鉄砲)だけ定義して認識出来るようにしました。
つらい道のりだった・・・ソースとか考え方の解説とかは来週にはなんとかできたらいいな(飲み会があったような気がするから無理かもしれないが)
以上、おわり
2012年9月23日日曜日
指の特定(とりあえず親指)
左右の手も多分 すぐに判別可能になる(予定) (´д`)−3
「一つだけ離れてる指が多分親指だよNE?」的な判断基準なので、指を3本以上出してないと画面位置で一番上の指を親指と判別してしまう。そこはもう来週で・・・(つд`)
↓参考写真(親指(と判定した指)に「thumb」と出すようにしてる)
おわり コードはそのうち解説付きでメモ予定
2012年9月2日日曜日
2012年8月31日金曜日
夏休みの工作 〜指の認識〜
もはや夏休みでも何でもない今日このごろ
実は先週の話だけどpythonとopencvでなんとか指の画像認識まで漕ぎつけたので概要をメモ
メモっとかないと絶対に忘れるわこれ・・・(;´д`)
〜あらすじ〜
凸包使って手は認識できるようになったけど指はちゃんと認識出来なかった。
しかも座標で一番上に来る指は全く認識出来なかった。
これまでの状態↓
〜今回のはなし〜
凸包凹状欠損はやっぱりやめて参考文献(前回参照)を参考に輪郭(3点)の角度を計算して指先を検出してみた。それに伴う微調整項目の説明とメモ
〜「手」の輪郭を決定したところから
rangewidth=int(〜〜) #3点の座標間の距離(2だと2個飛ばしで座標角度計算)
##指検出がらみの変数
renzoku=0 #エッジ点が連続してるかどうかのフラグ
egroup=[] #連続しているエッジのグループを入れるリスト。1グループ1指。
for k in range(20): ##指の位置配列20もいらないけどエラー防止で
egroup.append([]) #20個グループを作っておく
fingernum=0 #指の番号
robust=20 #多少不連続でも同じグループに入れる座標の個数
#処理画像に文字を入れる時に使う↓
MyFont = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, 1, 1, 0, 2, 4)
FontCOLOR = cv.CV_RGB(255, 0, 0)
#輪郭の座標数がそもそも少なすぎるときは判定しない
if len(contours)<rangewidth:
return frame ←今の画像をメインルーチンに返す
#座標1個ずつ処理(座標には1個ずつ通し番号がふられている)
for i in range(len(contours)):
#角度を求める座標3点(前、中心、後ろ)を決定
iprev=i-rangewidth
inext=i+rangewidth
#前後座標が座標番号を超過していたらそこだけ2週目に入る処理
if iprev<0:#前の座標が座標番号0番より前だったら・・・
iprev=len(contours)+(i-rangewidth)
if inext>=len(contours):#後ろの座標番号が最後の番号を超えていたら・・・
inext=rangewidth-(len(contours)-i)
#角度を計算(別に角度を計算する関数作っておく)
deg=kakudo(contours[iprev],contours[i],contours[inext])
#角度に応じた処理。ロバストの回数だけ連続じゃなかったら次のグループへ行く
if deg< 角度のしきい値 :##角度条件を満たした時(60度くらいがおすすめ、要調整)
if renzoku==1: ##1粒前の座標で条件を満足してたら・・
#print "renzoku"
#今のグループにこの座標も加える
egroup[fingernum].append(contours[i])
renzoku=1 #連続フラグを1(ON)に
robust=10 #連続だったらそのたびにロバストをリセット
else:##角度の条件を満たさなかった時
if renzoku ==1: #1粒前は満たしてたけど今回は満たしてなかった時
robust -=1 #ロバスト変数を減らす
renzoku=0
elif renzoku ==0: #1粒前も今回も満たしてなかった時
robust -=1 #ロバスト変数を減らす
renzoku=0
↑if とelifで処理一緒でワロタ。まとめてしまってOK
if robust ==0: #ロバスト変数が無くなったらグループ終了。次のグループへ
renzoku=0
fingernum +=1 #グループを次へ
#指ごとの処理はここまで
#各グループ(指だといいな)ごとの処理。指の先端とかを設定したりなど
egroup[0][:0]=egroup[fingernum] #謎の処理。いらないのではないか
fingcenter=[] #グループ座標群の通し番号真ん中の座標
fingeraverage=[] #グループ座標の平均。たぶん指の重心を表す。こっちがメイン
#グループ(指)の数だけ処理
for i in range(fingernum):
c=int(len(egroup[i])*0.5)#グループ通し番号真ん中の番号
#fingcenterに加える。
fingcenter.append(egroup[i][c])
sum=[0,0] #座標平均出すための合計計算用
#グループの座標数が10より多ければ処理(少ないと指じゃないのまで拾うかも・・・要調整)
if len(egroup[i])>10:
for j in egroup[i]:#グループ内の座標1粒ごとに。x,y座標ごとに全部足す
sum[0]=sum[0]+j[0]
sum[1]=sum[1]+j[1]
sum[0]=sum[0]/len(egroup[i]) #足したら座標の数で割って平均出す
sum[1]=sum[1]/len(egroup[i])
fingeraverage.append((sum[0],sum[1]))#指の重心座標をリストに加える
#重心に○でも書いておく
cv.Circle (frame,fingeraverage[i] , 5,(255, 255, 255),cv.CV_FILLED, cv.CV_AA, 0)
おわり。ここまでの処理画像(この例だとframe)をメインルーチンに戻す。
↓出てきた画像(動画)。リアルタイム(7fpsだが)で指の重心と通し番号を書いてます。
手のひらに余計な○が2つほどあるけど気にしない。
↓横にしても大丈夫!!(腕が写り込まなければね)
追伸:座標角度計算関数が地味に大事だったのを忘れてたので記述
def kakudo(a,b,c):#座標を3つ入力
v1=(a[0]-b[0],a[1]-b[1])
v2=(c[0]-b[0],c[1]-b[1])
#内積と外積を出しておく
dots=numpy.dot(v1,v2)
cros=numpy.cross(v1,v2)
v1=math.sqrt(numpy.dot(v1,v1))
v2=math.sqrt(numpy.dot(v2,v2))
coth=dots/(v1*v2)
kakudo=numpy.arccos(coth)
kakudo=kakudo*180/math.pi #内積を使った角度の計算
#↓ここ大事。こんな実装の仕方でいいのかは不明
この処理がないと指の股も検出されてしまう。逆に不等号変えると指の股だけ出せる。
if cros<0:##エッジの向きが指先の方向でなければ強制的に角度を180度に。指の股を弾く
kakudo =180
return kakudo
実は先週の話だけどpythonとopencvでなんとか指の画像認識まで漕ぎつけたので概要をメモ
メモっとかないと絶対に忘れるわこれ・・・(;´д`)
〜あらすじ〜
凸包使って手は認識できるようになったけど指はちゃんと認識出来なかった。
しかも座標で一番上に来る指は全く認識出来なかった。
これまでの状態↓
〜今回のはなし〜
凸包凹状欠損はやっぱりやめて参考文献(前回参照)を参考に輪郭(3点)の角度を計算して指先を検出してみた。それに伴う微調整項目の説明とメモ
〜「手」の輪郭を決定したところから
rangewidth=int(〜〜) #3点の座標間の距離(2だと2個飛ばしで座標角度計算)
##指検出がらみの変数
renzoku=0 #エッジ点が連続してるかどうかのフラグ
egroup=[] #連続しているエッジのグループを入れるリスト。1グループ1指。
for k in range(20): ##指の位置配列20もいらないけどエラー防止で
egroup.append([]) #20個グループを作っておく
fingernum=0 #指の番号
robust=20 #多少不連続でも同じグループに入れる座標の個数
#処理画像に文字を入れる時に使う↓
MyFont = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, 1, 1, 0, 2, 4)
FontCOLOR = cv.CV_RGB(255, 0, 0)
#輪郭の座標数がそもそも少なすぎるときは判定しない
if len(contours)<rangewidth:
return frame ←今の画像をメインルーチンに返す
#座標1個ずつ処理(座標には1個ずつ通し番号がふられている)
for i in range(len(contours)):
#角度を求める座標3点(前、中心、後ろ)を決定
iprev=i-rangewidth
inext=i+rangewidth
#前後座標が座標番号を超過していたらそこだけ2週目に入る処理
if iprev<0:#前の座標が座標番号0番より前だったら・・・
iprev=len(contours)+(i-rangewidth)
if inext>=len(contours):#後ろの座標番号が最後の番号を超えていたら・・・
inext=rangewidth-(len(contours)-i)
#角度を計算(別に角度を計算する関数作っておく)
deg=kakudo(contours[iprev],contours[i],contours[inext])
#角度に応じた処理。ロバストの回数だけ連続じゃなかったら次のグループへ行く
if deg< 角度のしきい値 :##角度条件を満たした時(60度くらいがおすすめ、要調整)
if renzoku==1: ##1粒前の座標で条件を満足してたら・・
#print "renzoku"
#今のグループにこの座標も加える
egroup[fingernum].append(contours[i])
renzoku=1 #連続フラグを1(ON)に
robust=10 #連続だったらそのたびにロバストをリセット
else:##角度の条件を満たさなかった時
if renzoku ==1: #1粒前は満たしてたけど今回は満たしてなかった時
robust -=1 #ロバスト変数を減らす
renzoku=0
elif renzoku ==0: #1粒前も今回も満たしてなかった時
robust -=1 #ロバスト変数を減らす
renzoku=0
↑if とelifで処理一緒でワロタ。まとめてしまってOK
if robust ==0: #ロバスト変数が無くなったらグループ終了。次のグループへ
renzoku=0
fingernum +=1 #グループを次へ
#指ごとの処理はここまで
#各グループ(指だといいな)ごとの処理。指の先端とかを設定したりなど
egroup[0][:0]=egroup[fingernum] #謎の処理。いらないのではないか
fingcenter=[] #グループ座標群の通し番号真ん中の座標
fingeraverage=[] #グループ座標の平均。たぶん指の重心を表す。こっちがメイン
#グループ(指)の数だけ処理
for i in range(fingernum):
c=int(len(egroup[i])*0.5)#グループ通し番号真ん中の番号
#fingcenterに加える。
fingcenter.append(egroup[i][c])
sum=[0,0] #座標平均出すための合計計算用
#グループの座標数が10より多ければ処理(少ないと指じゃないのまで拾うかも・・・要調整)
if len(egroup[i])>10:
for j in egroup[i]:#グループ内の座標1粒ごとに。x,y座標ごとに全部足す
sum[0]=sum[0]+j[0]
sum[1]=sum[1]+j[1]
sum[0]=sum[0]/len(egroup[i]) #足したら座標の数で割って平均出す
sum[1]=sum[1]/len(egroup[i])
fingeraverage.append((sum[0],sum[1]))#指の重心座標をリストに加える
#重心に○でも書いておく
cv.Circle (frame,fingeraverage[i] , 5,(255, 255, 255),cv.CV_FILLED, cv.CV_AA, 0)
おわり。ここまでの処理画像(この例だとframe)をメインルーチンに戻す。
↓出てきた画像(動画)。リアルタイム(7fpsだが)で指の重心と通し番号を書いてます。
手のひらに余計な○が2つほどあるけど気にしない。
↓横にしても大丈夫!!(腕が写り込まなければね)
追伸:座標角度計算関数が地味に大事だったのを忘れてたので記述
def kakudo(a,b,c):#座標を3つ入力
v1=(a[0]-b[0],a[1]-b[1])
v2=(c[0]-b[0],c[1]-b[1])
#内積と外積を出しておく
dots=numpy.dot(v1,v2)
cros=numpy.cross(v1,v2)
v1=math.sqrt(numpy.dot(v1,v1))
v2=math.sqrt(numpy.dot(v2,v2))
coth=dots/(v1*v2)
kakudo=numpy.arccos(coth)
kakudo=kakudo*180/math.pi #内積を使った角度の計算
#↓ここ大事。こんな実装の仕方でいいのかは不明
この処理がないと指の股も検出されてしまう。逆に不等号変えると指の股だけ出せる。
if cros<0:##エッジの向きが指先の方向でなければ強制的に角度を180度に。指の股を弾く
kakudo =180
return kakudo
フォントなどのテスト
######
#######
テスト12341234
あああ(´д`)ノ
import gtk
builder = gtk.Builder()
builder.add_from_file('test.glade')
win1=builder.get_object("window1")
win2=builder.get_object("window2")
def main():
dic = 123
MyFont = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, 1, 1, 0, 2, 4)
Font = cv.CV_RGB(255, 0, 0)
if len(contours)<rangewidth:
return frameb
for i in range(len(contours)):
iprev=i-rangewidth
inext=i+rangewidth
通勤中の筆者(これまで)
}
モニタ用
Serial.print("\n pin=");
Serial.print(pin,DEC);
Serial.print("\n mode=");
Serial.print(mode,DEC);
Serial.print("\n value=");
Serial.print(value,DEC);
if(mode == 1){
analogWrite(pin,value);
}
* *
* + テストです
n ∧_∧ n
+ (ヨ(*´∀`)E)
Y Y *
#######
テスト12341234
あああ(´д`)ノ
import gtk
builder = gtk.Builder()
builder.add_from_file('test.glade')
win1=builder.get_object("window1")
win2=builder.get_object("window2")
def main():
dic = 123
MyFont = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, 1, 1, 0, 2, 4)
Font = cv.CV_RGB(255, 0, 0)
if len(contours)<rangewidth:
return frameb
for i in range(len(contours)):
iprev=i-rangewidth
inext=i+rangewidth
通勤中の筆者(これまで)
}
モニタ用
Serial.print("\n pin=");
Serial.print(pin,DEC);
Serial.print("\n mode=");
Serial.print(mode,DEC);
Serial.print("\n value=");
Serial.print(value,DEC);
if(mode == 1){
analogWrite(pin,value);
}
* *
* + テストです
n ∧_∧ n
+ (ヨ(*´∀`)E)
Y Y *
登録:
投稿 (Atom)