iOS上的相機(jī)捕捉

UIImagePickerController

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

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

使用方式

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

:需要導(dǎo)入 <MobileCoreServices/MobileCoreServices.h> 框架是因?yàn)?kUTTypeImagekUTTypeMovie 被定義在其中吏砂。

:實(shí)際上我們不會(huì)實(shí)現(xiàn) UINavigationControllerDelegate 中定義的任何協(xié)議方法撵儿,但由于 UIImagePickerController 繼承自 UINavigationController,并且改變了 UINavigationController的行為狐血。因此淀歇,我們?nèi)匀恍枰鲃?dòng)聲明我們的視圖控制器遵守 UINavigationControllerDelegate 協(xié)議。

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

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

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

UIImagePickerControllerSourceType 是一個(gè)枚舉類型匈织,用于設(shè)置要跳轉(zhuǎn)到哪個(gè)界面(相機(jī)拍照浪默、照片圖庫、相冊(cè)) :

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)置像機(jī)
    UIImagePickerControllerSourceTypeSavedPhotosAlbum API_DEPRECATED("Will be removed in a future release, use PHPicker.", ios(2, API_TO_BE_DEPRECATED)), // 相冊(cè)
} API_UNAVAILABLE(tvos);

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

設(shè)置示例:

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

3. 設(shè)置攝像頭捕捉模式

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

UIImagePickerControllerCameraCaptureMode 是一個(gè)枚舉類型:

UIImagePickerControllerCameraCaptureModePhoto, // 拍照模式零远,默認(rèn)
UIImagePickerControllerCameraCaptureModeVideo   // 視頻錄制模式

拍攝圖片:

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

拍攝視頻:

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

4. 攝像頭設(shè)備

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

UIImagePickerControllerCameraDeviceRear,   //后置攝像頭牵辣,默認(rèn)
UIImagePickerControllerCameraDeviceFront   //前置攝像頭         

設(shè)置示例:

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

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

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

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

設(shè)置示例:

imagePicker.videoQuality = UIImagePickerControllerQualityTypeLow;

6. 設(shè)置閃光燈模式

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

UIImagePickerControllerCameraFlashMode 是一個(gè)枚舉類型,用于設(shè)置閃光燈關(guān)閉师脂、自動(dòng)担孔、打開江锨。

UIImagePickerControllerCameraFlashModeOff  = -1,
UIImagePickerControllerCameraFlashModeAuto = 0,默認(rèn)
UIImagePickerControllerCameraFlashModeOn   = 1

設(shè)置示例:

imagePicker.cameraFlashMode = UIImagePickerControllerCameraFlashModeAuto

7. 遵循協(xié)議

imagePicker.delegate = self;

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

// 顯示標(biāo)準(zhǔn)相機(jī) UI糕篇,
imagePicker.showsCameraControls = NO;

9. 設(shè)置自定義覆蓋圖層

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 對(duì)象

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

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

你需要關(guān)注協(xié)議中的這兩個(gè)方法:

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

擴(kuò)展函數(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取出此時(shí)攝像頭的媒體類型
    NSString *mediaType = info[UIImagePickerControllerMediaType];
    
    if ([mediaType isEqualToString:(NSString *)kUTTypeImage]) {
        // 【拍照模式】
        // 通過info字典獲取選擇的照片
        UIImage *image = info[UIImagePickerControllerOriginalImage];
     
        // 將照片放入U(xiǎn)IImageView對(duì)象顯示在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);
        }
    }
    
    // 關(guān)閉UIImagePickerController對(duì)象
    [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 封裝成了一個(gè)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

使用時(shí)先導(dǎo)入頭文件拌消,設(shè)置屬性:

@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;    //媒體管理會(huì)話
@property (strong, nonatomic) AVCaptureDeviceInput *captureInput;   //輸入數(shù)據(jù)對(duì)象
@property (strong, nonatomic) AVCaptureStillImageOutput *imageOutput;   //輸出數(shù)據(jù)對(duì)象
@property (strong, nonatomic) AVCaptureVideoPreviewLayer *captureLayer; //視頻預(yù)覽圖層


@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 {
    // 設(shè)置導(dǎo)航欄標(biāo)題&字體&顏色
    self.navigationItem.title = @"AVFoundation拍照";
    [self.navigationController.navigationBar setTitleTextAttributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17],NSForegroundColorAttributeName:ThemeColor}];
}

#pragma mark 初始化攝像頭
- (void)initCaptureSession {
    /*
     1?? AVCaptureSession
     媒體捕捉會(huì)話挑豌,管理輸入與輸出之間的數(shù)據(jù)流,以及在出現(xiàn)問題時(shí)生成運(yùn)行時(shí)錯(cuò)誤墩崩。
     負(fù)責(zé)把捕獲到的音頻視頻數(shù)據(jù)輸出到輸出設(shè)備上氓英,一個(gè)會(huì)話可以有多個(gè)輸入輸入。
     */
    // 1.創(chuàng)建媒體管理會(huì)話
    AVCaptureSession *captureSession = [[AVCaptureSession alloc] init];
    
    self.session = captureSession;
    
    // 判斷分辨率是否支持 1280*720鹦筹,支持就設(shè)置為:1280*720
    if ([captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {
        captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    
    /*
     2?? AVCaptureDevice
     關(guān)于相機(jī)硬件的接口债蓝。它被用于控制硬件特性,諸如鏡頭的位置盛龄、曝光饰迹、閃光燈等。
     */
    
    // 2.獲取后置攝像頭
    AVCaptureDevice *captureDevice = nil;
    NSArray *cameras = [AVCaptureDevice devicesWithMediaType:AVMediaTypeVideo];
    for (AVCaptureDevice *camera in cameras) {
        if (camera.position == AVCaptureDevicePositionBack) {
            // 獲得后置攝像頭
            captureDevice = camera;
        }
    }
    if (!captureDevice) {
        NSLog(@"2.取得后置攝像頭錯(cuò)誤余舶!");
        return;
    }
    
    // 取得前置攝像頭
    //    AVCaptureDevice *frontCaptureDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionFront];
    
    /*
     3?? AVCaptureDeviceInput
     設(shè)備輸入數(shù)據(jù)管理對(duì)象啊鸭,管理輸入數(shù)據(jù)
     */
    
    // 3.創(chuàng)建輸入數(shù)據(jù)對(duì)象
    NSError *error = nil;
    AVCaptureDeviceInput *captureInput = [AVCaptureDeviceInput deviceInputWithDevice:captureDevice error:&error];
    if (error) {
        NSLog(@"3.創(chuàng)建輸入數(shù)據(jù)對(duì)象錯(cuò)誤");
        return;
    }
    
    self.captureInput = captureInput;
    
    /*
     4?? AVCaptureOutput
     設(shè)備輸出數(shù)據(jù)管理對(duì)象,管理輸出數(shù)據(jù)匿值,通常使用它的子類:
     AVCaptureAudioDataOutput   //輸出音頻管理對(duì)象赠制,輸出數(shù)據(jù)為NSData
     AVCaptureStillImageDataOutput  //輸出圖片管理對(duì)象,輸出數(shù)據(jù)為NSData
     AVCaptureVideoDataOutput   //輸出視頻管理對(duì)象挟憔,輸出數(shù)據(jù)為NSData
     
     AVCaptureFileOutput
     輸出文件管理對(duì)象钟些,輸出數(shù)據(jù)以文件形式輸出
     {//子類
     AVCaptureAudioFileOutput   //輸出是音頻文件
     AVCaptureMovieFileOutput   //輸出是視頻文件
     }
     */
    
    // 4.創(chuàng)建輸出數(shù)據(jù)對(duì)象
    AVCaptureStillImageOutput *imageOutpot = [[AVCaptureStillImageOutput alloc] init];
    NSDictionary *setting = @{
                              AVVideoCodecKey:AVVideoCodecJPEG
                              };
    [imageOutpot setOutputSettings:setting];
    
    self.imageOutput = imageOutpot;
    
    // 5?? 5.添加【輸入數(shù)據(jù)對(duì)象】和【輸出對(duì)象】到會(huì)話中
    if ([captureSession canAddInput:captureInput]) {
        [captureSession addInput:captureInput];
    }
    if ([captureSession canAddOutput:imageOutpot]) {
        [captureSession addOutput:imageOutpot];
    }
    
    
    /*
     6?? AVCaptureVideoPreviewLayer
     實(shí)時(shí)預(yù)覽圖層
     AVCaptureVideoPreviewLayer 是 CALayer 的子類,可被用于自動(dòng)顯示相機(jī)產(chǎn)生的實(shí)時(shí)圖像绊谭。它還有幾個(gè)工具性質(zhì)的方法政恍,可將 layer 上的坐標(biāo)轉(zhuǎn)化到設(shè)備上。它看起來像輸出达传,但其實(shí)不是篙耗。另外,它擁有 session (outputs 被 session 所擁有)宪赶。
     */
    
    // 6.創(chuàng)建實(shí)時(shí)預(yù)覽圖層
    AVCaptureVideoPreviewLayer *previewlayer = [AVCaptureVideoPreviewLayer layerWithSession:captureSession];
    self.view.layer.masksToBounds = YES;
    previewlayer.frame = self.view.bounds;
    previewlayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
    // 【預(yù)覽圖層】插入在【拍照按鈕】的下方
    [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ù)設(shè)備輸出獲得連接
    AVCaptureConnection *connection = [self.imageOutput connectionWithMediaType:AVMediaTypeVideo];
    // 通過連接獲得設(shè)備的輸出數(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);   //保存到相冊(cè)
        self.captureLayer.hidden = YES;
        self.canptureBtn.hidden = YES;
        self.openCaptureBtn.hidden = NO;
        [self.session stopRunning];
    }];
}

@end

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

以及源碼:VideoCaptureDemo

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

人臉識(shí)別過程分3個(gè)步驟:

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

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

#pragma mark - 識(shí)別人臉

/**
 識(shí)別人臉?biāo)惴?
 @param image 輸入的圖片
 */
- (void)faceDetectWithImage:(UIImage *)image {
    
    NSDictionary *imageOptions =  [NSDictionary dictionaryWithObject:@(5) forKey:CIDetectorImageOrientation];
    
    // 將圖像轉(zhuǎn)換為CIImage
    CIImage *personciImage = [CIImage imageWithCGImage:image.CGImage];
    
    // 設(shè)置識(shí)別參數(shù)
    NSDictionary *opts = [NSDictionary dictionaryWithObject:
                          CIDetectorAccuracyHigh forKey:CIDetectorAccuracy];
    CIContext *context = [CIContext contextWithOptions:nil];
    //聲明一個(gè)CIDetector邓厕,并設(shè)定識(shí)別器類型為人臉識(shí)別
    CIDetector *faceDetector=[CIDetector detectorOfType:CIDetectorTypeFace context:context options:opts];
    
    // 識(shí)別出人臉數(shù)組
    // featuresInImage:方法 識(shí)別器會(huì)找到所給圖像中的人臉逝嚎,最后返回一個(gè)人臉數(shù)組
    NSArray *features = [faceDetector featuresInImage:personciImage options:imageOptions];
    
    // 得到圖片的尺寸
    CGSize inputImageSize = [personciImage extent].size;
    // 利用仿射變換將image沿Y軸對(duì)稱
    CGAffineTransform transform = CGAffineTransformScale(CGAffineTransformIdentity, 1, -1);
    // 將圖片上移
    transform = CGAffineTransformTranslate(transform, 0, -inputImageSize.height);
    
    // 遍歷識(shí)別到的人臉
    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){}
    }
    
    // 裁剪識(shí)別到的人臉
    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í)別人臉數(shù)::%lu",(unsigned long)[features count]);
    }   
}

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

image

參考

OpenCV 相關(guān)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市遵绰,隨后出現(xiàn)的幾起案子辽幌,更是在濱河造成了極大的恐慌,老刑警劉巖椿访,帶你破解...
    沈念sama閱讀 216,402評(píng)論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件乌企,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡成玫,警方通過查閱死者的電腦和手機(jī)逛犹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來梁剔,“玉大人虽画,你說我怎么就攤上這事∪俨。” “怎么了码撰?”我有些...
    開封第一講書人閱讀 162,483評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長个盆。 經(jīng)常有香客問我脖岛,道長朵栖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評(píng)論 1 292
  • 正文 為了忘掉前任柴梆,我火速辦了婚禮陨溅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘绍在。我一直安慰自己门扇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,176評(píng)論 6 388
  • 文/花漫 我一把揭開白布偿渡。 她就那樣靜靜地躺著臼寄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪溜宽。 梳的紋絲不亂的頭發(fā)上吉拳,一...
    開封第一講書人閱讀 51,146評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音适揉,去河邊找鬼留攒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛嫉嘀,可吹牛的內(nèi)容都是我干的稼跳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,032評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼吃沪,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼汤善!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起票彪,我...
    開封第一講書人閱讀 38,896評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤红淡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后降铸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體在旱,經(jīng)...
    沈念sama閱讀 45,311評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,536評(píng)論 2 332
  • 正文 我和宋清朗相戀三年推掸,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了桶蝎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,696評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谅畅,死狀恐怖登渣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毡泻,我是刑警寧澤胜茧,帶...
    沈念sama閱讀 35,413評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響呻顽,放射性物質(zhì)發(fā)生泄漏雹顺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,008評(píng)論 3 325
  • 文/蒙蒙 一廊遍、第九天 我趴在偏房一處隱蔽的房頂上張望嬉愧。 院中可真熱鬧,春花似錦喉前、人聲如沸没酣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽四康。三九已至搪搏,卻和暖如春狭握,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背疯溺。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評(píng)論 1 269
  • 我被黑心中介騙來泰國打工论颅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人囱嫩。 一個(gè)月前我還...
    沈念sama閱讀 47,698評(píng)論 2 368
  • 正文 我出身青樓恃疯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親墨闲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子今妄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,592評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

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