ML Kit udostępnia zoptymalizowany pakiet SDK do segmentacji selfie. Zasoby narzędzia do segmentacji selfie są statycznie połączone z Twoją aplikacją w czasie jej kompilacji. Spowoduje to zwiększenie rozmiaru aplikacji nawet o 24 MB, a opóźnienie interfejsu API może wahać się od 7 ms do około 12 ms w zależności od rozmiaru obrazu wejściowego (mierzonego na iPhonie X).
Wypróbuj
- Przetestuj przykładową aplikację, aby zobaczyć przykład użycia tego interfejsu API.
Zanim zaczniesz
W pliku Podfile umieść te biblioteki ML Kit:
pod 'GoogleMLKit/SegmentationSelfie', '3.2.0'
Po zainstalowaniu lub zaktualizowaniu podów projektu otwórz projekt Xcode w pliku .
xcworkspace
. Pakiet ML Kit jest obsługiwany w Xcode w wersji 13.2.1 lub nowszej.
1. Tworzenie instancji narzędzia Segmenter
Aby przeprowadzić segmentację zdjęcia selfie, najpierw utwórz wystąpienie Segmenter
z parametrem SelfieSegmenterOptions
i opcjonalnie określ ustawienia podziału na segmenty.
Opcje segmentowania
Tryb segmentowania
Segmenter
działa w 2 trybach. Wybierz taką, która pasuje do Twojego przypadku użycia.
STREAM_MODE (default)
Ten tryb jest przeznaczony do strumieniowego przesyłania klatek z filmu lub kamery. W tym trybie segmenter korzysta z wyników z poprzednich klatek, aby uzyskać płynniejszy wynik podziału na segmenty.
SINGLE_IMAGE_MODE (default)
Ten tryb jest przeznaczony dla pojedynczych zdjęć, które nie są ze sobą powiązane. W tym trybie narzędzie do segmentacji przetwarza każde zdjęcie niezależnie, bez wygładzania klatek.
Włącz maskę rozmiaru nieprzetworzonego
Wymaga od segmentowania, aby zwracał maskę rozmiaru nieprzetworzonego, która odpowiada rozmiarowi wyjściowemu modelu.
Rozmiar nieprzetworzonej maski (np. 256 x 256) jest zwykle mniejszy niż rozmiar obrazu wejściowego.
Jeśli nie określisz tej opcji, segmentator przeskaluje maskę nieprzetworzonej, aby dopasować ją do rozmiaru obrazu wejściowego. Rozważ użycie tej opcji, jeśli chcesz zastosować niestandardową logikę skalowania lub przeskalowanie nie jest konieczne w Twoim przypadku.
Określ opcje segmentacji:
Swift
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
Objective-C
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Na koniec pobierz wystąpienie Segmenter
. Przekaż określone opcje:
Swift
let segmenter = Segmenter.segmenter(options: options)
Objective-C
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Przygotowywanie obrazu wejściowego
Aby podzielić selfie na segmenty, wykonaj te czynności w przypadku każdego obrazu lub klatki filmu.
Jeśli masz włączony tryb strumieniowania, musisz utworzyć obiekty VisionImage
z CMSampleBuffer
.
Utwórz obiekt VisionImage
za pomocą UIImage
lub CMSampleBuffer
.
Jeśli używasz konta UIImage
, wykonaj te czynności:
- Utwórz obiekt
VisionImage
zUIImage
. Pamiętaj, aby podać prawidłowy atrybut.orientation
.Swift
let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
Objective-C
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Jeśli używasz konta CMSampleBuffer
, wykonaj te czynności:
-
Określ orientację danych obrazu zawartych w pliku
CMSampleBuffer
.Aby określić orientację zdjęcia:
Swift
func imageOrientation( deviceOrientation: UIDeviceOrientation, cameraPosition: AVCaptureDevice.Position ) -> UIImage.Orientation { switch deviceOrientation { case .portrait: return cameraPosition == .front ? .leftMirrored : .right case .landscapeLeft: return cameraPosition == .front ? .downMirrored : .up case .portraitUpsideDown: return cameraPosition == .front ? .rightMirrored : .left case .landscapeRight: return cameraPosition == .front ? .upMirrored : .down case .faceDown, .faceUp, .unknown: return .up } }
Objective-C
- (UIImageOrientation) imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation cameraPosition:(AVCaptureDevicePosition)cameraPosition { switch (deviceOrientation) { case UIDeviceOrientationPortrait: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored : UIImageOrientationRight; case UIDeviceOrientationLandscapeLeft: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored : UIImageOrientationUp; case UIDeviceOrientationPortraitUpsideDown: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored : UIImageOrientationLeft; case UIDeviceOrientationLandscapeRight: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored : UIImageOrientationDown; case UIDeviceOrientationUnknown: case UIDeviceOrientationFaceUp: case UIDeviceOrientationFaceDown: return UIImageOrientationUp; } }
- Utwórz obiekt
VisionImage
, używając obiektuCMSampleBuffer
i orientacji:Swift
let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
Objective-C
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. Przetwarzanie obrazu
Przekaż obiekt VisionImage
do jednej z metod przetwarzania obrazu w Segmenter
. Możesz użyć asynchronicznej metody process(image:)
lub synchronicznej results(in:)
.
Aby synchronicznie podzielić zdjęcie selfie na segmenty:
Swift
var mask: [SegmentationMask] do { mask = try segmenter.results(in: image) } catch let error { print("Failed to perform segmentation with error: \(error.localizedDescription).") return } // Success. Get a segmentation mask here.
Objective-C
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
Aby asynchronicznie przeprowadzić segmentację zdjęcia selfie:
Swift
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
Objective-C
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. Pobieranie maski podziału na segmenty
Wynik segmentacji możesz uzyskać w następujący sposób:
Swift
let maskWidth = CVPixelBufferGetWidth(mask.buffer) let maskHeight = CVPixelBufferGetHeight(mask.buffer) CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) var maskAddress = CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory( to: Float32.self, capacity: maskBytesPerRow * maskHeight) for _ in 0...(maskHeight - 1) { for col in 0...(maskWidth - 1) { // Gets the confidence of the pixel in the mask being in the foreground. let foregroundConfidence: Float32 = maskAddress[col] } maskAddress += maskBytesPerRow / MemoryLayout<Float32>.size }
Objective-C
size_t width = CVPixelBufferGetWidth(mask.buffer); size_t height = CVPixelBufferGetHeight(mask.buffer); CVPixelBufferLockBaseAddress(mask.buffer, kCVPixelBufferLock_ReadOnly); size_t maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer); float *maskAddress = (float *)CVPixelBufferGetBaseAddress(mask.buffer); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { // Gets the confidence of the pixel in the mask being in the foreground. float foregroundConfidence = maskAddress[col]; } maskAddress += maskBytesPerRow / sizeof(float); }
Pełny przykład użycia wyników segmentacji znajdziesz w przykładzie krótkiego wprowadzenia do ML Kit.
Wskazówki dotyczące poprawy skuteczności
Jakość wyników zależy od jakości obrazu wejściowego:
- Aby narzędzie ML Kit mogło otrzymać dokładny wynik podziału na segmenty, obraz powinien mieć co najmniej 256 x 256 pikseli.
- Jeśli segmentujesz selfie w aplikacji, która pokazuje dane w czasie rzeczywistym, warto też wziąć pod uwagę ogólne wymiary zdjęć wejściowych. Mniejsze obrazy mogą być przetwarzane szybciej, więc aby zmniejszyć opóźnienia, rób zdjęcia w niższej rozdzielczości. Pamiętaj jednak o powyższych wymaganiach dotyczących rozdzielczości i dopilnuj, aby obiekt zajmował jak najwięcej miejsca.
- Słaba ostrość obrazu może również wpływać na dokładność. Jeśli nie uda się uzyskać zadowalających wyników, poproś użytkownika o ponowne zrobienie zdjęcia.
Jeśli chcesz wykorzystać podział na segmenty w aplikacji przesyłających dane w czasie rzeczywistym, postępuj zgodnie z tymi wytycznymi, aby uzyskać najlepszą liczbę klatek:
- Użyj trybu segmentacji
stream
. - Rozważ robienie zdjęć w niższej rozdzielczości. Pamiętaj jednak o wymaganiach dotyczących wymiarów obrazów w tym interfejsie API.
- Do przetwarzania klatek wideo użyj synchronicznego interfejsu API
results(in:)
narzędzia segmentacji. Wywołaj tę metodę z funkcji AVCaptureVideoDataOutputSampleBufferDelegate w funkcji captureOutput(_, didOutput:from:), aby synchronicznie pobierać wyniki z danej klatki wideo. Aby ograniczyć wywołania do segmentu, pozostaw wartość „prawda” w polu AVCaptureVideoDataOutput w ustawieniu alwaysDiscardsLateVideoFrames. Jeśli nowa klatka wideo stanie się dostępna, gdy działa moduł segmentacji, zostanie usunięta. - Jeśli używasz danych wyjściowych narzędzia do segmentacji w celu nałożenia grafiki na obraz wejściowy, najpierw pobierz wynik z ML Kit, a następnie w jednym kroku wyrenderuj obraz i nakładkę. Dzięki temu renderujesz na wyświetlaczu tylko raz dla każdej przetworzonej ramki wejściowej. Przykład znajdziesz w klasach previewOverlayView i CameraViewController w przykładzie krótkiego wprowadzenia do ML Kit.