cvl-robot's diary

研究ノート メモメモ https://github.com/dotchang/

ofFboにofTextureから大きな画像の一部を斜めに切り出す方法

大きな画像から、任意の位置と角度で、適当な矩形を切り出す方法をメモしておきます。

注記:普通にやるならこちらです。
cvl-robot.hateblo.jp


f:id:cvl-robot:20170515212006p:plain
画像サイズ1024×768。描画なしで800fps程度。描画ありだと、verticalSyncに引っ張られて60fps。
普通にOpenCVで書くのとどっちが速いだろう???

カーソル操作は自動車(戦車?)のように移動します。
← 左に1度旋回
→ 右に1度旋回
↑ 前進
↓ 後退

マウスは、ドラッグした位置に切り抜き中心が移動します。スクロール上で左に旋回、下で右に旋回します。 

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
	// 描画の有無とフレームレートの設定
	flag_draw = true;
	ofSetVerticalSync(flag_draw);
	ofSetFrameRate(0);

	// 切り抜き画像サイズ
	crop_size.x = 100.f;
	crop_size.y = 100.f;

	// 画像の読み込み
	ofLoadImage(pix, "Koala.jpg"); // 汎用性のためにPixelsで読み込み
	tex.allocate(pix);
	tex.setTextureWrap(GL_CLAMP_TO_BORDER, GL_CLAMP_TO_BORDER);

	// 切り抜き中心位置の初期化
	pos.x = tex.getWidth() / 2.f;
	pos.y = tex.getHeight() / 2.f;

	// fboの準備
	fbo.allocate((int)crop_size.x, (int)crop_size.y);

	// メインウィンドウの大きさ調整
	ofSetWindowShape((int)ceil(tex.getWidth() + crop_size.x), (int)tex.getHeight());
	flag_updated = true; // 更新フラグ
}

//--------------------------------------------------------------
void ofApp::update(){
	// cout << "FrameRate: " << ofToString(ofGetFrameRate(), 0) << "\t\r";
}

//--------------------------------------------------------------
// Texture tの中心位置p,角度aからFbo fにfのサイズで画像を切り出す関数
void ofApp::rotationalCrop(ofFbo& f, ofTexture& t, ofVec2f p, float a)
{
	float w = f.getWidth();
	float h = f.getHeight();
	// float diag = sqrtf(powf(w, 2.f) + powf(h, 2.f));	// 対角線の長さ

	// 外接する長方形の長辺
	float rad = ofDegToRad(a);
	float i = fabs(w*cos(rad)) + fabs(h*sin(rad));
	float j = fabs(w*sin(rad)) + fabs(h*cos(rad));
	float r = ceil((i>j)?i:j);

	f.begin();
	ofClear(0.f);
	ofPushMatrix();
	// Fboの中心で回転
	ofTranslate(0.5f*w, 0.5f*h);
	ofRotate(a);
	ofTranslate(-0.5f*w, -0.5f*h);
	// 長辺長の正方形を位置pを中心にして描画
	t.drawSubsection(-0.5f*(r-w), -0.5f*(r-h), r, r, p.x - 0.5f*r, p.y - 0.5f*r, r, r);
	ofPopMatrix();
	f.end();
}

//--------------------------------------------------------------
void ofApp::draw(){
	if (tex.isAllocated() && fbo.isAllocated() && flag_updated) {
		rotationalCrop(fbo, tex, pos, angle);
		flag_updated = false;
	}

	if (!flag_draw) return;

	if (tex.isAllocated()) {
		// テクスチャの表示
		tex.draw(0, 0);

		// カーソルの表示
		ofPushStyle();
		ofNoFill();
		ofPushMatrix();
		ofTranslate(pos.x, pos.y);
		ofRotate(-angle);
		ofDrawRectangle(-crop_size.x/2.f, -crop_size.y/2.f, crop_size.x, crop_size.y);
		ofPopMatrix();
		ofPopStyle();
	}

	if (fbo.isAllocated()) {
		// 切り抜き結果の表示
		ofPushMatrix();
		ofTranslate(tex.getWidth(), 0);
		fbo.draw(0, 0);
		ofPopMatrix();
	}

	// フレームレートの表示
	// ofDrawBitmapString(ofToString(ofGetFrameRate(), 0), 20, 20); // bug
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
	// 回転
	if (key == OF_KEY_LEFT) {
		angle += 1.f;
		flag_updated = true;
	}
	else if (key == OF_KEY_RIGHT) {
		angle -= 1.f;
		flag_updated = true;
	}

	// 前進・後退
	if (key == OF_KEY_UP) {
		float r = 2.f;
		float rad = ofDegToRad(angle + 90.f);
		ofVec2f d(r*cos(rad), -r*sin(rad));
		pos = ofVec2f(ofClamp(pos.x + d.x, 0.f, tex.getWidth()), ofClamp(pos.y + d.y, 0.f, tex.getHeight()));
		flag_updated = true;
	}
	else if (key == OF_KEY_DOWN) {
		float r = 2.f;
		float rad = ofDegToRad(angle + 90.f);
		ofVec2f d(-r*cos(rad), r*sin(rad));
		pos = ofVec2f(ofClamp(pos.x + d.x, 0.f, tex.getWidth()), ofClamp(pos.y + d.y, 0.f, tex.getHeight()));
		flag_updated = true;
	}

	// 切り出したFboをファイルに保存
	// from here: http://www.atnr.net/how-to-save-fbo-screenshot-on-of/
	if (key == 's') {
		ofImage img;
		ofPixels pixels;

		fbo.readToPixels(pixels);
		img.setFromPixels(pixels);
		char fileNameStr[255];
		string date = ofGetTimestampString(); // タイムスタンプをファイル名にする
		sprintf(fileNameStr, "%s.png", date.c_str());
		img.save(fileNameStr, OF_IMAGE_QUALITY_BEST);
	}
}

//--------------------------------------------------------------
void ofApp::keyReleased(int key){
	
}

//--------------------------------------------------------------
void ofApp::mouseMoved(int x, int y ){
	
}

//--------------------------------------------------------------
void ofApp::mouseDragged(int x, int y, int button){
	pos = ofVec2f(ofClamp(x, 0, tex.getWidth()), ofClamp(y, 0, tex.getHeight()));
	flag_updated = true;
}

//--------------------------------------------------------------
void ofApp::mousePressed(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseReleased(int x, int y, int button){

}

//--------------------------------------------------------------
void ofApp::mouseEntered(int x, int y){

}

//--------------------------------------------------------------
void ofApp::mouseExited(int x, int y){

}

//--------------------------------------------------------------
void ofApp::windowResized(int w, int h){

}

//--------------------------------------------------------------
void ofApp::gotMessage(ofMessage msg){

}

//--------------------------------------------------------------
void ofApp::dragEvent(ofDragInfo dragInfo){ 

}

//--------------------------------------------------------------
void ofApp::mouseScrolled(int x, int y, float scrollX, float scrollY) {
	angle += scrollY;
	flag_updated = true;
}

ofApp.h

#pragma once

#include "ofMain.h"

#include "ofPixels.h"
#include "ofTexture.h"
#include "ofFbo.h"

class ofApp : public ofBaseApp {
public:
	void setup();
	void update();
	void draw();

	void keyPressed(int key);
	void keyReleased(int key);
	void mouseMoved(int x, int y);
	void mouseDragged(int x, int y, int button);
	void mousePressed(int x, int y, int button);
	void mouseReleased(int x, int y, int button);
	void mouseEntered(int x, int y);
	void mouseExited(int x, int y);
	void windowResized(int w, int h);
	void dragEvent(ofDragInfo dragInfo);
	void gotMessage(ofMessage msg);
	
	void mouseScrolled(int x, int y, float scrollX, float scrollY);
	void rotationalCrop(ofFbo& f, ofTexture& t, ofVec2f p, float a);

protected:
	ofPixels pix;
	ofTexture tex;
	ofFbo fbo;

	ofVec2f crop_size;
	ofVec2f pos;
	float angle;

private:
	bool flag_updated;
	bool flag_draw;
};

64ビットコンパイルすると、時々落ちるようになってしまったので、Framerateの表示をコメントアウトしました。
f:id:cvl-robot:20170518171139p:plain

Lidarを使った2D SLAM実装のためのお勉強

Atsushi SakaiさんのブログMyEnigmaで紹介されているmatlabのコードをいくつかc++(openFrameworks環境)に移植してみました。
myenigma.hatenablog.com
とても勉強になり大変に有難いです。移植の目的は、matlabに慣れたかったのと、現実的な実行速度を確認したかった為です。matlabpythonよりc++寄りで移植しやすいですね。

1. ofxGridMapSample.h


f:id:cvl-robot:20170511220657p:plain

2. ofxParticleFilterLocalization.h


f:id:cvl-robot:20170511220819p:plain

3. ofxICPSample.h


f:id:cvl-robot:20170511220928p:plain

main()関数やsetup(), update(), draw()の呼び出しは、openFrameworksのテンプレートから適当に作ってください。
また追加のaddonとしてofMatrix2x2をプロジェクトに加えてください。
GitHub - naokiring/ofMatrix2x2: 2x2 matrix class for openFrameworks

Globally Optimalな点群位置合わせ手法の調査

2010年台ごろに入ってから、3次元点群位置合わせの手法として"Globally Optimal*1"な物の提案が増えてきました。理論はおいて置いて、どのくらい実用になるかをテストするために、実装が公開されているものを中心にリストにまとめておきたいと思います。

1. Go-ICP: A Globally Optimal Solution to 3D ICP Point-Set Registration

Jiaolong Yang, et. al. ICCV2013, PAMI
iitlab.bit.edu.cn
解説

www.slideshare.net

2. Super4PCS: Fast Global Pointcloud Registration via Smart Indexing

Nicolas Mellado, Dror Aiger, Niloy J. Mitra CGF2014
SGP | UCL
github.com
つかってみた、試してみた解説
「 Super4PCS 」という点群結合のライブラリを試してみました - Natural Software
JVR ― 自腹でバーチャルリアリティ ― Super4PCS 速度面で実用的ではなさそう。

3. Fast Global Registration

Qian-Yi Zhou, Jaesik Park, Vladlen Koltun, ECCV2016
Fast Global Registration - Vladlen Koltun
github.com
入力が3次元点群+FPFHなどの3次元特徴量ベクトルなので、3次元特徴量の計算にも時間コストがかかることに留意。
速度的には、まだAlignment.exe(Indexed Imageを使った方法)で位置合わせした方が速そう。
読むべき論文多すぎて、サーベイがおっつかないよ。。。

*1:Globally Optimalの正しい和訳は知りませんが、端的な意訳は"初期位置あわせの要らない"だと思います。

初任給で買うと良いものの研究2017

初任給を貰ったら、まず親に御礼代わりのプレゼントを買うべきです。

が、自分で稼いだお金で自分の欲しいものを買うのも良いことです。
とくに、新生活が始まったばかりの人にとっては、今後の生活の潤いのためになるものを揃えておくのが良いでしょう。
というわけで、今自分が新社会人だったら何が欲しかったかなぁ、と考えてリストにまとめます。(仕事が手につかずに遊んでいる、ともいうが。。。)
物入りな時期でしょうから、予算は最大5万円をめどとします。

1. 大事なデータを保存しておくNAS

デジカメで撮りためた写真や書類など、日々溜まっていく重要なデータはほっておくと、すぐにどこかに行ってしまいなくして後悔します。
早めにデータの置き場所を決めて固めておいておく癖をつけておくとよいです。とくに、転勤などで引っ越しがあったときにも慌てずに済むので、オススメです。
HDDは時間が経つと壊れてしまうので、おすすめはミラーリングで良いのでRAID構成にしておくことです。

いろいろ調べた結果、性能値段比が一番いいのはQNAP TS-228のようです。

本体にはHDDが付属していませんので、別途2台用意します。TOSHIBAの4TBモデルのAMAZON限定モデルは、2年保証が付いているのでお得感があります。(会社が心配ですが・・・)

2. 寝っ転がって本を読むタブレット端末

NASにデータを集める環境が作れたら、そのデータを簡単に閲覧する方法が欲しくなります。
PCがあればそれでも良いのですが、仕事で疲れているときには寝っ転がって写真を見たり本を読んだりしたくなります。
とくに仕事を始めると、眼の疲れを最小にすることの重要さにそのうち気が付きます。
タブレット端末は進化が早いので安いやつで良いのですが、ディスプレイの質の良いものがオススメです。
今選ぶなら、HuaweiのMedia T2 8.0 Proが2万未満で買えて、画面が奇麗でよいです。

3. 腰や肩を大事にするための敷布団マットレス

仕事中は自由に体を動かせず、毎日決まった姿勢をし続けなければならないような状況が起きてしまいます。
とくにPCを相手にする仕事の場合、腰に負担がかかります。
普通の布団では寝ていても腰に力が掛かってしまい、あまり疲れが取れないという状況が起きます。
良さげなマットレスが世の中にいっぱいありますが、お年寄向けのものも多く、ちょっと気が引けます。
その点、西川Airはオシャレです。価格の高いモデルもあり、更にかっこよいですが、エアー01で十分効果があります。
長年使っているうちに多少ヘタって来てしまいますので、標準体重ぐらいの男性はハード、女性はソフトが良いように思います。

シーツは洗濯の時に取り外ししやすい、純正のラップシーツが良いですよ。

4. 首を大事にするための良い枕

首は、腰と同じように負担がかかります。
自分の体に合った枕を選ぶのが一番良いですが、西川Airのマットレスを買ったら、同じシリーズの枕で揃えるのも良いですね。
中のマットを抜くと高さを調整することができます。

カバーは、マットレスの時と同じように、洗濯のために取り外ししやすいものが良いですよ。

5. 快適な睡眠のためのゴアテックス羽毛掛け布団

敷布団は、西川Airがとても良いのですが、合わせる掛け布団に悩みます。
温かくて、軽くて、水分調整ができて、虫がわきにくい、そんな都合が良い羽毛掛け布団が西川にあります。

ちょっと5万円をはみ出てしまいますが、普通の羽毛布団に比べてメンテナンスが楽な分、長い間使えるメリットがあります。
掛け布団カバーは、デザイン重視でお好きなものを。
西川 リビング 掛けふとん カバー 150×210cm ON15 シングル ベージュ 2138-15137

西川 リビング 掛けふとん カバー 150×210cm ON15 シングル ベージュ 2138-15137

毛布は肌触りの良いものを。


結局、ほとんど睡眠用品になってしまいましたが、社会人にはそれだけ睡眠の質が重要だということ。

Emlid Navio2で動かすardupilotにxwindowを入れる

要らないものもあるかもしれないけれど、とりあえず起動するのでOK。

sudo apt-get update
sudo apt-get install lxde
sudo apt-get install lightdm
sudo apt-get install xinit
sudo apt-get install xutils
sudo apt-get install xserver-xorg

startxでLXDEというデスクトップが立ち上がります。

lxdeは古いので、新しいPIXELを使いたい、という場合はこちら。

sudo apt-get install raspberrypi-ui-mods

sudo apt-get install firefox-esr

単にxwindowを入れた状態ではlibEGLの設定がおかしな状態になっていて、openFrameworksやQtが立ち上がらないという問題があります。
[1]のサイトを参考に、libEGLのライブラリへのリンクを張りなおすと立ち上がるようになります。
raspi-configでGL DriverをLegacyに設定するのを忘れずに。

# sudo ln -fs /opt/vc/lib/libEGL.so /usr/lib/arm-linux-gnueabihf/libEGL.so
# sudo ln -fs /opt/vc/lib/libGLESv2.so /usr/lib/arm-linux-gnueabihf/libGLESv2.so
# sudo ldconfig

[1] raspbian - Qt applications don't work due to libEGL - Raspberry Pi Stack Exchange

掃除機の買い方の研究

掃除機は、多くの人には数年に一度しか買い替える機会がないものかと思います。
急に掃除機が欲しくなるというよりも、そろそろ買い替えたいなー、という気分になったときしばらく時間をかけて検討して買い替えするかと思います。
ということは、時々安くなるセールを狙って買えるということです!定期的に安くなるモデルを見つけたので、紹介します。

何を買うか

ダイソンのハンディのバッテリー駆動モデルと、ロボット掃除機のルンバを狙います。

ビックカメラの量販店モデル
ダイソンDC74 MH EX: 

ルンバ631: 

ジャパネットたかたの量販店モデル
ルンバ626: 

いずれもお買い得感のあるエントリーモデルです。

どこで買うか

ビックカメラ・コジマか、ジャパネットたかたをチェックします。
店頭でもネットでも価格は同じでしたが、ポイントの付き方が違います。
アマゾンポイントが安く買えて使えるときは、アマゾンがお得かと思います。

いつ買うか

ジャパネットたかたのセールの日に、ビックカメラがえげつなくセールをぶつけてくることが多いので
半期に一度ぐらいのジャパネットたかたのセールをテレビで見かけたら、ビックカメラのセール品をチェックします。

いくらで買うか

半年に一度ぐらい、どちらも\29800で販売されることがあります。
去年の実績は10月ごろと3月ごろに、それぞれ一日だけその価格で販売されていました。

LAUNCHXL-F28069MとBOOSTXL-DRV8305EVMでSCIを使うとき、問題を避けるためにADCの割り込み優先順位を上げる方法

TiのC2000Piccoloの割り込みは、独自のPIEテーブルとかいうものに基づいたもので、とても分かりにくく使いにくいです。
C2000シリーズではPIEテーブルの順序の変更も容易ではなく(掲示板にはデキナイ、との書き込みも)、さらにその定義も小首を傾げたくなるような順序に並んでいます。
InstaSPINモータドライバを使うとき、ADCがフル稼働しているのですが、標準だとこの優先順位がSCI(UART)よりも低く設定されているため、SCI割り込みが入るとモータが動かせないという謎仕様です。
ただし、これは問題を知っていれば簡単に回避できます。

割り込みトリガーをADC_IntNumber_1からADC_IntNumber_1HP(High Priority)に変更

なぜか、同じADCに優先順位の設定が2つ割り当てられていて、10番(標準)と1番(優先)です。
デフォルトで10番に割り当てられているものを1番のPIEで駆動されるように変更します。

hal.c

void HAL_enableAdcInts(HAL_Handle handle)
{
  HAL_Obj *obj = (HAL_Obj *)handle;

  // enable the PIE interrupts associated with the ADC interrupts
  PIE_enableAdcInt(obj->pieHandle,ADC_IntNumber_1HP); // ADC_IntNumber_1 to ADC_IntNumber_1HP

  // enable the ADC interrupts
  ADC_enableInt(obj->adcHandle,ADC_IntNumber_1);

  // enable the cpu interrupt for ADC interrupts
  CPU_enableInt(obj->cpuHandle,CPU_IntNumber_1); // CPU_IntNumber_10 to CPU_IntNumber_1

  return;
} // end of HAL_enableAdcInts() function

hal.h

static inline void HAL_initIntVectorTable(HAL_Handle handle)
{
  HAL_Obj *obj = (HAL_Obj *)handle;
  PIE_Obj *pie = (PIE_Obj *)obj->pieHandle;

  ENABLE_PROTECTED_REGISTER_WRITE_MODE;

  pie->ADCINT1_HP = &mainISR; // pie->ADCINT1 to pie->ADCINT1_HP
  //pie->SCIRXINTA = &sciarxISR;

  DISABLE_PROTECTED_REGISTER_WRITE_MODE;

  return;
} // end of HAL_initIntVectorTable() function

hal.h

static inline void HAL_acqAdcInt(HAL_Handle handle,const ADC_IntNumber_e intNumber)
{
  HAL_Obj *obj = (HAL_Obj *)handle;

  // clear the ADC interrupt flag
  ADC_clearIntFlag(obj->adcHandle,intNumber);

  // Acknowledge interrupt from PIE group 1
  PIE_clearInt(obj->pieHandle,PIE_GroupNumber_1); // PIE_GroupNumber_10 to PIE_GroupNumber_1

  return;
} // end of HAL_acqAdcInt() function

これでも、SCITX割り込みがうまく動かない。なんでた”~。