久しぶりに開発のエントリを。
これまで画像処理系のプログラムを書いたことがなかったので調べてみた。
カメラアプリ系の画像処理を行うには
- UIImageのピクセルデータへアクセスする方法を知る
- 画像処理アルゴリズムを知る
UIImageのピクセルデータへアクセスする
以下のサイトがとても参考になった。sonson@Picture&Software – [iPhone SDK] UIImageでピクセルを扱う
WWDC2009_Kazuki_Endo
UIImageからCGImageを取得してごにょごにょするとピクセルデータにアクセスできる様子。
上のサイトでは画像をグレーにするサンプルコードを公開してくれていたので、それを丸ごと拝借してみる。
いちいち変換クラスを用意するのは面倒なので、カテゴリを使ってUIImageを拡張してみる。
UIImageAddtion.h
#import <UIKit/UIKit.h> @interface UIImage (Effect) @end |
UIImageAddtion.m
#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やらでタッチやムーブ時にグレーにする処理を書いてみると
- (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
#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回実行してみた。






これはいろんなアルゴリズムを試してみるとおもしろそう。
関連する投稿
One Response to “【iPhone】カメラアプリ系の画像処理をする”
コメント
Additional comments powered by BackType

6月 18th, 2010 at 5:48 PM
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~