Merge pull request #4815 from priankakariatyml:ios-image-segmenter-basic-tests
PiperOrigin-RevId: 567102079
This commit is contained in:
commit
19c9d328cb
|
@ -53,6 +53,7 @@ objc_library(
|
|||
"//mediapipe/tasks/testdata/vision:test_protos",
|
||||
],
|
||||
deps = [
|
||||
"//mediapipe/tasks/ios/common:MPPCommon",
|
||||
"//mediapipe/tasks/ios/test/vision/utils:MPPImageTestUtils",
|
||||
"//mediapipe/tasks/ios/test/vision/utils:MPPMaskTestUtils",
|
||||
"//mediapipe/tasks/ios/vision/image_segmenter:MPPImageSegmenter",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.h>
|
||||
|
||||
#import "mediapipe/tasks/ios/common/sources/MPPCommon.h"
|
||||
#import "mediapipe/tasks/ios/test/vision/utils/sources/MPPImage+TestUtils.h"
|
||||
#import "mediapipe/tasks/ios/test/vision/utils/sources/MPPMask+TestUtils.h"
|
||||
#import "mediapipe/tasks/ios/vision/image_segmenter/sources/MPPImageSegmenter.h"
|
||||
|
@ -30,11 +31,36 @@ static MPPFileInfo *const kSegmentationImageFileInfo =
|
|||
[[MPPFileInfo alloc] initWithName:@"segmentation_input_rotation0" type:@"jpg"];
|
||||
static MPPFileInfo *const kSegmentationGoldenImageFileInfo =
|
||||
[[MPPFileInfo alloc] initWithName:@"segmentation_golden_rotation0" type:@"png"];
|
||||
static MPPFileInfo *const kImageSegmenterModel = [[MPPFileInfo alloc] initWithName:@"deeplabv3"
|
||||
type:@"tflite"];
|
||||
|
||||
static MPPFileInfo *const kMozartImageFileInfo = [[MPPFileInfo alloc] initWithName:@"mozart_square"
|
||||
type:@"jpg"];
|
||||
static MPPFileInfo *const kMozart128x128SegmentationGoldenImageFileInfo =
|
||||
[[MPPFileInfo alloc] initWithName:@"selfie_segm_128_128_3_expected_mask" type:@"jpg"];
|
||||
static MPPFileInfo *const kMozart144x256SegmentationGoldenImageFileInfo =
|
||||
[[MPPFileInfo alloc] initWithName:@"selfie_segm_144_256_3_expected_mask" type:@"jpg"];
|
||||
|
||||
static MPPFileInfo *const kImageSegmenterModelFileInfo =
|
||||
[[MPPFileInfo alloc] initWithName:@"deeplabv3" type:@"tflite"];
|
||||
static MPPFileInfo *const kSelfie128x128ModelFileInfo =
|
||||
[[MPPFileInfo alloc] initWithName:@"selfie_segm_128_128_3" type:@"tflite"];
|
||||
static MPPFileInfo *const kSelfie144x256ModelFileInfo =
|
||||
[[MPPFileInfo alloc] initWithName:@"selfie_segm_144_256_3" type:@"tflite"];
|
||||
|
||||
static NSString *const kExpectedErrorDomain = @"com.google.mediapipe.tasks";
|
||||
static NSString *const kLiveStreamTestsDictImageSegmenterKey = @"image_segmenter";
|
||||
static NSString *const kLiveStreamTestsDictExpectationKey = @"expectation";
|
||||
|
||||
constexpr float kSimilarityThreshold = 0.96f;
|
||||
constexpr NSInteger kMagnificationFactor = 10;
|
||||
constexpr NSInteger kExpectedDeeplabV3ConfidenceMaskCount = 21;
|
||||
constexpr NSInteger kExpected128x128SelfieSegmentationConfidenceMaskCount = 2;
|
||||
constexpr NSInteger kExpected144x256SelfieSegmentationConfidenceMaskCount = 1;
|
||||
|
||||
#define AssertEqualErrors(error, expectedError) \
|
||||
XCTAssertNotNil(error); \
|
||||
XCTAssertEqualObjects(error.domain, expectedError.domain); \
|
||||
XCTAssertEqual(error.code, expectedError.code); \
|
||||
XCTAssertEqualObjects(error.localizedDescription, expectedError.localizedDescription)
|
||||
|
||||
namespace {
|
||||
double sum(const std::vector<float> &mask) {
|
||||
|
@ -70,9 +96,12 @@ double softIOU(const float *mask1, const float *mask2, size_t size) {
|
|||
|
||||
return unionSum > 0.0 ? interSectionSum / unionSum : 0.0;
|
||||
}
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
@interface MPPImageSegmenterTests : XCTestCase <MPPImageSegmenterLiveStreamDelegate>
|
||||
@interface MPPImageSegmenterTests : XCTestCase <MPPImageSegmenterLiveStreamDelegate> {
|
||||
NSDictionary<NSString *, id> *_liveStreamSucceedsTestDict;
|
||||
NSDictionary<NSString *, id> *_outOfOrderTimestampTestDict;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
@ -99,7 +128,7 @@ double softIOU(const float *mask1, const float *mask2, size_t size) {
|
|||
|
||||
- (void)testSegmentWithCategoryMaskSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModel];
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
options.shouldOutputConfidenceMasks = NO;
|
||||
options.shouldOutputCategoryMask = YES;
|
||||
|
||||
|
@ -113,17 +142,332 @@ double softIOU(const float *mask1, const float *mask2, size_t size) {
|
|||
|
||||
- (void)testSegmentWithConfidenceMaskSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModel];
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
[self assertResultsOfSegmentImageWithFileInfo:kCatImageFileInfo
|
||||
usingImageSegmenter:imageSegmenter
|
||||
hasConfidenceMasksCount:
|
||||
kExpectedDeeplabV3ConfidenceMaskCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:kCatGoldenImageFileInfo
|
||||
atIndex:8
|
||||
shouldHaveCategoryMask:NO];
|
||||
}
|
||||
|
||||
- (void)testSegmentWith128x128SegmentationSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kSelfie128x128ModelFileInfo];
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
[self assertResultsOfSegmentImageWithFileInfo:kMozartImageFileInfo
|
||||
usingImageSegmenter:imageSegmenter
|
||||
hasConfidenceMasksCount:
|
||||
kExpected128x128SelfieSegmentationConfidenceMaskCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:
|
||||
kMozart128x128SegmentationGoldenImageFileInfo
|
||||
atIndex:1
|
||||
shouldHaveCategoryMask:NO];
|
||||
}
|
||||
|
||||
- (void)testSegmentWith144x256SegmentationSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kSelfie144x256ModelFileInfo];
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
[self assertResultsOfSegmentImageWithFileInfo:kMozartImageFileInfo
|
||||
usingImageSegmenter:imageSegmenter
|
||||
hasConfidenceMasksCount:
|
||||
kExpected144x256SelfieSegmentationConfidenceMaskCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:
|
||||
kMozart144x256SegmentationGoldenImageFileInfo
|
||||
atIndex:0
|
||||
shouldHaveCategoryMask:NO];
|
||||
}
|
||||
|
||||
#pragma mark Running Mode Tests
|
||||
|
||||
- (void)testCreateImageSegmenterFailsWithDelegateInNonLiveStreamMode {
|
||||
MPPRunningMode runningModesToTest[] = {MPPRunningModeImage, MPPRunningModeVideo};
|
||||
for (int i = 0; i < sizeof(runningModesToTest) / sizeof(runningModesToTest[0]); i++) {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kSelfie128x128ModelFileInfo];
|
||||
|
||||
options.runningMode = runningModesToTest[i];
|
||||
options.imageSegmenterLiveStreamDelegate = self;
|
||||
|
||||
[self
|
||||
assertCreateImageSegmenterWithOptions: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)testCreateImageSegmenterFailsWithMissingDelegateInLiveStreamMode {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kSelfie128x128ModelFileInfo];
|
||||
|
||||
options.runningMode = MPPRunningModeLiveStream;
|
||||
|
||||
[self assertCreateImageSegmenterWithOptions: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)testSegmentFailsWithCallingWrongApiInImageMode {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
MPPImage *image = [MPPImage imageWithFileInfo:kCatImageFileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
NSError *liveStreamApiCallError;
|
||||
XCTAssertFalse([imageSegmenter segmentAsyncImage: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([imageSegmenter segmentVideoFrame: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)testSegmentFailsWithCallingWrongApiInVideoMode {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
options.runningMode = MPPRunningModeVideo;
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
MPPImage *image = [MPPImage imageWithFileInfo:kCatImageFileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
NSError *liveStreamApiCallError;
|
||||
XCTAssertFalse([imageSegmenter segmentAsyncImage: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([imageSegmenter segmentImage: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)testSegmentFailsWithCallingWrongApiInLiveStreamMode {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
options.runningMode = MPPRunningModeLiveStream;
|
||||
options.imageSegmenterLiveStreamDelegate = self;
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
MPPImage *image = [MPPImage imageWithFileInfo:kCatImageFileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
NSError *imageApiCallError;
|
||||
XCTAssertFalse([imageSegmenter segmentImage: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([imageSegmenter segmentVideoFrame: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)testSegmentWithVideoModeSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
options.runningMode = MPPRunningModeVideo;
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
MPPImage *image = [MPPImage imageWithFileInfo:kCatImageFileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
MPPImageSegmenterResult *result = [imageSegmenter segmentVideoFrame:image
|
||||
timestampInMilliseconds:i
|
||||
error:nil];
|
||||
[self assertImageSegmenterResult:result
|
||||
hasConfidenceMasksCount:
|
||||
kExpectedDeeplabV3ConfidenceMaskCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:kCatGoldenImageFileInfo
|
||||
atIndex:8
|
||||
shouldHaveCategoryMask:NO];
|
||||
}
|
||||
}
|
||||
|
||||
- (void)testSegmentWithOutOfOrderTimestampsAndLiveStreamModeFails {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
options.runningMode = MPPRunningModeLiveStream;
|
||||
options.imageSegmenterLiveStreamDelegate = self;
|
||||
|
||||
XCTestExpectation *expectation = [[XCTestExpectation alloc]
|
||||
initWithDescription:@"segmentWithOutOfOrderTimestampsAndLiveStream"];
|
||||
|
||||
expectation.expectedFulfillmentCount = 1;
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
_outOfOrderTimestampTestDict = @{
|
||||
kLiveStreamTestsDictImageSegmenterKey : imageSegmenter,
|
||||
kLiveStreamTestsDictExpectationKey : expectation
|
||||
};
|
||||
|
||||
MPPImage *image = [MPPImage imageWithFileInfo:kCatImageFileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
XCTAssertTrue([imageSegmenter segmentAsyncImage:image timestampInMilliseconds:1 error:nil]);
|
||||
|
||||
NSError *error;
|
||||
XCTAssertFalse([imageSegmenter segmentAsyncImage: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)testSegmentWithLiveStreamModeSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModelFileInfo];
|
||||
options.runningMode = MPPRunningModeLiveStream;
|
||||
options.imageSegmenterLiveStreamDelegate = 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 fulfilled
|
||||
// setting, `expectation.expectedFulfillmentCount` = `iterationCount` + 1 and
|
||||
// `expectation.isInverted = true` ensures that test succeeds ifexpectation is fulfilled <=
|
||||
// `iterationCount` times.
|
||||
XCTestExpectation *expectation =
|
||||
[[XCTestExpectation alloc] initWithDescription:@"segmentWithLiveStream"];
|
||||
|
||||
expectation.expectedFulfillmentCount = iterationCount + 1;
|
||||
expectation.inverted = YES;
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
_outOfOrderTimestampTestDict = @{
|
||||
kLiveStreamTestsDictImageSegmenterKey : imageSegmenter,
|
||||
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 = [MPPImage imageWithFileInfo:kCatImageFileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
for (int i = 0; i < iterationCount; i++) {
|
||||
XCTAssertTrue([imageSegmenter segmentAsyncImage:image timestampInMilliseconds:i error:nil]);
|
||||
}
|
||||
|
||||
NSTimeInterval timeout = 0.5f;
|
||||
[self waitForExpectations:@[ expectation ] timeout:timeout];
|
||||
}
|
||||
|
||||
- (void)imageSegmenter:(MPPImageSegmenter *)imageSegmenter
|
||||
didFinishSegmentationWithResult:(MPPImageSegmenterResult *)imageSegmenterResult
|
||||
timestampInMilliseconds:(NSInteger)timestampInMilliseconds
|
||||
error:(NSError *)error {
|
||||
[self assertImageSegmenterResult:imageSegmenterResult
|
||||
hasConfidenceMasksCount:
|
||||
kExpectedDeeplabV3ConfidenceMaskCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:kCatGoldenImageFileInfo
|
||||
atIndex:8
|
||||
shouldHaveCategoryMask:NO];
|
||||
|
||||
if (imageSegmenter == _outOfOrderTimestampTestDict[kLiveStreamTestsDictImageSegmenterKey]) {
|
||||
[_outOfOrderTimestampTestDict[kLiveStreamTestsDictExpectationKey] fulfill];
|
||||
} else if (imageSegmenter == _liveStreamSucceedsTestDict[kLiveStreamTestsDictImageSegmenterKey]) {
|
||||
[_liveStreamSucceedsTestDict[kLiveStreamTestsDictExpectationKey] fulfill];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Image Segmenter Initializers
|
||||
|
||||
- (MPPImageSegmenterOptions *)imageSegmenterOptionsWithModelFileInfo:(MPPFileInfo *)fileInfo {
|
||||
|
@ -142,6 +486,16 @@ double softIOU(const float *mask1, const float *mask2, size_t size) {
|
|||
return imageSegmenter;
|
||||
}
|
||||
|
||||
- (void)assertCreateImageSegmenterWithOptions:(MPPImageSegmenterOptions *)options
|
||||
failsWithExpectedError:(NSError *)expectedError {
|
||||
NSError *error = nil;
|
||||
MPPImageSegmenter *imageSegmenter = [[MPPImageSegmenter alloc] initWithOptions:options
|
||||
error:&error];
|
||||
|
||||
XCTAssertNil(imageSegmenter);
|
||||
AssertEqualErrors(error, expectedError);
|
||||
}
|
||||
|
||||
#pragma mark Assert Segmenter Results
|
||||
- (void)assertResultsOfSegmentImageWithFileInfo:(MPPFileInfo *)imageFileInfo
|
||||
usingImageSegmenter:(MPPImageSegmenter *)imageSegmenter
|
||||
|
@ -165,6 +519,8 @@ double softIOU(const float *mask1, const float *mask2, size_t size) {
|
|||
|
||||
- (void)assertResultsOfSegmentImageWithFileInfo:(MPPFileInfo *)imageFileInfo
|
||||
usingImageSegmenter:(MPPImageSegmenter *)imageSegmenter
|
||||
hasConfidenceMasksCount:
|
||||
(NSUInteger)expectedConfidenceMasksCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:
|
||||
(MPPFileInfo *)expectedConfidenceMaskFileInfo
|
||||
atIndex:(NSInteger)index
|
||||
|
@ -172,8 +528,24 @@ double softIOU(const float *mask1, const float *mask2, size_t size) {
|
|||
MPPImageSegmenterResult *result = [self segmentImageWithFileInfo:imageFileInfo
|
||||
usingImageSegmenter:imageSegmenter];
|
||||
|
||||
[self assertImageSegmenterResult:result
|
||||
hasConfidenceMasksCount:expectedConfidenceMasksCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:expectedConfidenceMaskFileInfo
|
||||
atIndex:index
|
||||
shouldHaveCategoryMask:shouldHaveCategoryMask];
|
||||
}
|
||||
|
||||
- (void)assertImageSegmenterResult:(MPPImageSegmenterResult *)result
|
||||
hasConfidenceMasksCount:
|
||||
(NSUInteger)expectedConfidenceMasksCount
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:
|
||||
(MPPFileInfo *)expectedConfidenceMaskFileInfo
|
||||
atIndex:(NSInteger)index
|
||||
shouldHaveCategoryMask:(BOOL)shouldHaveCategoryMask {
|
||||
XCTAssertNotNil(result.confidenceMasks);
|
||||
|
||||
XCTAssertEqual(result.confidenceMasks.count, expectedConfidenceMasksCount);
|
||||
|
||||
if (shouldHaveCategoryMask) {
|
||||
XCTAssertNotNil(result.categoryMask);
|
||||
} else {
|
||||
|
|
|
@ -203,7 +203,7 @@ NS_SWIFT_NAME(ImageSegmenter)
|
|||
*
|
||||
* @return `YES` if the image was sent to the task successfully, otherwise `NO`.
|
||||
*/
|
||||
- (BOOL)segmentAsyncInImage:(MPPImage *)image
|
||||
- (BOOL)segmentAsyncImage:(MPPImage *)image
|
||||
timestampInMilliseconds:(NSInteger)timestampInMilliseconds
|
||||
error:(NSError **)error
|
||||
NS_SWIFT_NAME(segmentAsync(image:timestampInMilliseconds:));
|
||||
|
|
|
@ -187,7 +187,7 @@ using ::mediapipe::tasks::core::PacketsCallback;
|
|||
shouldCopyMaskPacketData:NO];
|
||||
completionHandler(result, error);
|
||||
}
|
||||
- (BOOL)segmentAsyncInImage:(MPPImage *)image
|
||||
- (BOOL)segmentAsyncImage:(MPPImage *)image
|
||||
timestampInMilliseconds:(NSInteger)timestampInMilliseconds
|
||||
error:(NSError **)error {
|
||||
return [_visionTaskRunner processLiveStreamImage:image
|
||||
|
@ -243,16 +243,19 @@ using ::mediapipe::tasks::core::PacketsCallback;
|
|||
return;
|
||||
}
|
||||
|
||||
PacketMap &outputPacketMap = liveStreamResult.value();
|
||||
// Output packet map is moved to a block variable that will not be deallocated for the lifetime of
|
||||
// the `dispatch_async` call. Since masks are not copied, this ensures that they are only
|
||||
// deallocated after the delegate call completes.
|
||||
__block PacketMap outputPacketMap = std::move(liveStreamResult.value());
|
||||
if (outputPacketMap[kImageOutStreamName.cppString].IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
MPPImageSegmenterResult *result =
|
||||
[MPPImageSegmenter imageSegmenterResultWithOutputPacketMap:outputPacketMap
|
||||
shouldCopyMaskPacketData:NO];
|
||||
|
||||
dispatch_async(_callbackQueue, ^{
|
||||
MPPImageSegmenterResult *result =
|
||||
[MPPImageSegmenter imageSegmenterResultWithOutputPacketMap:outputPacketMap
|
||||
shouldCopyMaskPacketData:NO];
|
||||
|
||||
[self.imageSegmenterLiveStreamDelegate imageSegmenter:self
|
||||
didFinishSegmentationWithResult:result
|
||||
timestampInMilliseconds:result.timestampInMilliseconds
|
||||
|
|
Loading…
Reference in New Issue
Block a user