Archive for the 技術情報 Category

sqlite の PRAGMA Statements 備忘録

最近CoreDataを使ったアプリケーションの開発を行っていて「?」と思った事がありました。
.sqliteファイルのあるディレクトリに .sqlite.wall と言う名前のファイルがいつの間にか出来ていました。
最初は「まあいいか」と無視をしていたのですが、CoreDataからの更新がちゃんと反映されているかを確認するために .sqlite ファイルを sqlite3 コマンドで開いても全く更新されていない事があり調べてみました。

結論から言うと、iOS7からSQLiteのpersistent storeにjournal_modeというのがデフォルトで使われるようになったとの事です。
https://developer.apple.com/library/ios/releasenotes/DataManagement/WhatsNew_CoreData_iOS/
上記のリンクを開くと書いてありますが、これによりロールバックのジャーなリングにおいて、信頼性とパフォーマンスが向上するそうです。しかし読み込み専用やiOS4以前との互換性のためには推奨されないようです。

で、その journal_mode を使わないようにする方法です。
NSPersistentStoreCoordinator に sqlite の Persistent Store を追加する際に、optionで指定してやります。

    NSMutableDictionary* sqlLitePragmaOptions;
    sqlLitePragmaOptions = [NSMutableDictionary dictionaryWithCapacity:0];
    [sqlLitePragmaOptions setObject:@"DELETE"
                             forKey:@"journal_mode"];

    NSMutableDictionary* options;
    options = [NSMutableDictionary dictionaryWithCapacity:0];
    [options setObject:sqlLitePragmaOptions
                forKey:NSSQLitePragmasOption];

    NSError *error;
    if (![psc addPersistentStoreWithType:NSSQLiteStoreType
                           configuration:nil
                                     URL:storeUrl
                                 options:options
                                   error:&error]) {
		// Update to handle the error appropriately.
		exit(-1);  // Fail
    }

試してはいませんが、それ以外の sqlite の PRAGMA Statements も同様の方法で指定してやる事が出来るようです。
sqllite の PRAGMA Statements の一覧はこちらです。

opencv-2.4.6 on iOS 7.0.3 (64 bit)

iOS7と同時にリリースされたiPhone5sは64bitのCPUを搭載しています。
今までのアプリもそのままで動作し、高速ではあるのですが、やはり64bitのネイティブアプリを試してみたくなります。
一つのプロジェクトで構成されていれば簡単に出来てしまいますが、外部のフレームワークやライブラリなどを使っている場合にはそれらが64bit対応していなければなりません。

そこでまずOpenCVの64bit対応を試してみました。

定量的な測定はまだしていませんが、iPhone5sでは32bit版アプリでもかなり高速ですが、64bitアプリにすると更に高速になっている感じがあります。
つい先日発表されたiPad airやRetina版のiPad miniも64bit CPUを登載しているようですので、今後は64 bitアプリも多く出てくるのではないかと思います。

 

●環境

xcode 5.0.1
Darwin Kernel Version 13.0.0 (Mervericks)

platforms/ios/build_framework.py

def build_framework(srcroot, dstroot):
    "main function to do all the work"

    targets = ["iPhoneOS", "iPhoneOS", "iPhoneSimulator"]
    archs = ["armv7", "armv7s", "i386"]
    for i in range(len(targets)):
        build_opencv(srcroot, os.path.join(dstroot, "build"), targets[i], archs[i])
    put_framework_together(srcroot, dstroot)

112,113行目に以下のように編集し、ターゲットとアーキティクチャを追加する。

    targets = ["iPhoneOS", "iPhoneOS", "iPhoneOS", "iPhoneSimulator",  "iPhoneSimulator"]
    archs = ["arm64", "armv7", "armv7s", "i386", "x86_64"]

platforms/ios/cmake/Modules/Platform/iOS.cmake

# Hidden visibilty is required for cxx on iOS
set (CMAKE_C_FLAGS "")
set (CMAKE_CXX_FLAGS "-stdlib=libc++ -headerpad_max_install_names -fvisibility=hidden -fvisibility-inlines-hidden")

set (CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -O3 -fomit-frame-pointer -ffast-math")

if (HAVE_FLAG_SEARCH_PATHS_FIRST)
    set (CMAKE_C_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
    set (CMAKE_CXX_LINK_FLAGS "-Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
endif (HAVE_FLAG_SEARCH_PATHS_FIRST)

45行目を以下のように編集し、最適化オプションを変更する。

    set (CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -fomit-frame-pointer -ffast-math")

modules/legacy/src/dpstereo.cpp

#define CV_IMAX3(a,b,c) ((temp3 = (a) >= (b) ? (a) : (b)),(temp3 >= (c) ? temp3 : (c)))
#define CV_IMIN3(a,b,c) ((temp3 = (a) <= (b) ? (a) : (b)),(temp3 <= (c) ? temp3 : (c)))

このままではマクロでエラーになるため下記のように修正する。

 
#define CV_IMAX3(a,b,c) (temp3 = (a >= b ? (a >= c ? a : (b >= c ? b : c)) : (b >= c ? b : c)))
#define CV_IMIN3(a,b,c) (temp3 = (a <= b ? (a <= c ? a : (b <= c ? b : c)) : (b <= c ? b : c)))

最後に以下のコマンドでフレームワークを作成します。

python ../opencv/platforms/ios/build_framework.py ios

自力でStoryboardをコンパイルしてアプリで読み込む 備忘録

1. ibtoolをカスタムで起動させる。

まず、 “Build Phases” -> “Copy Bundle Resource” から拡張子が “.storyboard” となっているファイルを外します。
次に ”Build Phases” -> “Run Script” に追加して以下のように書き込みます。

ibtool --errors --warnings --notices --minimum-deployment-target 5.0 
       --output-format human-readable-text --compile
        'Sample/Storyboard.storyboardc' 
        'Sample/Storyboard.storyboard'

このコマンドの出力ファイルは Storyboard.storyboardc です。最後に “c” が追加されています。
シュミレータでアプリがインストールされるディレクトリを参照して頂ければ、Storyboardのファイルはこのように最後に “c” の付いたファイルになっている事が確認出来ると思います。
Storyboard.storyboardc を追加します。

また念のために “Build Phases” -> “Copy Bundle Resource” でこのファイルがコピー対象になっている事も確認します。

2. AppMainAppDelegate.mを書き換えます。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    NSString *reqSysVer = @"7.0";
    NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
    if ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending) {
        // iOS 7.0 or later
        UIStoryboard* storyboard;
        NSString* storyboardFile;
        storyboardFile = @"Storyboard";
        storyboard = [UIStoryboard storyboardWithName:storyboardFile bundle:nil];

        self.tabBarController = [storyboard instantiateInitialViewController];

    }
    else {
        // Override point for customization after application launch.
        UIViewController *viewController1 = [[[AppMainFirstViewController alloc] initWithNibName:@"AppMainFirstViewController" bundle:nil] autorelease];
        UIViewController *viewController2 = [[[AppMainSecondViewController alloc] initWithNibName:@"AppMainSecondViewController" bundle:nil] autorelease];
        self.tabBarController = [[[UITabBarController alloc] init] autorelease];
        self.tabBarController.viewControllers = @[viewController1, viewController2];
    }

    _window = [UIWindow alloc];
    _window = [_window initWithFrame:[UIScreen mainScreen].bounds];
    self.window.rootViewController = self.tabBarController;
    [self.window makeKeyAndVisible];

    return YES;
}

この例ではxcodeのabBarのテンプレートから生成されるコードを元に追加変更しています。

3. iOS7への移行を考える

これを書いている時点ではまだWWDCも終わっていません。ですのでまだどんなサプライズがあるかはわかりません。しかし今の時点で出てきている部分だけを見てもUIに関しては大きな変更が加わっています。ある意味、総取っ替えの感もあります。もちろんUIButtonなどの今までのクラスは引き続き使う事は出来ます。ですが外観が大きく変わり、サイズも変わっています。全体の整合性を取るためにはiOS7以前と後ではUIを別の物として考えた方が良いのではないかとさえ思います。
XcodeもDeveloper Previewの段階ですのでまだ使い勝手が良くなるのかもしれません。
しかし既に幾つかのアプリをリリースしてしまっている側としてはマイグレーションの方法を早めに見つけておいた方が良いかと思います。

この例ではNIBを使った今までのコードにStoryboardを加えたわけですが、すでにStoryboardを使って開発しているアプリも、iOS7以前と以後のStoryboardを分けて実装する事も可能となります。

iOS 7 雑感

iOS7がリリースされました。

大幅にUIが変更されましたね。
さっそくXcode 5 Developer Previewをダウンロードして試しています。
Xcodeの完成度としてはまだまだな気がしますが、とりあえずシミュレータを起動して、基本的な動きや外観を見てみました。

事前に噂されていたようにUIは大幅にフラットなイメージに統一されています。エミュレータでは限定したアプリ(Safariや写真、設定など)しかありませんし、バックグラウンドのイメージも無い為、最初の印象は少々貧弱に感じてしまいました。しかしWWDCのKeynoteなどを見ているとけっこう上品な感じに仕上がっているように感じます。

さて、Xcodeを使いながら色々試しています。
ハッキリ言って「困った…」と感じています。

UIButtonやUIToolbarなど、標準のコントロールもフラットでシンプルなものになってしまいましたが、それに伴いサイズも変わってしまいました。
既にリリース済みのアプリをシミュレータで動かした所、レイアウトの変更を強く感じます。
また標準のUIがフラットになったため、今まで使っていたPNGなどによる立体的なボタンの画像などもiOS7用に作り直す必要があるかもしれません。
もちろんiOS6以前も対応とする場合には、同じiPhoneでも2種類のUIを用意しなければならないかもしれません。
iOS6ではNSLayoutConstraintなどがあるため比較的対応がし易いかもしれませんが、それ以前もとなると…

もう一つ気になったのが、iOS7では「レイアを積み重ねる」というコンセプトらしく、どうもステータスバーもそのレイアの一つになっているようです。今まではステータスバーを表示させるとその高さ分はアプリケーションの関知出来ないエリアでした。
ですがiOS7ではどうもその領域にもアプリケーションが描画する事ができて、その上にステータスバーが半透明でレイアを重ねるという形になるような気がします。
するとステータスバーを表示しているアプリは全てそれに沿ったレイアウトに変更しなければならなくなるかも知れません。たかだか20ポイント分なのですが、上部にギリギリまで何かを描画しているような場合には気を付けなければならないかもしれません。

朗報は、xcodeにおいてstoryboardを使ってレイアウトする場合に、下記画面の右側でUIをiOS7用かiOS6.1以前用かを切り替える事が出来るようになっているようです。

 

 

AdMobのカスタムイベント(i-mobile用)

AdMobのカスタムイベントを作ってみました。

 

     1	//
     2	//  IMobileCustomEventBanner.h
     3	//
     4	//  Created by Keiichi Okamoto on 2013/05/09.
     5	//  Copyright (c) 2013 FuturesVision. All rights reserved.
     6	//
     7
     8	#import <UIKit/UIKit.h>
     9	#import "GADCustomEventBanner.h"
    10	#import "GADCustomEventBannerDelegate.h"
    11	#import "imobileAds/IMobileAdDelegateProtocol.h"
    12	#import "imobileAds/IMAdWhirlBannerView.h"
    13
    14
    15	@interface IMobileCustomEventBanner :NSObject 
    16
    17	@property (nonatomic, retain) IMAdWhirlBannerView *imAdView;
    18
    19	@end

 


 

     1	//
     2	//  IMobileCustomEventBanner.m
     3	//
     4	//  Created by Keiichi Okamoto on 2013/05/09.
     5	//  Copyright (c) 2013 FuturesVision. All rights reserved.
     6	//
     7
     8	#import "IMobileCustomEventBanner.h"
     9
    10
    11	@implementation IMobileCustomEventBanner
    12
    13	@synthesize delegate = __delegate;
    14	@synthesize imAdView = __imAdView;
    15
    16
    17	- (void)requestBannerAd:(GADAdSize)adSize
    18	              parameter:(NSString *)serverParameter
    19	                  label:(NSString *)serverLabel
    20	                request:(GADCustomEventRequest *)request {
    21
    22	    NSString* val;
    23	    int publisherId = 0;
    24	    int mediaId = 0;
    25	    int spotId = 0;
    26	    NSArray* array;
    27	    array = [serverParameter componentsSeparatedByString:@","];
    28	    @try {
    29	        val = [array objectAtIndex:0];
    30	        publisherId = [val integerValue];
    31	        val = [array objectAtIndex:1];
    32	        mediaId = [val integerValue];
    33	        val = [array objectAtIndex:2];
    34	        spotId = [val integerValue];
    35	    }
    36	    @catch (NSException *exception) {
    37	        return;
    38	    }
    39	
    40	    CGRect frame;
    41	    frame = CGRectMake(0, 0, kIMAdViewDefaultWidth, kIMAdViewDefaultHeight);
    42	    
    43	    __imAdView = [IMAdWhirlBannerView imAdWhirlBannerViewWithFrame:frame
    44	                                                      withDelegate:self
    45	                                                          testMode:NO];
    46	    [__imAdView retain];
    47	    [__imAdView setWithPublisherId:publisherId
    48	                           mediaId:mediaId
    49	                            spotId:spotId
    50	                          testMode:NO];
    51	    [__imAdView start];
    52	
    53	}
    54	
    55	- (void)dealloc
    56	{
    57	    self.delegate = nil;
    58	    [__imAdView release];
    59	
    60	    [super dealloc];
    61	}
    62	
    63	
    64	#pragma IMobileAdDelegate
    65	
    66	- (void)imAdViewDidFinishReceiveAd:(IMobileAdView *)imobileAdView {
    67	    CGRect frame;
    68	    frame = imobileAdView.frame;
    69	    [self.delegate customEventBanner:self didReceiveAd:imobileAdView];
    70	}
    71	
    72	- (void)imAdViewDidFailToReceiveAd:(IMobileAdView *)imobileAdView {
    73	    [self.delegate customEventBanner:self didFailAd:nil];
    74	}
    75	@end

 

 

AdMobのメディエーション

AdMobがメディエーション機能の提供を始めるにあたって、AdWhirlのメディエーション機能が終息していくようなので広告を使う新規アプリや既存のアプリのメディエーションをAdMobに移行するように作業をしています。
そこで新たにi-mobileもインプリしようといていたのですが、どうでもいいトコでハマってしまいました。

AdMobのメディエーションの実装は非常に簡単で、アプリ側ではGADBannerViewを
インスタンス化してGADRequestを付けて

        [self.gadBannerView loadRequest:self.gadRequest];

としてやるだけです。

しかしハマってしまったのは

            gadBannerView = [gadBannerView initWithAdSize:kGADAdSizeBanner];

 

初期化メソッドを呼ぶ際にバナーのサイズを指定してやるのですが、ここでkGADAdSizeSmartBannerPortrait等を指定してやると、どうやらAdMobとiAdくらいしか表示してくれない…

https://developers.google.com/mobile-ads-sdk/docs/admob/mediationにも

Note: AdMob Ad Network Mediation does not fully support Smart Banners currently.

と書いてはあるのですが、どうやら殆どサポートされていないようです。
誤って設定すると、AdMobのメディエーションのレポート画面を見てもi-mobile等は設定した比率に合わせてリクエストは来ているのですが、impressionがゼロという状況になってしまいます。

Portraitの時はkGADAdSizeBannerを指定するべきです。
Landscapeの時は?どうやらPortrait用の広告は諦めるか、AdMobとiAdのみを表示するかと言う事しか無いようです。
元々、i-mobileは320×50しかサポートしていないトコに問題があるのですが、そういう場合には320×50の広告をセンタリングして表示するとか…

その他、 gadBannerView.rootViewController で指定すべきUIViewControllerにも何だか癖がありそう…

AdWhirlはオープンソースになっていたので、ある程度は自分で調整出来たのですが、AdMobはクローズドなので面倒です。
後は自分でCustomEventをハンドリングするようにしなければいけない?
面倒だなぁ。

Blender → SIO2 (備忘録)

Blenderで作った3DモデルをSIO2に持って行く際に、Blender上でattache出来たと思っていたTexture (Material)をSIO2側で実行すると上手く行かない事がありました。その際に注意すべき点を備忘録として書き残しておきます。

  • SIO2にTextureを持って行く為にはBlender上でUVマップにUnwrapしておく必要があります。
  • Blenderではメッシュは全てQuadで作成されます。これはOpenGLの仕様の違いだと思います。OpenGL ESではTriangleでレンダリングして行く必要があるため各オブジェクトはExportする前に変換しておく必要があります。
    1. オブジェクトを選択する
    2. Edit Modeに入る。
    3. Meshメニューから “Faces” -> “Quad to Tri” を選び実行する。

    以上

 

 

AdWhirlでRotation (備忘録)

久しぶりにAdWhirlを経由した広告モデルのアプリを書いています。
以前のiRulerはLandscapeのみのUIでしたが今回のアプリではPortraitもサポートしてRotationを前提としています。
AdWhirlのAPIとしてはRotationした場合には下記のAPIを呼ぶ事になっています。


[AdWhirlView rotateToOrientation:(UIInterfaceOrientation)orientation]

iAdは全然大丈夫なのですが、その他の広告を表示しようとするとRotationが上手く行かない場合があります。

調べてみたのですが単純でした。
このメソッドは各ネットワークアダプタの同名のメソッドを呼ぶのですが、例えばAdMob用のネットワークアダプタはこのメソッドが実装されておらず、親クラスの何もしないメソッドが呼ばれていました。

ですので解決策としてはAdMob用のネットワークアダプタでメソッドをオーバラードしてやれば良いだけです。

AdWhirlAdapterGoogleAdMobAds.m

-(void)rotateToOrientation:(UIInterfaceOrientation)orientation {

    GADBannerView* adMobView;
    adMobView = (GADBannerView*)adNetworkView;

    switch (orientation) {
        case UIInterfaceOrientationPortrait:
        case UIInterfaceOrientationPortraitUpsideDown:
            adMobView.adSize = kGADAdSizeSmartBannerPortrait;
            break;
        case UIInterfaceOrientationLandscapeLeft:
        case UIInterfaceOrientationLandscapeRight:
            adMobView.adSize = kGADAdSizeSmartBannerLandscape;
            break;
        default:
            break;
    }
}

UIImageからOpenGLのテクスチャーを作る (備忘録)

OpenGL ESは非常に柔軟で強力なAPIを提供してくれています。
しかしその一方でOpen GL ESが非常に柔軟過ぎるが為にどこから取っ付いて良いか分からなくなってしまうのが、その移行への障害になっているように思います。

iOSではUIImageを始めとして非常に使い勝手の良い画像処理用のクラスが用意されています。しかしそれをOpenGLで使おうとすると画像フォーマットの変換やCPUとGPU間のメモリー転送等、考慮しなければいけない課題が多いように思います。

OpenGLで提供されている画像イメージはTextureの概念に拘束されます。従って、iOSで一般的に使用されているUIImageやCGImageとTextureの間の画像フォーマット変換とメモリーの転送の効率は非常に重要な問題になると思います。

調べているうちにiOS5からは便利なクラスが提供されていました。

GLKTextureLoader

このクラスを使用するとファイル上のイメージデータやUIImage(CGImageに変換した後)をかなり効率的にOpenGL ESのTextureに変換出来ます。

UIImage* uiImage;
CGImage* cgImage;
uiImage = [UIImage imageNamed:@"xxxx.png"];
cgImage = uiImage.CGImage;

NSMutableDictionary* dic;
NSError* error;

dic = [NSMutableDictionary alloc];
dic = [dic initWithCapacity:0];
[dic setObject:[NSNumber numberWithBool:YES] forKey:GLKTextureLoaderApplyPremultiplication];
[dic setObject:[NSNumber numberWithBool:NO] forKey:GLKTextureLoaderGenerateMipmaps];
[dic setObject:[NSNumber numberWithBool:NO] forKey:GLKTextureLoaderOriginBottomLeft];
[dic setObject:[NSNumber numberWithBool:NO] forKey:GLKTextureLoaderGrayscaleAsAlpha];

textureInfo = [GLKTextureLoader textureWithCGImage:cgImage options:dic error:&error];
[textureInfo retain];

他のビットマップイメージを直接アクセスしながら変換する方法と、パフォーマンスの違いがどの程度あるのかは、今のところ測定していませんが、Appleから直接提供されているAPIですのである程度は信頼出来るのではないかと思います。

最終的に取得される GLKTextureInfo の nameプロパティでTextureにアクセス出来ます。

Build OpenCV for armv7s

現在のOpenCVは正式な環境では armv7s 対応のライブラリを作ってくれません。
そのため、アプリをビルドする場合にはプロジェクトから armv7s を外さなければなりません。
これからアプリを出そうという際には最新のアーキティクチャを使えないのはとうも納得が行きません。
そこでいろいろ調べてみました。
私はcmakeに関しては全くの素人で、これから知識を深めたいというわけでもありませんので、「とりあえずビルドできる」を目標にしています。

結論から言うと
opencv/ios/cmake/Toolchains/Toolchain-iPhoneOS_Xcode.cmake を編集してやります。

essage (STATUS “Setting up iPhoneOS toolchain”)
set (IPHONEOS TRUE)

# Standard settings
set (CMAKE_SYSTEM_NAME iOS)
# Include extra modules for the iOS platform files
set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} “${CMAKE_CURRENT_SOURCE_DIR}/ios/cmake/Modules”)

# Force the compilers to gcc for iOS
include (CMakeForceCompiler)
CMAKE_FORCE_C_COMPILER (gcc gcc)
CMAKE_FORCE_CXX_COMPILER (g++ g++)

set (CMAKE_C_SIZEOF_DATA_PTR 4)
set (CMAKE_C_HAS_ISYSROOT 1)
set (CMAKE_C_COMPILER_ABI ELF)
set (CMAKE_CXX_SIZEOF_DATA_PTR 4)
set (CMAKE_CXX_HAS_ISYSROOT 1)
set (CMAKE_CXX_COMPILER_ABI ELF)

SET (CMAKE_OSX_ARCHITECTURES “armv7” “armv7s”)

# Skip the platform compiler checks for cross compiling
set (CMAKE_CXX_COMPILER_WORKS TRUE)
set (CMAKE_C_COMPILER_WORKS TRUE)

赤で色分けされた行を追加してやるだけです。
とりあえずテストプロジェクトでは iPhone 5用にビルドしても問題なく armv7s でコンパイルされており、リンカーでもエラーは出ません。

 

ただもう一つ問題が…

結論から言うと、armv7でもarmv7sでもパフォーマンスはあまり変わりませんでした(処理にも依存すると思うのですが)。

で、OpenCVをビルドした時のログを見てみると全くオプティマイズされていないようなのです。

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xct oolchain/usr/bin/clang -x c -arch armv7s -fmessage-length=0 -Wno-trigraphs -fpascal-strings -O0 -Wno-missing-field-initializers -Wno-missing-prototy pes -Wno-return-type -Wformat -Wno-missing-braces -Wparentheses -Wswitch – Wno-unused-function

 

これは多分armv7でビルドした際も同じだと思います。OpenCVのソース内までデバッガーで追って行くつもりは無いのでやはり “-Os” でコンパイルしたいです。
またOpenCVのcmakeと格闘です。
今回の事で少しだけcmakeも理解(?)出来た気もしますので時間のある時に試してみます。