Zadanie MediaPipe Image Embedder pozwala przekształcić dane obrazu na reprezentację liczbową w celu realizacji zadań przetwarzania obrazu związanych z systemami uczącymi się, takich jak porównywanie podobieństwa 2 obrazów. W tych instrukcjach pokazujemy, jak korzystać z narzędzia do umieszczania obrazów w aplikacjach na Androida.
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 prosta implementacja aplikacji do umieszczania obrazów na urządzeniu z Androidem. W tym przykładzie używamy aparatu na fizycznym urządzeniu z Androidem do ciągłego umieszczania obrazów, a także można uruchomić narzędzie do umieszczania na plikach graficznych zapisanych na urządzeniu.
Możesz użyć aplikacji jako punktu wyjścia dla własnej aplikacji na Androida lub skorzystać z niej podczas modyfikowania istniejącej aplikacji. Przykładowy kod do umieszczania obrazów jest hostowany na GitHub.
Pobieranie kodu
Z instrukcji poniżej dowiesz się, jak utworzyć lokalną kopię przykładowego kodu za pomocą narzędzia wiersza poleceń git.
Aby pobrać przykładowy kod:
- Sklonuj repozytorium git za pomocą tego polecenia:
git clone https://github.com/google-ai-edge/mediapipe-samples
- Opcjonalnie skonfiguruj instancję git tak, aby używała rozproszonego procesu płatności, aby mieć tylko pliki dla przykładowej aplikacji do umieszczania obrazów:
cd mediapipe git sparse-checkout init --cone git sparse-checkout set examples/image_embedder/android
Po utworzeniu lokalnej wersji przykładowego kodu możesz zaimportować projekt do Android Studio i uruchomić aplikację. Instrukcje znajdziesz w przewodniku konfiguracji na Androida.
Kluczowe elementy
Te pliki zawierają kluczowy kod tej przykładowej aplikacji do umieszczania obrazów:
- ImageEmbedderHelper.kt: inicjuje narzędzie do umieszczania obrazów oraz obsługuje wybór modelu i przedstawicieli.
- MainActivity.kt: implementuje aplikację i tworzy komponenty interfejsu użytkownika.
Konfiguracja
W tej sekcji znajdziesz najważniejsze czynności, jakie należy wykonać, aby skonfigurować środowisko programistyczne i określić projekty kodu, które mają korzystać z kreatora obrazów. Ogólne informacje o konfigurowaniu środowiska programistycznego na potrzeby zadań MediaPipe, w tym o wymaganiach dotyczących wersji platformy, znajdziesz w przewodniku konfiguracji dla Androida.
Zależności
Umieszczanie obrazów korzysta z biblioteki com.google.mediapipe:tasks-vision
. Dodaj tę zależność do pliku build.gradle
swojego projektu na Androida.
Zaimportuj wymagane zależności za pomocą tego kodu:
dependencies {
...
implementation 'com.google.mediapipe:tasks-vision:latest.release'
}
Model
Zadanie do umieszczania obrazów MediaPipe wymaga wytrenowanego modelu zgodnego z tym zadaniem. Więcej informacji o dostępnych wytrenowanych modelach do umieszczania obrazów znajdziesz w sekcji poświęconej modelom na stronie z omówieniem zadań.
Wybierz i pobierz model, a następnie zapisz go w katalogu projektu:
<dev-project-root>/src/main/assets
Podaj ścieżkę modelu w parametrze ModelAssetPath
. W przykładowym kodzie model jest zdefiniowany w funkcji setupImageEmbedder()
w pliku ImageEmbedderHelper.kt:
Aby określić ścieżkę używaną przez model, użyj metody BaseOptions.Builder.setModelAssetPath()
. Tę metodę omówimy w przykładowym kodzie w następnej sekcji.
Tworzenie zadania
Aby utworzyć zadanie, możesz użyć funkcji createFromOptions
. Funkcja createFromOptions
akceptuje opcje konfiguracji umożliwiające ustawienie opcji umieszczania. Więcej informacji o opcjach konfiguracji znajdziesz w artykule Omówienie konfiguracji.
Zadanie do umieszczania obrazów obsługuje 3 typy danych wejściowych: obrazy, pliki wideo i strumienie wideo na żywo. Podczas tworzenia zadania musisz określić tryb działania odpowiadający typowi danych wejściowych. Wybierz kartę odpowiadającą typowi danych wejściowych, aby zobaczyć, jak utworzyć zadanie i uruchomić wnioskowanie.
Obraz
ImageEmbedderOptions options = ImageEmbedderOptions.builder() .setBaseOptions( BaseOptions.builder().setModelAssetPath("model.tflite").build()) .setQuantize(true) .setRunningMode(RunningMode.IMAGE) .build(); imageEmbedder = ImageEmbedder.createFromOptions(context, options);
Wideo
ImageEmbedderOptions options = ImageEmbedderOptions.builder() .setBaseOptions( BaseOptions.builder().setModelAssetPath("model.tflite").build()) .setQuantize(true) .setRunningMode(RunningMode.VIDEO) .build(); imageEmbedder = ImageEmbedder.createFromOptions(context, options);
Transmisja na żywo
ImageEmbedderOptions options = ImageEmbedderOptions.builder() .setBaseOptions( BaseOptions.builder().setModelAssetPath("model.tflite").build()) .setQuantize(true) .setRunningMode(RunningMode.LIVE_STREAM) .setResultListener((result, inputImage) -> { // Process the embedding result here. }) .build(); imageEmbedder = ImageEmbedder.createFromOptions(context, options);
Przykładowa implementacja kodu pozwala użytkownikowi przełączać się między trybami przetwarzania. Takie podejście zwiększa złożoność kodu tworzenia zadania i może nie być odpowiednie w Twoim przypadku. Ten kod znajdziesz w funkcji setupImageEmbedder()
w pliku ImageEmbedderHelper.kt.
Opcje konfiguracji
To zadanie ma te opcje konfiguracji aplikacji na Androida:
Nazwa opcji | Opis | Zakres wartości | Wartość domyślna |
---|---|---|---|
runningMode |
Ustawia tryb działania zadania. Są 3 tryby: IMAGE: tryb wprowadzania pojedynczych obrazów. WIDEO: tryb dekodowanych klatek filmu. TRANSMISJA NA ŻYWO: tryb transmisji danych wejściowych na żywo, np. z kamery. W tym trybie należy wywołać metodę resultListener, aby skonfigurować odbiornik, który będzie odbierał wyniki asynchronicznie. |
{IMAGE, VIDEO, LIVE_STREAM } |
IMAGE |
l2_normalize |
Określa, czy znormalizować zwrócony wektor cech z normą L2. Użyj tej opcji tylko wtedy, gdy model nie zawiera jeszcze natywnej operacji L2_NORMALIZATION TFLite. W większości przypadków tak się dzieje i w ten sposób można uzyskać normalizację L2 za pomocą wnioskowania TFLite bez potrzeby użycia tej opcji. | Boolean |
False |
quantize |
Określa, czy zwrócone umieszczenie ma zostać poddane kwantyzacji do liczby bajtów za pomocą kwantyzacji skalarnej. Osadzone elementy są domyślnie uznawane za normę jednostki, dlatego każdy wymiar na pewno ma wartość [-1,0, 1,0]. Jeśli tak nie jest, użyj opcji l2_normalize. | Boolean |
False |
resultListener |
Ustawia odbiornik, który asynchronicznie otrzymuje wyniki umieszczania, gdy kreator obrazów działa w trybie transmisji na żywo. Tego ustawienia można używać tylko wtedy, gdy tryb biegowy jest ustawiony na LIVE_STREAM |
Nie dotyczy | Nie ustawiono |
errorListener |
Ustawia opcjonalny detektor błędów. | Nie dotyczy | Nie ustawiono |
Przygotuj dane
Narzędzie do umieszczania obrazów obsługuje obrazy, pliki wideo i transmisje wideo na żywo. To zadanie obsługuje wstępne przetwarzanie danych wejściowych, w tym zmianę rozmiaru, rotację i normalizację wartości.
Przed przekazaniem obrazu wejściowego lub ramki do obiektu com.google.mediapipe.framework.image.MPImage
musisz go przekonwertować do zadania umieszczania obrazów.
Obraz
import com.google.mediapipe.framework.image.BitmapImageBuilder; import com.google.mediapipe.framework.image.MPImage; // Load an image on the user’s device as a Bitmap object using BitmapFactory. // Convert an Android’s Bitmap object to a MediaPipe’s Image object. Image mpImage = new BitmapImageBuilder(bitmap).build();
Wideo
import com.google.mediapipe.framework.image.BitmapImageBuilder; import com.google.mediapipe.framework.image.MPImage; // Load a video file on the user's device using MediaMetadataRetriever // From the video’s metadata, load the METADATA_KEY_DURATION and // METADATA_KEY_VIDEO_FRAME_COUNT value. You’ll need them // to calculate the timestamp of each frame later. // Loop through the video and load each frame as a Bitmap object. // Convert the Android’s Bitmap object to a MediaPipe’s Image object. Image mpImage = new BitmapImageBuilder(frame).build();
Transmisja na żywo
import com.google.mediapipe.framework.image.MediaImageBuilder; import com.google.mediapipe.framework.image.MPImage; // Create a CameraX’s ImageAnalysis to continuously receive frames // from the device’s camera. Configure it to output frames in RGBA_8888 // format to match with what is required by the model. // For each Android’s ImageProxy object received from the ImageAnalysis, // extract the encapsulated Android’s Image object and convert it to // a MediaPipe’s Image object. android.media.Image mediaImage = imageProxy.getImage() Image mpImage = new MediaImageBuilder(mediaImage).build();
W przykładowym kodzie przygotowanie danych odbywa się w pliku ImageEmbedderHelper.kt.
Uruchamianie zadania
Aby aktywować wnioskowanie, możesz wywołać funkcję embed
odpowiadającą Twojemu trybowi biegowemu. Interfejs Image Embedder API zwraca wektory dystrybucyjne dla obrazu wejściowego lub ramki.
Obraz
ImageEmbedderResult embedderResult = imageEmbedder.embed(image);
Wideo
// Calculate the timestamp in milliseconds of the current frame. long frame_timestamp_ms = 1000 * video_duration * frame_index / frame_count; // Run inference on the frame. ImageEmbedderResult embedderResult = imageEmbedder.embedForVideo(image, frameTimestampMs);
Transmisja na żywo
// Run inference on the frame. The embedding results will be available // via the `resultListener` provided in the `ImageEmbedderOptions` when // the image embedder was created. imageEmbedder.embedAsync(image, frameTimestampMs);
Uwaga:
- Gdy urządzenie działa w trybie wideo lub transmisji na żywo, do zadania umieszczania obrazów musisz też podać sygnaturę czasową ramki wejściowej.
- W trybie obrazu lub wideo zadanie umieszczania obrazów zablokuje bieżący wątek do momentu zakończenia przetwarzania obrazu wejściowego lub klatki. Aby uniknąć zablokowania bieżącego wątku, wykonaj przetwarzanie w wątku w tle.
- W trybie transmisji na żywo zadanie umieszczania obrazów nie blokuje bieżącego wątku, ale zwraca je natychmiast. Wywołuje on detektor wyników z wynikiem wykrywania za każdym razem, gdy zakończy przetwarzanie ramki wejściowej. Jeśli funkcja
embedAsync
zostanie wywołana, gdy zadanie umieszczania obrazów jest zajęte przetwarzaniem innej ramki, zadanie ignoruje nową ramkę wejściową.
W przykładowym kodzie funkcja embed
jest zdefiniowana w pliku ImageEmbedderHelper.kt.
Obsługa i wyświetlanie wyników
Po uruchomieniu wnioskowania zadanie umieszczania obrazów zwraca obiekt ImageEmbedderResult
zawierający listę reprezentacji właściwościowych (zmiennoprzecinkowych lub kwantyzowanych) obrazu wejściowego.
Poniżej znajdziesz przykład danych wyjściowych z 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
Uzyskano taki wynik przez umieszczenie następującego obrazu:
Podobieństwo 2 reprezentacji właściwościowych możesz porównać za pomocą funkcji ImageEmbedder.cosineSimilarity
. Oto przykładowy kod.
// Compute cosine similarity.
double similarity = ImageEmbedder.cosineSimilarity(
result.embeddingResult().embeddings().get(0),
otherResult.embeddingResult().embeddings().get(0));