Hatena::ブログ(Diary)

Over&Out その後 このページをアンテナに追加 RSSフィード

2012-07-21

『第3回 iphone_dev_jp 東京iPhone/Mac勉強会』で vImage について発表してきました

先日の記事で第2回勉強会向けに用意していた発表内容について書きましたが、本日(7/21)開催された第3回で発表の機会をいただいたので、資料とサンプルコードをアップデートして発表してきました。


iOS 5 から Accelerate.framework に追加された高速画像処理ライブラリ、vImage についての話です。


前回との差分は、サンプルコードでできる画像処理が増えたこと(回転とか膨張/収縮とか、前回記事のときから倍ぐらい対応メソッドが増えてます)と、資料でそのへんについて触れているp19〜p25あたりと、OpenCV, CoreImageとの比較を表にしたp26あたりです。




資料の内容抜粋

  • vImageとは
    • Accelerate.framework内のライブラリ
    • iOSデバイスのハードウェア向けに最適化されていて高速
    • 畳み込みや幾何変換,ヒストグラム計算などの画像処理系の関数をまとめたものでiOS5から利用可能
  • 何がいいの?
    • シンプルに書ける
    • 速い・・・14倍高速!!
    • 省電力・・・90%off!!
  • vImageの使い方
    • Accelerateフレームワークをプロジェクトに追加し、 Accelerate.hをインポートすればOK
#import <Accelerate/Accelerate.h>
    • とりあえずvImageConvolve_ARGB8888() ひとつで色々できる
      • ブラー、エッジ抽出、エンボス、先鋭化 etc...
  • その他の使い方
    • 回転
    • 膨張/収縮
    • ヒストグラム均一化
  • サンプルコード:https://github.com/shu223/vImageCategory
  • vImage vs OpenCV vs CoreImage
  • ドキュメント
    • WWDC2011のセッションビデオ&スライド
      • 概要を知るには一番わかりやすい
    • vImage Programming Guide
      • 処理前後のサンプル画像や、フィルタ配列の図もあってわかりやすい
    • vImageリファレンス
      • たくさんあるので必要に応じてどうぞ・・・
      • Conversion / Convolution / Decompression / Geometry / Histogram / Morphology / Transform etc...
  • vImageを使用している画像処理ライブラリ

サンプルコード

githubに上げたサンプルコードは UIImage のカテゴリとして作ってあってAPIも下記のようにシンプルにしてあります。

// Convolution Oprations
- (UIImage *)gaussianBlur;
- (UIImage *)edgeDetection;
- (UIImage *)emboss;
- (UIImage *)sharpen;
- (UIImage *)unsharpen;

// Geometric Operations
- (UIImage *)rotateInRadians:(float)radians;

// Morphological Operations
- (UIImage *)dilate;
- (UIImage *)erode;
- (UIImage *)dilateWithIterations:(int)iterations;
- (UIImage *)erodeWithIterations:(int)iterations;
- (UIImage *)gradientWithIterations:(int)iterations;
- (UIImage *)tophatWithIterations:(int)iterations;
- (UIImage *)blackhatWithIterations:(int)iterations;

// Histogram Operations
- (UIImage *)equalization;

実行して画面をタッチするとこんな感じで vImage による画像処理が適用されます。


f:id:shu223:20120721170411p:image:w320


※左上がオリジナル画像です。



反省など

一番肝心なところの調査が足りず、中途半端な発表をしてしまったなーと反省しております。


何がって、資料p26の vImage vs CoreImage vs OpenCV の部分、あそこで「どういう場合において vImage を選択するべきか」を明確に言えてないので、この発表を聞いたところで vImage を採用しようとか採用しないでおこうとか、判断できないところ。


  • CoreImage は内部的に vImage を使ってるのかどうか。
    • 全面的に使ってるのか、一部で使ってるのか。
  • vImage にできて、CoreImage にできないこと。その逆。
  • vImage, CoreImage, OpenCV, OpenGLESでの処理速度の比較(計測コード書く)

このあたりはまた追って調べたいと思います!



2012-05-26

『第2回 iphone_dev_jp 東京iPhone/Mac勉強会』で vImage について発表・・するつもりで参加してきました

めんどくさいタイトルですいません。。


僕がこの勉強会のATNDに気づいたころには120人の参加者枠もとっくにうまり、発表者枠も既に10人に達していたのですが、「時間がもし余ったら」と @k_katsumi さんの粋な計らいでLT補欠として参加させていただいた、というわけでこのタイトルになりました。


で、結果として時間はちょうどいい感じに10人の発表で終了時間となりLT枠はなかったのですが、いちおう資料とサンプルコードはつくってアップしておいたのでこちらにも載せておきます。



iOS 5 から追加された vImage について紹介しています。


資料の内容抜粋

  • vImageとは
    • Accelerate.framework内のライブラリ
    • iOSデバイスのハードウェア向けに最適化されていて高速
    • 畳み込みや幾何変換,ヒストグラム計算などの画像処理系の関数をまとめたものでiOS5から利用可能
  • 何がいいの?
    • シンプルに書ける
    • 速い・・・14倍高速!!
    • 省電力・・・90%off!!
  • vImageの使い方
    • Accelerateフレームワークをプロジェクトに追加し、 Accelerate.hをインポートすればOK
#import <Accelerate/Accelerate.h>
  • OpenCV vs vImage
  • ドキュメント
    • WWDC2011のセッションビデオ&スライド
      • 概要を知るには一番わかりやすい
    • vImage Programming Guide
      • 処理前後のサンプル画像や、フィルタ配列の図もあってわかりやすい
    • vImageリファレンス
      • たくさんあるので必要に応じてどうぞ・・・
      • Conversion / Convolution / Decompression / Geometry / Histogram / Morphology / Transform etc...
  • vImageを使用している画像処理ライブラリ

サンプルコード

githubに上げたサンプルコードは UIImage のカテゴリとして作ってあってAPIも下記のようにシンプルにしてあります。

-(UIImage*)gaussianBlur;
-(UIImage*)edgeDetection;
-(UIImage*)emboss;
-(UIImage*)sharpen;
-(UIImage*)unsharpen;

実行して画面をタッチするとこんな感じで vImage による画像処理が適用されます。


f:id:shu223:20120526174615p:image:w320


※左上がオリジナル画像です。


補足など

資料に書いてなくて発表のときに口頭で話そうと思ってたこととして、「畳み込みと色空間変換と拡大縮小とヒストグラムでかなり色んな画像処理ができる」ということがあります。


で、一番処理的に重いのが畳み込みなので、畳み込みが高速化されるようになるということは画像処理にとって相当ありがたいことです。


というわけで資料では vImageConvolve_ARGB8888() しか紹介していませんがこれだけでも結構 vImage の恩恵があるんじゃないかと思っております。



ちなみに他の発表者の皆様のスライドが下記にまとまっていて、とても勉強になります。

http://slidrs.net/sessions/13


で、このなかに @kamiyan さんの『簡単!OpenGL ES2.0フラグメントシェーダー』という発表がありまして、これを見てると、本気で画像処理を高速化するのであればシェーダを書いてOpenGL ES2.0で処理するのが一番速そうです。(ちなみにこの発表はシェーダをiPhone上で書けて実行できるサンプルアプリつき!!!)


このあたりはまた勉強して新しく知ったことなどを別記事でまとめたいと思います。



2012-05-12

逆ジオコーディングで取得した CLPlacemark の住所情報の内容

先日CLGeocoder を用いた逆ジオコーディングの記事を書きましたが、その結果得られる CLPlacemark 型の addressDictionary プロパティに入っている住所情報について、どのキーがどういう情報なのか、国ごとにどういう違いがあるのか、毎回調べるのが面倒なのでここに書いておきます。


近所で位置情報を取得し逆ジオコーディングし、addressDictionaryの内容をログ出力するとこんな感じでした。(エンコードおかしいですが気にしないでください・・・)

City = "\U938c\U5009\U5e02";
Country = "\U65e5\U672c";
CountryCode = JP;
FormattedAddressLines =     (
                             "\U3012248-0007 \U795e\U5948\U5ddd \U938c\U5009\U5e02 \U5927\U753a 2\U4e01\U76ee6\U756a13\U53f7"
                             );
Name = "\U5927\U753a 2\U4e01\U76ee6\U756a13\U53f7";
State = "\U795e\U5948\U5ddd";
Street = "\U5927\U753a 2\U4e01\U76ee6\U756a13\U53f7";
SubLocality = "\U5927\U753a";
SubThoroughfare = "6\U756a13\U53f7";
Thoroughfare = "\U5927\U753a 2\U4e01\U76ee";
ZIP = "\U3012248-0007";

ここからわかることは、

  • FormattedAddressLines には、"ZIP", "State", "City", "Street"の順に入っている
  • 日本でいうと、"State"は都道府県、"City"は市区町村にあたり、"Street"には「小町 2丁目3番1号」といったような町名地番が入る
  • "Street" は、"Thoroughfare" と "SubThoroughfare" を繋げたもので、"SubLocality" は町名(市区町村以降の地名。"Thoroughfare" にも含まれる)


ただどの国でもこれらの項目が得られるかというとそうではなく、たとえばチュニジアの緯度経度で逆ジオコーディングすると、

Country = "\U30c1\U30e5\U30cb\U30b8\U30a2";
CountryCode = TN;
FormattedAddressLines =     (
"RN 3E",
"\U30c1\U30e5\U30cb\U30b8\U30a2"
);
Name = "RN 3E";
State = "\U30b7\U30c7\U30a3\U30d6\U30b8\U30c3\U30c9";
Street = "RN 3E";
Thoroughfare = "RN 3E";

addressDictionaryにはこういう感じの結果が返ってきました。


"City" や "SubThoroughfare" 、"SubLocality" といった項目がはいっていません。データがないのか、もともと住所の構造がシンプルなのかわかりませんが、日本と同様に市区町村にあたる項目があるという前提で処理を書くとおかしなことになりそうです。



そもそもこういうのは世界共通で規格みたいなものがあるのかもしれないので、こういう場当たり的な調べ方は的外れなのかもしれません。悪しからず・・・



2012-05-06

iOS 5.0 より追加された CLGeocoder を使用して逆ジオコーディング

Appleのドキュメントによると

iOS 5.0では、MKReverseGeocoderとMKReverseGeocoderDelegateは非推奨になっているの で、新たにアプリケーションを開発する場合はCLGeocoderを使ってください。

とのことなので、逆ジオコーディングを CLGeocoder を使用してやってみたのでその手順をこちらにメモしておきます。

(上記Appleのドキュメントのコードは少しずつ変数名が間違ってたり、独自定義のクラスが引数の型に指定されていたりして、そのままでは使いづらい・・・)


で、手順といってもヘッダのインポートやオブジェクトの生成とかを除けば、

  • reverseGeocodeLocation:completionHandler: を呼ぶ

これしかありません・・・


僕の場合は下記のようにクラスメソッドを定義しました。

+ (void)reverseGeocodeLocation:(CLLocation *)location
{
    CLGeocoder *geocoder = [[CLGeocoder alloc] init];
    [geocoder reverseGeocodeLocation:location completionHandler:
     ^(NSArray* placemarks, NSError* error) {
         if ([placemarks count] > 0) {
             
             CLPlacemark *placemark = (CLPlacemark *)[placemarks lastObject];

             // 取得したplacemarkを格納したNSNotificationを発行
         }
     }];
}

Blocksで非同期に処理されるので、CLPlacemarkオブジェクトを取得したらNSNotificationを発行して必要なオブジェクトに通知するようにしました。


なお、reverseGeocodeLocation:completionHandler: で得られる値が NSArray 型になっていますが、CLGeocoderのクラスリファレンスによると

Contains an array of CLPlacemark objects. For most geocoding requests, this array should contain only one entry. However, forward-geocoding requests may return multiple placemark objects in situations where the specified address could not be resolved to a single location.

とあり、逆ジオコーディングのときは基本的に1つしかオブジェクトが入ってこないと思ってよさそうです。(正ジオコーディングのときはアドレス→緯度経度なので、1つとは限らないため、NSArrayになっている)



2012-03-27

NSUserDefaults に保存する際に自動的に iCloud にも保存してくれるライブラリ "MKiCloudSync"

MKiCloudSync を使用すると、たった1行コードを追加しておくだけで、あとは自動的に NSUserDefaults の内容を iCloud に同期してくれます。


もともとシンプルな iCloud 同期ですが、もっとシンプルになるのでとりあえず iCloud 対応したい、ちょっと試してみたい、等の場合にオススメです。


使い方

1. ソースをダウンロード

https://github.com/MugunthKumar/MKiCloudSync


2. ヘッダをインポート

#import "MKiCloudSync.h"

3. start メソッドをコール

AppDelegate の application:didFinishLaunchingWithOptions: 内で、以下のように start メソッドをコール

[MKiCloudSync start];

たったこれだけ。


「おいおい、iCloudに書き込みたいときはどうすんだ?」と思った方もいらっしゃったかもしれませんが、そこを自動化してくれてるのが MKiCloudSync なので、上記だけでいいのです。



MKiCloudSync のしくみ

簡単にいうと(実際に中身も簡単なのですが)、

NSUserDefaults の変更を監視 → 変更があれば iCloud に反映

というしくみになっています。


ソースをのぞいてみると、start メソッドをコールすると2つの通知の監視を開始していることがわかります。

if([NSUbiquitousKeyValueStore defaultStore]) {  // is iCloud enabled
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(updateFromiCloud:) 
                                                 name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification 
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self 
                                             selector:@selector(updateToiCloud:) 
                                                 name:NSUserDefaultsDidChangeNotification
                                               object:nil];

NSUserDefaultsDidChangeNotification は、NSUserDefaults に変更があったときに発行される通知で、この通知が発行されたときに実行される updateToiCloud: メソッドで NSUserDefaults の内容を iCloud に同期するようになっています。


また NSUbiquitousKeyValueStoreDidChangeExternallyNotification は、iCloud のキーに外部からの変更があった場合に発行される通知で、この通知を受け取ると updateFromiCloud: メソッドで iCloud の内容を NSUserDefaults に同期するようになっています。(もちろんこの間のNSUserDefaultsDidChangeNotification の監視は止めてあります)



注意点

MKiCloudSync では NSUserDefaults のキーの削除には対応していません。NSUserDefaultsDidChangeNotification の通知を受け取ったときに実行されるハンドラメソッドである updateToiCloud では、

NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];

[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    
    [[NSUbiquitousKeyValueStore defaultStore] setObject:obj forKey:key];
}];

このように NSUserDefaults に存在するキーを列挙して iCloud に書き込んでいるので、NSUserDefaults に存在しなくなったキーを iCloud に書き込んではくれないのです。


これで何が困るかというと、開発中のアプリを削除して最初から動作確認しようとしても、再インストール時に NSUbiquitousKeyValueStoreDidChangeExternallyNotification 通知が発行されて iCloud からの自動同期が行われてしまい、諸々の設定値が復活してしまうという点です。


この辺りは、キーの削除への対応であれば fork して使うなり、アプリ削除への対応であればデバッグモード用に iCloud の内容をリセットする機能を追加するなりする必要があります。


たとえば僕の場合、デバッグビルド版にはこんな感じで iCloud の内容をリセットする機能をつけています。

NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
NSDictionary *dict = [iCloudStore dictionaryRepresentation];                    
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
    
    [iCloudStore removeObjectForKey:key];
}];


2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2012 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2013 | 01 | 02 | 03 | 04 | 05 |