酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Xamarin.iOSでUImageからピクセル配列を取得して画像処理する

Xamarin.iOSを使って、画像処理をしてみましょう。

基本的にはObjective-CのコードをそのままC#への置き換えする形となります。本記事では、過去にWindowsストアアプリ向けに実装したグレースケール変換処理を移植してみましょう。

UIImageオブジェクトからピクセル配列を取得するには、以下の手順で処理を実行する必要があります。

+UIImageを構成するCGImageを取得する +データプロバイダーを介してピクセルデータのバッファのポインタを取得する +ピクセルデータのバッファをbyte配列のバッファにコピー

取得したbyte配列のバッファにグレースケール処理をおこないます。逆にbyte配列からUIImageオブジェクトに変換する場合には、以下の手順で処理をおこないます。

+画像処理したbyte配列のバッファを元にデータプロバイダーを作成 +データプロバイダーを介してCGImageを作成 +CGImageからUIImageを作成

この手順をC#で置き換えて実装してみましょう。

|cs| ///

/// Graies the scale. /// /// The scale. /// Source image. UIImage GrayScale (UIImage srcImage) { var cgImage = srcImage.CGImage;

int width = cgImage.Width;
int height = cgImage.Height;
int bitsPerComponent = cgImage.BitsPerComponent;
int bitsPerPixel = cgImage.BitsPerPixel;
int bytesPerRow = cgImage.BytesPerRow;
var colorSpace = cgImage.ColorSpace;
var bitmapInfo = cgImage.BitmapInfo;
var shouldInterpolate = cgImage.ShouldInterpolate;
var intent = cgImage.RenderingIntent; 

// データプロバイダを取得する
byte[] bytes;
using (var dataProvider = cgImage.DataProvider)
using (var data = dataProvider.CopyData())
{
    // ビットマップデータを取得する
    var butter = data.Bytes;
    bytes = new byte[data.Length];
    System.Runtime.InteropServices.Marshal.Copy(
        butter, bytes, 0, bytes.Length);
}

// グレースケール処理をおこなう
int pixelCount = width * height;
for (int i = 0; i < pixelCount; i++)
{
    var index = i * 4;

    // 単純平均法で輝度を求める
    var sum = bytes[index + 0] + bytes[index + 1] + bytes[index + 2];
    var y = (double)sum / 3;

    bytes[index + 0] = (byte)Math.Min(255, Math.Max(0, y));
    bytes[index + 1] = (byte)Math.Min(255, Math.Max(0, y));
    bytes[index + 2] = (byte)Math.Min(255, Math.Max(0, y));
    bytes[index + 3] = bytes[index + 3];
}

// 画像処理後のbyte配列を元にデータプロバイダーを作成する
var effectedDataProvider
    = new MonoTouch.CoreGraphics.CGDataProvider(bytes, 0, bytes.Length);

// データプロバイダーからCGImageを作成し、CGImageからUIImageを作成する
var effectedCgImage = new MonoTouch.CoreGraphics.CGImage(
    width, height, 
    bitsPerComponent, bitsPerPixel, bytesPerRow, 
    colorSpace, bitmapInfo, effectedDataProvider, 
    null, shouldInterpolate, intent);
return new UIImage(effectedCgImage);

} ||<

例えば、アプリケーションの起動時(というか画面の表示後)にUIImageViewに表示されている画像をグレースケール変換し、再度UIImageViewに設定して表示するコードは以下の通りです。

|cs| public override void ViewDidAppear (bool animated) { base.ViewDidAppear (animated);

UIImage grayImage = GrayScale(imageView.Image);
imageView.Image = grayImage;

} ||<

画面が表示された瞬間はカラー画像、しばらくして画像処理が完了した後はグレースケール画像が表示されます。

f:id:ch3cooh393:20130322021229p:plain

うーん。コードの共有化が可能なところを整理し直せば、WriteableBitmapEffectorのUIImage版が作成できそうだなぁ。

**サンプルプロジェクト

サンプルプロジェクトをGitHubで公開しています。

005_ImageProcessingSample

  • 関連記事

Xamarin.iOSを使ってアプリ開発する際に逆引きとしてお使いください。