2012年7月2日 星期一

Filter4Cam 實作: 18. 拍照

since: 2012/07/02
update: 2012/07/02

reference:
I touchs: Filter4Cam 學習之 Core Image Tutorial

A. 說明:
      先檢查專案是否有加入: AssetsLibrary framework, 如果沒有的話就加入
      (之前應該有加入了). AssetsLibrary framework 是用來存取被影像應用程式
      所管理的照片影片.

-----------------------------------------------------------------------------

B. 開啟 ViewController.h 檔案, 修改如下:
....
//@add
#import <AVFoundation/AVFoundation.h>
#import <CoreMedia/CoreMedia.h>
#import <CoreVideo/CoreVideo.h>
#import <QuartzCore/QuartzCore.h>
#import <CoreImage/CoreImage.h>
#import <ImageIO/ImageIO.h>
#import <AssetsLibrary/AssetsLibrary.h>
....
@interface ViewController : GLKViewController <AVCaptureVideoDataOutputSampleBufferDelegate, UITableViewDelegate, UITableViewDataSource>
{
    //@add
    CIContext *coreImageContext;
    CIFilter *coreImageFilter;
    CIImage *ciImage;
....
    BOOL isUsingFrontCamera;

    //@add 設備上一次方向是否為水平擺放(向左或向右皆算)
    BOOL isPreviousOrientationLandscape;
    BOOL applyFilter; // 是否套用濾鏡
....
}

//@add
@property (strong, nonatomic) EAGLContext *glContext;
@property (strong, nonatomic) CIContext *coreImageContext;
@property (strong, nonatomic) CIFilter *coreImageFilter;
@property (strong, nonatomic) CIImage *ciImage;
....
@property (assign) BOOL isUsingFrontCamera;

@property (assign) BOOL isPreviousOrientationLandscape;
@property (assign) BOOL applyFilter;
....

-----------------------------------------------------------------------------

C. 開啟 ViewController.m 檔案, 修改如下:
....
// step 1:
//@add
@synthesize glContext = _glContext;
@synthesize coreImageContext = _coreImageContext;
@synthesize coreImageFilter = _coreImageFilter;
@synthesize ciImage = _ciImage;
....

@synthesize applyFilter = _applyFilter;


// step 2:
....
- (void)viewDidLoad

{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    //@add
    self.applyFilter = NO;
....
}

// step 3:
....
- (CIImage *)filterTest:(CIImage *)sourceImage
{
    /*
    CIFilter *filter = [CIFilter filterWithName:@"CISepiaTone" keysAndValues:
                        kCIInputImageKey, sourceImage,
                        @"inputIntensity", [NSNumber numberWithFloat:0.8], nil];
   
    return [filter outputImage];
    */

    //@add
    self.applyFilter = YES;

    self.coreImageFilter = [CIFilter filterWithName:@"CISepiaTone" keysAndValues:
                        kCIInputImageKey, sourceImage,
                        @"inputIntensity", [NSNumber numberWithFloat:0.8], nil];
   
    return [self.coreImageFilter outputImage];
}

....
// step 4:
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
   
    // 將取得的 sampleBuffer 轉成 CVPixelBuffer
    CVPixelBufferRef pixelBuffer = (CVPixelBufferRef)CMSampleBufferGetImageBuffer(sampleBuffer);
   
    //@update: comment it
    /*
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
    ciImage = [self orientationTransform:ciImage];
    [self.coreImageContext drawImage:ciImage atPoint:CGPointZero fromRect:[ciImage extent]];
    ciImage = [self filterTest:ciImage];
    [self filterOrientationTransform:ciImage];
    */
   
    //@update: use instance var (ciImage)
    // 接著, 將 CVPixelBuffer 利用 Core Image 的初始化方法再轉成 CIImage.
    self.ciImage = [CIImage imageWithCVPixelBuffer:pixelBuffer];
   
    //@add:方向轉變的調整
    self.ciImage = [self orientationTransform:self.ciImage];
   
    // 然後使用 CIContext 物件將其內容畫到 render buffer
    [self.coreImageContext drawImage:self.ciImage atPoint:CGPointZero fromRect:[self.ciImage extent]];
   
    // 濾鏡功能測試
    self.ciImage = [self filterTest:self.ciImage];
   
    // e.g. [self.coreImageContext drawImage:self.ciImage
    // inRect:CGRectMake(23.5, 123.5, 274, 274) fromRect:[self.ciImage extent]];
    [self filterOrientationTransform:self.ciImage];
   
    // 最後, 在螢幕上呈現出來.
    [self.glContext presentRenderbuffer:GL_RENDERBUFFER];
}

....
// step 5:
- (void)savePhoto
{
    //@TODO
    //NSLog(@"savePhoto");

    // 從 filter 取得 CIImage 的輸出:
    // 不需要再作, 因為 self.ciImage 可能有使用過 filter 或是未套用濾鏡的原始影像
   
// self.ciImage = [self.coreImageFilter outputImage];
   
    // 產生 CGImageRef
    CGImageRef cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:[self.ciImage extent]];

    // 將 CGImageRef 儲存到相簿裡
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
   
    [library writeImageToSavedPhotosAlbum:cgImage
                                 metadata:[self.ciImage properties]
                          completionBlock:^(NSURL *assetURL, NSError *error) {
                             
                              // 釋放 CGImage, 這是一個 callback block, 當處理完畢時才會觸發.
                              CGImageRelease(cgImage);
                          }];
}
....

-----------------------------------------------------------------------------

D. 編譯並執行:
      拍照畫面:

       拍照結果:
       紅框的範圍內才是套用濾鏡的結果, 而不是整個螢幕. 

-----------------------------------------------------------------------------

E. 修正拍照結果
     開啟 ViewController.m 檔案, 修改如下:
....
- (void)savePhoto
{   
    // 從 filter 取得 CIImage 的輸出:
    // 不需要再作, 因為 self.ciImage 可能有使用過 filter 或是未套用濾鏡的原始影像
    // self.ciImage = [self.coreImageFilter outputImage];
   
    // 產生 CGImageRef
    //CGImageRef cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:[self.ciImage extent]];
    CGImageRef cgImage = nil;
   
    //@update for only capture filtered scope
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
       
    //@擷取的濾鏡範圍與 filterOrientationTransform: 中
    // drawImage:inRect:fromRect: 裡的 fromRect: 相同
    //
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:CGRectMake(23, -557.5, 274, 274)];
    }
    // 設備垂直 180 度擺放
    else if (orientation == UIDeviceOrientationPortraitUpsideDown)
    {
        cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:CGRectMake(-458.5, 83, 274, 274)];
    }
    // 設備水平向右擺放
    else if (orientation == UIDeviceOrientationLandscapeRight)
    {
        cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:CGRectMake(-517, -457.5, 274, 274)];
    }
    // 設備水平向左擺放(最穩定)
    else if (orientation == UIDeviceOrientationLandscapeLeft) 
   {
        cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:CGRectMake(123.5, 23.5, 274, 274)];
    }
    // 其它 (UIDeviceOrientationFaceUp, UIDeviceOrientationFaceDown,
    // UIDeviceOrientationUnknown)

    else {          
        // 上一次設備為水平擺放
        if (self.isPreviousOrientationLandscape == YES) 
        {
            cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:CGRectMake(123.5, 23.5, 274, 274)];
        }
        // 上一次設備為垂直擺放
        else 
        {
            cgImage = [self.coreImageContext createCGImage:self.ciImage fromRect:CGRectMake(23.5, 83.5, 274, 274)];
        }
    }

    // 將 CGImageRef 儲存到相簿裡
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
   
    [library writeImageToSavedPhotosAlbum:cgImage
                                 metadata:[self.ciImage properties]
                          completionBlock:^(NSURL *assetURL, NSError *error) {
                             
                              // 釋放 CGImage, 這是一個 callback block, 當處理完畢時才會觸發.
                              CGImageRelease(cgImage);
                          }];
}
....

-----------------------------------------------------------------------------

F. 編譯並執行
     拍照結果:

沒有留言:

張貼留言

注意:只有此網誌的成員可以留言。