You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
94 lines
3.9 KiB
94 lines
3.9 KiB
//
|
|
// SCCaptureFaceDetectionParser.m
|
|
// Snapchat
|
|
//
|
|
// Created by Jiyang Zhu on 3/13/18.
|
|
// Copyright © 2018 Snapchat, Inc. All rights reserved.
|
|
//
|
|
|
|
#import "SCCaptureFaceDetectionParser.h"
|
|
|
|
#import <SCFoundation/NSArray+Helpers.h>
|
|
#import <SCFoundation/SCLog.h>
|
|
#import <SCFoundation/SCTraceODPCompatible.h>
|
|
|
|
@implementation SCCaptureFaceDetectionParser {
|
|
CGFloat _minimumArea;
|
|
}
|
|
|
|
- (instancetype)initWithFaceBoundsAreaThreshold:(CGFloat)minimumArea
|
|
{
|
|
self = [super init];
|
|
if (self) {
|
|
_minimumArea = minimumArea;
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSDictionary<NSNumber *, NSValue *> *)parseFaceBoundsByFaceIDFromMetadataObjects:
|
|
(NSArray<__kindof AVMetadataObject *> *)metadataObjects
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
NSMutableArray *faceObjects = [NSMutableArray array];
|
|
[metadataObjects
|
|
enumerateObjectsUsingBlock:^(__kindof AVMetadataObject *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
|
|
if ([obj isKindOfClass:[AVMetadataFaceObject class]]) {
|
|
[faceObjects addObject:obj];
|
|
}
|
|
}];
|
|
|
|
SC_GUARD_ELSE_RETURN_VALUE(faceObjects.count > 0, nil);
|
|
|
|
NSMutableDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID =
|
|
[NSMutableDictionary dictionaryWithCapacity:faceObjects.count];
|
|
for (AVMetadataFaceObject *faceObject in faceObjects) {
|
|
CGRect bounds = faceObject.bounds;
|
|
if (CGRectGetWidth(bounds) * CGRectGetHeight(bounds) >= _minimumArea) {
|
|
[faceBoundsByFaceID setObject:[NSValue valueWithCGRect:bounds] forKey:@(faceObject.faceID)];
|
|
}
|
|
}
|
|
return faceBoundsByFaceID;
|
|
}
|
|
|
|
- (NSDictionary<NSNumber *, NSValue *> *)parseFaceBoundsByFaceIDFromCIFeatures:(NSArray<__kindof CIFeature *> *)features
|
|
withImageSize:(CGSize)imageSize
|
|
imageOrientation:
|
|
(CGImagePropertyOrientation)imageOrientation
|
|
{
|
|
SCTraceODPCompatibleStart(2);
|
|
NSArray<CIFaceFeature *> *faceFeatures = [features filteredArrayUsingBlock:^BOOL(id _Nonnull evaluatedObject) {
|
|
return [evaluatedObject isKindOfClass:[CIFaceFeature class]];
|
|
}];
|
|
|
|
SC_GUARD_ELSE_RETURN_VALUE(faceFeatures.count > 0, nil);
|
|
|
|
NSMutableDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID =
|
|
[NSMutableDictionary dictionaryWithCapacity:faceFeatures.count];
|
|
CGFloat width = imageSize.width;
|
|
CGFloat height = imageSize.height;
|
|
SCLogGeneralInfo(@"Face feature count:%d", faceFeatures.count);
|
|
for (CIFaceFeature *faceFeature in faceFeatures) {
|
|
SCLogGeneralInfo(@"Face feature: hasTrackingID:%d, bounds:%@", faceFeature.hasTrackingID,
|
|
NSStringFromCGRect(faceFeature.bounds));
|
|
if (faceFeature.hasTrackingID) {
|
|
CGRect transferredBounds;
|
|
// Somehow the detected bounds for back camera is mirrored.
|
|
if (imageOrientation == kCGImagePropertyOrientationRight) {
|
|
transferredBounds = CGRectMake(
|
|
CGRectGetMinX(faceFeature.bounds) / width, 1 - CGRectGetMaxY(faceFeature.bounds) / height,
|
|
CGRectGetWidth(faceFeature.bounds) / width, CGRectGetHeight(faceFeature.bounds) / height);
|
|
} else {
|
|
transferredBounds = CGRectMake(
|
|
CGRectGetMinX(faceFeature.bounds) / width, CGRectGetMinY(faceFeature.bounds) / height,
|
|
CGRectGetWidth(faceFeature.bounds) / width, CGRectGetHeight(faceFeature.bounds) / height);
|
|
}
|
|
if (CGRectGetWidth(transferredBounds) * CGRectGetHeight(transferredBounds) >= _minimumArea) {
|
|
[faceBoundsByFaceID setObject:[NSValue valueWithCGRect:transferredBounds]
|
|
forKey:@(faceFeature.trackingID)];
|
|
}
|
|
}
|
|
}
|
|
return faceBoundsByFaceID;
|
|
}
|
|
|
|
@end
|