2012年7月23日 星期一

Filter4Cam 實作: 19. 前後鏡頭切換

since: 2012/07/23
update: 2012/07/23

reference:
1. The iOS 5 Developer's Cookbook, 3/e
2. iphone - CATransform3D vs. CGAffineTransform? - Stack Overflow

A. 開啟 ViewController.m 檔案, 修改如下:

....
// step 1:
- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
   
    //@add
    self.applyFilter = NO;
    self.isUsingFrontCamera = NO;
....
}
....

// step 2:
//@add:建立相機 Session
- (void)establishCamera:(uint)whichCamera
{
....
    // Choose camera
    /*
    self.isUsingFrontCamera = NO;
    if ((whichCamera == kCameraFront) && [Filter4CamHelper frontCameraAvailable])
    {
        self.isUsingFrontCamera = YES;
    }
    */
    //@update
    if ((whichCamera == kCameraFront) && [Filter4CamHelper frontCameraAvailable])
    {
        self.isUsingFrontCamera = YES;
    }
    else
    {

        self.isUsingFrontCamera = NO;
    }
....
}
....

// step 3:
//@add: "鏡頭切換"
- (void)switchCameras
{
    if (![Filter4CamHelper numberOfCameras] > 1) return;
   
    self.isUsingFrontCamera = !self.isUsingFrontCamera;

    if ((self.isUsingFrontCamera == YES) && ![Filter4CamHelper frontCameraAvailable]) {
        self.isUsingFrontCamera = NO;
    }
   
    AVCaptureDevice *newDevice = self.isUsingFrontCamera ? [Filter4CamHelper frontCamera] : [Filter4CamHelper backCamera];
   
    [self.session beginConfiguration];
   
    // Remove existing inputs
    for (AVCaptureInput *input in [self.session inputs])
        [self.session removeInput:input];
   
    // Change the input
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:newDevice error:nil];

    [self.session addInput:captureInput];
   
    [self.session commitConfiguration];
}
....

編譯並執行:
預設後置鏡頭:


切換到前置鏡頭之結果:
1. 當設備垂直擺放: 物體左右相反.
    (說明: "麥斯威爾" 應該要從右往左排列)

2. 當設備水平擺放: 物體上下顛倒.

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

B. 有關 "前置鏡頭": 左右相反與上下顛倒之問題:
     1. 說明:
         a. 方向轉變的調整, 在 ViewController.m 檔案的
             captureOutput:didOutputSampleBuffer:fromConnection: 方法中,
             (1). 先呼叫了: orientationTransform: 方法, 對整個螢幕作方向調整;
             (2). 後來再呼叫: filterOrientationTransform: 方法, 只對套用濾鏡
                    顯示範圍作方向調整.

         b. 由於目前的問題, 是使用前置鏡頭時, 整個螢幕產生左右相反(垂直擺放)
             與上下顛倒(水平擺放)之問題, 因此要對 orientationTransform: 方法作調整.

         c. 先看看之前在 orientationTransform: 方法中, 對方向轉變的調整:
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
....
    return [sourceImage imageByApplyingTransform:affineTransform];
   
    備註: 在這邊使用了 CGAffineTransform(仿射轉換) 來處理方向的問題,
              但是如果要處理左右相反的問題, 就需要使用 CATransform3D.

         d. CGAffineTransform 與 CATransform3D 的差異:
             (1). CGAffineTransform線性的 2D 轉換, 適用於 NSViews, UIViews
                    與其它 2D 的 Core Graphics 基礎上.

             (2). CATransform3D 是架構在 Core Animation 上的三維投影轉換, 適用於
                    CALayers 上. CATransform3D 有與 OpenGLmodel view 矩陣相同的
                    內部結構, 這是因為 Core Animation 是建立在 OpenGL 的基礎上.
                    (例如: CALayers 是由 OpenGL 的 textures 包裹而成的)   

 **********************************************************

     2. 測試: "設備垂直擺放", 開啟 ViewController.m 檔案, 修改如下:
....
//@add for orientation Transform(方向轉變的調整)
- (CIImage *)orientationTransform:(CIImage *)sourceImage
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    CATransform3D transform3D = CATransform3DIdentity; // 3D 轉換
   
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        //@add for 3D Transform
        if (self.isUsingFrontCamera) {
            // 將整個 GLKView 對 Y 軸作 180 度旋轉
            transform3D = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
            self.glView.layer.transform = transform3D;
        }
 
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
    ....
       return [sourceImage imageByApplyingTransform:affineTransform];
   }
....

編譯並執行:
設備垂直擺放, 切換到前置鏡頭:
a. "麥斯威爾" 從右往左排列是正確的.
b. 變成左右相反的有: TableView(Cell位置與文字), 4 個 button(排列位置與文字)

     3. 測試: 修正 TableView 與 button 的問題, 修改如下:
....

//@add for orientation Transform(方向轉變的調整)
- (CIImage *)orientationTransform:(CIImage *)sourceImage
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    CATransform3D transform3D = CATransform3DIdentity; // 3D 轉換
   
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        //@add for 3D Transform
        if (self.isUsingFrontCamera) {
            // 將整個 GLKView 對 Y 軸作 180 度旋轉
            transform3D = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
            self.glView.layer.transform = transform3D;
            self.filterListTableView.layer.transform = transform3D;
            self.saveButton.layer.transform = transform3D;
            self.switchButton.layer.transform = transform3D;
            self.torchButton.layer.transform = transform3D;
            self.observerButton.layer.transform = transform3D;
        }
 
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
    ....
       return [sourceImage imageByApplyingTransform:affineTransform];
   }
....

編譯並執行:
設備垂直擺放, 切換到前置鏡頭:
a. TableView(Cell位置與文字)已正確顯示了.
b. 4 個 button 的文字顯示正確.
c. 4 個 button 的排列位置, 從左到右應該是: Save, Switch, Torch, Observe.

     4. 測試: 修正 button 的排列位置, 修改如下:
....
//@add for orientation Transform(方向轉變的調整)
- (CIImage *)orientationTransform:(CIImage *)sourceImage
{
    UIDeviceOrientation orientation = [[UIDevice currentDevice] orientation];
    CGAffineTransform affineTransform; // 仿射轉換
    CATransform3D transform3D = CATransform3DIdentity; // 3D 轉換
   
    // 設備垂直擺放
    if (orientation == UIDeviceOrientationPortrait)
    {
        //@add for 3D Transform
        if (self.isUsingFrontCamera) {
            // 將整個 GLKView 對 Y 軸作 180 度旋轉
            transform3D = CATransform3DMakeRotation(M_PI, 0.0f, 1.0f, 0.0f);
            self.glView.layer.transform = transform3D;
            self.filterListTableView.layer.transform = transform3D;
            self.saveButton.layer.transform = transform3D;
            self.switchButton.layer.transform = transform3D;
            self.torchButton.layer.transform = transform3D;
            self.observerButton.layer.transform = transform3D;

            //@update for Button position            
            [self.saveButton setFrame:CGRectMake(241, 420, 66, 40)];
            [self.switchButton setFrame:CGRectMake(164, 420, 66, 40)];
            [self.torchButton setFrame:CGRectMake(87, 420, 66, 40)];
            [self.observerButton setFrame:CGRectMake(10, 420, 66, 40)];
        }
 
        affineTransform = CGAffineTransformMakeRotation(-M_PI / 2);
    }
    ....
       return [sourceImage imageByApplyingTransform:affineTransform];
   }
....

編譯並執行:
設備垂直擺放, 切換到前置鏡頭: 4 個 button 的排列位置已正確了.

備註: 在之後的修正實作時, 會將 4 個 button 加到一個 UIView 之下,
           再調整此 UIView 的方向與位置.

沒有留言:

張貼留言

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