ML Kit to zoptymalizowany pakiet SDK do segmentacji selfie. Zasoby segmentu selfie są statycznie połączone z Twoją aplikacją w czasie tworzenia kampanii. Zwiększy to rozmiar aplikacji o nawet 24 MB, a opóźnienie interfejsu API może wynosić od 7 ms do 12 ms w zależności od rozmiaru obrazu wejściowego (mierzonego na iPhonie X).
Wypróbuj
- Wypróbuj przykładową aplikację, aby: zobaczysz 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 za pomocą pliku .
xcworkspace
. ML Kit jest obsługiwany w Xcode w wersji 13.2.1 lub nowszej.
1. Tworzenie instancji segmentacji
Aby przeprowadzić segmentację zdjęcia selfie, najpierw utwórz wystąpienie elementu Segmenter
z atrybutem SelfieSegmenterOptions
i opcjonalnie określ ustawienia podziału na segmenty.
Opcje segmentowania
Tryb segmentacji
Segmenter
działa w 2 trybach. Wybierz taki, który pasuje do Twojego przypadku użycia.
STREAM_MODE (default)
Ten tryb jest przeznaczony do strumieniowania klatek filmu lub kamery. W tym trybie segmenter korzysta z wyników z poprzednich klatek, aby zwrócić płynniejszy wynik segmentacji.
SINGLE_IMAGE_MODE (default)
Ten tryb jest przeznaczony do pojedynczych zdjęć, które nie są ze sobą powiązane. W tym trybie segmenter przetwarza każde zdjęcie niezależnie, bez wygładzania klatek.
Włącz maskę rozmiaru nieprzetworzonego
Prosi segmentację o zwrócenie maski 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, segmenter przeskaluje maskę nieprzetworzoną, aby dopasować ją do rozmiaru obrazu wejściowego. Rozważ użycie tej opcji, jeśli chcesz zastosować niestandardową logikę zmiany skali lub ponowne skalowanie nie jest potrzebne w Twoim przypadku użycia.
Określ opcje segmentowania:
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 instancję Segmenter
. Prześlij 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, wykonaj te czynności w przypadku każdego zdjęcia lub każdej 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 UIImage
, wykonaj te czynności:
- Utwórz obiekt
VisionImage
za pomocąUIImage
. Pamiętaj, by określić prawidłowy.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 CMSampleBuffer
, wykonaj te czynności:
-
Określ orientację danych zdjęć zawartych w pliku
CMSampleBuffer
Aby sprawdzić orientację obrazu:
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
za pomocąCMSampleBuffer
obiekt i orientacja: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 zdjęcia
Przekaż obiekt VisionImage
do jednej z metod przetwarzania obrazu w Segmenter
. Możesz używać asynchronicznej metody process(image:)
lub synchronicznej results(in:)
.
Aby synchronicznie przeprowadzić segmentację zdjęcia selfie:
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. Pobierz maskę podziału na segmenty
Wynik podziału na segmenty możesz uzyskać w ten 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łen przykład korzystania z wyników segmentacji znajdziesz Przykład krótkiego wprowadzenia do ML Kit.
Wskazówki dotyczące poprawy skuteczności
Jakość obrazu zależy od jakości obrazu wejściowego:
- Aby narzędzie ML Kit mogło uzyskać dokładny wynik podziału na segmenty, obraz powinien mieć co najmniej 256 × 256 pikseli.
- Jeśli segmentujesz selfie w aplikacji działającej 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, ale pamiętaj o powyższych wymaganiach dotyczących rozdzielczości i postaraj się, aby obiekt zajmował jak najwięcej miejsca.
- Słaba ostrość obrazu również może mieć wpływ na dokładność. Jeśli wyniki nie są zadowalające, poproś użytkownika o ponowne wykonanie zdjęcia.
Jeśli chcesz zastosować segmentację w aplikacji w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek:
- Użyj trybu segmentacji
stream
. - Rozważ robienie zdjęć w niższej rozdzielczości. Pamiętaj jednak o wymaganiach tego interfejsu API dotyczących wymiarów zdjęć.
- Do przetwarzania klatek wideo używaj synchronicznego interfejsu API
results(in:)
. Wywołaj tę metodę z funkcji captureOutput(_, didOutput:from:) w funkcji AVCaptureVideoDataOutputSampleBufferDelegate w celu synchronicznego pobierania wyników z danej klatki wideo. Aby ograniczyć wywołania do segmentatora, w atrybucie AVCaptureVideoDataOutput ustaw wartość alwaysDiscardsLateVideoFrames na wartość true (prawda). Jeśli w trakcie działania segmentacji pojawi się nowa klatka wideo, zostanie ona usunięta. - Jeśli użyjesz danych wyjściowych segmentatora do nałożenia grafiki na obraz wejściowy, najpierw pobierz wynik z pakietu ML Kit, a potem w jednym kroku wyrenderuj obraz i nakładkę. Dzięki temu renderowanie na powierzchni wyświetlania będzie odbywać się tylko raz na każdą przetworzoną klatkę wejściową. Przykład znajdziesz w klasach previewOverlayView i CameraViewController w przykładzie krótkiego wprowadzenia do pakietu ML Kit.