頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


iOSでPOCに挑戦(4) フーリエ変換する

頭と尻尾はくれてやる! iOSでPOCに挑戦(3) 画像のデータを配列に入れる
の続きだよ。
今まで必要な引数を確認してきたんだけど、いよいよ画像をFFTしてみよう。

コードの流れはこんな感じ。
{
    //メモリ確保
    DSPSplitComplex splitComplex;
    splitComplex.realp = calloc(nData, sizeof(float));
    splitComplex.imagp = calloc(nData, sizeof(float));
    
    
    //データのコピー
    memcpy(splitComplex.realp, brArray, nData*sizeof(float));
    
    
    //FFTの準備
    FFTSetup fftSetup = vDSP_create_fftsetup(8 , FFT_RADIX2);//2^8=256,radix => 底、基数
    

    //FFT
    vDSP_fft2d_zip(fftSetup,&splitComplex,1,0,8,8,FFT_FORWARD);
    
    
    //スペクトルを得る
    float spectrumArray[nData];
    {
        vDSP_zvmags(&splitComplex,1,spectrumArray,1 , nData);//実部と虚部の自乗和
        float added = 1.f;
        vDSP_vsadd(spectrumArray,1, &added , spectrumArray,1 , nData);//1足す
        float B = 1.f;
        vDSP_vdbcon(spectrumArray,1,&B,spectrumArray,1,nData,0);//logとる
    }
    

    //正規化する
    {
        float max,min;
        vDSP_maxv(spectrumArray,1, &max,nData);//最大値
        vDSP_minv(spectrumArray,1, &min,nData);//最小値
        
        float delta = max-min;
        for (int ii=0;ii < nData;ii++) {
            spectrumArray[ii] = (spectrumArray[ii] - min)/delta*255.f;
        }
    }
    
    
    //配列のデータを表示してみる
    UIImageView *fftImageView = makeImageViewFromArray(spectrumArray , 256 , 256);
    fftImageView.frame = CGRectMake(10, 140+130,256/2,256/2);
    [self.view addSubview:fftImageView];
    
    
    //メモリ解放
    free(splitComplex.realp);
    free(splitComplex.imagp);
}
細かいメソッドなんかはないので、このままコピペしても動かないけど流れはわかるんじゃないかな。 上のコードを実行すると、、、

FFTの結果

一番上がオリジナルの画像、二番目のは配列に輝度を入れ(よって白黒)それをUIImageViewオブジェクトにして表示したもの、一番下がそれをフーリエ変換し、パワースペクトルを表示したもの(実機で実行してスクショ撮影)。

なお、この状態で表示されたパワースペクトルの図は画像左上が低周波で画像中央部が高周波になるんだ。文献によっては中央部分が低周波になってることもあるし、どこが高周波の領域なのか異なることがあるので注意しないといけないんだよね。

POC(位相限定相関法)を使って二枚の画像を比べるのにパワースペクトルの図を表示させるのが必要というわけじゃないけど、こんな感じでフーリエ変換できる、というのはOKかな?
それじゃあ次はいよいよ二枚の画像を比べてみたいけど、長くなったので今日はここまでってことで。





XcodeのCode Snippetにはショートカットを設定しよう

XcodeにCode Snippetって機能があって自分がよく使うコードを置いておけるんだけど、実はあまり使っていなかったんだよね。

Xcodeの右下

↑ほら、画面右下にあるでしょ。
自分がよく使うコードを入れてはいたんだけど、使う時にcommand + option + 0で表示させて(俺は普段右端のユーティリティー領域は隠しているんだ)、そこから欲しいコードを探してドラッグとかする、、、ってのがなんとも面倒だったからいつの間にか使わなくなっていたんだ。

ところが、最近知ったのがショートカットを設定できるってこと。

Code Snippetの編集
↑うん、確かにCode Snippetの編集項目にショートカットの欄があるわ。
ここに自分が覚えやすくて入力しやすい適当な文字を入れておくと、、、

候補表示
↑普通に入力時に候補として出てくれる!超絶便利!!

これを知ってからはCode Snippet万歳!って感じだよ。
ちなみにうまく種類を設定しておかないと欲しいところで出てくれない場合もあるので注意してね。


新しいアルカリイオン整水器を買って10日経過

Panasonicのアルカリイオン整水器 TK-AS43を取り付けてから10日ほど経ったのでちょいと感想などを書くよ。

今まで使っていたのは7〜8年くらい前に買ったTOTOのアルカリ7というアルカリイオン整水器なんだけど、そろそろ寿命なのか毎朝エラーが出るような状態でさ。修理しようとしたら結構お金がかかりそうなので、それなら新しいのでもいいんじゃないってことで新しいのを購入したんだ。


↑買ったのはこちら。
取り付けてみたんだけどさ、、、前の機種と何が違うって、グリセロリン酸カルシウムを入れなくてもアルカリイオン水ができるんだよ!
これがいかに画期的かってことは古い機種を使っている人にはわかってもらえるんじゃないかな。
前の機種ではアルカリイオン水を生成するのにグリセロリン酸カルシウムっていうのが必須だったんだよ。これは粉状の薬みたいなモノなんだけど、これを整水器に入れるのが面倒極まりなかったんだ。ほんの数日しかもたないし、入れる容器が縦長の筒状でこれが入れにくいんだよ。結局アルカリイオン整水器のはずがいつのまにかただの浄水器として使っていたんだ。
だから今回の買い替えでもただの浄水器だけでもいいなと思っていたんだけど、どうも最近の機種はグリセロリン酸カルシウムは不要みたいと知って、それじゃあってことでアルカリイオン整水器を買うに至ったんだ。

取り付け自体は説明書通りで簡単にいけたし、使い勝手もいい。
次のフィルター交換までどのくらいの量(リットル)かってのが表示されるのが面白い、、、面白いのは最初だけだけかもしれないけど、交換時期が近づいてきたらこの機能の便利さを再確認することになるんだろうな。

問題は、と言えば、、、前に使っていた機種と違って浄水(アルカリイオン水も)が本体の上部から出ている金属のホースから出るんだよね。

アルカリイオン整水器

↑ほら、本体の上から無駄に長い金属のホースがびよーんって出てるでしょ(背景があまりにもちらかってるのでぼかしてるよ)。これは長さの調整はできないけど、簡単に左右に動かすことができるので、食器を洗う時なんかはじゃまにならないように手元から遠くになるようにしてたりするんだ。その後にその状態で浄水を出してしまいシンクじゃないところに水が、なんてことが最近あったよ。買った当初は気をつけていたけど逆に慣れてきてから頻発してきたんだ。
特に前に使っていた機種が本体からではなく水道の蛇口から出るような仕様だったからよけいにうっかりやっちゃうんだよね。
まあ床に流れ落ちるわけじゃないからちょっと拭けばいいだけなんだけど、浄水を使う時にいちいちホースの位置を確認する必要がある、というのは軽いストレスだな。
でも、いい買い物したと思ってるよ。


シングルトンなクラスを継承で作れる?

Objective-Cでシングルトンなクラスをラクに作りたいってお話だよ。

よく聞くよね、シングルトン、singleton。トンってなんだよ、シングルと何が違うのって辞書を見て思わないでもないけど、ともかくこのシングルトンパターンを試してみたんだよ。
今までの俺の理解だとアプリ全体で一つだけしかないオブジェクトをうっかり作り直したりしないようにするためのデザインパターンかなと思っていたんだよ。だったら気を付ければいいじゃないか!ってことでさしてメリットも感じずスルーしてきたんだけどさ、、、ふとかじったところ、これが便利なことに気が付いたよ。やっぱり先人の知恵は敬うべきだね。

Google先生にObjective-Cでシングルトンなクラスの作り方を聞いてみるとよさそうなコードをすぐに教えてくれたよ。
俺はARCは使わない(使えない)ので allocWithZone: や retain をオーバーライドする方法を試してみたんだ。
Objective-C でシングルトンパターン | Sun Limited Mt.
↑ここにコードもあるよ。
これで確かに実現できる。

でも、これだとシングルトンなクラスを作るのに結構な量のコピペが必要になるよね。コードの管理ってことを考えるとコピペするのもあまり好きじゃないし、関連コードを頻繁に目にするのはうっとおしいと思うんだよ、俺は。できればブラックボックス化して目につかないくらいにしたいと思ってさ、こんなことを考えたんだよ。

シングルトンなクラスにするためのNSObjectを継承するサブクラス(名前をSingletonObjectクラスとするよ)を作っておいて、シングルトンなクラス(例えばDataControllerとするよ)を作る場合にこのSingletonObjectクラスを継承すりゃいいんじゃね?って。

SingletonObjectクラスは上のリンクを参考に作っておいて、DataControllerクラス(元々はNSObjectのサブクラス)を次のようにしてSingletonObjectのサブクラスにしておく。
//DataController.h
#import 
#import "SingletonObject.h"

@interface DataController : SingletonObject
これで実際に使ってみると、、、いいじゃない! alloc + init もできない!インポートしてスーパークラスの名前を書き換えるだけでシングルトン用の記述を目にしなくて済むし、コードの管理も俺的にはスッキリする!
こいつはいい!と思ったんだけど、実はすっとぼけた状態でね、、、

同じアプリ内にシングルトンにしたいクラスがもう一つあったとするよ。SoundControllerクラス(NSObjectのサブクラス)としようか。
これも同じ様にSingletonObjectのサブクラスにしようとSingletonObjectクラスを継承させたんだよ。

クラス関係図

すると、、、
DataController *dataController = [DataController sharedInstance];
SoundController *soundController = [SoundController sharedInstance];
この二つのポインタを確認すると同じになるんだよ、、、orz

実行結果

違う、違う、そうじゃない、俺がやりたいのは!そこまで一つにならんでいい!
って嘆いていてもどうしようもない。仕方なく、こんな方法でやってみたんだ。

//static id sharedInstance = nil;//削除
static NSMutableDictionary *dictionary = nil;//追加

+(id)sharedInstance
{
    @synchronized(self) {
        
        if (!dictionary) dictionary = [[NSMutableDictionary alloc] initWithCapacity:0];//追加

        /* 削除
        if (sharedInstance == nil) {
            sharedInstance = [[self alloc] init];
        }
        */
        
        if (![dictionary objectForKey:[[self class] description]]) {//4行追加
            id singletonObject = [[self alloc] init];
            [dictionary setObject:singletonObject forKey:[[self class] description]];
        }
    }
    //return sharedInstance;//削除
    return [dictionary objectForKey:[[self class] description]];//追加
}

+ (id)allocWithZone:(NSZone *)zone 
{
    @synchronized(self) {
        id sharedInstance = [dictionary objectForKey:[[self class] description]];//追加
        if (sharedInstance == nil) {
            sharedInstance = [super allocWithZone:zone];
            return sharedInstance;
        }
    }
    return nil;
}
SingletonObjectクラス内でそれを継承しているサブクラスの名前が [[self class] description]] で取得できるのでそれをキーに、作ったオブジェクトをオブジェクトにしてNSDictionaryに入れる、というなんとも奇怪なやり方なんだけどね。
すでにオブジェクトを作ってあれば、dictionaryから呼び出したサブクラスの名前をキーにオブジェクトを返しているんだ。

実行結果

↑これだととりあえず別なオブジェクトになっていて、実現できているようなんだけどさ、、、
これ本当に問題ないのかな???





修正版がリリース

頭と尻尾はくれてやる! サマータイムがある所で不具合発生してた
↑予想外に速く修正版が審査通過したんだよ。
申請したのが確か2/7(木)で土曜の朝にはIn Review、日曜の朝には通過してリリースされてた。
今までだと一週間くらいかかっていたんだけど、、、なんなんだろうね。

ちなみにバージョンは
赤ちゃんの成長グラフ ver 1.7.1
赤ちゃんの成長グラフ Lite ver 1.3.1

今回の修正でサマータイムで月日がおかしくなるのはなくなった、、、はず。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad