利用CoreImage實現(xiàn)人臉識別_iOS

前言

因需求需要,需要實現(xiàn)人臉檢測功能慨仿,這次正好將這個功能整理了一下久脯,簡單的寫了一個Demo。代碼有點亂镰吆,不過帘撰,也不怎么想花時間去改了,感覺層次方面還算比較清晰的万皿,好了摧找,進(jìn)入正題核行。

一、導(dǎo)入框架蹬耘,實現(xiàn)自定義相機(jī)

1芝雪、導(dǎo)入框架

#import <AVFoundation/AVFoundation.h>
#import <CoreImage/CoreImage.h>

2、實現(xiàn)自定義相機(jī)

2.1初始化相機(jī)
#pragma mark - 初始化相機(jī)
- (void)getCameraSession
{
    //初始化會話
    _captureSession=[[AVCaptureSession alloc]init];
    if ([_captureSession canSetSessionPreset:AVCaptureSessionPreset1280x720]) {//設(shè)置分辨率
        _captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
    }
    //獲得輸入設(shè)備
    AVCaptureDevice *captureDevice=[self getCameraDeviceWithPosition:AVCaptureDevicePositionFront];//取得前置攝像頭
    if (!captureDevice) {
        NSLog(@"取得前置攝像頭時出現(xiàn)問題.");
        return;
    }
    
    NSError *error=nil;
    //根據(jù)輸入設(shè)備初始化設(shè)備輸入對象综苔,用于獲得輸入數(shù)據(jù)
    _captureDeviceInput=[[AVCaptureDeviceInput alloc]initWithDevice:captureDevice error:&error];
    if (error) {
        NSLog(@"取得設(shè)備輸入對象時出錯惩系,錯誤原因:%@",error.localizedDescription);
        return;
    }
    [_captureSession addInput:_captureDeviceInput];
    
    //初始化設(shè)備輸出對象,用于獲得輸出數(shù)據(jù)
    _captureStillImageOutput=[[AVCaptureStillImageOutput alloc]init];
    NSDictionary *outputSettings = @{AVVideoCodecKey:AVVideoCodecJPEG};
    [_captureStillImageOutput setOutputSettings:outputSettings];//輸出設(shè)置
    
    //將設(shè)備輸入添加到會話中
    if ([_captureSession canAddInput:_captureDeviceInput]) {
        [_captureSession addInput:_captureDeviceInput];
    }
    
    //將設(shè)備輸出添加到會話中
    if ([_captureSession canAddOutput:_captureStillImageOutput]) {
        [_captureSession addOutput:_captureStillImageOutput];
    }
    
    //創(chuàng)建視頻預(yù)覽層休里,用于實時展示攝像頭狀態(tài)
    _captureVideoPreviewLayer=[[AVCaptureVideoPreviewLayer alloc]initWithSession:self.captureSession];
    
    CALayer *layer=self.videoMainView.layer;
    layer.masksToBounds=YES;
    _captureVideoPreviewLayer.frame=layer.bounds;
    _captureVideoPreviewLayer.videoGravity=AVLayerVideoGravityResizeAspectFill;//填充模式
    
    //將視頻預(yù)覽層添加到界面中
    [layer addSublayer:_captureVideoPreviewLayer];
    [layer insertSublayer:_captureVideoPreviewLayer below:self.focusCursor.layer];
}

三蛆挫、獲取相機(jī)數(shù)據(jù)流

因為我需要動態(tài)進(jìn)行人臉識別,所以需要啟用數(shù)據(jù)流妙黍,在這里需要設(shè)置并遵守代理

// 遵守代理
<AVCaptureVideoDataOutputSampleBufferDelegate>
    AVCaptureVideoDataOutput *captureOutput = [[AVCaptureVideoDataOutput alloc] init];
    captureOutput.alwaysDiscardsLateVideoFrames = YES;
    dispatch_queue_t queue;
    queue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
    [captureOutput setSampleBufferDelegate:self queue:queue];
    
    NSString *key = (NSString *)kCVPixelBufferPixelFormatTypeKey;
    NSNumber *value = [NSNumber numberWithUnsignedInt:kCVPixelFormatType_32BGRA];
    NSDictionary *settings = @{key:value};
    [captureOutput setVideoSettings:settings];
    [self.captureSession addOutput:captureOutput];
四悴侵、實現(xiàn)相機(jī)數(shù)據(jù)流的代理方法
#pragma mark - Samle Buffer Delegate
// 抽樣緩存寫入時所調(diào)用的委托程序
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
       fromConnection:(AVCaptureConnection *)connection
{

}
// 這個方法是將數(shù)據(jù)流的幀轉(zhuǎn)換成圖片
//在該代理方法中,sampleBuffer是一個Core Media對象拭嫁,可以引入Core Video供使用
// 通過抽樣緩存數(shù)據(jù)創(chuàng)建一個UIImage對象
- (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer
{
    CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CIImage *ciImage = [CIImage imageWithCVPixelBuffer:imageBuffer];
    CIContext *temporaryContext = [CIContext contextWithOptions:nil];
    CGImageRef videoImage = [temporaryContext createCGImage:ciImage fromRect:CGRectMake(0, 0, CVPixelBufferGetWidth(imageBuffer), CVPixelBufferGetHeight(imageBuffer))];
    UIImage *result = [[UIImage alloc] initWithCGImage:videoImage scale:1.0 orientation:UIImageOrientationLeftMirrored];
    CGImageRelease(videoImage);
    return result;
}

五可免、對圖片進(jìn)行處理

在這里需要說明一下,因為上面的方法轉(zhuǎn)換出來的圖片都是反過來的做粤,所以需要再轉(zhuǎn)一下

/**
 *  用來處理圖片翻轉(zhuǎn)90度
 *
 *  @param aImage
 *
 *  @return
 */
- (UIImage *)fixOrientation:(UIImage *)aImage
{
    // No-op if the orientation is already correct
    if (aImage.imageOrientation == UIImageOrientationUp)
        return aImage;
    
    CGAffineTransform transform = CGAffineTransformIdentity;
    
    switch (aImage.imageOrientation) {
        case UIImageOrientationDown:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height);
            transform = CGAffineTransformRotate(transform, M_PI);
            break;
            
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
            transform = CGAffineTransformRotate(transform, M_PI_2);
            break;
            
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, 0, aImage.size.height);
            transform = CGAffineTransformRotate(transform, -M_PI_2);
            break;
        default:
            break;
    }
    
    switch (aImage.imageOrientation) {
        case UIImageOrientationUpMirrored:
        case UIImageOrientationDownMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.width, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
            
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRightMirrored:
            transform = CGAffineTransformTranslate(transform, aImage.size.height, 0);
            transform = CGAffineTransformScale(transform, -1, 1);
            break;
        default:
            break;
    }
    
    // Now we draw the underlying CGImage into a new context, applying the transform
    // calculated above.
    CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height,
                                             CGImageGetBitsPerComponent(aImage.CGImage), 0,
                                             CGImageGetColorSpace(aImage.CGImage),
                                             CGImageGetBitmapInfo(aImage.CGImage));
    CGContextConcatCTM(ctx, transform);
    switch (aImage.imageOrientation) {
        case UIImageOrientationLeft:
        case UIImageOrientationLeftMirrored:
        case UIImageOrientationRight:
        case UIImageOrientationRightMirrored:
            // Grr...
            CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage);
            break;
            
        default:
            CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage);
            break;
    }
    
    // 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;
}
六浇借、利用CoreImage中的detectFace進(jìn)行人臉檢測
/**識別臉部*/
-(NSArray *)detectFaceWithImage:(UIImage *)faceImag
{
    //此處是CIDetectorAccuracyHigh,若用于real-time的人臉檢測怕品,則用CIDetectorAccuracyLow妇垢,更快
    CIDetector *faceDetector = [CIDetector detectorOfType:CIDetectorTypeFace
                                                  context:nil
                                                  options:@{CIDetectorAccuracy: CIDetectorAccuracyHigh}];
    CIImage *ciimg = [CIImage imageWithCGImage:faceImag.CGImage];
    NSArray *features = [faceDetector featuresInImage:ciimg];
    return features;
}
七、總結(jié)

Demo源碼:
鏈接 https://github.com/daniel1214/CoreImage_Detector

我的思路是將相機(jī)里獲取的數(shù)據(jù)肉康,通過代理方法闯估,將幀轉(zhuǎn)換成每一張圖片,拿到圖片吼和,去實現(xiàn)人臉識別涨薪。功能沒問題,但是很耗性能炫乓,但是暫時我不太清楚還有什么好的方法來實現(xiàn)刚夺,如果有什么好的方法,也可以留言告訴我末捣,感謝侠姑!亦或者對我寫的有些疑問也可以留言,看到我會第一時間回復(fù)的箩做,當(dāng)然也可以電郵我:gzd1214@163.com莽红,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卒茬,一起剝皮案震驚了整個濱河市船老,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌圃酵,老刑警劉巖柳畔,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異郭赐,居然都是意外死亡薪韩,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門捌锭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俘陷,“玉大人,你說我怎么就攤上這事观谦±埽” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵豁状,是天一觀的道長捉偏。 經(jīng)常有香客問我,道長泻红,這世上最難降的妖魔是什么夭禽? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮谊路,結(jié)果婚禮上讹躯,老公的妹妹穿的比我還像新娘。我一直安慰自己缠劝,他們只是感情好潮梯,可當(dāng)我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著剩彬,像睡著了一般酷麦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喉恋,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天沃饶,我揣著相機(jī)與錄音,去河邊找鬼轻黑。 笑死糊肤,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的氓鄙。 我是一名探鬼主播馆揉,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼抖拦!你這毒婦竟也來了升酣?” 一聲冷哼從身側(cè)響起舷暮,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎噩茄,沒想到半個月后下面,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡绩聘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年沥割,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凿菩。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡机杜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出衅谷,到底是詐尸還是另有隱情椒拗,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布会喝,位于F島的核電站陡叠,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏肢执。R本人自食惡果不足惜枉阵,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望预茄。 院中可真熱鬧兴溜,春花似錦、人聲如沸耻陕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽诗宣。三九已至膘怕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間召庞,已是汗流浹背岛心。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留篮灼,地道東北人忘古。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像诅诱,于是被迫代替她去往敵國和親髓堪。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,629評論 2 354

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