ML Kit udostępnia zoptymalizowane SDK do segmentacji selfie. Komponenty Selfie Segmenter są statycznie powiązane z aplikacją w momencie kompilacji. Spowoduje to zwiększenie rozmiaru aplikacji nawet o 24 MB, a opóźnienie wywołania interfejsu API może się wahać od około 7 do 12 ms w zależności od rozmiaru obrazu wejściowego (zmierzono na iPhonie X).
Wypróbuj
- Aby zobaczyć przykład użycia tego interfejsu API, wypróbuj przykładową aplikację.
Zanim zaczniesz
Dodaj do pliku Podfile te biblioteki ML Kit:
pod 'GoogleMLKit/SegmentationSelfie', '7.0.0'
Po zainstalowaniu lub zaktualizowaniu pakietów projektu otwórz projekt Xcode za pomocą pliku .
xcworkspace
. ML Kit jest obsługiwany w wersji Xcode 13.2.1 lub nowszej.
1. Tworzenie instancji Segmenter
Aby przeprowadzić podział na segmenty na podstawie zdjęcia selfie, najpierw utwórz instancję funkcji Segmenter
za pomocą funkcji SelfieSegmenterOptions
i opcjonalnie określ ustawienia podziału na segmenty.
Opcje segmentera
Tryb segmentatora
Usługa Segmenter
działa w 2 trybach. Pamiętaj, aby wybrać model, który pasuje do Twojego przypadku użycia.
STREAM_MODE (default)
Ten tryb jest przeznaczony do przesyłania strumieniowego klatek z filmu lub kamery. W tym trybie segmentator korzysta z wyników z poprzednich klatek, aby uzyskać bardziej płynne wyniki podziału na segmenty.
SINGLE_IMAGE_MODE (default)
Ten tryb jest przeznaczony do pojedynczych obrazów, które nie są ze sobą powiązane. W tym trybie segmentator przetworzy każdy obraz niezależnie, bez wygładzania klatek.
Włącz maskę rozmiaru nieskompresowanego.
Prosi segmenter o zwrócenie maski rozmiaru nieprzetworzonego, która odpowiada rozmiarowi wyjściowemu modelu.
Rozmiar surowej maski (np. 256 x 256) jest zwykle mniejszy niż rozmiar wejściowego obrazu.
Bez użycia tej opcji segmenter przeskalowuje surową maskę, aby dopasować ją do rozmiaru wejściowego obrazu. Użyj tej opcji, jeśli chcesz zastosować niestandardową logikę zmiany skali lub jeśli zmiana skali nie jest potrzebna w Twoim przypadku użycia.
Określ opcje segmentacji:
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
Na koniec pobierz instancję Segmenter
. Przekaż określone opcje:
let segmenter = Segmenter.segmenter(options: options)
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. Przygotuj obraz wejściowy
Aby podzielić na segmenty zdjęcia selfie, wykonaj te czynności w przypadku każdego obrazu lub każdego kadru filmu.
Jeśli masz włączony tryb strumieniowania, musisz utworzyć obiekty VisionImage
z CMSampleBuffer
.
Utwórz obiekt VisionImage
za pomocą obiektu UIImage
lub CMSampleBuffer
.
Jeśli używasz UIImage
, wykonaj te czynności:
- Utwórz obiekt
VisionImage
za pomocą funkcjiUIImage
. Pamiętaj, aby podać prawidłowy adres.orientation
.let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
Jeśli używasz CMSampleBuffer
, wykonaj te czynności:
-
Określ orientację danych obrazu zawartych w elemencie
CMSampleBuffer
.Aby uzyskać orientację obrazu:
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 } }
- (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 jego orientacji:let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3. Przetwarzanie obrazu
Przekaż obiekt VisionImage
jednej z metod przetwarzania obrazu w bibliotece Segmenter
. Możesz użyć asynchronicznej metody process(image:)
lub synchronicznej metody results(in:)
.
Aby wykonać segmentację obrazu selfie synchronicznie:
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.
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
Aby asynchronicznie przeprowadzić segmentację obrazu selfie:
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
[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 podziału możesz uzyskać w ten sposób:
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 }
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 podziału na segmenty znajdziesz w przykładowym pliku quickstart pakietu ML Kit.
Wskazówki dotyczące zwiększania skuteczności
Jakość wyników zależy od jakości obrazu wejściowego:
- Aby ML Kit mógł uzyskać dokładny wynik podziału na segmenty, obraz powinien mieć co najmniej 256 x 256 pikseli.
- Jeśli segmentację selfie wykonujesz w aplikacji działającej w czasie rzeczywistym, warto też wziąć pod uwagę ogólne wymiary obrazów wejściowych. Mniejsze obrazy można przetwarzać szybciej, więc aby zmniejszyć opóźnienie, rób zdjęcia w niższej rozdzielczości, pamiętając o wymaganiach dotyczących rozdzielczości podanych powyżej i upewnij się, że fotografowany obiekt zajmuje jak największą część obrazu.
- Na dokładność może też wpływać niewłaściwe ustawienie ostrości. Jeśli nie uzyskasz zadowalających wyników, poproś użytkownika o ponowne zrobienie zdjęcia.
Jeśli chcesz używać segmentacji w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi wytycznymi, aby uzyskać najlepszą liczbę klatek na sekundę:
- Użyj trybu
stream
. - Rozważ robienie zdjęć w niższej rozdzielczości. Pamiętaj jednak o wymaganiach dotyczących wymiarów obrazu w tym interfejsie API.
- Do przetwarzania klatek wideo użyj synchronicznego interfejsu API segmentera
results(in:)
. Wywołaj tę metodę z funkcji AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:), aby synchronicznie uzyskać wyniki z danego kadru wideo. Aby ograniczyć wywołania do segmentera, ustaw parametr AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames na wartość „true” (prawda). Jeśli podczas działania segmentera pojawi się nowa klatka wideo, zostanie ona odrzucona. - Jeśli używasz danych wyjściowych segmentera do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a potem wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu renderowanie na powierzchni wyświetlacza odbywa się tylko raz dla każdego przetworzonego kadru wejściowego. Przykładem są klasy previewOverlayView i CameraViewController w pliku ML Kit quickstart sample.