Added iOS image segmenter basic Objective C tests
This commit is contained in:
parent
d3f7368b27
commit
fad7f9cdb4
62
mediapipe/tasks/ios/test/vision/image_segmenter/BUILD
Normal file
62
mediapipe/tasks/ios/test/vision/image_segmenter/BUILD
Normal file
|
@ -0,0 +1,62 @@
|
|||
load("@build_bazel_rules_apple//apple:ios.bzl", "ios_unit_test")
|
||||
load(
|
||||
"//mediapipe/framework/tool:ios.bzl",
|
||||
"MPP_TASK_MINIMUM_OS_VERSION",
|
||||
)
|
||||
load(
|
||||
"@org_tensorflow//tensorflow/lite:special_rules.bzl",
|
||||
"tflite_ios_lab_runner",
|
||||
)
|
||||
|
||||
package(default_visibility = ["//mediapipe/tasks:internal"])
|
||||
|
||||
licenses(["notice"])
|
||||
|
||||
# Default tags for filtering iOS targets. Targets are restricted to Apple platforms.
|
||||
TFL_DEFAULT_TAGS = [
|
||||
"apple",
|
||||
]
|
||||
|
||||
# Following sanitizer tests are not supported by iOS test targets.
|
||||
TFL_DISABLED_SANITIZER_TAGS = [
|
||||
"noasan",
|
||||
"nomsan",
|
||||
"notsan",
|
||||
]
|
||||
|
||||
objc_library(
|
||||
name = "MPPImageSegmenterObjcTestLibrary",
|
||||
testonly = 1,
|
||||
srcs = ["MPPImageSegmenterTests.mm"],
|
||||
copts = [
|
||||
"-ObjC++",
|
||||
"-std=c++17",
|
||||
"-x objective-c++",
|
||||
],
|
||||
data = [
|
||||
"//mediapipe/tasks/testdata/vision:test_images",
|
||||
"//mediapipe/tasks/testdata/vision:test_models",
|
||||
"//mediapipe/tasks/testdata/vision:test_protos",
|
||||
],
|
||||
deps = [
|
||||
"//mediapipe/tasks/ios/test/vision/utils:MPPImageTestUtils",
|
||||
"//mediapipe/tasks/ios/test/vision/utils:MPPMaskTestUtils",
|
||||
"//mediapipe/tasks/ios/vision/image_segmenter:MPPImageSegmenter",
|
||||
"//mediapipe/tasks/ios/vision/image_segmenter:MPPImageSegmenterResult",
|
||||
] + select({
|
||||
"//third_party:opencv_ios_sim_arm64_source_build": ["@ios_opencv_source//:opencv_xcframework"],
|
||||
"//third_party:opencv_ios_arm64_source_build": ["@ios_opencv_source//:opencv_xcframework"],
|
||||
"//third_party:opencv_ios_x86_64_source_build": ["@ios_opencv_source//:opencv_xcframework"],
|
||||
"//conditions:default": ["@ios_opencv//:OpencvFramework"],
|
||||
}),
|
||||
)
|
||||
|
||||
ios_unit_test(
|
||||
name = "MPPImageSegmenterObjcTest",
|
||||
minimum_os_version = MPP_TASK_MINIMUM_OS_VERSION,
|
||||
runner = tflite_ios_lab_runner("IOS_LATEST"),
|
||||
tags = TFL_DEFAULT_TAGS + TFL_DISABLED_SANITIZER_TAGS,
|
||||
deps = [
|
||||
":MPPImageSegmenterObjcTestLibrary",
|
||||
],
|
||||
)
|
|
@ -0,0 +1,245 @@
|
|||
// Copyright 2023 The MediaPipe Authors.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <XCTest/XCTest.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"
|
||||
#import "mediapipe/tasks/ios/vision/image_segmenter/sources/MPPImageSegmenterResult.h"
|
||||
|
||||
static MPPFileInfo *const kCatImageFileInfo = [[MPPFileInfo alloc] initWithName:@"cat"
|
||||
type:@"jpg"];
|
||||
static MPPFileInfo *const kCatGoldenImageFileInfo = [[MPPFileInfo alloc] initWithName:@"cat_mask"
|
||||
type:@"jpg"];
|
||||
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 NSString *const kExpectedErrorDomain = @"com.google.mediapipe.tasks";
|
||||
constexpr float kSimilarityThreshold = 0.96f;
|
||||
constexpr NSInteger kMagnificationFactor = 10;
|
||||
|
||||
double sum(const float *mask, size_t size) {
|
||||
double sum = 0.0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
sum += mask[i];
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float *multiply(const float *mask1, const float *mask2, size_t size) {
|
||||
double sum = 0.0;
|
||||
float *multipliedMask = (float *)malloc(size * sizeof(float));
|
||||
if (!multipliedMask) {
|
||||
exit(-1);
|
||||
}
|
||||
for (int i = 0; i < size; i++) {
|
||||
multipliedMask[i] = mask1[i] * mask2[i];
|
||||
}
|
||||
|
||||
return multipliedMask;
|
||||
}
|
||||
|
||||
double softIOU(const float *mask1, const float *mask2, size_t size) {
|
||||
float *interSectionVector = multiply(mask1, mask2, size);
|
||||
double interSectionSum = sum(interSectionVector, size);
|
||||
free(interSectionVector);
|
||||
|
||||
float *m1m1Vector = multiply(mask1, mask1, size);
|
||||
double m1m1 = sum(m1m1Vector, size);
|
||||
free(m1m1Vector);
|
||||
|
||||
float *m2m2Vector = multiply(mask2, mask2, size);
|
||||
double m2m2 = sum(m2m2Vector, size);
|
||||
free(m2m2Vector);
|
||||
|
||||
double unionSum = m1m1 + m2m2 - interSectionSum;
|
||||
|
||||
return unionSum > 0.0 ? interSectionSum / unionSum : 0.0;
|
||||
}
|
||||
|
||||
@interface MPPImageSegmenterTests : XCTestCase <MPPImageSegmenterLiveStreamDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@implementation MPPImageSegmenterTests
|
||||
|
||||
#pragma mark General Tests
|
||||
|
||||
- (void)setUp {
|
||||
// When expected and actual mask sizes are not equal, iterating through mask data results in a
|
||||
// segmentation fault. Setting this property to `NO`, prevents each test case from executing the
|
||||
// remaining flow after a failure. Since expected and actual mask sizes are compared before
|
||||
// iterating through them, this prevents any illegal memory access.
|
||||
self.continueAfterFailure = NO;
|
||||
}
|
||||
|
||||
+ (NSString *)filePathWithName : (NSString *)fileName extension : (NSString *)extension {
|
||||
NSString *filePath =
|
||||
[[NSBundle bundleForClass:[MPPImageSegmenterTests class]] pathForResource:fileName
|
||||
ofType:extension];
|
||||
return filePath;
|
||||
}
|
||||
|
||||
#pragma mark Image Mode Tests
|
||||
|
||||
- (void)testSegmentWithCategoryMaskSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModel];
|
||||
options.shouldOutputConfidenceMasks = NO;
|
||||
options.shouldOutputCategoryMask = YES;
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
[self assertResultsOfSegmentImageWithFileInfo:kSegmentationImageFileInfo
|
||||
usingImageSegmenter:imageSegmenter
|
||||
approximatelyEqualsExpectedCategoryMaskImageWithFileInfo:kSegmentationGoldenImageFileInfo
|
||||
shouldHaveConfidenceMasks:NO];
|
||||
}
|
||||
|
||||
- (void)testSegmentWithConfidenceMaskSucceeds {
|
||||
MPPImageSegmenterOptions *options =
|
||||
[self imageSegmenterOptionsWithModelFileInfo:kImageSegmenterModel];
|
||||
|
||||
MPPImageSegmenter *imageSegmenter = [self createImageSegmenterWithOptionsSucceeds:options];
|
||||
|
||||
[self assertResultsOfSegmentImageWithFileInfo:kCatImageFileInfo
|
||||
usingImageSegmenter:imageSegmenter
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:kCatGoldenImageFileInfo
|
||||
atIndex:8
|
||||
shouldHaveCategoryMask:NO];
|
||||
}
|
||||
|
||||
#pragma mark - Image Segmenter Initializers
|
||||
|
||||
- (MPPImageSegmenterOptions *)imageSegmenterOptionsWithModelFileInfo:(MPPFileInfo *)fileInfo {
|
||||
MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
|
||||
options.baseOptions.modelAssetPath = fileInfo.path;
|
||||
return options;
|
||||
}
|
||||
|
||||
- (MPPImageSegmenter *)createImageSegmenterWithOptionsSucceeds:(MPPImageSegmenterOptions *)options {
|
||||
NSError *error;
|
||||
MPPImageSegmenter *imageSegmenter = [[MPPImageSegmenter alloc] initWithOptions:options
|
||||
error:&error];
|
||||
XCTAssertNotNil(imageSegmenter);
|
||||
XCTAssertNil(error);
|
||||
|
||||
return imageSegmenter;
|
||||
}
|
||||
|
||||
#pragma mark Assert Segmenter Results
|
||||
- (void)assertResultsOfSegmentImageWithFileInfo:(MPPFileInfo *)imageFileInfo
|
||||
usingImageSegmenter:(MPPImageSegmenter *)imageSegmenter
|
||||
approximatelyEqualsExpectedCategoryMaskImageWithFileInfo:
|
||||
(MPPFileInfo *)expectedCategoryMaskFileInfo
|
||||
shouldHaveConfidenceMasks:(BOOL)shouldHaveConfidenceMasks {
|
||||
MPPImageSegmenterResult *result = [self segmentImageWithFileInfo:imageFileInfo
|
||||
usingImageSegmenter:imageSegmenter];
|
||||
|
||||
XCTAssertNotNil(result.categoryMask);
|
||||
|
||||
if (shouldHaveConfidenceMasks) {
|
||||
XCTAssertNotNil(result.confidenceMasks);
|
||||
}
|
||||
else {
|
||||
XCTAssertNil(result.confidenceMasks);
|
||||
}
|
||||
|
||||
[self assertCategoryMask:result.categoryMask
|
||||
approximatelyEqualsExpectedCategoryMaskImageWithFileInfo:expectedCategoryMaskFileInfo];
|
||||
}
|
||||
|
||||
- (void)assertResultsOfSegmentImageWithFileInfo:(MPPFileInfo *)imageFileInfo
|
||||
usingImageSegmenter:(MPPImageSegmenter *)imageSegmenter
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:
|
||||
(MPPFileInfo *)expectedConfidenceMaskFileInfo
|
||||
atIndex:(NSInteger)index
|
||||
shouldHaveCategoryMask:(BOOL)shouldHaveCategoryMask {
|
||||
MPPImageSegmenterResult *result = [self segmentImageWithFileInfo:imageFileInfo
|
||||
usingImageSegmenter:imageSegmenter];
|
||||
|
||||
XCTAssertNotNil(result.confidenceMasks);
|
||||
|
||||
if (shouldHaveCategoryMask) {
|
||||
XCTAssertNotNil(result.categoryMask);
|
||||
}
|
||||
else {
|
||||
XCTAssertNil(result.categoryMask);
|
||||
}
|
||||
|
||||
XCTAssertLessThan(index, result.confidenceMasks.count);
|
||||
|
||||
[self assertConfidenceMask:result.confidenceMasks[index]
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:expectedConfidenceMaskFileInfo];
|
||||
}
|
||||
|
||||
- (MPPImageSegmenterResult *)segmentImageWithFileInfo:(MPPFileInfo *)fileInfo
|
||||
usingImageSegmenter:(MPPImageSegmenter *)imageSegmenter {
|
||||
MPPImage *image = [MPPImage imageWithFileInfo:fileInfo];
|
||||
XCTAssertNotNil(image);
|
||||
|
||||
NSError *error;
|
||||
MPPImageSegmenterResult *result = [imageSegmenter segmentImage:image error:&error];
|
||||
XCTAssertNil(error);
|
||||
XCTAssertNotNil(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)assertCategoryMask:(MPPMask *)categoryMask
|
||||
approximatelyEqualsExpectedCategoryMaskImageWithFileInfo:
|
||||
(MPPFileInfo *)expectedCategoryMaskImageFileInfo {
|
||||
MPPMask *expectedCategoryMask =
|
||||
[[MPPMask alloc] initWithImageFileInfo:expectedCategoryMaskImageFileInfo];
|
||||
|
||||
XCTAssertEqual(categoryMask.width, expectedCategoryMask.width);
|
||||
XCTAssertEqual(categoryMask.height, expectedCategoryMask.height);
|
||||
|
||||
size_t maskSize = categoryMask.width * categoryMask.height;
|
||||
|
||||
const UInt8 *categoryMaskPixelData = categoryMask.uint8Data;
|
||||
const UInt8 *expectedCategoryMaskPixelData = expectedCategoryMask.uint8Data;
|
||||
|
||||
NSInteger consistentPixels = 0;
|
||||
|
||||
for (int i = 0; i < maskSize; i++) {
|
||||
consistentPixels +=
|
||||
categoryMaskPixelData[i] * kMagnificationFactor == expectedCategoryMaskPixelData[i] ? 1 : 0;
|
||||
}
|
||||
|
||||
XCTAssertGreaterThan((float)consistentPixels / (float)maskSize, kSimilarityThreshold);
|
||||
}
|
||||
|
||||
- (void)assertConfidenceMask:(MPPMask *)confidenceMask
|
||||
approximatelyEqualsExpectedConfidenceMaskImageWithFileInfo:
|
||||
(MPPFileInfo *)expectedConfidenceMaskImageFileInfo {
|
||||
MPPMask *expectedConfidenceMask =
|
||||
[[MPPMask alloc] initWithImageFileInfo:expectedConfidenceMaskImageFileInfo];
|
||||
|
||||
XCTAssertEqual(confidenceMask.width, expectedConfidenceMask.width);
|
||||
XCTAssertEqual(confidenceMask.height, expectedConfidenceMask.height);
|
||||
|
||||
size_t maskSize = confidenceMask.width * confidenceMask.height;
|
||||
|
||||
XCTAssertGreaterThan(
|
||||
softIOU(confidenceMask.float32Data, expectedConfidenceMask.float32Data, maskSize),
|
||||
kSimilarityThreshold);
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in New Issue
Block a user