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