2014 snapchat source code
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

  1. //
  2. // SCCaptureFaceDetectionParser.m
  3. // Snapchat
  4. //
  5. // Created by Jiyang Zhu on 3/13/18.
  6. // Copyright © 2018 Snapchat, Inc. All rights reserved.
  7. //
  8. #import "SCCaptureFaceDetectionParser.h"
  9. #import <SCFoundation/NSArray+Helpers.h>
  10. #import <SCFoundation/SCLog.h>
  11. #import <SCFoundation/SCTraceODPCompatible.h>
  12. @implementation SCCaptureFaceDetectionParser {
  13. CGFloat _minimumArea;
  14. }
  15. - (instancetype)initWithFaceBoundsAreaThreshold:(CGFloat)minimumArea
  16. {
  17. self = [super init];
  18. if (self) {
  19. _minimumArea = minimumArea;
  20. }
  21. return self;
  22. }
  23. - (NSDictionary<NSNumber *, NSValue *> *)parseFaceBoundsByFaceIDFromMetadataObjects:
  24. (NSArray<__kindof AVMetadataObject *> *)metadataObjects
  25. {
  26. SCTraceODPCompatibleStart(2);
  27. NSMutableArray *faceObjects = [NSMutableArray array];
  28. [metadataObjects
  29. enumerateObjectsUsingBlock:^(__kindof AVMetadataObject *_Nonnull obj, NSUInteger idx, BOOL *_Nonnull stop) {
  30. if ([obj isKindOfClass:[AVMetadataFaceObject class]]) {
  31. [faceObjects addObject:obj];
  32. }
  33. }];
  34. SC_GUARD_ELSE_RETURN_VALUE(faceObjects.count > 0, nil);
  35. NSMutableDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID =
  36. [NSMutableDictionary dictionaryWithCapacity:faceObjects.count];
  37. for (AVMetadataFaceObject *faceObject in faceObjects) {
  38. CGRect bounds = faceObject.bounds;
  39. if (CGRectGetWidth(bounds) * CGRectGetHeight(bounds) >= _minimumArea) {
  40. [faceBoundsByFaceID setObject:[NSValue valueWithCGRect:bounds] forKey:@(faceObject.faceID)];
  41. }
  42. }
  43. return faceBoundsByFaceID;
  44. }
  45. - (NSDictionary<NSNumber *, NSValue *> *)parseFaceBoundsByFaceIDFromCIFeatures:(NSArray<__kindof CIFeature *> *)features
  46. withImageSize:(CGSize)imageSize
  47. imageOrientation:
  48. (CGImagePropertyOrientation)imageOrientation
  49. {
  50. SCTraceODPCompatibleStart(2);
  51. NSArray<CIFaceFeature *> *faceFeatures = [features filteredArrayUsingBlock:^BOOL(id _Nonnull evaluatedObject) {
  52. return [evaluatedObject isKindOfClass:[CIFaceFeature class]];
  53. }];
  54. SC_GUARD_ELSE_RETURN_VALUE(faceFeatures.count > 0, nil);
  55. NSMutableDictionary<NSNumber *, NSValue *> *faceBoundsByFaceID =
  56. [NSMutableDictionary dictionaryWithCapacity:faceFeatures.count];
  57. CGFloat width = imageSize.width;
  58. CGFloat height = imageSize.height;
  59. SCLogGeneralInfo(@"Face feature count:%d", faceFeatures.count);
  60. for (CIFaceFeature *faceFeature in faceFeatures) {
  61. SCLogGeneralInfo(@"Face feature: hasTrackingID:%d, bounds:%@", faceFeature.hasTrackingID,
  62. NSStringFromCGRect(faceFeature.bounds));
  63. if (faceFeature.hasTrackingID) {
  64. CGRect transferredBounds;
  65. // Somehow the detected bounds for back camera is mirrored.
  66. if (imageOrientation == kCGImagePropertyOrientationRight) {
  67. transferredBounds = CGRectMake(
  68. CGRectGetMinX(faceFeature.bounds) / width, 1 - CGRectGetMaxY(faceFeature.bounds) / height,
  69. CGRectGetWidth(faceFeature.bounds) / width, CGRectGetHeight(faceFeature.bounds) / height);
  70. } else {
  71. transferredBounds = CGRectMake(
  72. CGRectGetMinX(faceFeature.bounds) / width, CGRectGetMinY(faceFeature.bounds) / height,
  73. CGRectGetWidth(faceFeature.bounds) / width, CGRectGetHeight(faceFeature.bounds) / height);
  74. }
  75. if (CGRectGetWidth(transferredBounds) * CGRectGetHeight(transferredBounds) >= _minimumArea) {
  76. [faceBoundsByFaceID setObject:[NSValue valueWithCGRect:transferredBounds]
  77. forKey:@(faceFeature.trackingID)];
  78. }
  79. }
  80. }
  81. return faceBoundsByFaceID;
  82. }
  83. @end