1. Zanim zaczniesz
Wpisywanie kodu to świetny sposób na wyrobienie pamięci mięśniowej i pogłębienie wiedzy. Kopiowanie i wklejanie może oszczędzać czas, ale inwestowanie w tę praktykę może w dłuższej perspektywie zwiększyć wydajność i poprawić umiejętności kodowania.
Z tego samouczka dowiesz się, jak utworzyć aplikację na Androida, która wykonuje segmentację obrazu w czasie rzeczywistym na żywym obrazie z kamery za pomocą nowego środowiska wykonawczego Google dla TensorFlow Lite, czyli LiteRT. Zaczniesz od prostej aplikacji na Androida i dodasz do niej funkcje segmentacji obrazu. Omówimy też kroki wstępnego przetwarzania, wnioskowania i przetwarzania końcowego. W ramach ćwiczenia:
- Utwórz aplikację na Androida, która segmentuje obrazy w czasie rzeczywistym.
- Zintegruj wytrenowany model segmentacji obrazów LiteRT.
- Wstępnie przetwórz obraz wejściowy na potrzeby modelu.
- Używaj środowiska wykonawczego LiteRT do przyspieszania działania na procesorze i GPU.
- Dowiedz się, jak przetwarzać dane wyjściowe modelu, aby wyświetlać maskę segmentacji.
- Dowiedz się, jak dostosować ustawienia przedniego aparatu.
Na koniec uzyskasz efekt podobny do tego na obrazie poniżej:
Wymagania wstępne
Te ćwiczenia z programowania są przeznaczone dla doświadczonych programistów aplikacji mobilnych, którzy chcą zdobyć doświadczenie w zakresie uczenia maszynowego. Musisz znać:
- Tworzenie aplikacji na Androida w Kotlinie i Android Studio
- Podstawowe pojęcia dotyczące przetwarzania obrazów
Czego się nauczysz
- Jak zintegrować i używać środowiska wykonawczego LiteRT w aplikacji na Androida.
- Jak przeprowadzić segmentację obrazu za pomocą wytrenowanego modelu LiteRT.
- Jak wstępnie przetworzyć obraz wejściowy na potrzeby modelu.
- Jak przeprowadzić wnioskowanie w przypadku modelu.
- Jak przetwarzać dane wyjściowe modelu segmentacji, aby wizualizować wyniki.
- Jak używać CameraX do przetwarzania obrazu z kamery w czasie rzeczywistym.
Czego potrzebujesz
- Najnowsza wersja Android Studio (testowana w wersji 2025.1.1).
- fizyczne urządzenie z Androidem; Najlepiej przetestować ją na urządzeniach Galaxy i Pixel.
- przykładowy kod (z GitHuba);
- Podstawowa wiedza na temat tworzenia aplikacji na Androida w języku Kotlin.
2. Segmentacja obrazu
Segmentacja obrazu to zadanie z zakresu widzenia komputerowego, które polega na podzieleniu obrazu na wiele segmentów lub regionów. W przeciwieństwie do wykrywania obiektów, które rysuje ramkę ograniczającą wokół obiektu, segmentacja obrazu przypisuje określoną klasę lub etykietę do każdego piksela na obrazie. Dzięki temu uzyskasz znacznie bardziej szczegółowe i dokładne informacje o zawartości obrazu, co pozwoli Ci poznać dokładny kształt i granice każdego obiektu.
Na przykład zamiast tylko wiedzieć, że w ramce znajduje się „osoba”, możesz dokładnie określić, które piksele do niej należą. Z tego samouczka dowiesz się, jak przeprowadzić segmentację obrazu w czasie rzeczywistym na urządzeniu z Androidem za pomocą wstępnie wytrenowanego modelu uczenia maszynowego.
LiteRT: przesuwanie granic uczenia maszynowego na urządzeniu
Kluczową technologią umożliwiającą segmentację w czasie rzeczywistym i wysokiej jakości na urządzeniach mobilnych jest LiteRT. LiteRT to środowisko wykonawcze TensorFlow Lite nowej generacji o wysokiej wydajności. Zostało zaprojektowane tak, aby w pełni wykorzystywać możliwości sprzętu.
Osiąga to dzięki inteligentnemu i zoptymalizowanemu wykorzystaniu akceleratorów sprzętowych, takich jak GPU (procesor graficzny) i NPU (procesor sieci neuronowych). Przenosząc intensywne obliczenia modelu segmentacji z procesora ogólnego przeznaczenia na te wyspecjalizowane procesory, LiteRT znacznie skraca czas wnioskowania. To przyspieszenie umożliwia płynne działanie złożonych modeli na żywo w aplikacji Aparat, co poszerza możliwości uczenia maszynowego bezpośrednio na telefonie. Bez tego poziomu wydajności segmentacja w czasie rzeczywistym byłaby zbyt wolna i niestabilna, aby zapewnić użytkownikom wygodę.
3. Konfiguracja
Klonowanie repozytorium
Najpierw sklonuj repozytorium LiteRT:
git clone https://github.com/google-ai-edge/LiteRT.git
LiteRT/litert/samples/image_segmentation
to katalog ze wszystkimi potrzebnymi zasobami. W tym ćwiczeniu potrzebny będzie tylko projekt kotlin_cpu_gpu/android_starter
. Jeśli utkniesz, możesz sprawdzić gotowy projekt: kotlin_cpu_gpu/android
Uwaga dotycząca ścieżek do plików
W tym samouczku ścieżki plików są podane w formacie Linux/macOS. Jeśli korzystasz z systemu Windows, musisz odpowiednio dostosować ścieżki.
Warto też zwrócić uwagę na różnicę między widokiem projektu w Android Studio a standardowym widokiem systemu plików. Widok projektu w Android Studio to uporządkowana reprezentacja plików projektu, zorganizowana pod kątem tworzenia aplikacji na Androida. Ścieżki plików w tym samouczku odnoszą się do ścieżek w systemie plików, a nie do ścieżek w widoku projektu w Android Studio.
Importowanie aplikacji startowej
Zacznijmy od zaimportowania aplikacji początkowej do Androida Studio.
- Otwórz Android Studio i kliknij Otwórz.
- Przejdź do katalogu
kotlin_cpu_gpu/android_starter
i otwórz go.
Aby mieć pewność, że wszystkie zależności są dostępne dla aplikacji, po zakończeniu procesu importowania zsynchronizuj projekt z plikami Gradle.
- Na pasku narzędzi Android Studio wybierz Sync Project with Gradle Files (Synchronizuj projekt z plikami Gradle).
- Nie pomijaj tego kroku – jeśli nie zadziała, reszta samouczka nie będzie miała sensu.
Uruchom aplikację startową
Po zaimportowaniu projektu do Android Studio możesz po raz pierwszy uruchomić aplikację.
Podłącz urządzenie z Androidem do komputera przez USB i na pasku narzędzi Androida Studio kliknij Uruchom.
Aplikacja powinna się uruchomić na urządzeniu. Zobaczysz obraz na żywo z kamery, ale segmentacja nie będzie jeszcze działać. Wszystkie zmiany w plikach, które wprowadzisz w tym samouczku, będą dotyczyć katalogu LiteRT/litert/samples/image_segmentation/kotlin_cpu_gpu/android_starter/app/src/main/java/com/google/aiedge/examples/image_segmentation
(teraz już wiesz, dlaczego Android Studio zmienia jego strukturę 😃).
Zobaczysz też TODO
komentarze w plikach ImageSegmentationHelper.kt
, MainViewModel.kt
i view/SegmentationOverlay.kt
. W kolejnych krokach zaimplementujesz funkcję segmentacji obrazu, wypełniając te pola TODO
.
4. Informacje o aplikacji startowej
Aplikacja startowa ma już podstawowy interfejs i logikę obsługi aparatu. Oto krótkie omówienie najważniejszych plików:
app/src/main/java/com/google/aiedge/examples/image_segmentation/MainActivity.kt
: to główny punkt wejścia do aplikacji. Konfiguruje interfejs za pomocą Jetpack Compose i zarządza uprawnieniami dostępu do aparatu.app/src/main/java/com/google/aiedge/examples/image_segmentation/MainViewModel.kt
: ten ViewModel zarządza stanem interfejsu i koordynuje proces segmentacji obrazu.app/src/main/java/com/google/aiedge/examples/image_segmentation/ImageSegmentationHelper.kt
: w tym miejscu dodamy podstawową logikę segmentacji obrazu. Zajmie się wczytywaniem modelu, przetwarzaniem klatek z kamery i przeprowadzaniem wnioskowania.app/src/main/java/com/google/aiedge/examples/image_segmentation/view/CameraScreen.kt
: ta funkcja Composable wyświetla podgląd z kamery i nakładkę segmentacji.app/src/main/assets/selfie_multiclass.tflite
: jest to wstępnie wytrenowany model segmentacji obrazu TensorFlow Lite, którego będziemy używać.
5. Informacje o LiteRT i dodawanie zależności
Teraz dodajmy do aplikacji startowej funkcję segmentacji obrazu.
1. Dodaj zależność LiteRT
Najpierw musisz dodać bibliotekę LiteRT do projektu. Jest to kluczowy pierwszy krok, który umożliwia uczenie maszynowe na urządzeniu z użyciem zoptymalizowanego środowiska wykonawczego Google.
Otwórz plik app/build.gradle.kts
i dodaj ten wiersz do bloku dependencies
:
// LiteRT for on-device ML
implementation(libs.litert)
Po dodaniu zależności zsynchronizuj projekt z plikami Gradle, klikając przycisk Synchronizuj teraz, który pojawi się w prawym górnym rogu Android Studio.
2. Poznaj kluczowe interfejsy API LiteRT
OtwórzImageSegmentationHelper.kt
Zanim napiszesz kod implementacji, musisz poznać podstawowe komponenty interfejsu LiteRT API, których będziesz używać. Upewnij się, że importujesz z pakietu com.google.ai.edge.litert
. Dodaj te importy na początku pliku ImageSegmentationHelper.kt
:
import com.google.ai.edge.litert.Accelerator
import com.google.ai.edge.litert.CompiledModel
CompiledModel
: jest to główna klasa do interakcji z modelem TFLite. Jest to model, który został wstępnie skompilowany i zoptymalizowany pod kątem konkretnego akceleratora sprzętowego (np. procesora lub GPU). Wstępna kompilacja to kluczowa funkcja LiteRT, która zapewnia szybsze i wydajniejsze wnioskowanie.CompiledModel.Options
: za pomocą tej klasy narzędzia do tworzenia możesz skonfigurowaćCompiledModel
. Najważniejsze ustawienie to określenie akceleratora sprzętowego, którego chcesz używać do uruchamiania modelu.Accelerator
: ten wyliczenie umożliwia wybór sprzętu do wnioskowania. Projekt startowy jest już skonfigurowany do obsługi tych opcji:Accelerator.CPU
: do uruchamiania modelu na procesorze urządzenia. Jest to najbardziej uniwersalna opcja.Accelerator.GPU
: do uruchamiania modelu na procesorze graficznym urządzenia. W przypadku modeli opartych na obrazach jest to często znacznie szybsze niż w przypadku procesora.
- Bufory wejściowe i wyjściowe (
TensorBuffer
): LiteRT używaTensorBuffer
do danych wejściowych i wyjściowych modelu. Dzięki temu masz szczegółową kontrolę nad pamięcią i unikasz niepotrzebnych kopii danych. Te bufory uzyskasz bezpośrednio z instancjiCompiledModel
za pomocą funkcjimodel.createInputBuffers()
imodel.createOutputBuffers()
, a następnie zapiszesz w nich dane wejściowe i odczytasz wyniki. model.run()
: ta funkcja wykonuje wnioskowanie. Przekazujesz do niego bufory wejściowe i wyjściowe, a LiteRT zajmuje się złożonym zadaniem uruchomienia modelu na wybranym akceleratorze sprzętowym.
6. Kończenie wstępnej implementacji ImageSegmentationHelper
Teraz czas napisać kod. Przeprowadzisz wstępną implementację ImageSegmentationHelper.kt
. Obejmuje to skonfigurowanie Segmenter
klasy prywatnej do przechowywania modelu LiteRT i wdrożenie funkcji cleanup()
, która umożliwia jego prawidłowe zwalnianie.
- Ukończ
Segmenter
klasę icleanup()
funkcję: w plikuImageSegmentationHelper.kt
znajdziesz szkielet klasy prywatnej o nazwieSegmenter
i funkcji o nazwiecleanup()
. Najpierw uzupełnij klasęSegmenter
, definiując jej konstruktor do przechowywania modelu, tworząc właściwości buforów wejściowych i wyjściowych oraz dodając metodęclose()
do zwalniania modelu. Następnie zaimplementuj funkcjęcleanup()
, aby wywoływała nową metodęclose()
.Zastąp istniejącą klasęSegmenter
i funkcjęcleanup()
tym kodem: (~wiersz 83)private class Segmenter( // Add this argument private val model: CompiledModel, private val coloredLabels: List<ColoredLabel>, ) { // Add these private vals private val inputBuffers: = model.createInputBuffers() private val outputBuffers: = model.createOutputBuffers() fun cleanup() { // cleanup buffers inputBuffers.forEach { it.close() } outputBuffers.forEach { it.close() } // cleanup model model.close() } }
- Zdefiniuj metodę toAccelerator: ta metoda mapuje zdefiniowane wyliczenia akceleratora z menu akceleratora na wyliczenia akceleratora specyficzne dla zaimportowanych modułów LiteRT (~wiersz 225):
fun toAccelerator(acceleratorEnum: AcceleratorEnum): Accelerator { return when (acceleratorEnum) { AcceleratorEnum.CPU -> Accelerator.CPU AcceleratorEnum.GPU -> Accelerator.GPU } }
- Zainicjuj
CompiledModel
: teraz znajdź funkcjęinitSegmenter
. W tym miejscu utworzyszCompiledModel
instancję i użyjesz jej do utworzenia instancji zdefiniowanej klasySegmenter
. Ten kod konfiguruje model z określonym akceleratorem (CPU lub GPU) i przygotowuje go do wnioskowania. Zastąp znakTODO
winitSegmenter
tym kodem (naciśnij Cmd/Ctrl+f „initSegmenter” lub przejdź do wiersza 62):cleanup() try { withContext(singleThreadDispatcher) { val model = CompiledModel.create( context.assets, "selfie_multiclass.tflite", CompiledModel.Options(toAccelerator(acceleratorEnum)), null, ) segmenter = Segmenter(model, coloredLabels) Log.d(TAG, "Created an image segmenter") } } catch (e: Exception) { Log.i(TAG, "Create LiteRT from selfie_multiclass is failed: ${e.message}") _error.emit(e) }
7. Rozpocznij segmentację i przetwarzanie wstępne
Teraz, gdy mamy już model, musimy uruchomić proces segmentacji i przygotować dane wejściowe dla modelu.
Aktywowanie segmentacji
Proces segmentacji rozpoczyna się w MainViewModel.kt
, które otrzymuje klatki z kamery.
OtwórzMainViewModel.kt
- Wywoływanie segmentacji z klatek kamery: funkcje
segment
wMainViewModel
są punktem wejścia dla naszego zadania segmentacji. Są one wywoływane, gdy z aparatu jest dostępne nowe zdjęcie lub gdy zostanie ono wybrane z galerii. Te funkcje wywołują następnie metodęsegment
w naszymImageSegmentationHelper
. Zastąp symboleTODO
w obu funkcjachsegment
tymi wartościami (wiersz ~107):// For ImageProxy (from CameraX) fun segment(imageProxy: ImageProxy) { segmentJob = viewModelScope.launch { imageSegmentationHelper.segment(imageProxy.toBitmap(), imageProxy.imageInfo.rotationDegrees) imageProxy.close() } } // For Bitmaps (from gallery) fun segment(bitmap: Bitmap, rotationDegrees: Int) { segmentJob = viewModelScope.launch { val argbBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true) imageSegmentationHelper.segment(argbBitmap, rotationDegrees) } }
Wstępne przetwarzanie obrazu
Wróćmy teraz do ImageSegmentationHelper.kt
, aby zająć się wstępnym przetwarzaniem obrazu.
OtwórzImageSegmentationHelper.kt
- Wdróż funkcję Public
segment
Function: ta funkcja służy jako otoka, która wywołuje prywatną funkcjęsegment
w klasieSegmenter
. ZastąpTODO
(~wiersz 95):try { withContext(singleThreadDispatcher) { segmenter?.segment(bitmap, rotationDegrees)?.let { if (isActive) _segmentation.emit(it) } } } catch (e: Exception) { Log.i(TAG, "Image segment error occurred: ${e.message}") _error.emit(e) }
- Wdrożenie wstępnego przetwarzania: prywatna
segment
funkcja w klasieSegmenter
to miejsce, w którym przeprowadzimy niezbędne przekształcenia obrazu wejściowego, aby przygotować go do modelu. Obejmuje to skalowanie, obracanie i normalizowanie obrazu. Ta funkcja wywoła następnie inną prywatną funkcjęsegment
, aby przeprowadzić wnioskowanie. ZastąpTODO
w funkcjisegment(bitmap: Bitmap, ...)
tym ciągiem znaków (~line 121):val totalStartTime = SystemClock.uptimeMillis() val rotation = -rotationDegrees / 90 val (h, w) = Pair(256, 256) // Preprocessing val preprocessStartTime = SystemClock.uptimeMillis() var image = bitmap.scale(w, h, true) image = rot90Clockwise(image, rotation) val inputFloatArray = normalize(image, 127.5f, 127.5f) Log.d(TAG, "Preprocessing time: ${SystemClock.uptimeMillis() - preprocessStartTime} ms") // Inference val inferenceStartTime = SystemClock.uptimeMillis() val segmentResult = segment(inputFloatArray) Log.d(TAG, "Inference time: ${SystemClock.uptimeMillis() - inferenceStartTime} ms") Log.d(TAG, "Total segmentation time: ${SystemClock.uptimeMillis() - totalStartTime} ms") return SegmentationResult(segmentResult, SystemClock.uptimeMillis() - inferenceStartTime)
8. Podstawowe wnioskowanie z użyciem LiteRT
Po wstępnym przetworzeniu danych wejściowych możemy uruchomić podstawowe wnioskowanie za pomocą LiteRT.
OtwórzImageSegmentationHelper.kt
- Implementacja wykonywania modelu: prywatna funkcja
segment(inputFloatArray: FloatArray)
to miejsce, w którym bezpośrednio wchodzimy w interakcję z metodą LiteRTrun()
. Zapisujemy wstępnie przetworzone dane w buforze wejściowym, uruchamiamy model i odczytujemy wyniki z bufora wyjściowego. ZastąpTODO
w tej funkcji tym kodem (~wiersz 188):val (h, w, c) = Triple(256, 256, 6) // MODEL EXECUTION PHASE val modelExecStartTime = SystemClock.uptimeMillis() // Write input data - measure time val bufferWriteStartTime = SystemClock.uptimeMillis() inputBuffers[0].writeFloat(inputFloatArray) val bufferWriteTime = SystemClock.uptimeMillis() - bufferWriteStartTime Log.d(TAG, "Buffer write time: $bufferWriteTime ms") // Optional tensor inspection logTensorStats("Input tensor", inputFloatArray) // Run model inference - measure time val modelRunStartTime = SystemClock.uptimeMillis() model.run(inputBuffers, outputBuffers) val modelRunTime = SystemClock.uptimeMillis() - modelRunStartTime Log.d(TAG, "Model.run() time: $modelRunTime ms") // Read output data - measure time val bufferReadStartTime = SystemClock.uptimeMillis() val outputFloatArray = outputBuffers[0].readFloat() val outputBuffer = FloatBuffer.wrap(outputFloatArray) val bufferReadTime = SystemClock.uptimeMillis() - bufferReadStartTime Log.d(TAG, "Buffer read time: $bufferReadTime ms") val modelExecTime = SystemClock.uptimeMillis() - modelExecStartTime Log.d(TAG, "Total model execution time: $modelExecTime ms") // Optional tensor inspection logTensorStats("Output tensor", outputFloatArray) // POSTPROCESSING PHASE val postprocessStartTime = SystemClock.uptimeMillis() // Process mask from model output val inferenceData = InferenceData(width = w, height = h, channels = c, buffer = outputBuffer) val mask = processImage(inferenceData) val postprocessTime = SystemClock.uptimeMillis() - postprocessStartTime Log.d(TAG, "Postprocessing time (mask creation): $postprocessTime ms") return Segmentation( listOf(Mask(mask, inferenceData.width, inferenceData.height)), coloredLabels, )
9. Przetwarzanie końcowe i wyświetlanie nakładki
Po przeprowadzeniu wnioskowania otrzymujemy surowe dane wyjściowe z modelu. Musimy przetworzyć te dane wyjściowe, aby utworzyć wizualną maskę segmentacji, a następnie wyświetlić ją na ekranie.
OtwórzImageSegmentationHelper.kt
- Wdrożenie przetwarzania danych wyjściowych: funkcja
processImage
konwertuje surowe dane wyjściowe zmiennoprzecinkowe z modelu na wartośćByteBuffer
, która reprezentuje maskę segmentacji. W tym celu wyszukuje klasę o najwyższym prawdopodobieństwie dla każdego piksela. Zastąp jegoTODO
(~wiersz 238):val mask = ByteBuffer.allocateDirect(inferenceData.width * inferenceData.height) for (i in 0 until inferenceData.height) { for (j in 0 until inferenceData.width) { val offset = inferenceData.channels * (i * inferenceData.width + j) var maxIndex = 0 var maxValue = inferenceData.buffer.get(offset) for (index in 1 until inferenceData.channels) { if (inferenceData.buffer.get(offset + index) > maxValue) { maxValue = inferenceData.buffer.get(offset + index) maxIndex = index } } mask.put(i * inferenceData.width + j, maxIndex.toByte()) } } return mask
OtwórzMainViewModel.kt
- Zbieranie i przetwarzanie wyników segmentacji: wracamy teraz do
MainViewModel
, aby przetworzyć wyniki segmentacji zImageSegmentationHelper
.segmentationUiShareFlow
zbieraSegmentationResult
, przekształca maskę w kolorowąBitmap
i przekazuje ją do interfejsu. ZastąpTODO
w właściwościsegmentationUiShareFlow
(~wiersz 63) – nie zastępuj kodu, który już tam jest, tylko wypełnij treść:viewModelScope.launch { imageSegmentationHelper.segmentation .filter { it.segmentation.masks.isNotEmpty() } .map { val segmentation = it.segmentation val mask = segmentation.masks[0] val maskArray = mask.data val width = mask.width val height = mask.height val pixelSize = width * height val pixels = IntArray(pixelSize) val colorLabels = segmentation.coloredLabels.mapIndexed { index, coloredLabel -> ColorLabel(index, coloredLabel.label, coloredLabel.argb) } // Set color for pixels for (i in 0 until pixelSize) { val colorLabel = colorLabels[maskArray[i].toInt()] val color = colorLabel.getColor() pixels[i] = color } // Get image info val overlayInfo = OverlayInfo(pixels = pixels, width = width, height = height) val inferenceTime = it.inferenceTime Pair(overlayInfo, inferenceTime) } .collect { flow.emit(it) } }
Otwórzview/SegmentationOverlay.kt
Ostatnim elementem jest prawidłowe zorientowanie nakładki segmentacji, gdy użytkownik przełączy się na przedni aparat. Obraz z przedniego aparatu jest naturalnie odbity lustrzanie, więc musimy zastosować to samo odbicie w poziomie do naszej nakładki Bitmap
, aby była ona prawidłowo dopasowana do podglądu z aparatu.
- Obsługa orientacji nakładki: znajdź
TODO
w plikuSegmentationOverlay.kt
i zastąp go tym kodem. Ten kod sprawdza, czy przedni aparat jest aktywny, a jeśli tak, stosuje do nakładkiBitmap
poziome odwrócenie przed narysowaniem jej naCanvas
. (~line 42):val orientedBitmap = if (lensFacing == CameraSelector.LENS_FACING_FRONT) { // Create a matrix for horizontal flipping val matrix = Matrix().apply { preScale(-1f, 1f) } Bitmap.createBitmap(image, 0, 0, image.width, image.height, matrix, false).also { image.recycle() } } else { image }
10. Uruchamianie i używanie gotowej aplikacji
Wszystkie niezbędne zmiany w kodzie zostały wprowadzone. Czas uruchomić aplikację i zobaczyć efekty swojej pracy.
- Uruchom aplikację: podłącz urządzenie z Androidem i kliknij Uruchom na pasku narzędzi Android Studio.
- Przetestuj funkcje: po uruchomieniu aplikacji powinien pojawić się obraz z kamery na żywo z kolorową nakładką segmentacji.
- Przełączanie aparatów: kliknij ikonę zmiany aparatu u góry, aby przełączać się między przednim a tylnym aparatem. Zwróć uwagę, jak nakładka prawidłowo się orientuje.
- Zmień akcelerator: kliknij przycisk „CPU” lub „GPU” u dołu, aby przełączyć akcelerator sprzętowy. Obserwuj zmianę czasu wnioskowania wyświetlanego u dołu ekranu. GPU powinien być znacznie szybszy.
- Użyj obrazu z galerii: u góry kliknij kartę „Galeria”, aby wybrać obraz z galerii zdjęć na urządzeniu. Aplikacja przeprowadzi segmentację wybranego obrazu statycznego.
Masz teraz w pełni funkcjonalną aplikację do segmentacji obrazu w czasie rzeczywistym opartą na LiteRT.
11. Zaawansowane (opcjonalnie): korzystanie z procesora NPU
W tym repozytorium znajduje się też wersja aplikacji zoptymalizowana pod kątem jednostek przetwarzania neuronowego (NPU). Wersja NPU może znacznie zwiększyć wydajność na urządzeniach z kompatybilnym procesorem NPU.
Aby wypróbować wersję NPU, otwórz projekt kotlin_npu/android
w Android Studio. Kod jest bardzo podobny do wersji na CPU/GPU i jest skonfigurowany do używania delegata NPU.
Aby korzystać z delegata NPU, musisz zarejestrować się w programie wcześniejszego dostępu.
12. Gratulacje!
Udało Ci się utworzyć aplikację na Androida, która wykonuje segmentację obrazu w czasie rzeczywistym za pomocą biblioteki LiteRT. Wiesz już, jak:
- Zintegruj środowisko wykonawcze LiteRT z aplikacją na Androida.
- Wczytywanie i uruchamianie modelu segmentacji obrazów TFLite.
- przetwarzanie wstępne danych wejściowych modelu;
- Przetwórz dane wyjściowe modelu, aby utworzyć maskę segmentacji.
- Użyj CameraX w aplikacji aparatu działającej w czasie rzeczywistym.
Następne kroki
- Spróbuj użyć innego modelu segmentacji obrazu.
- Eksperymentuj z różnymi delegatami LiteRT (CPU, GPU, NPU).