#oFでバッチ処理したい
機会学習の前処理だったり、結果だったり。
画像のリサイズとかくらいならpythonのcv2でやっちゃえばいいですが、座標とか扱い出すと面倒臭いし、確認がだるい。
あとc++が好き。すっごく好き。
あとは適当に確認用に描画すると何よりなんかたくさん処理してる感が出て精神衛生上非常に効果的
ofAppでディレクトリ舐めて、バッファクリアしてどうこうするのは結構大変。エラーで終了って書けないし。
#どうするか
pythonでもnodeでもなんでもいいのですけど、自分の楽な環境からコマンドラインを叩けばいいと楽ですよね。
pythonのglobとかでディレクトリを舐めて、適当なパスを渡してoFが実行できると幸せになれますよね。
例えばpythonのsubprocessで蹴るとして、oFで作ったアプリのバイナリに対象のファイルのパスを渡せばいい訳ですよ。
と思ったら.appでもopen hoge.app --args hoge
でもいけるみたいですね。でもなんか気持ち悪いので普通にやるなら。
hogebookpro:~hoge$ hoge/bin/hoge.app/Contents/MacOS/hoge -path hoge/hoo/images
的な
この-path hoge/hoo/images
ってのがこの場合のコマンドライン引数。覚えておいて損はないです。
#コマンドライン引数を使いましょう
コマンドライン引数は、コードのどこに来てるのかというとint main(int argv, int* argc[]){ ... }
のargvとかargcです。
oFだとint main( ){ ... }
こうなってますね。
ここに引数を書いてあげれば普通に受け取れます。
int main(int argc, char *argv[]){
ofSetupOpenGL(1000,100,OF_WINDOW);
ofRunApp(new ofApp());
}
つまりこうすればいいと。あとは使えばいいと。
ofAppに渡してsetup関数内で使うのが普通な気がするから、とりあえずofAppから使えるようにしようと。
じゃあコンストラクタ引数で渡して...
ofApp::ofApp(int argc, int** argv){ ... }
int main(int argc, char *argv[]){
ofSetupOpenGL(1000,100,OF_WINDOW);
ofRunApp(new ofApp(argc, argv));
}
これはナンセンスですね。僕がサンタだったらこんなコード書いてる子供の家には砂利しか持っていきません。
素直にシングルトンなパーサーを書けばいいんですよ。
#include <string>
#include <vector>
struct cmdline_parser{
static void set(int argv, int** argc){
for(int i = 0 ; i < argv ; ++i) get_ref().args.emplace_back(argc[i]);
}
static const std::vector<std::string>& get(){ return get_ref().args; }
private:
cmdline_parser(){}
static cmdline_parser& get_ref(){
static cmdline_parser _cmdlp;
return _cmdlp;
}
std::vector<std::string> args;
};
class ofApp : public ofBaseApp{
public:
void setup(){
for(const auto& e : cmdline_parser::get()) std::cout << e << std::endl;
}
};
int main(int argc, char *argv[]){
cmdline_parser::set(argc, argv);
ofSetupOpenGL(1000,100,OF_WINDOW);
ofRunApp(new ofApp());
}
これだけでとりあえずstd::string型のstd::vectorで引き回せますね。
パーサーとは(哲学)感がありますが、まぁこれでいいじゃん。
でもこれだとデフォルトでくるバイナリ自体のパスとか、邪魔じゃん?
そもそも実行時に渡したいものなんてstring
とint
とfloat
とまぁ使う人でbool
かそんなもんな訳で。
毎回そこの判定とか書くのも面倒臭いわけで。
どうせ"-hoge"
と"value"
のセットなわけで。
そこらへん割り切って汎用化できるのが大人なのかなと思うわけです。
#もうちょっと大人っぽくなりたい
さっきのシングルトンパターンでいいのですが、もうちょっと大人っぽく書けたら素敵ですね。
サンタさんなんて居なかったんだ。
なんとなく使いたい場面を想像しつつ、基本渡す値は"-hoge"
と"value"
のセットとして
とりあえずsto~系でいけるものをカバーしてみる。
#ifndef cmdline_parser_h
#define cmdline_parser_h
#include <iostream>
#include <string>
#include <map>
#include <vector>
namespace aaa{
namespace detail{
struct cmd_variable :std::string{
cmd_variable()
: std::string("")
{}
cmd_variable(char* str)
: std::string(str)
{}
template<typename U>
U operator()(U initial){
U u(initial);
if(get_str() != ""){
try{
u = get_variable<U>();
}catch(...){
u = initial;
}
}
return u;
}
const std::string get_str()const{ return *((std::string*)this); }
private:
template<typename U>
U get_variable(){
return U();
}
};
template<> std::string cmd_variable::get_variable<std::string>(){ return get_str(); }
template<> const char* cmd_variable::get_variable<const char*>(){ return get_str().c_str(); }
template<> int cmd_variable::get_variable<int>(){ return std::stoi(get_str()); }
template<> long cmd_variable::get_variable<long>(){ return std::stol(get_str()); }
template<> unsigned long cmd_variable::get_variable<unsigned long>(){ return std::stoul(get_str()); }
template<> long long cmd_variable::get_variable<long long>(){ return std::stoll(get_str()); }
template<> unsigned long long cmd_variable::get_variable<unsigned long long>(){ return std::stoull(get_str()); }
template<> float cmd_variable::get_variable<float>(){ return std::stof(get_str()); }
template<> double cmd_variable::get_variable<double>(){ return std::stod(get_str()); }
template<> long double cmd_variable::get_variable<long double>(){ return std::stold(get_str()); }
};
struct cmdline_parser{
static void set(int argc, char** argv){
for(int i = 0 ; i < argc ; ++i){
std::string str(argv[i]);
if(str[0] == '-'){
if(i < (argc - 1)) ref().key_map[argv[i]] = detail::cmd_variable(argv[i + 1]);
}
ref().args.emplace_back(argv[i]);
}
}
static detail::cmd_variable& get(std::string key){ return ref().key_map[key]; }
static const std::vector<std::string>& get_args(){ return ref().args; }
private:
cmdline_parser(){}
static cmdline_parser& ref(){
static cmdline_parser _clb;
return _clb;
}
std::map<std::string, detail::cmd_variable> key_map;
std::vector<std::string> args;
};
};
using cmdp = aaa::cmdline_parser;
#endif /* cmdline_parser_h */
こんな感じ。'-'
から始まる文字列且つ次のargがあればmapに突っ込み、とりあえずstd::sto~系でエラーが出るかもなので握りつぶし、初期値を渡せるようにしておく
んで、こう使う。
#include "ofMain.h"
#include "cmdline_parser.h"
class ofApp : public ofBaseApp{
public:
void setup(){
ofSetBackgroundColor(0);
std::string path = cmdp::get("-path")("None");
int NUM = cmdp::get("-NUM")(10);
float y = cmdp::get("-y")(0.01);
std::cout << "path : " << path << std::endl;
std::cout << "NUM : " << NUM << std::endl;
std::cout << "y : " << y << std::endl;
std::exit(0);
}
};
int main(int argc, char *argv[]){
cmdp::set(argc, argv);
ofSetupOpenGL(1000,100,OF_WINDOW);
ofRunApp(new ofApp());
}
あとはbool
型とかあった方が幸せになれるかもしれないけれど、幸せの形は人それぞれなので。
あとはpythonなり何なりで回してね。例えば
import subprocess, os
dir_path = "hoge/aaa/bbb"
app_path = "hogebookpro:~hoge$ hoge/bin/hoge.app/Contents/MacOS/hoge"
for img_dir in os.listdir(dir_path):
img_dir_path = os.path.join(dir_path, img_dir)
if os.path.isdir(img_dir_path)
cmd = app_path + " -path " + img_dir_path
os.subprocess(cmd, shell=True)
的な。
それではみなさま楽しいバッチ処理ライフを
もうちょっと汎用化したらofx~でaddon化しますね。