ニャオニャオ21世紀

雑記と、MSXぽいアプリ開発と、ゲーム他の様々なアイデアを公開しています

リアルタイム加工カメラアプリの作り方 iOS

 

今回は、リアルタイムに加工し8bit風画像にする8bit world cameraのようなリアルタイム加工カメラ(iOS)の作り方を共有したいと思います。

 

8bit world cameraをリリースして1ヶ月以上経ちました。

様々なサイト様に取り上げていただきまして大変感謝しております!!

f:id:nyaonyaokun:20141228030600p:plain

f:id:nyaonyaokun:20141228030609p:plain

 

ダウンロード数の方は1万ダウンロードもいかないという大苦戦ですが、お使い頂いた方には大変喜んでいただいているので、リリースして良かったです。

 

比較的マイナーな部類に入るアプリだから仕方ないといえば仕方ないのかも。

 

このアプリはネット上の情報などを参考にして作りました。ありがとうございます。

私もiOSのリアルタイム処理カメラの作り方を共有したいと思います。

なお、今回はopenCVは使っておりません。

カメラの各ドットのRGB情報の数値を弄ることでリアルタイム加工をしております。多少面倒くさいですが、openCVのフィルターにないものも作れますので…(openCVを使ったアプリも出したいなぁ。特徴点とか使って!)

 

こちらのサイト様を参考に8bit world cameraを作りました。


iOSのカメラ機能を使う方法まとめ【13日目】 | Developers.IO

 

3つカメラアプリを作る方法があるのですが、最後の

3.AVFoundation FrameworkのAVCaptureVideoDataOutputを使用する方法

を使用しております。

(なお、このソースコードAudioServicesPlaySystemSound(1108);

コメントアウトするだけで、静音カメラが出来上がります。簡単ですね!)

 

はじめてこのソースコードを見たら、どこにカメラ画像の加工をいれればいいかわからないと思います。

私はこのようにしました。

 

- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer

{

  //カメラ 画像

    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

    

    // ピクセルバッファのベースアドレスをロックする

    CVPixelBufferLockBaseAddress(imageBuffer, 0);

    

    // Get information of the image

    uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0);

    

    size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer);

    size_t width = CVPixelBufferGetWidth(imageBuffer);

    size_t height = CVPixelBufferGetHeight(imageBuffer);

    

    // RGBの色空間

    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();

    

    CGContextRef newContext = CGBitmapContextCreate(baseAddress,

                                                    width,

                                                    height,

                                                    8,

                                                    bytesPerRow,

                                                    colorSpace,

                                                    kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);

 

 

//追加----------------------------------------------------------------

画面全てのドットのRGBデータを取得、加工、置き換え

  for (int j = 0; j < height; j++) {

        for (int i = 0; i < width; i++) {

           UInt8 *tmp3;

            

            // ピクセルのポインタを取得する

            tmp3 = baseAddress + j * bytesPerRow + i * 4;

 

            // RGBの値を取得する

               UInt8 r = *(tmp3+2);

               UInt8 g = *(tmp3 + 1);

               UInt8 b = *(tmp3);

    

//RGBデータの加工

 

//RGBのR(赤)の値が100以下なら0にします。100以上なら255にします。

  if(r <= 100){

          r=0;

   }else if (r >100) {

          r=255;

   }

//RGBのG(緑)の値が100以下なら0にします。100以上なら255にします。

  if(g <= 100){

            g=0;

    }else if (g >100) {

             g=255;

    }

 //RGBのB(青)の値が100以下なら0にします。100以上なら255にします。              

   if( b <= 100){

           b=0;

    }else if (b >100) {

            b=255;

    }

   

//RGBデータの置き換え

           *(tmp3+2) = r;

            *(tmp3 + 1) = g;

            *(tmp3 ) = b;

       }

}

    

//ここまで追加-----------------------------------------------       

 

 

    CGImageRef cgImage = CGBitmapContextCreateImage(newContext);

    CGContextRelease(newContext);

    CGColorSpaceRelease(colorSpace);

    CVPixelBufferUnlockBaseAddress(imageBuffer, 0);

   

//このあたりは変更しています

UIImage * img

 = [UIImage imageWithCGImage:cgImage scale:1.0 orientation: UIImageOrientationUp];

    CGImageRef      imgRef = [img CGImage];

    CGContextRef    context;

   

 //画像回転

    int angle = 270;

 switch (angle) {

        case 90:

            UIGraphicsBeginImageContextWithOptions(CGSizeMake(img.size.height, img.size.width), YES, img.scale);

            context = UIGraphicsGetCurrentContext();

            CGContextTranslateCTM(context, img.size.height, img.size.width);

            CGContextScaleCTM(context, 1, -1);

            CGContextRotateCTM(context, M_PI_2);

            break;

        case 180:

            UIGraphicsBeginImageContextWithOptions(CGSizeMake(img.size.width, img.size.height), YES, img.scale);

            context = UIGraphicsGetCurrentContext();

            CGContextTranslateCTM(context, img.size.width, 0);

            CGContextScaleCTM(context, 1, -1);

            CGContextRotateCTM(context, -M_PI);

            break;

        case 270:

            UIGraphicsBeginImageContextWithOptions(CGSizeMake(img.size.height, img.size.width), YES, img.scale);

            context = UIGraphicsGetCurrentContext();

            CGContextScaleCTM(context, 1, -1);

            CGContextRotateCTM(context, -M_PI_2);

            break;

        default:

            return img;

            break;

    }

    

    CGContextDrawImage(context, CGRectMake(0, 0, img.size.width, img.size.height), imgRef);

    result = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

  

    CGImageRelease(cgImage);

 

    return result;

    }

 

2015/01/28追加

ヘッダーファイルに

UIImage *result;

を追加してください。

 

 

普通に表示すると、カメラ画像横向いてしまうのでこちらのサイト様の

UIImageの回転(90,180,270): 開発不定記

を利用させてもらい270 度回転させています。

 

この部分ですが、

// ピクセルのポインタを取得する

            tmp3 = baseAddress + j * bytesPerRow + i * 4;

カメラ画像のx,y(ここではj,iですが)座標のポインタを取得しています。

 

            UInt8 r = *(tmp3+2);

               UInt8 g = *(tmp3 + 1);

               UInt8 b = *(tmp3);

このように座標のRGBのデータを取得します。

そうして取得したRGBデータを加工して、またもとのポインタに書き換えてやることで画像を加工します。

//RGBデータの置き換え

            *(tmp3+2) = r; //加工済みの r,g,b

            *(tmp3 + 1) = g;

            *(tmp3 ) = b;

 

(このソースの加工は8色に変換するものです)

AVFoundation Frameworkは結構ややこしいですが、一度覚えてしまえば色々できますので是非使ってみてください。

座標のRGBデータを配列に入れてあれこれしてやれば、某顔歪みカメラみたいなのもできると思います。

動画撮影も割と簡単にできると思います。ただ加工しながらの動画撮影は処理が重いのでどうなるかは試していません;

 

以上、8bit world cameraの参考ソースを書いてみました。