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足すとき
今日のところはとりあえず終了。
登録:
投稿 (Atom)