Przewodnik po umieszczaniu obrazów w wersji na iOS

Zadanie MediaPipe Image Embedder umożliwia konwertowanie danych obrazu na postać liczbową, aby wykonywać zadania związane z przetwarzaniem obrazu za pomocą uczenia maszynowego, takie jak porównywanie podobieństwa 2 obrazów.

Przykładowy kod opisany w tych instrukcjach jest dostępny na GitHubzie. Aby zobaczyć, jak to zadanie działa w praktyce, obejrzyj to demo. Więcej informacji o możliwościach, modelach i opcjach konfiguracji tego zadania znajdziesz w sekcji Omówienie.

Przykładowy kod

Przykładowy kod MediaPipe Tasks to podstawowa implementacja aplikacji Image Embedder na iOS. W przykładzie do ciągłego umieszczania obrazów używana jest kamera na fizycznym urządzeniu z iOS. Można też uruchomić narzędzie do umieszczania w pliku z obrazami z galerii urządzenia.

Możesz użyć tej aplikacji jako punktu wyjścia do tworzenia własnej aplikacji na iOS lub skorzystać z niej podczas modyfikowania istniejącej aplikacji. Przykładowy kod Image Embedder jest hostowany na GitHub.

Pobieranie kodu

Z tych instrukcji dowiesz się, jak utworzyć lokalną kopię przykładowego kodu za pomocą narzędzia wiersza poleceń git.

Aby pobrać przykładowy kod:

  1. Sklonuj repozytorium Git za pomocą tego polecenia:

    git clone https://github.com/google-ai-edge/mediapipe-samples
    
  2. Opcjonalnie skonfiguruj instancję Git, aby używać rzadkiego sprawdzania, dzięki czemu będziesz mieć tylko pliki przykładowej aplikacji Image Embedder:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_embedder/ios
    

Po utworzeniu lokalnej wersji przykładowego kodu możesz zainstalować bibliotekę zadań MediaPipe, otworzyć projekt za pomocą Xcode i uruchomić aplikację. Instrukcje znajdziesz w przewodniku konfiguracji dla iOS.

Kluczowe komponenty

Te pliki zawierają kluczowy kod aplikacji przykładowej Image Embedder:

Konfiguracja

W tej sekcji znajdziesz najważniejsze czynności, które musisz wykonać, aby skonfigurować środowisko programistyczne i projekty kodu pod kątem korzystania z narzędzia do umieszczania obrazów. Ogólne informacje o konfigurowaniu środowiska programistycznego do korzystania z zadań MediaPipe, w tym wymagania dotyczące wersji platformy, znajdziesz w przewodniku konfiguracji dla iOS.

Zależności

Image Embedder korzysta z biblioteki MediaPipeTasksVision, którą trzeba zainstalować za pomocą CocoaPods. Biblioteka jest zgodna z aplikacją w języku Swift i Objective-C i nie wymaga dodatkowej konfiguracji językowej.

Instrukcje instalacji CocoaPods na macOS znajdziesz w przewodniku instalacji CocoaPods. Instrukcje tworzenia Podfile z podstawowymi komponentami potrzebnymi do działania aplikacji znajdziesz w artykule Korzystanie z CocoaPods.

Dodaj blok MediaPipeTasksVision w Podfile za pomocą tego kodu:

target 'MyImageEmbedderApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

Jeśli Twoja aplikacja zawiera cele testów jednostkowych, dodatkowe informacje o konfigurowaniu Podfile znajdziesz w przewodniku konfiguracji na iOS.

Model

Zadanie MediaPipe Image Embedder wymaga wytrenowanego modelu, który jest zgodny z tym zadaniem. Więcej informacji o dostępnych wytrenowanych modelach Image Embedder znajdziesz w sekcji Modele.

Wybierz i pobierz model, a potem dodaj go do katalogu projektu za pomocą Xcode. Instrukcje dodawania plików do projektu Xcode znajdziesz w artykule Zarządzanie plikami i folderami w projekcie Xcode.

Użyj właściwości BaseOptions.modelAssetPath, aby określić ścieżkę do modelu w pakiecie aplikacji.

Tworzenie zadania

Zadanie Wstawianie obrazu możesz utworzyć, wywołując jedną z jego funkcji inicjującej. Inicjalizator ImageEmbedder(options:) może przyjmować wartości opcji konfiguracji.

Jeśli nie potrzebujesz modułu Image Embedder zainicjowanego za pomocą niestandardowych opcji konfiguracji, możesz użyć modyfikatora ImageEmbedder(modelPath:), aby utworzyć moduł Image Embedder z opcjami domyślnymi. Więcej informacji o opcjach konfiguracji znajdziesz w artykule Omówienie konfiguracji.

Zadanie Wstawianie obrazów obsługuje 3 typy danych wejściowych: obrazy, pliki wideo i strumienie wideo na żywo. Domyślnie ImageEmbedder(modelPath:) inicjuje zadanie dotyczące obrazów statycznych. Jeśli chcesz, aby zadanie zostało zainicjowane w celu przetwarzania plików wideo lub transmisji na żywo, użyj parametru ImageEmbedder(options:), aby określić tryb działania związany z odtwarzaniem filmów lub transmisji na żywo. Tryb transmisji na żywo wymaga też dodatkowej opcji konfiguracji imageEmbedderLiveStreamDelegate, która umożliwia dodatkowi Image Embedder dostarczanie wyników umieszczania obrazów do delegowanego elementu asynchronicznie.

Aby dowiedzieć się, jak utworzyć zadanie i przeprowadzić wnioskowanie, wybierz kartę odpowiadającą trybowi działania.

Swift

Obraz

import MediaPipeTasksVision

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageEmbedderOptions()
options.baseOptions.modelAssetPath = modelPath
options.quantize = true
options.l2Normalize = true

let imageEmbedder = try ImageEmbedder(options: options)
    

Wideo

import MediaPipeTasksVision

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageEmbedderOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.quantize = true
options.l2Normalize = true

let imageEmbedder = try ImageEmbedder(options: options)
    

Transmisja na żywo

import MediaPipeTasksVision

// Class that conforms to the `ImageEmbedderLiveStreamDelegate` protocol and
// implements the method that the image embedder calls once it finishes
// embedding each input frame.
class ImageEmbedderResultProcessor: NSObject, ImageEmbedderLiveStreamDelegate {

  func imageEmbedder(
    _ imageEmbedder: ImageEmbedder,
    didFinishEmbedding result: ImageEmbedderResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the image embedder result or errors here.

  }
}

let modelPath = Bundle.main.path(
  forResource: "model",
  ofType: "tflite")

let options = ImageEmbedderOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.quantize = true
options.l2Normalize = true

// Assign an object of the class to the `imageEmbedderLiveStreamDelegate`
// property.
let processor = ImageEmbedderResultProcessor()
options.imageEmbedderLiveStreamDelegate = processor

let imageEmbedder = try ImageEmbedder(options: options)
    

Objective-C

Obraz

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageEmbedderOptions *options = [[MPPImageEmbedderOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.quantize = YES;
options.l2Normalize = YES;

MPPImageEmbedder *imageEmbedder =
  [[MPPImageEmbedder alloc] initWithOptions:options error:nil];
    

Wideo

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageEmbedderOptions *options = [[MPPImageEmbedderOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.quantize = YES;
options.l2Normalize = YES;

MPPImageEmbedder *imageEmbedder =
  [[MPPImageEmbedder alloc] initWithOptions:options error:nil];
    

Transmisja na żywo

@import MediaPipeTasksVision;

// Class that conforms to the `MPPImageEmbedderLiveStreamDelegate` protocol
// and implements the method that the image embedder calls once it finishes
// embedding each input frame.
@interface APPImageEmbedderResultProcessor : NSObject 

@end

@implementation APPImageEmbedderResultProcessor

-   (void)imageEmbedder:(MPPImageEmbedder *)imageEmbedder
    didFinishEmbeddingWithResult:(MPPImageEmbedderResult *)imageEmbedderResult
         timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                           error:(NSError *)error {

    // Process the image embedder result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageEmbedderOptions *options = [[MPPImageEmbedderOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.quantize = YES;
options.l2Normalize = YES;

// Assign an object of the class to the `imageEmbedderLiveStreamDelegate`
// property.
APPImageEmbedderResultProcessor *processor =
  [APPImageEmbedderResultProcessor new];
options.imageEmbedderLiveStreamDelegate = processor;

MPPImageEmbedder *imageEmbedder =
  [[MPPImageEmbedder alloc] initWithOptions:options error:nil];
    

Opcje konfiguracji

W tym zadaniu dostępne są te opcje konfiguracji aplikacji na iOS:

Nazwa opcji Opis Zakres wartości Wartość domyślna
runningMode Ustawia tryb działania zadania. Narzędzie do wklejania obrazów ma 3 tryby:

OBRAZ: tryb do wklejania pojedynczych obrazów.

WIDEO: tryb zdekodowanych klatek w filmie.

LIVE_STREAM: tryb transmisji na żywo danych wejściowych, np. z kamery. W tym trybie parametr imageEmbedderLiveStreamDelegate musi być ustawiony na instancję klasy implementującej interfejs ImageEmbedderLiveStreamDelegate, aby asynchronicznie otrzymywać wyniki wstawiania ramek obrazu.
{RunningMode.image, RunningMode.video, RunningMode.liveStream} {RunningMode.image}
l2Normalize Określa, czy zwrócony wektor cech ma być znormalizowany za pomocą normy L2. Użyj tej opcji tylko wtedy, gdy model nie zawiera natywnego operatora L2_NORMALIZATION TFLite. W większości przypadków tak jest, więc normalizację L2 można osiągnąć dzięki wnioskowaniu TFLite bez konieczności korzystania z tej opcji. Wartość logiczna fałsz
quantize Określa, czy zwrócony wektor dystrybucyjny ma być zaokrąglony do bajtów za pomocą kwantyzacji skalarnej. Zakłada się, że wektory mają długość 1, a dlatego każdy wymiar ma wartość z zakresu [-1,0, 1,0]. W przeciwnym razie użyj opcji l2Normalize. Wartość logiczna fałsz

Gdy tryb działania jest ustawiony na transmisję na żywo, moduł do umieszczania obrazów wymaga dodatkowej opcji konfiguracji imageEmbedderLiveStreamDelegate, która umożliwia asynchroniczne dostarczanie wyników umieszczania obrazów. Delegat musi implementować metodę imageEmbedder(_:didFinishEmbedding:timestampInMilliseconds:error:), którą Image Embedder wywołuje po przetworzeniu wyników umieszczania każdego kadru obrazu wejściowego.

Nazwa opcji Opis Zakres wartości Wartość domyślna
imageEmbedderLiveStreamDelegate Umożliwia wtyczce Image Embedder asynchroniczne otrzymywanie wyników umieszczania obrazów w trybie transmisji na żywo. Klasa, której instancja jest ustawiona w tej właściwości, musi implementować metodę imageEmbedder(_:didFinishEmbedding:timestampInMilliseconds:error:). Nie dotyczy Nie ustawiono

Przygotuj dane

Musisz przekonwertować obraz lub ramkę wejściową na obiekt MPImage, zanim przekażesz go do narzędzia do dodawania obrazów. MPImage obsługuje różne typy formatów obrazów iOS i może używać ich w dowolnym trybie działania w celu wnioskowania. Więcej informacji o MPImage znajdziesz w dokumentacji interfejsu MPImage API.

Wybierz format obrazu iOS na podstawie przypadku użycia i trybułu działania wymaganego przez aplikację. MPImage obsługuje formaty obrazów iOS UIImage, CVPixelBufferCMSampleBuffer.

UIImage

Format UIImage jest odpowiedni do tych trybów działania:

  • Obrazy: obrazy z pakietu aplikacji, galerii użytkownika lub systemu plików sformatowane jako obrazy UIImage można przekształcić w obiekt MPImage.

  • Filmy: za pomocą narzędzia AVAssetImageGenerator wyodrębnij klatki wideo do formatu CGImage, a potem przekonwertuj je na UIImage obrazy.

Swift

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(uiImage: image)
    

Objective-C

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

Ten przykład inicjuje obiekt MPImage z domyślną orientacją UIImage.Orientation.Up. Możesz zainicjować MPImage dowolną z obsługiwanych wartości UIImage.Orientation. Narzędzie do umieszczania obrazów nie obsługuje orientacji lustrzanej, takiej jak .upMirrored, .downMirrored, .leftMirrored, .rightMirrored.

Więcej informacji o UIImage znajdziesz w dokumentacji UIImage dla deweloperów Apple.

CVPixelBuffer

Format CVPixelBuffer sprawdza się w aplikacjach, które generują ramki i wykorzystują do przetwarzania platformę CoreImage na iOS.

Format CVPixelBuffer jest odpowiedni do tych trybów działania:

  • Obrazy: aplikacje, które generują obrazy CVPixelBuffer po przetworzeniu ich za pomocą interfejsu CoreImage w iOS, mogą wysyłać je do Image Embedder w trybie uruchamiania obrazu.

  • Filmy: ramki wideo można przekonwertować do formatu CVPixelBuffer na potrzeby przetwarzania, a następnie wysłać do modułu Image Embedder w trybie wideo.

  • transmisja na żywo: aplikacje korzystające z kamery iOS do generowania klatek mogą zostać przekonwertowane do formatu CVPixelBuffer w celu przetwarzania, zanim zostaną wysłane do modułu wstawiania obrazów w trybie transmisji na żywo.

Swift

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(pixelBuffer: pixelBuffer)
    

Objective-C

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

Więcej informacji o CVPixelBuffer znajdziesz w dokumentacji dla deweloperów Apple dotyczącej CVPixelBuffer.

CMSampleBuffer

Format CMSampleBuffer przechowuje próbki multimediów w ramach jednolitego typu multimediów i sprawdza się w trybie biegu transmisji na żywo. Klatki na żywo z kamer z iOS są asynchronicznie dostarczane w formacie CMSampleBuffer przez AVCaptureVideoDataOutput w systemie iOS.

Swift

// Obtain a CMSampleBuffer.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(sampleBuffer: sampleBuffer)
    

Objective-C

// Obtain a `CMSampleBuffer`.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
    

Więcej informacji o właściwości CMSampleBuffer znajdziesz w dokumentacji dla programistów Apple CMSampleBuffer.

Uruchamianie zadania

Aby uruchomić mechanizm umieszczania obrazów, użyj metody embed() specyficznej dla przypisanego trybu uruchamiania:

  • Statyczny obraz: embed(image:)
  • Film: embed(videoFrame:timestampInMilliseconds:)
  • Transmisja na żywo: embedAsync(image:timestampInMilliseconds:)

Poniżej znajdziesz przykładowy kod pokazujący, jak uruchomić narzędzie do dodawania obrazów w różnych trybach działania:

Swift

Obraz

let result = try imageEmbedder.embed(image: image)
    

Wideo

let result = try imageEmbedder.embed(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

Transmisja na żywo

try imageEmbedder.embedAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

Obraz

MPPImageEmbedderResult *result =
  [imageEmbedder embedImage:image error:nil];
    

Wideo

MPPImageEmbedderResult *result =
  [imageEmbedder embedVideoFrame:image
           timestampInMilliseconds:timestamp
                             error:nil];
    

Transmisja na żywo

BOOL success =
  [imageEmbedder embedAsyncImage:image
           timestampInMilliseconds:timestamp
                             error:nil];
    

Przykładowy kod Image Embedder pokazuje szczegółowo implementację każdego z tych trybów: embed(image:), embed(videoFrame:timestampInMilliseconds:)embedAsync(image:timestampInMilliseconds:). Przykładowy kod pozwala użytkownikowi przełączać się między trybami przetwarzania, co może nie być wymagane w Twoim przypadku użycia.

Pamiętaj:

  • W trybie wideo lub transmisji na żywo musisz też podać zadaniu Image Embedder sygnaturę czasową ramki wejściowej.

  • Gdy działa w trybie obrazu lub filmu, zadanie osadzania obrazu blokuje bieżący wątek, dopóki nie przetworzy podawanego obrazu lub kadru. Aby uniknąć zablokowania bieżącego wątku, wykonaj przetwarzanie w wątku w tle za pomocą struktur systemu iOS Dispatch lub NSOperation. Jeśli aplikacja została utworzona w Swift, możesz też użyć makro Swifta dotyczącym współbieżności do wykonywania wątków w tle.

  • W trybie transmisji na żywo zadanie Image Embedder zwraca wynik natychmiast i nie blokuje bieżącego wątku. Wywołuje metodę imageEmbedder(_:didFinishEmbedding:timestampInMilliseconds:error:) z wynikami po umieszczeniu każdej ramki wejściowej. Wstawiacz obrazu wywołuje tę metodę asynchronicznie w dedykowanej kolejce wysyłania sekwencyjnego. Aby wyświetlać wyniki w interfejsie, po przetworzeniu wyników prześlij je do kolejki głównej. Jeśli funkcja embedAsync jest wywoływana, gdy zadanie Image Embedder jest zajęte przetwarzaniem innego kadru, zadanie Image Embedder zignoruje nowy klatka wejściowa.

Obsługa i wyświetlanie wyników

Po przeprowadzeniu wnioskowania Image Embedder zwraca obiekt ImageEmbedderResult, który zawiera listę elementów zaimplementowanych (zmiennych typu float lub skali kwantyzowanej) dla obrazu wejściowego.

Poniżej znajdziesz przykładowe dane wyjściowe tego zadania:

ImageEmbedderResult:
  Embedding #0 (sole embedding head):
    float_embedding: {0.0, 0.0, ..., 0.0, 1.0, 0.0, 0.0, 2.0}
    head_index: 0

Ten wynik został uzyskany dzięki umieszczeniu tego obrazu:

Za pomocą funkcji ImageEmbedder.cosineSimilarity możesz porównać podobieństwo 2 wkładników.

Swift

let similarity = try ImageEmbedder.cosineSimilarity(
  embedding1: result.embeddingResult.embeddings[0],
  embedding2: otherResult.embeddingResult.embeddings[0])
    

Objective-C

NSNumber *similarity = [MPPImageEmbedder
      cosineSimilarityBetweenEmbedding1:result.embeddingResult.embeddings[0]
                          andEmbedding2:otherResult.embeddingResult.embeddings[0]
                                  error:nil];