【openFrameworks】OSCコントロールを使った連番画像の複数同時並行保存

こんにちは。
今回は、openFrameworksからSyphonで出力した画像の連番保存を複数同時に行ったことについてです。
連番を三つ同時に保存しようとしたときにOSCをつかってタイミングを合わせたというだけの内容ですが、そこまでに検証したことをまとめます。
もしかしたら既存でなにかサービスがすでにあるのかもしれないですが、わからなかったのでこの方法で行いました。

実際には以前記事でのテクスチャの保存などに用いることができると思います。

oFを使った連番の保存は検索するとこちらの記事などが出てきます。
ここではsatiruhigaさんのofxExportImageSequenceを使用しています。
僕も今回これを使わせていただきます。

今回やること

・openFrameworksで連番画像の書き出し保存
・openFrameworksでOSCを使った連番画像の並行書き出し保存

環境

・Mac Book Pro (Catalina)
・openFrameworks v0.11.0
・Xcode 10.3

・openFrameworksで連番画像の書き出し保存
satiruhigaさんのGithubからofxExportImageSequenceをダウンロードします。
このアドオンはpocoというC++クラスのライブラリを必要とします。
僕はHomebrewを使ってインストールしました。
下記をターミナルで実行してください。
すでにインストールされていれば必要ありません。

$brew install poco

ofxExportImageSequence-master.zipを解凍したら、
../of_v0.11.0_osx_release/addonsに保存します。
まずは、projectgeneratorからexampleをXcodeで開きます。
ofApp.cppに全てかかれていますのでofApp.hとmain.hは削除して実行します。
ここでpocoがインストールされていないとビルドエラーでpocoがありませんと言われます。
うまくいけば、フレーム数が印字された連番画像が、dataフォルダ内に生成されます。

画像1

ここではじめは
ofxExportImageSequence exp;
を単純に複数用意すればいいじゃん思い、それで行ってみたのですが、自分の環境では3つ並行して保存しようとすると、100フレーム目でアプリケーションが落ちてしまいました。
アドオンの内部構造をわかっていないとこういった時の対処が聞かないのでなんとも情けないです。
ということで原因を放置したままですが、アプリケーションを複数つくって同時に保存を走らせようという方向をとることにしました。

・openFrameworksでOSCを使った連番画像の並行書き出し保存

projectgeneratorからofxExportImageSequenceのアドオンを追加してプロジェクトを生成します。
以下のコードを書いてください。

main.cpp

#include "ofMain.h"
#include "ofxExportImageSequence.h"
#include "ofxSyphon.h"
#include "ofxOsc.h"

#define PORT 9002

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);
		
    ofEasyCam cam;
    ofxExportImageSequence exp;
    vector<string> fileName;
    
    ofxSyphonClient client;
    
    ofxOscReceiver receiver;
    int trigger = 1;

    int nameNumber = 0;
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofSetFrameRate(30);
    //     ofSetVerticalSync(true);
    ofBackground(0);
    
    fileName = {"OUTPUT01", "OUTPUT02", "OUTPUT03"};
    
    exp.setup(256, 256, 30);
    exp.setFrameRange(100, 400);
    exp.setOutputDir(fileName[nameNumber]);
    exp.setOverwriteSequence(false);
    exp.setAutoExit(true);
    
    client.setup();
    client.setServerName(fileName[nameNumber]);
    client.setApplicationName("muitiSyphonSender"); //muitiのスペルミスは目をつぶってください。
    
    receiver.setup(PORT);
    
}

//--------------------------------------------------------------
void ofApp::update(){
    ofSetWindowTitle(ofToString(ofGetFrameRate()));
    
    exp.begin();
    ofClear(0);
    ofSetColor(0, 0, 0, 0);
    ofDrawRectangle(0, 0, 256, 256);
    ofSetColor(255);
    client.draw(0, 0);
    
    exp.end();
    
    // check for waiting messages
    while(receiver.hasWaitingMessages()){
         // get the next message
         ofxOscMessage m;
         receiver.getNextMessage(m);
         
         // check for mouse moved message
         if(m.getAddress() == "/Trigger"){
              // both the arguments are floats
              trigger= m.getArgAsInt(0);
         }
    }
    
    if(trigger == 0){
         exp.startExport();
         trigger = 1;
    }
    
}

//--------------------------------------------------------------
void ofApp::draw(){
    exp.draw(0, 0);
    
    ofDrawBitmapString(ofToString(exp.getFrameNum()), 100, 50);
    ofDrawBitmapString(ofToString(trigger), 100, 100);
    
}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if(key == ' '){
         exp.startExport();
    }
    
}

アプリケーションを実行して実行アプリケーションが作成されたら、Finderで名前をtextuersExport_1のように変更してください。
その後、ofApp.hのPORTの値と、namenumberの数を変えて再び実行します。
ここでは
(PORT 9002, namenumber = 0), 
(PORT 9003, namenumber = 1), 
(PORT 9004, namenumber = 2), 
のように三度実行しその都度実行アプリケーションの名前を変えました。
binフォルダ内が以下のようになります。

画像2

コードの内容ですが、
以下で連番の作成数と画像の番号を指定します。

exp.setFrameRange(100, 400);

以下でOSCから"/Trigger"のアドレスを受け取ります。
Int型の0が送られてくると保存を開始するようになっています。

// check for waiting messages
    while(receiver.hasWaitingMessages()){
         // get the next message
         ofxOscMessage m;
         receiver.getNextMessage(m);
         
         // check for mouse moved message
         if(m.getAddress() == "/Trigger"){
              // both the arguments are floats
              trigger= m.getArgAsInt(0);
         }
    }

次にテスト送信側のコードです。

ofApp.h

#include "ofMain.h"
#include "ofxSyphon.h"
#include "ofxOsc.h"

#define HOST "localhost"
#define PORTa 9002
#define PORTb 9003
#define PORTc 9004

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);
    
    //Syphon
    ofxSyphonServer output01;
    ofxSyphonServer output02;
    ofxSyphonServer output03;
    ofFbo fboS;
    
    //OSC
    ofxOscSender sender;
    ofxOscSender senderTrigger[3];
    
    int count;
		
};

ofApp.cpp

#include "ofApp.h"

//--------------------------------------------------------------
void ofApp::setup(){
    ofSetVerticalSync(true);
    ofSetFrameRate(30);
    ofBackground(0);
    
    //Syphon
    output01.setName("OUTPUT01");
    output02.setName("OUTPUT02");
    output03.setName("OUTPUT03");
    fboS.allocate(256, 256, GL_RGBA);
    
    //OSC
    senderTrigger[0].setup(HOST, PORTa);
    senderTrigger[1].setup(HOST, PORTb);
    senderTrigger[2].setup(HOST, PORTc);
    

}

//--------------------------------------------------------------
void ofApp::update(){
    count++;

}

//--------------------------------------------------------------
void ofApp::draw(){
    fboS.begin();
    ofClear(0);
    ofSetColor((count*4)%255, 200, 200);
    ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight());
    
    stringstream mess;
    mess << count;
    ofDrawBitmapStringHighlight(mess.str(), ofGetWidth()*0.4, ofGetHeight()*0.5);
    fboS.end();
    
    fboS.draw(0, 0);
    
    output01.publishTexture(&fboS.getTexture());
    output02.publishTexture(&fboS.getTexture());
    output03.publishTexture(&fboS.getTexture());

}

//--------------------------------------------------------------
void ofApp::keyPressed(int key){
    if(key == ' '){
         for(int i=0; i<3; i++){
              count = 100;
              ofxOscMessage m;
              m.setAddress("/Trigger");
              int prm = 0;
              m.addIntArg(prm);
              senderTrigger[i].sendMessage(m);
         }
    }

}

送信側のアプリでスペースキーを押すと保存を始めます。
↓こんな感じになるかと思います。

Exampleと同様に決められた作成数保存が終了するとアプリケーションは自動で終了します。
この方法だと、途中で落ちることなく複数の同時保存ができました。
30fpsで600枚までは実際に確認できました。

この記事が気に入ったらサポートをしてみませんか?