oF_iosでカメラやライブラリを使う

openFrameworks iOS版でアプリを作る人ってどれくらいいるのでしょうか?
あんまり情報転がってない気がするので少ないのかしら。

openFrameworksのexampleを一通り確認したりすると思いますが、その時は以下が参考になります。こういう記事ありがたいですよね。
https://qiita.com/shu223/items/e28110bf729fdf8712e0
http://bick.xyz/archives/427

で、実際にサンプル適当に動かしてみようとすると、たとえば videoGrabberExampleなどで、ビルドは通るけど、実行時(カメラ起動時)に以下のエラーが出ました。

videoGrabberExample[13251:5503360] [access] This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app’s Info.plist must contain an NSCameraUsageDescription key with a string value explaining to the user how the app uses this data.

で、調べてみると、iOS10以降では、どうやらiPhone等でカメラを使うよっていう許可アラートを表示させる設定が必須みたいです。
要はカメラとかマイクとかライブラリとか連絡先とか、そこらへんを使用する際にはアクセス許可のアラートを設定する必要があるそうです。

たしかにカメラとかマイク使うアプリ入れた時に使用してもいいですか?ってアラートあるよね。あるよね。

ちょっと調べた感じではswiftなどでの対策が出てくるけど、じゃあoFでどうするのってところは特に記事なかったので一応メモ📝


参考にした記事
https://qiita.com/Takumi_Mori/items/f53c6eec1676d3df59dc

https://dev.classmethod.jp/smartphone/iphone/ios10-privacy-data-purpose-description/

によると、info.plistってファイルにアラートについての追記をするらしい。

oFではこれが、ofxiOS-Info.plist というファイルに該当します。

ここで右クリックでAdd row

Priと入力して「Privacy – Camera Usage Description」などを追加します。
(Pは大文字じゃないと補完が効かないみたい。)

横のStringにはアラートに表示させる文章を入力します。ちなみに\nなどは反映されない模様。

of_iosのサンプルImagePickerExampleの場合だと、カメラの使用とライブラリの使用と、ライブラリへの追加、の3行を追加します。以下のような感じ。

Localizationを変更するとカメラ画面の「写真」や「キャンセル」などが変更されます。はじめはたぶんEnglishとかなのでJapanに変更しました。

 

これでビルドすれば、カメラ起動時にアクセス許可のアラートが表示されます。

一回OKすればあとは表示されないはず。

 


カメラのシャッタースピードとか調整したいんだけど、そこらへんはswiftとかじゃないと難しいのかなあ。
SetDesireFrameなる関数がofVideoGrabberにあるのですが、これを設定しても特に変化が見られなかった。。。

ちなみにiosでカメラを扱うには、ofxiOSImagePickerを使うやり方と、ofxiOSVideoGrabber(ofVideoGrabber)を使うやり方があるみたいです。この辺はおいおい見ていきたい。
あとsetDeviceIDでiPhoneのフロントカメラか、バックカメラ?か選択できます。

openFrameworksでアプリ楽に作って試せるのはおもしろい。
oF勉強して作ったものをアプリにして持ち歩いて、友人にどんな仕事してるのって聞かれた時に、こんなことって言えたら、ドヤスマートかも。

ポインタ・参照についてちゃんと勉強する

 

僕はopenframeworksからプログラムを始めた人間なので、C++を触っていますが、使っているといえるほどC++のことわかってません。
このままだと非常に恥ずいので勉強していこうと思います。

oFの環境で慣れているのと、やっぱりC++が必要な場面はoFで使うことが多いと思うので、
検証のコードもoFで動かして試しています。
試す際には main.cppのofRunAppの行だけコメントアウトしてざっくりと検証しています。

以下、ポインタについて私用メモです。
要所要所で用語も書いていますが、個人的に覚えていたほうがかっこいいなと思う程度で、実際に覚える必要はないと思います、気の持ちよう。

 


#1 *と&

ポインタはメモリ(1byteごと)をいくつか使って変数を示すアドレスを保存する。
いくつメモリ使うかは型によって異なり、さらに言えばwin32bitか64bitかとか、コンパイラによってもことなる。

例えばint型なら4byte(4x8bit=32bit)のメモリ領域を使ってint変数のアドレスを保持する。
型ごとのメモリのサイズはsizeof(int)みたいな感じで調べられる。

宣言時に*を使うことで、この変数にはアドレス入れますよ〜って宣言する。このアドレス入れますよ〜って変数をポインタ(ポインタ変数など)と呼ぶ。
この変数にアドレス代入するよ〜の際には &(アドレス演算子)を使う。

= #1 =
int *a;
int x=100;
a= &x;  //xのアドレス(0x7fff5fbff~~みたいなやつ)

&は普通の変数の前につけることで、その変数を示すアドレスを返すことができる。
ポインタ変数にアドレスを代入、格納することでaにはxのアドレスが入っていることになりますが、
aから->xのアドレス->xの中身 を取り出すには、
あらためて *(逆参照演算子)を使います。

この*をポインタの前につけることで、
このポインタはアドレスを示しているけど*がついてるので普通の変数のように振る舞うよ〜というニュアンス(わかりづらいか)

そのため、
例えば cout<< *&x <<endl; とすればxの中身(100)を出力できます。(要は普通にxを出力しているだけですが、、)
これは&xでアドレスを示す(ポインタとして働いている)ものに対して*(逆参照演算子)をつけているので、その中身である100を取り出すことができています。

個人的に*がなんとなくわかりづらくしている気がするのですが、
「宣言時の*と、取り出す時(逆参照時)の*は別のもの」と考えたらスッキリできました。
概念の理解って独学では勘違いしてそうで、むじぃ。

*と&の関係性が把握できるようになってきた気がします。

#2 ポインタと配列

配列の名前だけの変数には配列のアドレスが入っている。(知らなかったー)

= #2 =
int array[] = {10,20,30}
cout<<“array:”<<array<<endl; // array:d0x7fff5fbff69c

そのため、配列をポインタに代入する際は
int *pA;
pA = arrayでok
pA = &arrayではダメなのだった。要はarrayがすでに配列のアドレスを示しているので、それに&をつけても意味ないですよ。ということ。
ただし、array[1]とかをポインタに入れる場合は&が必要。(array[1]は中身を示しているので、ポインタにはarray[1]のアドレスを入れる必要がある)

でさらに、この配列のアドレスは以下のように扱うことができる。

 

#3 関数

参考にした本では値を入れ替えるswap関数を例にポインタ引数と参照引数、そしてポインタを使用する理由が説明されていました。
参考にした本
http://amzn.asia/h9z809D

swap(int kariA, int kariB){
int temp;
temp = kariA;
kariA = kariB;
kariB = temp;
cout <<“kariA”<< kariA <<” kariB:”<<kariB<<endl; //kariA:20 kariB:10 入れ替わってる

}
int main(){
int jitsuA = 10;
int jitsuB = 20;
swap(jitsuA,jitsuB);
cout <<“jitsuA”<< jitsuA <<” jitsuB:”<<jitsuB<<endl; //jitsuA:10 jitsuB:20 入れ替わってない!
}

重要なことは、
関数の中で使用される引数(ここではswap関数の中で使用されるkariA, kariB(仮引数))はあくまで関数の中で有効であって、swap関数を呼び出す際に引数として入力した値jitsuA,jitsuB(実引数)は変更されないということ。

このswap関数の結果をjitsuA,jitsuBにも反映させる仕組みが、ポインタ引数参照引数というものを使った仕組み。
上記のような関数内の仮引数を実引数に反映させたいという理由なら、たぶん参照引数が一般的なので、とりあえずそちらだけ考えてみます。

参照渡し、参照引数というものは、関数で使用される引数(仮引数)を参照の値とすることで、実現することができます。
こうすることで、main()から関数を呼び出した際、引数によって渡されるのは、jitsuAとjitsuBのアドレスになります。
normalSwapとrefSwapで中身がどう処理されているか考えてみると、

noramlSwapでは。
normlSwapが呼び出されると、実引数 jitsuA,jitsuBの中身の値が、normalSwapのkariA,kariBにそれぞれコピーされます。
これを受け取って、normalSwapでは入れ替えの処理を行い、特に返り値もなく関数が終了します。

refSwapでは。
refSwapが呼び出されると、実引数 jitsuA,jitsuBのアドレスが、refSwapのkariA,kariBに代入されます。
これを受け取って、refSwapの中では、kariAは&kariA(アドレス)にある中身の値を、kariBは&kariB(アドレス)にある中身の値を意味します。

大事なのは、関数間ではkariAとjitsuA、kariBとjitsuBの示す値自体は別物であったのに対し、別の関数の中であってもkariAとjitsuA、kariBとjitsuBが示すアドレスは共通である ということ。
んーうまく言葉にするのむずかしい。

 

 


❏ 余談

openframeworksで
ofTextureやofPixels,ofBufferなどにgetData()メソッドがあるけど、これは先頭ポインタを返すもので、これによって配列をまとめて扱うことができるイメージ。
ofPixels p;
p.getData(); //pに入ってる配列要素の先頭ポインタを返す
vectorにおけるdata()も同様。

この辺のイメージが定着してくると、オブジェクトの変換とかでも勘でなんとかできるようになりそう。

 

❏ 余談2

swapの仕組みは実はtempなどの一旦値を保持しておくものがなくても書けるっていう小技。

void niceSwap(int &kariA ,int &kariB){
    kariA = kariB – kariA;
    kariB = kariB – kariA;
    kariA = kariA + kariB;
}

これも参考にした本に書いてました。詳しいことはここでは控えます。

oF_ofThread/ofThreadChannelの使い方

 

ofThread/ofThreadChannelの使い方について勉強中なので、わかったことなど備忘録としてメモしていきます。
あまり日本語の情報がなかったので、なにか参考になれば。。

openFrameworksやUnityなど、たいていupdate()みたいな関数があって、その中で毎フレーム処理するコードを書いていくことが多いと思います。
これは要するに、アプリを実行するとupdate()関数が毎フレーム呼び出されて、その中身が実行されているというわけですが、もうちょっとコンピュータ視点から見ると、この処理はCPUで行っているわけで、いいCPUなら高いFPSが出てアプリもヌルヌル動いていい感じです。

CPUを細かく見るとコアとスレッドというものがあって、今回の話で言えば、スレッドというものが1つの処理(カーネル)を行える作業場みたいなもの。スレッドはひとつのCPUにたいてい4個とか8個とか複数あるのですが、普通にアプリを動かす分にはシングルスレッドでしか動いてくれないっぽいです。それだと計算の効率悪いので、暇してる別のスレッドも一緒に動かして計算処理しようというのが、マルチスレッドを使った処理になります。

ここらへんの概念的なところは勘違いしているかもしれないし、言葉で説明するのも難しい。。。

 

で、実際にoFでスレッドを使うにはという手順をまとめます。


参考にした、するべきは、以下の記事

miso-engine – openFrameworksでofThreadを利用しマルチスレッドを実装する
http://miso-engine.hatenablog.com/entry/2016/07/20/183745

Fukaonozomi – openFrameworks-ofThreadでマルチスレッドを実装する
http://fukaonozomi.yokohama/?p=38

(英語バリバリマンなら)ofBook – Threads
https://github.com/openframeworks/ofBook/blob/master/chapters/threads/chapter.md

oF スレッドに関する日本語の記事はそんなに多くないかも。
特にofThreadChannelについてはめちゃ便利なのに、日本語文献はない、たぶん。

まずofThreadの基本的な手順としては、

  1. スレッド処理を行うスレッド用クラスを作る。(ofThreadを継承)
  2. スレッド用クラスのコンストラクタ・デストラクタなどでstartThread() / stopThread()
  3. void threadedFunction()を追加して、スレッドの処理内容を書く。
  4. メインのクラスからスレッド用クラスのインスタンスを作ってスレッド処理を開始します。

ofThreadの使い方でちょっと特殊なのが、threadedFunction()というもので、この関数はofThreadの中で(virtual voidとして)定義されています。
このthreadedFunction()をスレッド用に作ったクラスなどでオーバーライドして処理の内容を書き、ofThreadを継承したインスタンスが作成された瞬間から動きだします。
threadedFunction()は、ofAppのupdate()のように繰り返し実行されるのですが、この繰り返す速度がofAppのupdateよりも速いです。

僕の(たぶんちょっと間違った)解釈では、、、
ofApp::update()はディスプレイへの描画にも密接に関係するため、リフレッシュレートに合わせた速さで実行されていて、
CPU自体はものすごい速さで計算しているのだけど、結局1秒60回程度の速さで計算結果が反映されている(?)。
で、今回のofThread::threadedFunction()では、描画の制限に関係なくCPUのできるかぎりの速さで計算を行って、結果を随時反映させてくれている。ということかなと思いっています。
このためピクセルごとに計算して、値を書き換えるなどの処理であればthrededFunction()で処理するのが速い のだと思っています。

 

もう一つ、
どの記事でも注意されていますが、ofThreadを使う際に重要なのがlock()という処理が必要になるということです。

新規に立てたスレッドはとても速い処理速度でぶんぶん回っていて、このスレッドで処理された値をメインスレッドから取ってくる際に、変なタイミングで値を取ると処理が完了していない(途中までしか処理できていない)値を取ってきてしまう可能性があります。そこで、lock()なる関数を使い、mutexというboolのようなフラグを管理して、外部から変なタイミングで値を取得することができないようにしています。

で、このlock()の処理を比較的ラクにしてくれるのがofThreadChannelです。

参考になるのはやはりopenFrameworksのサンプル。utils -> threadChannelExample

threadChannelExampleでは、構成的に
①メインで動くofAppクラスがあって、
②ofThreadを継承したImgAnalysisThreadクラスがあり、
③さらにそこからthreadで処理するデータを管理するためのofThreadChannelクラス
と3部構成のイメージです。

threadChannelExampleで行われている処理は、

  1. videoGrabberでカメラ映像を取得して、
  2. getPixelsしたものを、ひとまずImgAnalysisThreadに送ります。(ここではImgAnalysisThreadの中で作られたanalyzeって関数で送っています)
  3. さらにそこからofThreadChannelへsendしてキューへ格納します。(この際にlock処理を行ってくれています)
  4. threadedFunction()でofThreadChannelのキューからデータをreceiveしてピクセルごとの処理をしています。

内部的な話では、ofThreadChannelでは、sendの際にlockをしてくれているようです。
※ただどこでunlockされているのかわかっていません。。unique_lockとやらで自動でunlock的なことしてくれてるのかな。

 

参考になるかどうかわかりませんが、threadChannelExampleにコメント追加したものを貼ってみます。


マルチスレッドによる処理は使えるようになれば表現の幅が格段に広がると思います。が、敷居たけぇーとも感じてしまいます。。。
同時にデバッグが難しいとも言われていて、それはCPUが出来る限りの速度で計算処理をするため、計算完了のタイミングが読めないところにあります。
複数の処理がマルチスレッドで実行された時、完了する順番によってバグが発生することもありえます。普通にアプリ動かす分には、一応正当な順番で処理が完了するけれど、別のアプリを動かしたとか何らかの原因で処理完了の順番がズレて、バグの原因になったりします。発生タイミングがわかりづらい・わからないバグなので、この排他制御が重要になってきます。

勉強してもう少しきちんと理解できれば随時内容修正していきますので、どこかで参考になればうれしです。

合わせてUnityのスレッドについても勉強中なので、そのうちメモします。
ちなみにUnityではCPUでスレッドを立てるのでなく、GPUでたくさんスレッドを立てて処理するComputeShaderなんて手段もあります。いわゆるGeneralPurposeGPUというもの。

今後、3Dデータとか膨大なデータ量を瞬時に計算する必要が出てくる時代なので、こういった計算処理の高速化は重要なスキルになるなーと思います。