FC2ブログ

頭と尻尾はくれてやる!

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


KerasのImageDataGeneratorで画像データの水増し

3チャンネル画像だとKerasとMPSの計算が合わない
↑この続き。
Kerasで学習のやり直しをすることに。せっかくなので画像データの水増しを試してみた。
TensorFlowでも画像データを増やせる関数などがあったがKerasにもImageDataGeneratorというものがあるようで取り入れてみた。

画像の前処理 - Keras Documentation
↑公式はこちら。

修正部分はこんな感じ↓
from keras.preprocessing.image import ImageDataGenerator

datagen = ImageDataGenerator(
                             rotation_range = 3 , 
                             width_shift_range = 0.1,
                             height_shift_range = 0.1,
                             zoom_range = 0.02,
                             channel_shift_range = 10.0, 
                             )#—(1)

datagen.fit(x_train)#—(2)


#history = model.fit(x_train, y_train, batch_size=batch_size, … #—(3)
history = model.fit_generator(
                              datagen.flow(x_train, y_train, batch_size=batch_size),…#—(4)

↑(1)ImageDataGeneratorで目的に合致するもので使えそうなのを選んで設定。
どのように画像が変化するのかは参考ページなどに。
(2)は内容によっては不要かも。
ImageDataGeneratorを使う前は(3)のfit関数で訓練を実行していたのだけど、(4)の形式に変更。

調子に乗って盛り込みすぎると学習が進まなくなってしまったので、ほどほどがよさそう、、、ほどほどってなんやねん!?将来はそういうのもKerasが良きに計らってくれたらいいのにな。


参考ページ
Kerasによるデータ拡張 - 人工知能に関する断創録
Kerasでデータ拡張(Data Augmentation)後の画像を表示する - Qiita


3チャンネル画像だとKerasとMPSの計算が合わない

画像の判別がCreateMLでダメだったのでKerasでやってみた
↑この続き。
Kerasで画像分類をさせて、その学習結果(重み、バイアス)を出力。それをmacOSアプリのMetal Performance Shadersで同じ計算をさせる。

計算量削減のために画像のRGBの3チャンネル分のデータを使う。するとKerasとMPSで同じ画像を与えても出てくる結果が違ってしまう。

ちなみに4チャンネルの画像を使い、同じようにKerasで学習させて係数をmacOSで使いMPSで計算させると同じ値になる。
よって、学習結果の係数の受け渡し方に問題はないと考えている。

MPSで画像データを与える場合に
- (void)replaceRegion:(MTLRegion)region mipmapLevel:(NSUInteger)level withBytes:(const void *)pixelBytes bytesPerRow:(NSUInteger)bytesPerRow;
↑このメソッドを使うとする(※1)。
//(1)4チャンネルの場合

{
    MTLRegion region = MTLRegionMake2D(0, 0, Width, Height);

    [srcImage.texture replaceRegion:region 
        mipmapLevel:0 
        withBytes:inputs 
        bytesPerRow:sizeof(unsigned char)*Width*NofChannel];
}
↑MPSではこんな感じでデータ列(ここではunsigned char型)をMPSImageのsrcImageのテクスチャに書き込む。
その範囲をMTLRegionで指定したり、1行あたりのバイト数を指定したりする。
これは意図通りに動いた。

問題は次の3チャンネルの場合だ。
//(2)3チャンネルの場合
{
    MTLRegion region = MTLRegionMake2D(0, 0, Width*3/4, Height);

    [srcImage.texture replaceRegion:region 
        mipmapLevel:0 
        withBytes:inputs 
        bytesPerRow:sizeof(unsigned char)*Width*NofChannel];
}
↑今回のケースではMPSImageは4チャンネルが基本(※2)なのだが、データは3チャンネルなのでbytesPerRowは1バイトx3チャンネルx横幅。
これに合わせてコピーする範囲の横幅をピクセル数の3/4倍としている(画像サイズは96x96なので横幅は4の倍数)。

これが、なぜかKerasと計算結果が全然合わない、、、😥

regionの設定が間違っているのか?と思いsrcImage.textureから再度データを取り出して画像を作成して元画像になってるのを確認したり、RGBが入れ替わっているのか?と思いチェックするなど、まあいろいろと調べたのだが、、、降参!

回避策は4チャンネルで学習、予測を行う、、、だ、ださいがまあ動くからいいか、、、なにこの敗北感。
何かあればお教えください。


※1
- (void)replaceRegion:(MTLRegion)region mipmapLevel:(NSUInteger)level slice:(NSUInteger)slice withBytes:(const void *)pixelBytes bytesPerRow:(NSUInteger)bytesPerRow bytesPerImage:(NSUInteger)bytesPerImage;
↑こちらのメソッドもあるが、今回は3チャンネルか4チャンネルなのでスライスは不要。

※2 これはMPSImageオブジェクトのpixel format次第。これはread only。MPSImageオブジェクトを作る時に使うMPSImageDescriptorオブジェクトの設定により自動的に決まるのだと思う。
MPSImageDescriptor *sourceImageDiscriptor = [MPSImageDescriptor imageDescriptorWithChannelFormat:MPSImageFeatureChannelFormatUnorm8 
        width:Width 
        height:Height 
        featureChannels:NofChannel];
↑このようにして作成するとsourceImageDiscriptor.pixelFormatは
MTLPixelFormatRGBA8Unorm
となり、
Ordinary format with four 8-bit normalized unsigned integer components in RGBA order
↑とリファレンスにあるように8ビット符号なし整数のRGBAの値を持つ(4バイト)。

ちなみにMNISTでは白黒画像を使っていたが
MPSImageDescriptor *sourceImageDiscriptor = [MPSImageDescriptor imageDescriptorWithChannelFormat:MPSImageFeatureChannelFormatUnorm8 
width:28 
height:28 
featureChannels:1];
↑このように画像を設定した場合だとpixel formatは
MTLPixelFormatR8Unorm
となり1ピクセルに1バイトの値しか持たない。





NSImage画像をRGBAのデータに展開する

カラー(※1)のNSImage画像(RGBAの4チャンネルのデータを持つ)をMetal Performance Shadersで使うためにデータ化する、というのをしたことがなかったのでデータ化するクラスメソッドを作った。

この手のメソッドは数ヶ月後か数年後かに参考にしたり使い回ししたくなるものなので可能ならクラスメソッドにして自分がすぐにわかるような置き場に置いておくようにしたい(自分への戒め)。

あらかじめその分のメモリは確保しておく必要がある。
// 呼び出し元
{
    unsigned char *toData = calloc(width*height*4, sizeof(unsigned char));
    [MyUtility storeColorImage:image to:toData];

    // (toDataを使う処理)

    free(toData);//—(1)
}

// MyUtilityクラスの実装ファイル
+(void)storeColorImage:(NSImage*)image to:(unsigned char *)toData
{
    size_t width = image.size.width;
    size_t height = image.size.height;
    size_t bitsPerComponent = 8;
    size_t bytesPerRow = width*4;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGImageAlphaInfo bitmapInfo = kCGImageAlphaNoneSkipLast;
    
    CGContextRef context = CGBitmapContextCreate(nil,
                                                 width,
                                                 height,
                                                 bitsPerComponent,
                                                 bytesPerRow,
                                                 colorSpace,
                                                 bitmapInfo);
    
    NSRect imageRect = NSMakeRect(0, 0, width,height);
    NSGraphicsContext *gctx = [NSGraphicsContext graphicsContextWithCGContext:context flipped:NO];
    [NSGraphicsContext setCurrentContext:gctx];
    [image drawInRect:imageRect];
    
    void *data = CGBitmapContextGetData (context);
    
    memcpy(toData, data, width*height*4);
    
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(context);
}
(1)使用後はメモリの解放が必要。そういう事情もあるので
+(unsigned char *)getDataForImage:(NSImage *)image;
という形式にできない(※2)のが少し残念、というかコードの可読性が低下するが仕方ない。


※1 わざわざ”カラー”としているのは、白黒も扱うことがあるのであえてメソッド名などにColorと入れてるだけ
※2 できなくもないけどメモリ解放を忘れてしまう可能性大なので避けてる


Game Centerの認証に成功してるのにお帰りなさい画面が出ない

最近Game Center / GameKitを使うアプリについて調べている。
Game Centerを使う場合、通常アプリと違って事前にやることがある。

Game Center機能を実装する - Qiita

↑こちらのページなどを参考にしつつ、
Developer PortalでApp IDを追加
App Store ConnectでGame Centerを使う
などを設定。

Xcodeで新規のプロジェクトを作成。

まずは認証のチェックをしたい。Game Center対応アプリなら起動直後に画面上部にいつも出てくるあの画面。

Game Centerのおかえりなさい画面

↑このお帰りなさい画面(※)。これを表示させたい。

authenticateHandler - GKLocalPlayer | Apple Developer Documentation
↑リファレンスにあるようなごくシンプルなコードで確認してみた。
認証は成功しているはずなのだが、どういうわけかお帰りなさい画面が出てこない。


自分のiPhone実機で検証をしていたのだが、この時点でアカウントがテスターじゃないとダメなのか?ということでiPhone実機のユーザーを変更、、、
したくないよな、なるべく。
なのでiOSシミュレータでテスターのアカウントでログイン(自動的にGame Centerはオンになっていた)。
そのシミュレータで起動すると

Game CenterのWelcome back画面

↑ちゃんとWelcome back、お帰りなさいが出た!
つまりGame Centerアプリ開発するなら、デバイスはテスト用のアカウントを使ってログインしておくべき、ということか。


※ このスクショは最近やってる「1000m Zombie Escape!」というゲーム画面の上部。



===== 修正・追記(12/11) =====

Game CenterのWelcome back画面

↑勘違いだったようで、テストアカウントではない実機デバイスでもお帰りなさい画面が出た。

結論:テスト用アカウントじゃなくてもお帰りなさい画面は出る




Duet Display2にバージョンアップ

iPadをセカンドディスプレイのように使えるアプリとしてDuet Displayを使っている。
Xcodeでコードを書く時に左右に分割して右側に参考にしたいファイルを表示しつつ、左側で書くことが多い。時々あるんだけど、さらにもう一つのファイルを見たいという場合にDuet Displayを起動する。
という感じで重宝しているDuet Displayなんだが、、、

Duet Display 2はハードウェアアクセラレーションを使って効率アップ | TechCrunch Japan
↑今朝こういう記事を見た。
自分が使っているDuet Displayのバージョンを確認したところiPadのアプリ側は最新の2.0.8だ。
Mac側のをDuet Displayの設定にある「アップデートの有無を確認」で確認すると、、、

Duet Displayのバージョン確認画面

↑1.7.1.4で最新版らしい、、、?最新版だけど2以上じゃない?
よくわからないので、公式ページで最新版をダウンロード。
すると2.0.3.8になった。


少し使ってみたけど、処理速度の改善というのは正直よくわからない。そもそも表示させてるだけ、というのが多いし。
もしかして起動時に自分でAirPlayでDuet iOSを選択する、みたいな作業をしなくてよくなった?というのはいいな。

Duet Display2を使ったところ

↑デフォでTouch Barがオンになってる。使いづらそうな環境なので消したけど。
不思議なことにiPad側の画面の壁紙が昔のままだ。これは仕様なのか、変更方法が不明。
あと終了時に3クリックじゃなく2クリックにして欲しいなんて思ったりするが、、、
なんやかんやでいいアプリだ。



macOS Mojave 10.14.1
iOS 12.1




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

FC2Ad