読者です 読者をやめる 読者になる 読者になる

酢ろぐ!

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

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

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

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

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

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

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

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

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

/// <summary>
/// Graies the scale.
/// </summary>
/// <returns>The scale.</returns>
/// <param name="srcImage">Source image.</param>
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に設定して表示するコードは以下の通りです。

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を使ってアプリ開発する際に逆引きとしてお使いください。