おわりに

※2014/10/01 もうちっとだけ続くんじゃ
約1年間に渡ってお送りしてきました「Puku's Laboratory」いかがでしたでしょうか?

めんどくさがり屋がブログとかやって大丈夫か?と思うこともありましたが、当ブログを見てくださる皆様のコメントに支えられてここまで続けることができました。

CV Droneの開発は大変でしたが、様々な方と情報交換することは私にとって良い経験となりました。
プログラム自体も初期の頃に比べてかなり使いやすくなったと思います。

cvdrone.jpg
↑画像をクリックするとCV Droneの開発ページに移動します。

まだ紹介できていないものや実装したい機能等いろいろありますが、私自身の研究(AR.Droneとはまた別のテーマです)に専念するため、今回でブログの更新を一旦終了します。
※公開している記事やソフトウェアは消しませんのでご安心を!

当ブログが、画像処理やロボットについて興味を持つきっかけとなれば幸いです。ネタを挟んだ甲斐があります。

ご覧いただいた方々、コメントやメールをくださったユーザの皆様 本当にありがとうございました!

それでは、また会う日まで。

平成25年9月30日 puku  
スポンサーサイト



AR.Droneを動かそう(バージョン情報の取得 非WinINet版)

CV Droneは当初Windowsのみサポートしていました。

開発後期でクロスプラットフォーム化するにあたり、それまで使っていたWinINetでのFTP通信が使用できなくなりました。

これはマズい。

何がマズいかというと、バージョン情報の取得方法を考え直さないといけないんですよ。

まぁ、FTPが使えなくてもTCP経由でconfig.iniを取得できますので、なんとかなるといえばそうなのですが...

初期化の順番を考えると、ATコマンド初期化→いろいろ設定→設定情報を取得、というように設定情報の取得は後に回すのが良いかなーと思うんですよね。

TCP通信に関しては自分で作ったTCPクラスがありますからこれを使うとして、AR.DroneのFTP通信ってどうなってるのかを見つつ実装していきましょう。

1. ポート5551を開く
 これは簡単、ただソケット作ってconnectするだけです。
 ポートを開くと何か文字列がやってきます。適当なバッファに突っ込んでおきましょう。
2. ログインする
 USERプロトコルを使って匿名(anonymous)でログインします。
 AR.Droneにはログインパスワードが設定されていないのでPASSは送らなくてもOKです。
3. Passiveモードに切り替え
 ここからがちょっと面倒です。
 PASVを送るとAR.Droneから"227 PASV ok (192,168,1,1,a,b)"といった文字列が送られてきます。
 Passiveモードにおけるデータ転送用のポート番号は「a * 256 + b」で与えられます。
 1.で作ったものとは別のソケットを用意し、上記のポートを開きましょう。
4. ファイル送信のリクエスト
 ポート5551を開いたソケットで"RETR ファイル名"を送りファイルを要求します。
 ファイル名には"version.txt"を設定します"
5. ファイル受信
 ファイルはデータ転送用のポートを開いたソケットに送られます。
 "version.txt"はただのテキストなので、char型のバッファに入れると良いでしょう。
 バッファに入れたら後はsscanfなり何なりでバージョン情報を抽出します。

↓実際のコードはこちらです。

#include <stdio.h>

// TCP通信
#include "tcp.h"

// マクロたち
#define ARDRONE_DEFAULT_ADDR "192.168.1.1" // AR.DroneデフォルトIPアドレス
#define ARDRONE_FTP_PORT (5551) // FTP用ポート

// バージョン情報
struct ARDRONE_VERSION {
int major;
int minor;
int revision;
};

int main(void)
{
TCPSocket socket1, socket2;

// ソケットを開く
if (!socket1.open(ARDRONE_DEFAULT_ADDR, ARDRONE_FTP_PORT)) {
printf("TCPSocket::open(port=%d) failed. (%s, %d)\n", ARDRONE_FTP_PORT, __FILE__, __LINE__);
return 0;
}

// ウェルカムメッセージを受け取る
char buf[1024] = {'\0'};
socket1.receive(buf, sizeof(buf));

// ログイン
socket1.sendf("USER %s\r\n\0", "anonymous");
socket1.receive(buf, sizeof(buf));

// PASVモードに切り替え
int a, b, c, dataport;
socket1.sendf("PASV\r\n\0");
socket1.receive(buf, sizeof(buf));
sscanf(buf, "227 PASV ok (%d,%d,%d,%d,%d,%d)\n", &c, &c, &c, &c, &a, &b);
dataport = a * 256 + b;

// データソケットを開く
if (!socket2.open(ARDRONE_DEFAULT_ADDR, dataport)) {
printf("TCPSocket::open(port=%d) failed. (%s, %d)\n", dataport, __FILE__, __LINE__);
return 0;
}

// ファイル送信リクエスト
socket1.sendf("RETR %s\r\n\0", "version.txt");

// ファイル受信
socket2.receive(buf, sizeof(buf));

// バージョン取得
ARDRONE_VERSION version;
sscanf(buf, "%d.%d.%d", &version.major, &version.minor, &version.revision);
printf("AR.Drone Ver %d.%d.%d\n", version.major, version.minor, version.revision);

// さようなら
socket1.close();
socket2.close();

return 1;
}
tcp.h

WinINetがないと結構手間がかかりますね。タイムアウトの設定も出来ませんし。

さて、当ブログがスタートしてからそろそろ1年になります。

「1年間続ける」ことと「毎週更新する」ことをは決めていましたので、
とりあえず目標達成 といったところでしょうか。フフフ

CV Droneも卒業研究や学会の論文や飛行ロボット大会などでも使われるようになるなど(公式SDKと間違われたりしているとか...マジか)、まぁぼちぼち有名になってきて嬉しい限りです。

構造体ポインタを使おう

C言語ここまで出来れば大丈夫!という基準は何か?

・とりあえず10,000行
・malloc/free使えりゃOK
・標準ライブラリ以外も使えるようになった
・10行でテトリスを書いた、私はナニカサレタヨウダ

うーん、人によってさまざまでしょう。

私なりの考えとしては、
「構造体ポインタ」を使えるでしょうか。

つまり、OpenCVでよく見る

// オブジェクトの確保
IplImage *img = cvCreateImage(...);

// 操作
cvThreshold(img, ...);

// 解放
cvReleaseImage(&img);
のような仕組みを理解できたらOK!ってことです。
ちなみに私はcvReleaseImage()の引数がなぜポインタのポインタなのかが未だに理解できません。無駄じゃね?

グローバルな構造体で済む場合もありますが、複数のファイルから構成されるプログラムの場合、extern宣言ばかりになってどこで定義したかわからなくなります。関数に渡すにしても参照渡しの方が効率が良いですし、何より.より->の方がカッコ良いです。

#include <stdio.h>
#include <stdlib.h>

/* 画像データ */
struct IMAGE {
int width;
int height;
unsigned char *data;
};
typedef struct IMAGE *ImageID;

/* 画像オブジェクトの生成 */
ImageID createImage(int img_width, int img_height);

/* 画像の2値化 */
void binalizeImage(ImageID img, int lower, int upper);

/* 画像の解放 */
void releaseImage(ImageID img);
10行目でImageIDにtypedefしているのがミソです。
ポインタを「オブジェクトのID」と考える方法はOpen Dynamics Engineを参考にしています。

#include "image.h"

/* 画像オブジェクトの生成 */
ImageID createImage(int width, int height)
{
ImageID img = (ImageID)malloc(sizeof(struct IMAGE));
if (!img) return 0;

/* 内部データ */
img->width = width;
img->height = height;
img->data = (unsigned char*)malloc(img->width * img->height * sizeof(unsigned char));
if (!img->data) {
free(img);
return 0;
}
return img;
}

/* 画像の2値化 */
void binalizeImage(ImageID img, int lower, int upper)
{
if (!img) return;

/* 閾値以内なら255 */
for (int i = 0; i < img->width*img->height; i++) {
if (lower < img->data[i] && img->data[i] < upper) img->data[i] = 255;
}
}

/* 画像の解放 */
void releaseImage(ImageID img)
{
if (img) {
free(img->data);
free(img);
img = 0;
}
}
上の例のように、実体はmallocで確保して、ユーザにはポインタだけ渡してやります。ポインタで管理する利点は「オブジェクトの確保に失敗したらヌルポインタを返す」とすれば、そのオブジェクトが存在しているか否かがわかることです。

#include "image.h"

int main(int argc, char **argv)
{
/* 画像の生成 */
ImageID img = createImage(320, 240);

/* 2値化 */
binalizeImage(img, 127, 255);

/* 解放 */
releaseImage(img);
return 0;
}
いやぁ、プログラミングって奥が深いですね!

構造体ポインタはリスト構造を組むときにも使えるので便利なテクニックです。

まぁ、一番いいのは他の人にも読みやすいコードが書けることですけどね!

Latent SVM 使ってみた

流行りのLatent-SVMに手を出してみました。

Latent-SVMはモデルをいろんなパーツを持った集合として捉え、モデルの隠れ変数となっている各パーツの位置関係を学習する手法です。参考URL → http://www.cs.berkeley.edu/~rbg/latent/

OpenCVにも実装されているので早速サンプルを動かしてみましょう!

#include <opencv2/opencv.hpp>

// --------------------------------------------------------------------------
// main(Number of arguments, Argument values)
// Description : This is the entry point of the program.
// Return value : SUCCESS:0 ERROR:-1
// --------------------------------------------------------------------------
int main(int argc, char **argv)
{
// 画像
cv::Mat image = cv::imread("cat.jpg");

// モデルデータ
std::vector<std::string> models;
models.push_back("cat.xml");

// LSVM
cv::LatentSvmDetector detector(models);

// 検出
std::vector<cv::LatentSvmDetector::ObjectDetection> detections;
detector.detect(image, detections);

// クラス名
const std::vector<std::string> classNames = detector.getClassNames();

// 検出したモデルの表示
for (size_t i = 0; i < detections.size(); i++) {
const cv::LatentSvmDetector::ObjectDetection &od = detections[i];
cv::rectangle(image, od.rect, CV_RGB(0, 255, 0), 2);
cv::putText(image, classNames[od.classID], cv::Point(od.rect.x+4, od.rect.y+13), cv::FONT_HERSHEY_SIMPLEX, 0.55, CV_RGB(255, 255, 255), 2);
}

// 画像表示
cv::imshow("cat", image);
cv::waitKey(0);

return 0;
}

lsvm_cat.jpg
ネコネコカワイイヤッター!

なんだか猫以外も検出していますね。

これ以外の学習済みモデルはopencv_extraにあります。いろいろ試してみましょう。

あれ?でもこれどうやって学習データ作るんだ?

OpenCV3.0が待ち遠しい

OpenCVの開発ページにOpenCV 3.0の予定が入りましたね。

ICVS2013のプレゼンにもあるように、3.0はこれまで以上に多機能化するそうです。
・C++に完全に移行
・ユーザ定義モジュールも追加可能に
・CUDA、OpenCLもっと強化
・マルチプラットフォーム(WinRT等)
・多言語(C#、JavaScript、D言語)への移植

GPU周りやスマホへの移植に注力していた2.x系からの流れとしては当然といったところでしょうか。

C++に完全移行するのでcv::Matで作るのに慣れておかないといけませんね。

個人的にはASIFT(Affine-SIFT)が気になるのですが、実装はまだですか!?

あとGSoCでOpenCVによるPTAM実装に挑戦される人がいるようです。こちらも気になります。
材料(FAST、5点アルゴリズム、SSBA、カルマンフィルタ)は揃っていますからね。

OpenCV3.0は来年の2月にリリース予定だそうです。これは楽しみ!

現行のOpenCV2.x系は10月リリース予定の2.4.8で最後になるようです。

あ、ついでに当ブログも9月末で更新終了します。質問はお早めにー。
プロフィール

puku

Author:puku
暇な時はゲームかプログラミングしてる人だよ。
だいたい月1更新。
CV Drone はこちら(GitHub)

最近はQiitaでOnsenUI2で遊んでいる。

最新記事
最新コメント
最新トラックバック
検索フォーム
カレンダー
08 | 2013/09 | 10
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 - - - - -
月別アーカイブ
カテゴリ
スポンサードリンク
RSSリンクの表示
FC2カウンター
リンク
ブロとも申請フォーム

この人とブロともになる

アクセスランキング
[ジャンルランキング]
コンピュータ
607位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
120位
アクセスランキングを見る>>
FC2ブログランキング

FC2Blog Ranking

QRコード
QR