iOS自定義拍照框拍照&裁剪(一)

iOS小熊發表於2021-10-15
  • 卡片機時代

很重要的一點是,相機本身是沒有方向概念的,它不理解拍攝的內容,只會以相機自己的座標系去儲存資料,下圖展示了相機對“F”進行四個角度拍攝時返回的圖片資料。

最初的卡片機時代,照片都會經由底片洗出來,那時不存在照片的方向問題,我們總可以把洗出來的照片通過簡單的旋轉來進行觀看。比如這張照片牆中的照片,你能否說哪些照片是橫著?哪些顛倒著?你甚至都無法判斷每張照片相機是以何種角度拍攝的,因為每張都已經旋轉至適合觀看的角度。

  • 數碼時代

可是到了數碼時代,不再需要底片,照片需要被存成一個影像檔案。對於上面的拍攝角度,儲存方式並沒有變化,所有的場景仍然是以相機的座標系來儲存。於是這些照片仍像上面一樣,原封不動的儲存了下來。

雖然儲存方式不變,和卡機機時代的實體相片不同的是,由於電子裝置可不知道照片應該如何旋轉,只能夠以它儲存於磁碟中的方向來展示。這便是為何照片傳到電腦上之後,會出現橫了,或者顛倒的情況。
  • 方向感測器

為了克服這一情況,讓照片可以真實的反應人們拍攝時看到的場景,現在很多相機中就加入了方向感測器,它能夠記錄下拍攝時相機的方向,並將這一資訊儲存在照片中。照片的儲存方式還是沒有任何改變,它仍然是以相機的座標系來儲存,只是當相機來瀏覽這些照片時,相機可以根據照片中的方向資訊,結合此時相機的方向,對照片進行旋轉,從而轉到適合人們觀看的角度。
  • iPhone上的情況

在iOS的裝置中也包含了方向感測器,它但是它預設的照片方向並不是豎著拿手機時的情況,而是橫向,即Home鍵在右側,如下:

如此一來,如果豎著拿手機拍攝時,就相當於對手機順時針旋轉了90度,也即上面相機圖片中的最後一幅

  • iPhone拍照方向問題處理

iPhone通過API獲取到的是圖片內容資料,不帶方向資訊,此時,我們需要手動的根據感測器方向將方向資訊填充進去,並且根據方向資訊對照片進行相應的旋轉,得到人類視角正向的圖片。程式碼如下:

//iOS11以上支援AVCapturePhotoOutput代理
- (void)captureOutput:(AVCapturePhotoOutput *)output didFinishProcessingPhoto:(AVCapturePhoto *)photo
                error:(NSError *)error {
    if (error) {
            NSLog(@"獲取圖片錯誤 --- %@",error.localizedDescription);
        }
        if (photo) {
           UIImageOrientation orient = UIImageOrientationUp;
            //後置攝像頭
            if (self.shootingOrientation == UIDeviceOrientationLandscapeRight){
                orient = UIImageOrientationDown;
            }else if(self.shootingOrientation == UIDeviceOrientationPortrait){
                orient = UIImageOrientationRight;
            }else if(self.shootingOrientation == UIDeviceOrientationLandscapeLeft){
                orient = UIImageOrientationUp;
            }else if(self.shootingOrientation == UIDeviceOrientationPortraitUpsideDown){
                orient = UIImageOrientationLeft;
            }
            //前置攝像頭
            AVCaptureDevice *currentDevice = [self.videoWriterManager.videoInput device];
            AVCaptureDevicePosition currentPosition = [currentDevice position];
            if (currentPosition == AVCaptureDevicePositionFront){
                //前置攝像頭
                if (self.shootingOrientation == UIDeviceOrientationLandscapeRight){
                    orient = UIImageOrientationUp;
                }else if(self.shootingOrientation == UIDeviceOrientationLandscapeLeft){
                    orient = UIImageOrientationDown;
                }
            }
            UIImage *image = [UIImage imageWithCGImage:photo.CGImageRepresentation scale:1 orientation:orient];
            //圖片修正為人類視角看起來正向的圖片
            //[self fixOrientation:image];
        }
}


//將原始圖片資料調整為 人類視角正向的圖片
- (UIImage *)fixOrientation:(UIImage *)image{
    // No-op if the orientation is already correct
    if (image.imageOrientation == UIImageOrientationUp) return image;
    
    // We need to calculate the proper transformation to make the image upright.
    // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
    CGAffineTransform transform = CGAffineTransformIdentity;
    CGFloat imageWidth = CGImageGetWidth(image.CGImage);
    CGFloat imageHeight = CGImageGetHeight(image.CGImage);
    //建立的點陣圖寬高
    CGFloat contextWidth = imageWidth < imageHeight ? imageWidth : imageHeight;
    CGFloat contextHeight = imageWidth > imageHeight ? imageWidth : imageHeight;
    

    switch (image.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:{
                //建立橫著顯示的點陣圖上下文,寬大於高
                CGFloat temp = contextWidth;
                contextWidth = contextHeight;
                contextHeight = temp;
                //旋轉180度
                transform = CGAffineTransformTranslate(transform, contextWidth, contextHeight);
                transform = CGAffineTransformRotate(transform, M_PI);
            }
            break;
            
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            //逆時針旋轉90度
            transform = CGAffineTransformTranslate(transform, contextWidth, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
            
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            //順時針90度
            transform = CGAffineTransformTranslate(transform, 0, contextHeight);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
            
        default:
            break;
    }
    
    switch (image.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            //映象圖片Y軸翻轉座標系
            transform = CGAffineTransformTranslate(transform, contextWidth, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            //映象圖片Y軸翻轉座標系
            transform = CGAffineTransformTranslate(transform, contextHeight, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        default:
            break;
    }
    
    //建立豎直顯示的畫布
    CGContextRef ctx = CGBitmapContextCreate(NULL, contextWidth, contextHeight,
                                             CGImageGetBitsPerComponent(image.CGImage), 0,
                                             CGImageGetColorSpace(image.CGImage),
                                             CGImageGetBitmapInfo(image.CGImage));

    CGContextConcatCTM(ctx, transform);
    CGContextDrawImage(ctx, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
    
    // And now we just create a new UIImage from the drawing context
    CGImageRef cgimg = CGBitmapContextCreateImage(ctx);
    UIImage *img = [UIImage imageWithCGImage:cgimg];
    CGContextRelease(ctx);
    CGImageRelease(cgimg);
    return img;
}

圖片來源於(https://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/)

參考 https://feihu.me/blog/2015/how-to-handle-image-orientation-on-iOS/

相關文章