2012年2月15日水曜日

iPhoneで画像処理 - 2


iPhoneで画像処理

続いて、画素単位へのアクセスを行います。
案外簡単な作業だと思うんですが、この処理を行うにはいろいろと手続きを踏まないと行けません。

UIImage型から画素単位までアクセスし、再びUIImageに戻す手続きは次のデータ型を経ます。

UIImage → CGImageRef → CFDataRef → CGImageRef →UIImage

実に面倒です。おまけに、CGImageRefからCFDataRefへ変換する際にはmallocが発生するので、
処理時間もかかってしまいます。

では具体的にコードを書いて行きます。

CGImageRef cg = inputImage.image.CGImage;
size_t height = CGImageGetHeight(cg);
size_t width = CGImageGetWidth(cg);
size_t bytesPerRow = CGImageGetBytesPerRow(cg);
size_t bitsPerPixel = CGImageGetBitsPerPixel(cg);
CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(cg));
UInt8* pixel = (UInt8*)CFDataGetBytePtr(dataRef);

// 処理内容
for( int h = 0; h < height; ++h){
for (int w = 0; w < width; ++w) {
UInt8* buf = pixel + h * bytesPerRow + w * bitsPerPixel / 8;
UInt8 r, g, b;
// rgb値取得
r = *(buf + 0);
g = *(buf + 1);
b = *(buf + 2);
// rgbを入れ替えて値出力
*(buf+0) = b;
*(buf+1) = g;
*(buf+2) = r;
}
}

CFDataRef dst = CFDataCreate(NULL, pixel, CFDataGetLength(dataRef));
CGDataProviderRef dstDataProvider = CGDataProviderCreateWithCFData(dst);
CGImageRef dstCGImage = CGImageCreate(width, height, CGImageGetBitsPerComponent(cg),
  bitsPerPixel, bytesPerRow,CGImageGetColorSpace(cg),
  CGImageGetBitmapInfo(cg),dstDataProvider, NULL,
  CGImageGetShouldInterpolate(cg),CGImageGetRenderingIntent(cg));
UIImage *dstUIImage = [UIImage imageWithCGImage:dstCGImage];
outputImage.image = dstUIImage;

CGImageRelease(dstCGImage);
CFRelease(dst);
CFRelease(dataRef);
CFRelease(dstDataProvider);


UIImageからCGImageRefへの変換、アクセスは
CGImageRef cg = inputImage.image.CGImage;

で可能です。
その後、画像の幅、高さ、一行当たりのバイト数、一画素当たりのビット数などを取得します。
CFDataRef dataRef = CGDataProviderCopyData(CGImageGetDataProvider(cg));
UInt8* pixel = (UInt8*)CFDataGetBytePtr(dataRef);

では、CGImage型からCFDataRef型へ変換を行います。CFDataは多分汎用的に扱えるデータ型です。
画像以外にも文章、音声でも可能です。(多分)
CFDataGetBytePtrでその先頭アドレスを取得します。

処理はよくやるforループを二重にまわせば出来ます。
画素(h,w)のアドレスにアクセスするには

UInt8* buf = pixel + h * bytesPerRow + w * bitsPerPixel / 8;


でできると思います。最後の /8 はビット→バイトの変換です。あんまりマジックナンバーは使わない方がいいんだろうけど。

CFDataからUIImageへの変換は以下の手続きを踏んで行います。

CFDataRef dst = CFDataCreate(NULL, pixel, CFDataGetLength(dataRef));
CGDataProviderRef dstDataProvider = CGDataProviderCreateWithCFData(dst);
CGImageRef dstCGImage = CGImageCreate(width, height, CGImageGetBitsPerComponent(cg),
  bitsPerPixel, bytesPerRow,CGImageGetColorSpace(cg),
  CGImageGetBitmapInfo(cg),dstDataProvider, NULL,
  CGImageGetShouldInterpolate(cg),CGImageGetRenderingIntent(cg));
UIImage *dstUIImage = [UIImage imageWithCGImage:dstCGImage];

CGImageCreate関数が実に引数が多くてどうにかならんのかと思いますが、それは仕方ないんでしょうね。
それぞれの行の変換の流れを追うと、
  1. ポインタpixelから始まるメモリのCFDataRefを作る
  2. CFDataからCGDataProviderRefを作る
  3. CGDataProviderRefからCGImageRefを作る
  4. CGImageからUIImageを作る

という流れです。もっと簡便にしてくれたらいいのにと思いますが。。。


残りの部分では,UIImageViewに貼付けたり、後処理としてリリースをしたりしています。
CFなんとか、CGなんとかで始まる関数や型はC言語ベースで、自動的にリリースされないのでご注意ください。

微分フィルタを作ってみたらこんな感じでした。




わかりづらかったから微分値を4倍してます。

0 件のコメント:

コメントを投稿