Merge pull request #4523 from priankakariatyml:ios-gesture-recognizer-add-tests
PiperOrigin-RevId: 538848389
This commit is contained in:
commit
4c4a1d93b2
|
@ -48,6 +48,9 @@ static const NSInteger kGestureExpectedIndex = -1;
|
||||||
static NSString *const kExpectedErrorDomain = @"com.google.mediapipe.tasks";
|
static NSString *const kExpectedErrorDomain = @"com.google.mediapipe.tasks";
|
||||||
static const float kLandmarksErrorTolerance = 0.03f;
|
static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
|
|
||||||
|
static NSString *const kLiveStreamTestsDictGestureRecognizerKey = @"gesture_recognizer";
|
||||||
|
static NSString *const kLiveStreamTestsDictExpectationKey = @"expectation";
|
||||||
|
|
||||||
#define AssertEqualErrors(error, expectedError) \
|
#define AssertEqualErrors(error, expectedError) \
|
||||||
XCTAssertNotNil(error); \
|
XCTAssertNotNil(error); \
|
||||||
XCTAssertEqualObjects(error.domain, expectedError.domain); \
|
XCTAssertEqualObjects(error.domain, expectedError.domain); \
|
||||||
|
@ -72,12 +75,15 @@ static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
XCTAssertTrue(gestureRecognizerResult.landmarks.count == 0); \
|
XCTAssertTrue(gestureRecognizerResult.landmarks.count == 0); \
|
||||||
XCTAssertTrue(gestureRecognizerResult.worldLandmarks.count == 0);
|
XCTAssertTrue(gestureRecognizerResult.worldLandmarks.count == 0);
|
||||||
|
|
||||||
@interface MPPGestureRecognizerTests : XCTestCase
|
@interface MPPGestureRecognizerTests : XCTestCase <MPPGestureRecognizerLiveStreamDelegate> {
|
||||||
|
NSDictionary<NSString *, id> *_liveStreamSucceedsTestDict;
|
||||||
|
NSDictionary<NSString *, id> *_outOfOrderTimestampTestDict;
|
||||||
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation MPPGestureRecognizerTests
|
@implementation MPPGestureRecognizerTests
|
||||||
|
|
||||||
#pragma mark Results
|
#pragma mark Expected Results
|
||||||
|
|
||||||
+ (MPPGestureRecognizerResult *)emptyGestureRecognizerResult {
|
+ (MPPGestureRecognizerResult *)emptyGestureRecognizerResult {
|
||||||
return [[MPPGestureRecognizerResult alloc] initWithGestures:@[]
|
return [[MPPGestureRecognizerResult alloc] initWithGestures:@[]
|
||||||
|
@ -106,6 +112,8 @@ static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
shouldRemoveZPosition:YES];
|
shouldRemoveZPosition:YES];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#pragma mark Assert Gesture Recognizer Results
|
||||||
|
|
||||||
- (void)assertMultiHandLandmarks:(NSArray<NSArray<MPPNormalizedLandmark *> *> *)multiHandLandmarks
|
- (void)assertMultiHandLandmarks:(NSArray<NSArray<MPPNormalizedLandmark *> *> *)multiHandLandmarks
|
||||||
areApproximatelyEqualToExpectedMultiHandLandmarks:
|
areApproximatelyEqualToExpectedMultiHandLandmarks:
|
||||||
(NSArray<NSArray<MPPNormalizedLandmark *> *> *)expectedMultiHandLandmarks {
|
(NSArray<NSArray<MPPNormalizedLandmark *> *> *)expectedMultiHandLandmarks {
|
||||||
|
@ -175,6 +183,16 @@ static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
areApproximatelyEqualToExpectedMultiHandGestures:expectedGestureRecognizerResult.gestures];
|
areApproximatelyEqualToExpectedMultiHandGestures:expectedGestureRecognizerResult.gestures];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)assertResultsOfRecognizeImageWithFileInfo:(ResourceFileInfo *)fileInfo
|
||||||
|
usingGestureRecognizer:(MPPGestureRecognizer *)gestureRecognizer
|
||||||
|
approximatelyEqualsGestureRecognizerResult:
|
||||||
|
(MPPGestureRecognizerResult *)expectedGestureRecognizerResult {
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult =
|
||||||
|
[self recognizeImageWithFileInfo:fileInfo usingGestureRecognizer:gestureRecognizer];
|
||||||
|
[self assertGestureRecognizerResult:gestureRecognizerResult
|
||||||
|
isApproximatelyEqualToExpectedResult:expectedGestureRecognizerResult];
|
||||||
|
}
|
||||||
|
|
||||||
#pragma mark File
|
#pragma mark File
|
||||||
|
|
||||||
+ (NSString *)filePathWithFileInfo:(ResourceFileInfo *)fileInfo {
|
+ (NSString *)filePathWithFileInfo:(ResourceFileInfo *)fileInfo {
|
||||||
|
@ -221,7 +239,7 @@ static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
AssertEqualErrors(error, expectedError);
|
AssertEqualErrors(error, expectedError);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma mark Assert Gesture Recognizer Results
|
#pragma mark Recognize Helpers
|
||||||
|
|
||||||
- (MPPImage *)imageWithFileInfo:(ResourceFileInfo *)fileInfo {
|
- (MPPImage *)imageWithFileInfo:(ResourceFileInfo *)fileInfo {
|
||||||
MPPImage *image = [MPPImage imageFromBundleWithClass:[MPPGestureRecognizerTests class]
|
MPPImage *image = [MPPImage imageFromBundleWithClass:[MPPGestureRecognizerTests class]
|
||||||
|
@ -254,16 +272,6 @@ static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
return gestureRecognizerResult;
|
return gestureRecognizerResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)assertResultsOfRecognizeImageWithFileInfo:(ResourceFileInfo *)fileInfo
|
|
||||||
usingGestureRecognizer:(MPPGestureRecognizer *)gestureRecognizer
|
|
||||||
approximatelyEqualsGestureRecognizerResult:
|
|
||||||
(MPPGestureRecognizerResult *)expectedGestureRecognizerResult {
|
|
||||||
MPPGestureRecognizerResult *gestureRecognizerResult =
|
|
||||||
[self recognizeImageWithFileInfo:fileInfo usingGestureRecognizer:gestureRecognizer];
|
|
||||||
[self assertGestureRecognizerResult:gestureRecognizerResult
|
|
||||||
isApproximatelyEqualToExpectedResult:expectedGestureRecognizerResult];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark General Tests
|
#pragma mark General Tests
|
||||||
|
|
||||||
- (void)testRecognizeWithModelPathSucceeds {
|
- (void)testRecognizeWithModelPathSucceeds {
|
||||||
|
@ -279,4 +287,421 @@ static const float kLandmarksErrorTolerance = 0.03f;
|
||||||
thumbUpGestureRecognizerResult]];
|
thumbUpGestureRecognizerResult]];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithEmptyResultsSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult =
|
||||||
|
[self recognizeImageWithFileInfo:kNoHandsImage usingGestureRecognizer:gestureRecognizer];
|
||||||
|
AssertGestureRecognizerResultIsEmpty(gestureRecognizerResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithScoreThresholdSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions = [[MPPClassifierOptions alloc] init];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.scoreThreshold = 0.5f;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult =
|
||||||
|
[self recognizeImageWithFileInfo:kThumbUpImage usingGestureRecognizer:gestureRecognizer];
|
||||||
|
|
||||||
|
MPPGestureRecognizerResult *expectedGestureRecognizerResult =
|
||||||
|
[MPPGestureRecognizerTests thumbUpGestureRecognizerResult];
|
||||||
|
|
||||||
|
XCTAssertTrue(gestureRecognizerResult.gestures.count == 1);
|
||||||
|
AssertEqualGestures(gestureRecognizerResult.gestures[0][0],
|
||||||
|
expectedGestureRecognizerResult.gestures[0][0], 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithNumHandsSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
const NSInteger numHands = 2;
|
||||||
|
gestureRecognizerOptions.numHands = numHands;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult =
|
||||||
|
[self recognizeImageWithFileInfo:kTwoHandsImage usingGestureRecognizer:gestureRecognizer];
|
||||||
|
|
||||||
|
XCTAssertTrue(gestureRecognizerResult.handedness.count == numHands);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithRotationSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
gestureRecognizerOptions.numHands = 1;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
MPPImage *mppImage = [self imageWithFileInfo:kPointingUpRotatedImage
|
||||||
|
orientation:UIImageOrientationRight];
|
||||||
|
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult = [gestureRecognizer recognizeImage:mppImage
|
||||||
|
error:nil];
|
||||||
|
|
||||||
|
XCTAssertNotNil(gestureRecognizerResult);
|
||||||
|
|
||||||
|
XCTAssertEqual(gestureRecognizerResult.gestures.count, 1);
|
||||||
|
XCTAssertEqualObjects(gestureRecognizerResult.gestures[0][0].categoryName,
|
||||||
|
kExpectedPointingUpLabel);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithCannedGestureFistSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
gestureRecognizerOptions.numHands = 1;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
|
||||||
|
[self assertResultsOfRecognizeImageWithFileInfo:kFistImage
|
||||||
|
usingGestureRecognizer:gestureRecognizer
|
||||||
|
approximatelyEqualsGestureRecognizerResult:
|
||||||
|
[MPPGestureRecognizerTests fistGestureRecognizerResultWithLabel:kFistLabel]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithAllowGestureFistSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions = [[MPPClassifierOptions alloc] init];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.scoreThreshold = 0.5f;
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.categoryAllowlist = @[ kFistLabel ];
|
||||||
|
|
||||||
|
gestureRecognizerOptions.numHands = 1;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
|
||||||
|
[self assertResultsOfRecognizeImageWithFileInfo:kFistImage
|
||||||
|
usingGestureRecognizer:gestureRecognizer
|
||||||
|
approximatelyEqualsGestureRecognizerResult:
|
||||||
|
[MPPGestureRecognizerTests fistGestureRecognizerResultWithLabel:kFistLabel]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithDenyGestureFistSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions = [[MPPClassifierOptions alloc] init];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.scoreThreshold = 0.5f;
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.categoryDenylist = @[ kFistLabel ];
|
||||||
|
|
||||||
|
gestureRecognizerOptions.numHands = 1;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult =
|
||||||
|
[self recognizeImageWithFileInfo:kFistImage usingGestureRecognizer:gestureRecognizer];
|
||||||
|
AssertGestureRecognizerResultIsEmpty(gestureRecognizerResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithPreferAllowlistOverDenylistSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *gestureRecognizerOptions =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions = [[MPPClassifierOptions alloc] init];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.scoreThreshold = 0.5f;
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.categoryAllowlist = @[ kFistLabel ];
|
||||||
|
gestureRecognizerOptions.cannedGesturesClassifierOptions.categoryDenylist = @[ kFistLabel ];
|
||||||
|
|
||||||
|
gestureRecognizerOptions.numHands = 1;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:gestureRecognizerOptions];
|
||||||
|
|
||||||
|
[self assertResultsOfRecognizeImageWithFileInfo:kFistImage
|
||||||
|
usingGestureRecognizer:gestureRecognizer
|
||||||
|
approximatelyEqualsGestureRecognizerResult:
|
||||||
|
[MPPGestureRecognizerTests fistGestureRecognizerResultWithLabel:kFistLabel]];
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma mark Running Mode Tests
|
||||||
|
|
||||||
|
- (void)testCreateGestureRecognizerFailsWithDelegateInNonLiveStreamMode {
|
||||||
|
MPPRunningMode runningModesToTest[] = {MPPRunningModeImage, MPPRunningModeVideo};
|
||||||
|
for (int i = 0; i < sizeof(runningModesToTest) / sizeof(runningModesToTest[0]); i++) {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
options.runningMode = runningModesToTest[i];
|
||||||
|
options.gestureRecognizerLiveStreamDelegate = self;
|
||||||
|
|
||||||
|
[self assertCreateGestureRecognizerWithOptions:options
|
||||||
|
failsWithExpectedError:
|
||||||
|
[NSError
|
||||||
|
errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"The vision task is in image or video mode. The "
|
||||||
|
@"delegate must not be set in the task's options."
|
||||||
|
}]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCreateGestureRecognizerFailsWithMissingDelegateInLiveStreamMode {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
options.runningMode = MPPRunningModeLiveStream;
|
||||||
|
|
||||||
|
[self
|
||||||
|
assertCreateGestureRecognizerWithOptions:options
|
||||||
|
failsWithExpectedError:
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"The vision task is in live stream mode. An "
|
||||||
|
@"object must be set as the delegate of the task "
|
||||||
|
@"in its options to ensure asynchronous delivery "
|
||||||
|
@"of results."
|
||||||
|
}]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeFailsWithCallingWrongApiInImageMode {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:options];
|
||||||
|
|
||||||
|
MPPImage *image = [self imageWithFileInfo:kFistImage];
|
||||||
|
|
||||||
|
NSError *liveStreamApiCallError;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeAsyncImage:image
|
||||||
|
timestampInMilliseconds:0
|
||||||
|
error:&liveStreamApiCallError]);
|
||||||
|
|
||||||
|
NSError *expectedLiveStreamApiCallError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @"The vision task is not initialized with live "
|
||||||
|
@"stream mode. Current Running Mode: Image"
|
||||||
|
}];
|
||||||
|
|
||||||
|
AssertEqualErrors(liveStreamApiCallError, expectedLiveStreamApiCallError);
|
||||||
|
|
||||||
|
NSError *videoApiCallError;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeVideoFrame:image
|
||||||
|
timestampInMilliseconds:0
|
||||||
|
error:&videoApiCallError]);
|
||||||
|
|
||||||
|
NSError *expectedVideoApiCallError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @"The vision task is not initialized with "
|
||||||
|
@"video mode. Current Running Mode: Image"
|
||||||
|
}];
|
||||||
|
AssertEqualErrors(videoApiCallError, expectedVideoApiCallError);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeFailsWithCallingWrongApiInVideoMode {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
options.runningMode = MPPRunningModeVideo;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:options];
|
||||||
|
|
||||||
|
MPPImage *image = [self imageWithFileInfo:kFistImage];
|
||||||
|
|
||||||
|
NSError *liveStreamApiCallError;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeAsyncImage:image
|
||||||
|
timestampInMilliseconds:0
|
||||||
|
error:&liveStreamApiCallError]);
|
||||||
|
|
||||||
|
NSError *expectedLiveStreamApiCallError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @"The vision task is not initialized with live "
|
||||||
|
@"stream mode. Current Running Mode: Video"
|
||||||
|
}];
|
||||||
|
|
||||||
|
AssertEqualErrors(liveStreamApiCallError, expectedLiveStreamApiCallError);
|
||||||
|
|
||||||
|
NSError *imageApiCallError;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeImage:image error:&imageApiCallError]);
|
||||||
|
|
||||||
|
NSError *expectedImageApiCallError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @"The vision task is not initialized with "
|
||||||
|
@"image mode. Current Running Mode: Video"
|
||||||
|
}];
|
||||||
|
AssertEqualErrors(imageApiCallError, expectedImageApiCallError);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeFailsWithCallingWrongApiInLiveStreamMode {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
options.runningMode = MPPRunningModeLiveStream;
|
||||||
|
options.gestureRecognizerLiveStreamDelegate = self;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:options];
|
||||||
|
|
||||||
|
MPPImage *image = [self imageWithFileInfo:kFistImage];
|
||||||
|
|
||||||
|
NSError *imageApiCallError;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeImage:image error:&imageApiCallError]);
|
||||||
|
|
||||||
|
NSError *expectedImageApiCallError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @"The vision task is not initialized with "
|
||||||
|
@"image mode. Current Running Mode: Live Stream"
|
||||||
|
}];
|
||||||
|
AssertEqualErrors(imageApiCallError, expectedImageApiCallError);
|
||||||
|
|
||||||
|
NSError *videoApiCallError;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeVideoFrame:image
|
||||||
|
timestampInMilliseconds:0
|
||||||
|
error:&videoApiCallError]);
|
||||||
|
|
||||||
|
NSError *expectedVideoApiCallError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey : @"The vision task is not initialized with "
|
||||||
|
@"video mode. Current Running Mode: Live Stream"
|
||||||
|
}];
|
||||||
|
AssertEqualErrors(videoApiCallError, expectedVideoApiCallError);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithVideoModeSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
options.runningMode = MPPRunningModeVideo;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:options];
|
||||||
|
|
||||||
|
MPPImage *image = [self imageWithFileInfo:kThumbUpImage];
|
||||||
|
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
MPPGestureRecognizerResult *gestureRecognizerResult =
|
||||||
|
[gestureRecognizer recognizeVideoFrame:image timestampInMilliseconds:i error:nil];
|
||||||
|
[self assertGestureRecognizerResult:gestureRecognizerResult
|
||||||
|
isApproximatelyEqualToExpectedResult:[MPPGestureRecognizerTests
|
||||||
|
thumbUpGestureRecognizerResult]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithOutOfOrderTimestampsAndLiveStreamModeFails {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
options.runningMode = MPPRunningModeLiveStream;
|
||||||
|
options.gestureRecognizerLiveStreamDelegate = self;
|
||||||
|
|
||||||
|
XCTestExpectation *expectation = [[XCTestExpectation alloc]
|
||||||
|
initWithDescription:@"recognizeWithOutOfOrderTimestampsAndLiveStream"];
|
||||||
|
|
||||||
|
expectation.expectedFulfillmentCount = 1;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:options];
|
||||||
|
|
||||||
|
_outOfOrderTimestampTestDict = @{
|
||||||
|
kLiveStreamTestsDictGestureRecognizerKey : gestureRecognizer,
|
||||||
|
kLiveStreamTestsDictExpectationKey : expectation
|
||||||
|
};
|
||||||
|
|
||||||
|
MPPImage *image = [self imageWithFileInfo:kThumbUpImage];
|
||||||
|
|
||||||
|
XCTAssertTrue([gestureRecognizer recognizeAsyncImage:image timestampInMilliseconds:1 error:nil]);
|
||||||
|
|
||||||
|
NSError *error;
|
||||||
|
XCTAssertFalse([gestureRecognizer recognizeAsyncImage:image
|
||||||
|
timestampInMilliseconds:0
|
||||||
|
error:&error]);
|
||||||
|
|
||||||
|
NSError *expectedError =
|
||||||
|
[NSError errorWithDomain:kExpectedErrorDomain
|
||||||
|
code:MPPTasksErrorCodeInvalidArgumentError
|
||||||
|
userInfo:@{
|
||||||
|
NSLocalizedDescriptionKey :
|
||||||
|
@"INVALID_ARGUMENT: Input timestamp must be monotonically increasing."
|
||||||
|
}];
|
||||||
|
AssertEqualErrors(error, expectedError);
|
||||||
|
|
||||||
|
NSTimeInterval timeout = 0.5f;
|
||||||
|
[self waitForExpectations:@[ expectation ] timeout:timeout];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testRecognizeWithLiveStreamModeSucceeds {
|
||||||
|
MPPGestureRecognizerOptions *options =
|
||||||
|
[self gestureRecognizerOptionsWithModelFileInfo:kGestureRecognizerBundleAssetFile];
|
||||||
|
options.runningMode = MPPRunningModeLiveStream;
|
||||||
|
options.gestureRecognizerLiveStreamDelegate = self;
|
||||||
|
|
||||||
|
NSInteger iterationCount = 100;
|
||||||
|
|
||||||
|
// Because of flow limiting, we cannot ensure that the callback will be invoked `iterationCount`
|
||||||
|
// times. An normal expectation will fail if expectation.fulfill() is not called
|
||||||
|
// `expectation.expectedFulfillmentCount` times. If `expectation.isInverted = true`, the test will
|
||||||
|
// only succeed if expectation is not fulfilled for the specified `expectedFulfillmentCount`.
|
||||||
|
// Since in our case we cannot predict how many times the expectation is supposed to be fullfilled
|
||||||
|
// setting, `expectation.expectedFulfillmentCount` = `iterationCount` + 1 and
|
||||||
|
// `expectation.isInverted = true` ensures that test succeeds ifexpectation is fullfilled <=
|
||||||
|
// `iterationCount` times.
|
||||||
|
XCTestExpectation *expectation =
|
||||||
|
[[XCTestExpectation alloc] initWithDescription:@"recognizeWithLiveStream"];
|
||||||
|
|
||||||
|
expectation.expectedFulfillmentCount = iterationCount + 1;
|
||||||
|
expectation.inverted = YES;
|
||||||
|
|
||||||
|
MPPGestureRecognizer *gestureRecognizer =
|
||||||
|
[self createGestureRecognizerWithOptionsSucceeds:options];
|
||||||
|
|
||||||
|
_liveStreamSucceedsTestDict = @{
|
||||||
|
kLiveStreamTestsDictGestureRecognizerKey : gestureRecognizer,
|
||||||
|
kLiveStreamTestsDictExpectationKey : expectation
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Mimic initialization from CMSampleBuffer as live stream mode is most likely to be used
|
||||||
|
// with the iOS camera. AVCaptureVideoDataOutput sample buffer delegates provide frames of type
|
||||||
|
// `CMSampleBuffer`.
|
||||||
|
MPPImage *image = [self imageWithFileInfo:kThumbUpImage];
|
||||||
|
|
||||||
|
for (int i = 0; i < iterationCount; i++) {
|
||||||
|
XCTAssertTrue([gestureRecognizer recognizeAsyncImage:image
|
||||||
|
timestampInMilliseconds:i
|
||||||
|
error:nil]);
|
||||||
|
}
|
||||||
|
|
||||||
|
NSTimeInterval timeout = 0.5f;
|
||||||
|
[self waitForExpectations:@[ expectation ] timeout:timeout];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)gestureRecognizer:(MPPGestureRecognizer *)gestureRecognizer
|
||||||
|
didFinishRecognitionWithResult:(MPPGestureRecognizerResult *)gestureRecognizerResult
|
||||||
|
timestampInMilliseconds:(NSInteger)timestampInMilliseconds
|
||||||
|
error:(NSError *)error {
|
||||||
|
[self assertGestureRecognizerResult:gestureRecognizerResult
|
||||||
|
isApproximatelyEqualToExpectedResult:[MPPGestureRecognizerTests
|
||||||
|
thumbUpGestureRecognizerResult]];
|
||||||
|
|
||||||
|
if (gestureRecognizer == _outOfOrderTimestampTestDict[kLiveStreamTestsDictGestureRecognizerKey]) {
|
||||||
|
[_outOfOrderTimestampTestDict[kLiveStreamTestsDictExpectationKey] fulfill];
|
||||||
|
} else if (gestureRecognizer ==
|
||||||
|
_liveStreamSucceedsTestDict[kLiveStreamTestsDictGestureRecognizerKey]) {
|
||||||
|
[_liveStreamSucceedsTestDict[kLiveStreamTestsDictExpectationKey] fulfill];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -59,7 +59,7 @@ using ::mediapipe::tasks::ios::test::vision::utils::get_proto_from_pbtxt;
|
||||||
handednessProto:{landmarkDetectionResultProto.classifications()}
|
handednessProto:{landmarkDetectionResultProto.classifications()}
|
||||||
handLandmarksProto:{landmarkDetectionResultProto.landmarks()}
|
handLandmarksProto:{landmarkDetectionResultProto.landmarks()}
|
||||||
worldLandmarksProto:{landmarkDetectionResultProto.world_landmarks()}
|
worldLandmarksProto:{landmarkDetectionResultProto.world_landmarks()}
|
||||||
timestampInMilliSeconds:0];
|
timestampInMilliseconds:0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
|
@ -67,7 +67,7 @@ static const int kMicroSecondsPerMilliSecond = 1000;
|
||||||
handLandmarksProto
|
handLandmarksProto
|
||||||
worldLandmarksProto:
|
worldLandmarksProto:
|
||||||
(const std::vector<::mediapipe::LandmarkList> &)worldLandmarksProto
|
(const std::vector<::mediapipe::LandmarkList> &)worldLandmarksProto
|
||||||
timestampInMilliSeconds:(NSInteger)timestampInMilliseconds;
|
timestampInMilliseconds:(NSInteger)timestampInMilliseconds;
|
||||||
@end
|
@end
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
NS_ASSUME_NONNULL_END
|
||||||
|
|
|
@ -46,7 +46,7 @@ static const NSInteger kDefaultGestureIndex = -1;
|
||||||
handLandmarksProto
|
handLandmarksProto
|
||||||
worldLandmarksProto:
|
worldLandmarksProto:
|
||||||
(const std::vector<LandmarkListProto> &)worldLandmarksProto
|
(const std::vector<LandmarkListProto> &)worldLandmarksProto
|
||||||
timestampInMilliSeconds:(NSInteger)timestampInMilliseconds {
|
timestampInMilliseconds:(NSInteger)timestampInMilliseconds {
|
||||||
NSMutableArray<NSMutableArray<MPPCategory *> *> *multiHandGestures =
|
NSMutableArray<NSMutableArray<MPPCategory *> *> *multiHandGestures =
|
||||||
[NSMutableArray arrayWithCapacity:(NSUInteger)handGesturesProto.size()];
|
[NSMutableArray arrayWithCapacity:(NSUInteger)handGesturesProto.size()];
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ static const NSInteger kDefaultGestureIndex = -1;
|
||||||
std::vector<NormalizedLandmarkListProto>>()
|
std::vector<NormalizedLandmarkListProto>>()
|
||||||
worldLandmarksProto:worldLandmarksPacket
|
worldLandmarksProto:worldLandmarksPacket
|
||||||
.Get<std::vector<LandmarkListProto>>()
|
.Get<std::vector<LandmarkListProto>>()
|
||||||
timestampInMilliSeconds:timestampInMilliseconds];
|
timestampInMilliseconds:timestampInMilliseconds];
|
||||||
}
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user