iOS上的相機捕捉

UIImagePickerController

UIImagePickerController 類用于管理可定制的,基于系統(tǒng)支持的用戶界面,用于在支持的設備上拍攝照片和視頻,并且在你的 app 中為用戶保存照片和視頻。 圖像選擇器控制器管理用戶交互并將這些交互的結(jié)果傳遞給委托對象。

UIImagePickerController 繼承于 UINavigationController,歸屬于 UIKit 框架,可以實現(xiàn)圖片選取、拍照、錄制視頻等功能,使用起來十分方便。

使用方式

需要導入框架:import <MobileCoreServices/MobileCoreServices.h>;
拍攝視頻需要導入包:#import <AssetsLibrary/AssetsLibrary.h>
需要遵循的協(xié)議:<UINavigationControllerDelegate,UIImagePickerControllerDelegate>;

:需要導入 <MobileCoreServices/MobileCoreServices.h> 框架是因為 kUTTypeImagekUTTypeMovie 被定義在其中。

:實際上我們不會實現(xiàn) UINavigationControllerDelegate 中定義的任何協(xié)議方法,但由于 UIImagePickerController 繼承自 UINavigationController,并且改變了 UINavigationController的行為。因此,我們?nèi)匀恍枰鲃勇暶魑覀兊囊晥D控制器遵守 UINavigationControllerDelegate 協(xié)議。

1. 創(chuàng)建 UIImagePickerController 圖片選擇器對象

UIImagePickerController *imagePicker = [[UIImagePickerController alloc] init];

2. 設置 sourceType 媒體數(shù)據(jù)源類型

UIImagePickerControllerSourceType 是一個枚舉類型,用于設置要跳轉(zhuǎn)到哪個界面(相機拍照、照片圖庫、相冊) :

typedef NS_ENUM(NSInteger, UIImagePickerControllerSourceType) {
    UIImagePickerControllerSourceTypePhotoLibrary API_DEPRECATED("Will be removed in a future release, use PHPicker.", ios(2, API_TO_BE_DEPRECATED)), // 照片庫
    UIImagePickerControllerSourceTypeCamera, // 系統(tǒng)內(nèi)置像機
    UIImagePickerControllerSourceTypeSavedPhotosAlbum API_DEPRECATED("Will be removed in a future release, use PHPicker.", ios(2, API_TO_BE_DEPRECATED)), // 相冊
} API_UNAVAILABLE(tvos);

?? 查看 Apple 的源碼可以發(fā)現(xiàn),部分類型已經(jīng)被標注為 API_DEPRECATED,意味著該屬性即將被廢棄,推薦使用 PHPicker 代替。

設置示例:

// 驗證設備是否能夠從所需的來源獲取內(nèi)容
BOOL isSourceTypeAvailable = [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera];
if (!isSourceTypeAvailable) {
    // 可能是權限未打開,也可能是手機硬件不支持相機功能。
    NSLog(@"啟動相機失敗,您的相機功能未開啟,請轉(zhuǎn)到手機設置中開啟相機權限!");
}else{
    imagePicker.sourceType = UIImagePickerControllerSourceTypeCamera;
}

3. 設置攝像頭捕捉模式

如果設置了 sourceType 媒體數(shù)據(jù)源類型為 UIImagePickerControllerSourceTypeCamera,那你還需要設置是打開相機的拍照還是錄制視頻功能。

UIImagePickerControllerCameraCaptureMode 是一個枚舉類型:

UIImagePickerControllerCameraCaptureModePhoto, // 拍照模式,默認
UIImagePickerControllerCameraCaptureModeVideo   // 視頻錄制模式

拍攝圖片:

// 設定拍攝的媒體類型:圖片
imagePicker.mediaTypes = @[(NSString *)kUTTypeImage];
// 設置攝像頭捕捉模式為捕捉圖片,默認
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;

拍攝視頻:

// 設定拍攝的媒體類型:視頻
imagePicker.mediaTypes = @[(NSString *)kUTTypeMovie];
// 設置攝像頭捕捉模式為捕捉視頻
imagePicker.cameraCaptureMode = UIImagePickerControllerCameraCaptureModePhoto;

4. 攝像頭設備

UIImagePickerControllerCameraDevice 是一個枚舉類型,代表前置/后置攝像頭:

UIImagePickerControllerCameraDeviceRear,   //后置攝像頭,默認
UIImagePickerControllerCameraDeviceFront   //前置攝像頭         

設置示例:

// 設置前置攝像頭
if ([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]) {
    imagePicker.cameraDevice = UIImagePickerControllerCameraDeviceFront;
}

5. 設置錄制視頻質(zhì)量

UIImagePickerControllerQualityType 是一個枚舉類型,設置返回圖片的質(zhì)量:

UIImagePickerControllerQualityTypeHigh // 高清
UIImagePickerControllerQualityTypeMedium // 中等,適合 WiFi 傳輸
UIImagePickerControllerQualityTypeLow // 低質(zhì)量,適合蜂窩網(wǎng)傳輸
UIImagePickerControllerQualityType640x480 // VGA 質(zhì)量,640*480
UIImagePickerControllerQualityTypeIFrame1280x720 // 1280*720
UIImagePickerControllerQualityTypeIFrame960x540 // 960*540

設置示例:

imagePicker.videoQuality = UIImagePickerControllerQualityTypeLow;

6. 設置閃光燈模式

在調(diào)用攝像頭的時候我們可以選擇使用閃光燈,但是默認條件下對視頻有 10 分鐘的限制,需要用 videoMaximumDuration 屬性更改默認時間。

UIImagePickerControllerCameraFlashMode 是一個枚舉類型,用于設置閃光燈關閉、自動、打開。

UIImagePickerControllerCameraFlashModeOff  = -1,
UIImagePickerControllerCameraFlashModeAuto = 0,默認
UIImagePickerControllerCameraFlashModeOn   = 1

設置示例:

imagePicker.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto

7. 遵循協(xié)議

imagePicker.delegate = self;

8. 是否顯示系統(tǒng)自帶的攝像頭控制面板,默認YES

// 顯示標準相機 UI,
imagePicker.showsCameraControls = NO;

9. 設置自定義覆蓋圖層

UIImageView *overlayImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
overlayImageView.image = [UIImage imageNamed:@"circle3.png"];
UIView *cameraOverlay = overlayImageView;

imagePicker.cameraOverlayView = cameraOverlay

10. 以模態(tài)形式顯示 UIImagePickerController 對象

[self presentViewController:imagePicker animated:YES completion:nil];

11. 遵守并實現(xiàn) UIImagePickerControllerDelegate 協(xié)議中的方法

你需要關注協(xié)議中的這兩個方法:

// 媒體項(圖片或視頻)選擇完成后調(diào)用
- (void)imagePickerController:(UIImagePickerController *)picker 
didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info;
// 取消選擇媒體項調(diào)用
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker;

擴展函數(shù),用于保存到相簿:

/* 保存圖片到相簿 */
void UIImageWriteToSavedPhotosAlbum(
    UIImage *image,//保存的圖片UIImage
    id completionTarget,//回調(diào)的執(zhí)行者
    SEL completionSelector, //回調(diào)方法
    void *contextInfo//回調(diào)參數(shù)信息
);
//上面一般保存圖片的回調(diào)方法為:
- (void)image:(UIImage *)image 
        didFinishSavingWithError:(NSError *)error 
        contextInfo:(void *)contextInfo;

/* 判斷是否能保存視頻到相簿 */
BOOL UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(NSString *videoPath);
/* 保存視頻到相簿 */
void UISaveVideoAtPathToSavedPhotosAlbum(
    NSString *videoPath, //保存的視頻文件路徑
    id completionTarget, //回調(diào)的執(zhí)行者
    SEL completionSelector,//回調(diào)方法
    void *contextInfo//回調(diào)參數(shù)信息
);
//上面一般保存視頻的回調(diào)方法為:
- (void)video:(NSString *)videoPath 
        didFinishSavingWithError:(NSError *)error 
        contextInfo:(void *)contextInfo;

示例代碼:

#pragma mark - 完成選擇圖片
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info{
    NSLog(@"%s",__func__);
    
    // 從info取出此時攝像頭的媒體類型
    NSString *mediaType = info[UIImagePickerControllerMediaType];
    
    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
        // 【拍照模式】
        // 通過info字典獲取選擇的照片
        UIImage *image = info[UIImagePickerControllerOriginalImage];
     
        // 將照片放入UIImageView對象顯示在UI界面
        self.imageView.image = image;
        
        // 保存圖像到相簿
        UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);

    }else if ([mediaType isEqualToString:(NSString *)kUTTypeMovie]) {
        // 【錄像模式】
        
        NSURL *url = info[UIImagePickerControllerMediaURL];
        NSString *path = url.path;
        
        
        if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path)) {
            // 保存視頻到相簿
            UISaveVideoAtPathToSavedPhotosAlbum(path, self, @selector(video:didFinishSavingWithError:contextInfo:), nil);
        }
    }
    
    // 關閉UIImagePickerController對象
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark 取消拍照
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker {
    NSLog(@"%s",__func__);
    [self dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - 保存圖像或視頻完成的回調(diào)
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"保存圖片完成");
}

- (void)video:(NSString *)videoPath didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"保存視頻完成");
}

封裝使用

VideoCaptureDemo 中將 UIImagePickerController 封裝成了一個Object 類使用,可以參考:

IDImagePickerCoordinator.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

/**
 圖片選擇協(xié)調(diào)器
 */
@interface IDImagePickerCoordinator : NSObject

#pragma mark - getter 方法
- (UIImagePickerController *)cameraVC;

@end

IDImagePickerCoordinator.m

#import "IDImagePickerCoordinator.h"
#import <MobileCoreServices/MobileCoreServices.h>
#import <AssetsLibrary/AssetsLibrary.h>

@interface IDImagePickerCoordinator () <UINavigationControllerDelegate, UIImagePickerControllerDelegate>

@property (nonatomic, strong) UIImagePickerController *camera;

@end

@implementation IDImagePickerCoordinator

#pragma mark - Init methods
- (instancetype)init
{
    self = [super init];
    if(self){
        _camera = [self setupImagePicker];
    }
    return self;
}

- (UIImagePickerController *)cameraVC
{
    return _camera;
}

#pragma mark - Private methods

- (UIImagePickerController *)setupImagePicker
{
    UIImagePickerController *camera;
    if([self isVideoRecordingAvailable]){
        camera = [UIImagePickerController new];
        camera.sourceType = UIImagePickerControllerSourceTypeCamera;
        camera.mediaTypes = @[(NSString *)kUTTypeMovie];
        camera.delegate = self;
    }
    return camera;
}

- (BOOL)isVideoRecordingAvailable
{
    if([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]){
        NSArray *availableMediaTypes = [UIImagePickerController availableMediaTypesForSourceType:UIImagePickerControllerSourceTypeCamera];
        if([availableMediaTypes containsObject:(NSString *)kUTTypeMovie]){
            return YES;
        }
    }
    return NO;
}

- (BOOL)setFrontFacingCameraOnImagePicker:(UIImagePickerController *)picker
{
    if([UIImagePickerController isCameraDeviceAvailable:UIImagePickerControllerCameraDeviceFront]){
        [picker setCameraDevice:UIImagePickerControllerCameraDeviceFront];
        return YES;
    }
    return NO;
}

- (void)configureCustomUIOnImagePicker:(UIImagePickerController *)picker
{
    UIView *cameraOverlay = [[UIView alloc] init];
    picker.showsCameraControls = NO;
    picker.cameraOverlayView = cameraOverlay;
}

#pragma mark - UIImagePickerControllerDelegate methods

// 完成選擇圖片
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingImage:(UIImage *)image editingInfo:(nullable NSDictionary<NSString *,id> *)editingInfo NS_DEPRECATED_IOS(2_0, 3_0) {
    
}

// 完成選擇視頻
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
    ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
    NSURL *recordedVideoURL= [info objectForKey:UIImagePickerControllerMediaURL];
    if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:recordedVideoURL]) {
        [library writeVideoAtPathToSavedPhotosAlbum:recordedVideoURL
                                    completionBlock:^(NSURL *assetURL, NSError *error){}
         ];
    }
    
    [picker dismissViewControllerAnimated:YES completion:nil];
}

// 取消選擇
- (void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    [picker dismissViewControllerAnimated:YES completion:nil];
}


@end

使用時先導入頭文件,設置屬性:

@property (nonatomic, strong) IDImagePickerCoordinator *imagePickerCoordinator;

再調(diào)用:

self.imagePickerCoordinator = [IDImagePickerCoordinator new];
[self presentViewController:[_imagePickerCoordinator cameraVC] animated:YES completion:nil];

AVFoundation

HQLAVFoundationViewController.h

#import <UIKit/UIKit.h>

/**
 使用AVFoundation類拍照
 */
@interface HQLAVFoundationViewController : UIViewController

@end

HQLAVFoundationViewController.m

#import "HQLAVFoundationViewController.h"
#import <AVFoundation/AVFoundation.h>
#define ThemeColor [UIColor colorWithDisplayP3Red:81/255.0 green:136/255.0 blue:247/255.0 alpha:1.0]

@interface HQLAVFoundationViewController ()

@property (weak, nonatomic) IBOutlet UIImageView *imageView;
@property (weak, nonatomic) IBOutlet UIButton *openCaptureBtn;
@property (weak, nonatomic) IBOutlet UIButton *canptureBtn;

@property (strong, nonatomic) AVCaptureSession *session;    //媒體管理會話
@property (strong, nonatomic) AVCaptureDeviceInput *captureInput;   //輸入數(shù)據(jù)對象
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutput;   //輸出數(shù)據(jù)對象
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureLayer; //視頻預覽圖層


@end

@implementation HQLAVFoundationViewController

#pragma mark - Lifecycle
- (void)viewDidLoad {
    [super viewDidLoad];
    [self setNavigationControllerAppearance];
    // 初始化攝像頭
    [self initCaptureSession];
    
    self.openCaptureBtn.hidden = NO;
    self.canptureBtn.hidden = YES;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

#pragma mark - Custom Accessors
- (void)setNavigationControllerAppearance {
    // 設置導航欄標題&字體&顏色
    self.navigationItem.title = @"AVFoundation拍照";
    [self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17],NSForegroundColorAttributeName:ThemeColor}];
}

#pragma mark 初始化攝像頭
- (void)initCaptureSession {
    /*
     1?? AVCaptureSession
     媒體捕捉會話,管理輸入與輸出之間的數(shù)據(jù)流,以及在出現(xiàn)問題時生成運行時錯誤。
     負責把捕獲到的音頻視頻數(shù)據(jù)輸出到輸出設備上,一個會話可以有多個輸入輸入。
     */
    // 1.創(chuàng)建媒體管理會話
    AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
    
    self.session = captureSession;
    
    // 判斷分辨率是否支持 1280*720,支持就設置為:1280*720
    if ([captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    /*
     2?? AVCaptureDevice
     關于相機硬件的接口。它被用于控制硬件特性,諸如鏡頭的位置、曝光、閃光燈等。
     */
    
    // 2.獲取后置攝像頭
    AVCaptureDevice *captureDevice = nil;
    NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *camera in cameras) {
        if (camera.position == AVCaptureDevicePositionBack) {
            // 獲得后置攝像頭
            captureDevice = camera;
        }
    }
    if (!captureDevice) {
        NSLog(@"2.取得后置攝像頭錯誤!");
        return;
    }
    
    // 取得前置攝像頭
    //    AVCaptureDevice *frontCaptureDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
    
    /*
     3?? AVCaptureDeviceInput
     設備輸入數(shù)據(jù)管理對象,管理輸入數(shù)據(jù)
     */
    
    // 3.創(chuàng)建輸入數(shù)據(jù)對象
    NSError *error = nil;
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (error) {
        NSLog(@"3.創(chuàng)建輸入數(shù)據(jù)對象錯誤");
        return;
    }
    
    self.captureInput = captureInput;
    
    /*
     4?? AVCaptureOutput
     設備輸出數(shù)據(jù)管理對象,管理輸出數(shù)據(jù),通常使用它的子類:
     AVCaptureAudioDataOutput   //輸出音頻管理對象,輸出數(shù)據(jù)為NSData
     AVCaptureStillImageDataOutput  //輸出圖片管理對象,輸出數(shù)據(jù)為NSData
     AVCaptureVideoDataOutput   //輸出視頻管理對象,輸出數(shù)據(jù)為NSData
     
     AVCaptureFileOutput
     輸出文件管理對象,輸出數(shù)據(jù)以文件形式輸出
     {//子類
     AVCaptureAudioFileOutput   //輸出是音頻文件
     AVCaptureMovieFileOutput   //輸出是視頻文件
     }
     */
    
    // 4.創(chuàng)建輸出數(shù)據(jù)對象
    AVCaptureStillImageOutput *imageOutpot = [[AVCaptureStillImageOutput alloc] init];
    NSDictionary *setting = @{
                              AVVideoCodecKey:AVVideoCodecJPEG
                              };
    [imageOutpot setOutputSettings:setting];
    
    self.imageOutput = imageOutpot;
    
    // 5?? 5.添加【輸入數(shù)據(jù)對象】和【輸出對象】到會話中
    if ([captureSession canAddInput:captureInput]) {
        [captureSession addInput:captureInput];
    }
    if ([captureSession canAddOutput:imageOutpot]) {
        [captureSession addOutput:imageOutpot];
    }
    
    
    /*
     6?? AVCaptureVideoPreviewLayer
     實時預覽圖層
     AVCaptureVideoPreviewLayer 是 CALayer 的子類,可被用于自動顯示相機產(chǎn)生的實時圖像。它還有幾個工具性質(zhì)的方法,可將 layer 上的坐標轉(zhuǎn)化到設備上。它看起來像輸出,但其實不是。另外,它擁有 session (outputs 被 session 所擁有)。
     */
    
    // 6.創(chuàng)建實時預覽圖層
    AVCaptureVideoPreviewLayer *previewlayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
    self.view.layer.masksToBounds = YES;
    previewlayer.frame = self.view.bounds;
    previewlayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    // 【預覽圖層】插入在【拍照按鈕】的下方
    [self.view.layer insertSublayer:previewlayer below:self.canptureBtn.layer];
    
    self.captureLayer = previewlayer;
}

#pragma mark - IBAction

#pragma mark 打開攝像頭
- (IBAction)takePhoto:(UIButton *)sender {
    
    self.captureLayer.hidden = NO;
    self.canptureBtn.hidden = NO;
    self.openCaptureBtn.hidden = YES;
    [self.session startRunning];    //開始捕捉
    
}

#pragma mark 拍照
- (IBAction)takeMedia:(id)sender {
    // 根據(jù)設備輸出獲得連接
    AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
    // 通過連接獲得設備的輸出數(shù)據(jù)
    [self.imageOutput captureStillImageAsynchronouslyFromConnection:connection completionHandler:^(CMSampleBufferRef imageDataSampleBuffer, NSError *error) {
        
        // 獲取輸出的JPG圖片
        NSData *imgData = [AVCaptureStillImageOutput jpegStillImageNSDataRepresentation:imageDataSampleBuffer];
        UIImage *image = [UIImage imageWithData:imgData];
        
        self.imageView.image = image;
        
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil);   //保存到相冊
        self.captureLayer.hidden = YES;
        self.canptureBtn.hidden = YES;
        self.openCaptureBtn.hidden = NO;
        [self.session stopRunning];
    }];
}

@end

關于 AVFoundation 視頻錄制的使用推薦閱讀:ctolib:在 iOS 上捕獲 視頻

以及源碼:VideoCaptureDemo

配合使用 CoreImage 實現(xiàn)人臉識別

人臉識別過程分3個步驟:

  1. 首先建立人臉的【面紋數(shù)據(jù)庫】;
  2. 獲取當前人臉面像圖片;
  3. 用當前的面紋編碼與數(shù)據(jù)庫中的面紋編碼進行比對。

CIDetectorCoreImage 中的一個特征識別濾鏡。它可以找到圖片中的人臉,但是是誰無法判斷,需要數(shù)據(jù)庫。要想識別可以看 OpenCVFace.com

#pragma mark - 識別人臉

/**
 識別人臉算法

 @param image 輸入的圖片
 */
- (void)faceDetectWithImage:(UIImage *)image {
    
    NSDictionary *imageOptions =  [NSDictionary dictionaryWithObject:@(5) forKey:CIDetectorImageOrientation];
    
    // 將圖像轉(zhuǎn)換為CIImage
    CIImage *personciImage = [CIImage imageWithCGImage:image.CGImage];
    
    // 設置識別參數(shù)
    NSDictionary *opts = [NSDictionary dictionaryWithObject:
                          CIDetectorAccuracyHigh forKey:CIDetectorAccuracy];
    CIContext *context = [CIContext contextWithOptions:nil];
    //聲明一個CIDetector,并設定識別器類型為人臉識別
    CIDetector *faceDetector=[CIDetector detectorOfType:CIDetectorTypeFace context:context options:opts];
    
    // 識別出人臉數(shù)組
    // featuresInImage:方法 識別器會找到所給圖像中的人臉,最后返回一個人臉數(shù)組
    NSArray *features = [faceDetector featuresInImage:personciImage options:imageOptions];
    
    // 得到圖片的尺寸
    CGSize inputImageSize = [personciImage extent].size;
    // 利用仿射變換將image沿Y軸對稱
    CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, -1);
    // 將圖片上移
    transform = CGAffineTransformTranslate(transform, 0, -inputImageSize.height);
    
    // 遍歷識別到的人臉
    for (CIFaceFeature *faceFeature in features) {
        
        // 獲取人臉的frame
        CGRect faceViewBounds = CGRectApplyAffineTransform(faceFeature.bounds, transform);
        CGSize viewSize = _imageView.bounds.size;
        CGFloat scale = MIN(viewSize.width / inputImageSize.width,
                            viewSize.height / inputImageSize.height);
        
        CGFloat offsetX = (viewSize.width - inputImageSize.width * scale) / 2;
        CGFloat offsetY = (viewSize.height - inputImageSize.height * scale) / 2;
        // 縮放
        CGAffineTransform scaleTransform = CGAffineTransformMakeScale(scale, scale);
        // 修正
        faceViewBounds = CGRectApplyAffineTransform(faceViewBounds, scaleTransform);
        faceViewBounds.origin.x += offsetX;
        faceViewBounds.origin.y += offsetY;
        
        // 描繪人臉區(qū)域
        UIView *faceView = [[UIView alloc] initWithFrame:faceViewBounds];
        faceView.layer.borderWidth = 2;
        faceView.layer.borderColor = [UIColor redColor].CGColor;
        [_imageView addSubview:faceView];
        
        // 判斷是否有左眼位置
        if(faceFeature.hasLeftEyePosition){}
        // 判斷是否有右眼位置
        if(faceFeature.hasRightEyePosition){}
        // 判斷是否有嘴位置
        if(faceFeature.hasMouthPosition){}
        // 判斷是否微笑
        if (faceFeature.hasSmile){}
    }
    
    // 裁剪識別到的人臉
    if ([features count]>0) {
      
        CIImage *image = [personciImage imageByCroppingToRect:[[features objectAtIndex:0] bounds]];
        UIImage *face = [UIImage imageWithCGImage:[context createCGImage:image fromRect:image.extent]];
        // 顯示裁剪后的人臉
        _imageView.image = face;
        
        NSLog(@"識別人臉數(shù)::%lu",(unsigned long)[features count]);
    }   
}

關于拍照完成后使用人臉識別并裁剪,顯示的人臉圖片方向自動逆時針旋轉(zhuǎn)90°顯示的問題:
原因:iPhone 默認的方向是HOME 鍵位于左邊的方向,故豎屏情況原始圖像被拍攝后的EXIF方向值是6,被裁剪后方向信息會被刪除,置為1。
解決方法:在人像識別之前先修改圖像的EXIF信息為1,再進行人像識別,CIDetectorImageOrientation 值也需要改為1。

image

參考

OpenCV 相關

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,080評論 4 61
  • 杜鵑呼喊著東風點燃了晚霞 櫻花逃進少女的羅帳爬上臉頰 布谷一聲聲咽下姹紫嫣紅 誰撒下粉色的眼淚葬了暮鼓晨鐘 拿酒就...
    野馬王閱讀 1,362評論 28 23
  • 【作者】佟梓涵 【派別】文魁派 【導師】王玉印老師 【分舵】第二分舵 思維分舵 【舵主】董季節(jié) 【導圖解說】上次發(fā)...
    佟衛(wèi)東Wendy閱讀 391評論 5 0
  • 符號是對民族文化的解讀,更是對當?shù)厝宋牡捏w現(xiàn) 謝謝欣賞,繼續(xù)努力!
    小茆屋閱讀 738評論 0 51
  • 夜晚的月光朦朦朧朧,我們一伙809000后還走街串巷的宣傳,為新店開業(yè)做預售,大家都激情四射,看不出疲憊,這就是夢...
    Yoga笑笑閱讀 175評論 0 0

友情鏈接更多精彩內(nèi)容