Xcode 9.4.1: com.apple.CoreSimulator.CoreSimulatorService の異常解消。

Xcode の 9.2 辺りまで、Xcode を起動しっぱなしにすると電池をやけに食う現象があり、long life 自慢の Macbook 12 in だったのに、3 時間ぐらいしか持たない始末。

なんでこんなことになるのだろうと、CPU の使用状況を調べてみたら、以下の謎のプロセス発見。 com.apple.CoreSimulator.CoreSimulatorService 

これ、会社の MacBook Air では発生しない現象。

なんだかよくわからないのですが、やけに CPU を食っていて、それに伴い電池も食う食う。結局のところ、Xcode の bug じゃないかと思い放置していたのですが、やはりそうだったようで、Xcode 9.4.1 辺りから、電池持ちが通常になってきて、CPU を確認したら、低位置にあった。

このプロセスなんだろうね。

 

タッチしたら・・の書き方。

アプリ作ってて、ある view をタッチしたら・・・というのを検出する話。

いつもは、対象となる uiview の sub class を作って、delegate を通して、あーめんどくセー・・・という流れだったのですが、今日、こんな書き方を知った。

 

-  (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch* touch = [touches anyObject];
    if ( [event touchesForView:self.moviePreview] ) // こういうやり方もあるのね
    {
        NSLog(@"moview preview がタッチされました。");
    }
}
 
event から touch した view を取れるのね。
これで、sub class 作って delegate 設定してとかしなくていいんでよかった。

CloudKit を使う: App を登録してからでないと使えないのね。

CloudKit を使おうと思い、Capability > iCloud > CloudKit を On にする。すると、steps にエラーの表示が。こんな感じ。

! Add the iCloud feture to your App ID.

! Add iCloud Containers to your App ID.

ってことで、provisioning profile とかをちゃんと設定してからでないと CloudKit を使えないのね。念のため、CloudKit dashboard のボタンを押してみたけれども、表示されなかった。

provisioning profile の設定って鬼門。すんなりいったことがかなり少ないように思います。ま、でも、実機に WiFi 経由でアプリを送ったりアップデートできるようになったりするので、実は便利なことが多くいいんですが。

明日やろう。

CoreML を使って画像認識、Objective-C で書くと・・・

CoreML を使って文字の認識をしたくて、色々調べているのですが、Swift の記事ばかりで、Objective-C の例が全然ない。Swift の記述を Objective-C に置き換えればよい話なのですが、Swift を理解していないので辛い・・・。書籍は持っているのですが、なかなか覚えられないものです。そんな中、ようやく Objective-C で記述できたので、参考までに記載。

まず、カメラを使って写真を撮ります。その写真を VGG16 model で画像認識させて、写っている物体を認識させます。いろいろな記事を読んでいると、スッゲー簡単みたいなことが書いてありますが、Objective-C の記載ではないので前準備の変数定義部分からして???なんですが、ようやく形になったと。具体的にはこんな感じ・・・

 

- (void) imageClassification
{
    NSString* filepath = [[NSBundle mainBundle] pathForResource:@"VGG16" ofType:@"mlmodelc"];
    NSURL* modelUrl = [NSURL URLWithString:filepath];
    NSError* error;
    MLModel* model = [MLModel modelWithContentsOfURL:modelUrl error:&error];
    if ( error )
    {
        NSLog(@"cover search error: model not configure");
    }

    

    VNCoreMLModel* coreMLModel = [VNCoreMLModel modelForMLModel:model error:&error];
    NSDictionary* dictionary = [NSDictionary new];
    VNImageRequestHandler* handler = [[VNImageRequestHandler alloc]
                                      initWithCGImage:self.cameraImage.image.CGImage
                                      options:dictionary];

    

    VNCoreMLRequest* corerequest = [[VNCoreMLRequest alloc]
                                    initWithModel:coreMLModel
                                    completionHandler:(VNRequestCompletionHandler) ^(VNRequest *request, NSError *error){
                                        if ( error )
                                        {
                                            NSLog(@"error です");
                                        }
                                        else
                                        {
                                            VNClassificationObservation* firstObservation = [request.results firstObject];

                                            

                                            VNClassificationObservation* secondObservation = [request.results objectAtIndex:1];

                                            

                                            VNClassificationObservation* thirdObservation = [request.results objectAtIndex:2];
                                            NSLog(@"認識しました。%@", firstObservation.identifier);
                                            self.displayLog.text = [NSString stringWithFormat:@"%@\n%@\n%@", firstObservation.identifier, secondObservation.identifier, thirdObservation.identifier];
                                        } // end of if
                                    }
                                ]; // end of request

    

    [handler performRequests:@[ corerequest ] error:&error]; // ここで実行
    if ( error )
    {
        NSLog(@"%@", error);
    }
}

意外とハマるのが、結果の取り出し部分で、配列に結果が返ってくるのはわかっていても、具体的にどうやって取り出すのかよくわからなかったです。また、request.results 配列に結果が返ってきますが、どれくらいのサイズがあるのかというと、999 もありました。ので、この例では、上位  3 つを取り出しています。

次は、画像を読み込む、textdetection で四角を切り取り、四角の中の文字を認識させるという流れになります。モデルを自作できるとよいのですが・・・

 

Vision.framework と CoreML

CoreML を使い始める。物体の写真を撮って、何が写っているか分類する。ネットに情報は色々あるけど、自分のやりたいことズバリに答えてくれる情報はない。しかも、Swift の情報ばかりで、Objective-C で書く人は完全にマイノリティ。しょうがないので、試行錯誤し、今日わかったのはこんなこと。

まず、モデルの指定方法。VGG16 というモデルが Developer サイトからダウンロードできるので、このデータを使う。Xcode にファイルを追加し、プログラム上ではファイルのパスを指定しモデルのポインタを生成する。ファイルパスの指定でつまずいてしまった・・・まず、ファイルの拡張子は.mlmodel となっているので、ofType で指定してやると見つけられないとのエラー。具体的にはこんな感じ。

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** +[NSString stringWithUTF8String:]: NULL cString

 
よく調べてみたら、Xcode に .mlmodel を追加すると、勝手にコンパイルされるのだそうで、指定する拡張子は mlmodelc にしないとならない。Xcode では .mlmodel と表示されているのに、プログラム上では .mlmodelc を指定する、具体的にはこんな感じ。
 
    NSString* filepath = [[NSBundle mainBundle] pathForResource:@"VGG16" ofType:@"mlmodelc"];
    NSURL* modelUrl = [NSURL URLWithString:filepath];
    NSError* error;
    MLModel* model = [MLModel modelWithContentsOfURL:modelUrl error:&error];
 
うーん、こんなのわかんないよーと思いつつ、さて、.mlmodelc は一体どこに存在するのかというと、build したアプリケーションの中に保存してありました。
素の .mlmodel は 500 M 超と、最近の HD は大容量になったよね・・・といった風潮でも、ちょっと看過できないぐらい大きい。実際にアプリに bundle してリリースするのはかなり気が引けるのですが・・・
コンパイルしたファイルの大きさは変わるのかどうか、build したアプリで調べてみたらほとんど容量が変わっていない・・・やっぱりアプリ内にモデルを置くのはまずいよね、こういう大きいのはネットから引いてくるべきなんだろうなと。でも、モデルファイルの置き場所をネットにしたら、使用する時に多分一度は全部ファイルを持ってこないとダメなはずで、今度は通信にすごい時間かかりそう。結局のところ、モデルファイルは小さくなるように自作するか、モデルとロジックをネットにおいて結果を受け取るような作りにするのが正しいやり方なんでしょうね。
試している段階で既に課題が出てきた・・・

tableView Prototype Cells で tag には 0 以外を指定しないと表示されないのね。

tableView の一覧表示で、表示する項目は Storyboard の Prototype Cells 部分に UILabel を配置し、configureCell method で表示する内容を指定する。

Storyboard で UILabel を配置するとき、tag のデフォルトは 0 なんだけど 0 のままだとうまく表示がされない。デフォルトで entryDate なんて書いておくと、そのまま entryDate と表示されてしまったりする。なんじゃこりゃ。

こんな時は、tag の番号を 1 とかにしてやるとうまく表示されるようになる。0 は予約されているのかね。configureCell に書く内容は例えば次のような形。

- (void)configureCell:(UITableViewCell *)cell withEvent:(Brand *) item

{

    // cell.textLabel.text = event.timestamp.description;

    UILabel* entryDate = (UILabel*)[cell viewWithTag:1];

    entryDate.text = [NSString stringWithString:item.entryDate.description];

}

ちなみに、Storyboard で UILabel を配置したからといって、outlet として接続すると error になったりしてしまいます。prototype cells はちょっと特別なんだなと。

tableView で、セルをタップしても detail に遷移しないぞ?の場合

tableView で、セルをタップしても detail に遷移しない。これ、基本的なことだろ?どういうことだ? prepareForSegue はちゃんと実装しているぞ?

理由は、storyboard で、detail へ segue の繋ぎ方を間違えていた。画面全体から繋いではダメで、xxxCell と書かれたのから detail へ繋がないとダメだと。そうしないと prepareForSegue は呼ばれないと。

 

色々めんどーなことがあるものだ・・・