【iPhone】カメラアプリ系の画像処理をする

Posted by: daichi  /  Category: iphone開発

久しぶりに開発のエントリを。
これまで画像処理系のプログラムを書いたことがなかったので調べてみた。

カメラアプリ系の画像処理を行うには

  1. UIImageのピクセルデータへアクセスする方法を知る
  2. 画像処理アルゴリズムを知る
が必要。

UIImageのピクセルデータへアクセスする

以下のサイトがとても参考になった。
sonson@Picture&Software – [iPhone SDK] UIImageでピクセルを扱う
WWDC2009_Kazuki_Endo

UIImageからCGImageを取得してごにょごにょするとピクセルデータにアクセスできる様子。
上のサイトでは画像をグレーにするサンプルコードを公開してくれていたので、それを丸ごと拝借してみる。
いちいち変換クラスを用意するのは面倒なので、カテゴリを使ってUIImageを拡張してみる。

UIImageAddtion.h


?View Code OBJECTIVE-C
#import <UIKit/UIKit.h>
@interface UIImage (Effect)
@end

UIImageAddtion.m


?View Code OBJECTIVE-C
#import "UIImageAddtion.h"
 
@implementation UIImage (Effect)
-(UIImage*)grayImage {
	[self grayImage:CGRectMake(0, 0, 320, 480)];
}
 
-(UIImage*)grayImage:(CGRect)rect {
	// CGImageを取得する
	CGImageRef cgImage;
	cgImage = self.CGImage;
 
	// 画像情報を取得する
	size_t width;
	size_t height;
	size_t bitsPerComponent;
	size_t bitsPerPixel;
	size_t bytesPerRow;
	CGColorSpaceRef colorSpace;
	CGBitmapInfo bitmapInfo;
	bool shouldInterpolate;
	CGColorRenderingIntent intent;
	width = CGImageGetWidth(cgImage);
	height = CGImageGetHeight(cgImage);
	bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
	bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
	bytesPerRow = CGImageGetBytesPerRow(cgImage);
	colorSpace = CGImageGetColorSpace(cgImage);
	bitmapInfo = CGImageGetBitmapInfo(cgImage);
	shouldInterpolate = CGImageGetShouldInterpolate(cgImage);
	intent = CGImageGetRenderingIntent(cgImage);
 
	// データプロバイダを取得する
	CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage);
 
	if (rect.size.width + rect.origin.x  > width) {
		rect.size.width = width - rect.origin.x;
	}
	if (rect.size.height + rect.origin.y > height) {
		rect.size.height = height - rect.origin.y;
	}
 
 
	// ビットマップデータを取得する
	CFDataRef data = CGDataProviderCopyData(dataProvider);
	UInt8* buffer = (UInt8*)CFDataGetBytePtr(data);
 
	// ビットマップに効果を与える
 
	NSUInteger i, j;
	for (j = rect.origin.y ; j < rect.origin.y + rect.size.height; j++)
	{
		for (i = rect.origin.x; i < rect.origin.x + rect.size.width; i++) 
		{
			// ピクセルのポインタを取得する
			UInt8* tmp = buffer + j * bytesPerRow + i * 4;
 
			// RGBの値を取得する
			UInt8 r, g, b;
			r = *(tmp + 3);
			g = *(tmp + 2);
			b = *(tmp + 1);
 
			// 輝度値を計算する
			UInt8 y = (77 * r + 28 * g + 151 * b) / 256;
 
			// 輝度の値をRGB値として設定する
			*(tmp + 1) = y;//b
			*(tmp + 2) = y;//g
			*(tmp + 3) = y;//r
		}
        }
 
	// 効果を与えたデータを作成する
	CFDataRef effectedData;
	effectedData = CFDataCreate(NULL, buffer, CFDataGetLength(data));
 
	// 効果を与えたデータプロバイダを作成する
	CGDataProviderRef effectedDataProvider;
	effectedDataProvider = CGDataProviderCreateWithCFData(effectedData);
 
	// 画像を作成する
	CGImageRef effectedCgImage = CGImageCreate(
		 width, height, 
		 bitsPerComponent, bitsPerPixel, bytesPerRow, 
		 colorSpace, bitmapInfo, effectedDataProvider, 
		 NULL, shouldInterpolate, intent);
 
        UIImage* effectedImage = [[[UIImage alloc] initWithCGImage:effectedCgImage] autorelease];
 
	// 作成したデータを解放する
	CGImageRelease(effectedCgImage);
	CFRelease(effectedDataProvider);
	CFRelease(effectedData);
	CFRelease(data);	
 
	return effectedImage;
}

上の二重ループの中でiが画像の横方向、jが画像の縦方向のピクセルに対応していて、1ピクセルずつ処理してる。

これで[image grayImage]のような形でグレー画像を生成できるようになる。
実行結果はこんな感じ。

加工前加工後

さらにCGRectでグレーにする部分を選べるようにしたので、UIImageViewやらでタッチやムーブ時にグレーにする処理を書いてみると

?View Code OBJECTIVE-C
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
	[self process:touches withEvent:event];
}
 
- (void) touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
	[self process:touches withEvent:event];
}
 
- (void) process:(NSSet *)touches withEvent:(UIEvent *)event {
	CGPoint pt = [[touches anyObject] locationInView:self];
	int margin = 10;
	self.image = [self.image grayImage:CGRectMake(pt.x - margin/2, pt.y - margin/2, margin, margin)];	
}

こんな感じでオイルキャンバスチックな画像ができた。

タッチ

ん。これはおもしろい。

画像処理アルゴリズムを調べてみる

調べてみるといろいろ参考になるサイトが見つかった。
プログラミング – [物理のかぎしっぽ]
画像処理におけるアルゴリズム
TEOライブラリによる画像処理プログラミングガイド

この中でも簡単そうな平均化フィルタを実装してみる。
平均化フィルタを作る – [物理のかぎしっぽ]

平均化フィルタは対象ピクセルの周辺の値の平均値を求めて、対象ピクセルの値を決める方法。これで単純な「ぼかし」が実現できる。

例によってカテゴリに追加。

UIImageAddtion.m


?View Code OBJECTIVE-C
#include <math.h>
 
- (UIImage*)averageFilter{
    [self averageFilter:1];
}
 
- (UIImage*)averageFilter:(NSInteger)pixel{
	// CGImageを取得する
	CGImageRef cgImage;
	cgImage = self.CGImage;
 
	// 画像情報を取得する
	size_t width = CGImageGetWidth(cgImage);
	size_t height = CGImageGetHeight(cgImage);
	size_t bitsPerComponent = CGImageGetBitsPerComponent(cgImage);
	size_t bitsPerPixel = CGImageGetBitsPerPixel(cgImage);
	size_t bytesPerRow = CGImageGetBytesPerRow(cgImage);
	CGColorSpaceRef colorSpace = CGImageGetColorSpace(cgImage);
	CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(cgImage);
	bool shouldInterpolate = CGImageGetShouldInterpolate(cgImage);
	CGColorRenderingIntent intent = CGImageGetRenderingIntent(cgImage);
 
	// データプロバイダを取得する
	CGDataProviderRef dataProvider = CGImageGetDataProvider(cgImage);
 
	// ビットマップデータを取得する
	CFDataRef data = CGDataProviderCopyData(dataProvider);
	UInt8* buffer = (UInt8*)CFDataGetBytePtr(data);
 
	// ビットマップに効果を与える
	NSUInteger i, j;
 
	for (j = pixel ; j < height -pixel; j++)
	{
		for (i = pixel ; i < width -pixel; i++) 
		{
			// ピクセルのポインタを取得する
			UInt8* tmp = buffer + j * bytesPerRow + i * 4;
 
			// RGBの値を取得する
			UInt8 r, g, b;
			r = *(tmp + 3);//b
			g = *(tmp + 2);//g
			b = *(tmp + 1);//r
 
			Float32 sumr, sumg, sumb;
			sumr = 0;
			sumg = 0;
			sumb = 0;
			for (int k=-pixel;k<=pixel;k++){
				for (int l=-pixel;l<=pixel;l++){
					UInt8* t = buffer + (j+k) * bytesPerRow + (i+l) * 4;
					sumr += *(t + 3);
					sumg += *(t + 2);
					sumb += *(t + 1);
				}
			}
 
			*(tmp + 1) = sumb/pow((1+(2*pixel)),2);//b
			*(tmp + 2) = sumg/pow((1+(2*pixel)),2);//g
			*(tmp + 3) = sumr/pow((1+(2*pixel)),2);//r
		}
        }
 
	// 効果を与えたデータを作成する
	CFDataRef effectedData = CFDataCreate(NULL, buffer, CFDataGetLength(data));
 
	// 効果を与えたデータプロバイダを作成する
	CGDataProviderRef effectedDataProvider = CGDataProviderCreateWithCFData(effectedData);
 
	// 画像を作成する
	CGImageRef effectedCgImage = CGImageCreate(
		width, height, 
		bitsPerComponent, bitsPerPixel, bytesPerRow, 
		colorSpace, bitmapInfo, effectedDataProvider, 
		NULL, shouldInterpolate, intent);
 
    UIImage* effectedImage = [[[UIImage alloc] initWithCGImage:effectedCgImage] autorelease];
 
	// 作成したデータを解放する
	CGImageRelease(effectedCgImage);
	CFRelease(effectedDataProvider);
	CFRelease(effectedData);
	CFRelease(data);
 
	return effectedImage;	
}

これで[image averageFilter]を呼ぶとぼかしがかかる。
5回実行してみた。

加工前ぼかし1ぼかし2ぼかし3ぼかし4ぼかし5

これはいろんなアルゴリズムを試してみるとおもしろそう。

タグ: iphone, カメラ, 画像処理

関連する投稿

One Response to “【iPhone】カメラアプリ系の画像処理をする”

  1. Wonwoo Park Says:

    Hi,

    Nice 2 Meet U

    Thanks for nice code.

    But, With Above image processing code,

    Only Jpeg image is good work, But In case of Png image, There are vertical line is occurred.

    What's wrong with the code?

    Thanks ant any rate~

コメント

Additional comments powered by BackType

Get Adobe Flash playerPlugin by wpburn.com wordpress themes