Zadanie MediaPipe Hand Pointer umożliwia wykrywanie punktów orientacyjnych dłoni na zdjęciu. Te instrukcje pokazują, jak korzystać z Podręcznego punktu orientacyjnego w aplikacjach na iOS. Przykładowy kod opisany w tych instrukcjach jest dostępny na GitHub.
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 Hand MTAer na iOS. W tym przykładzie użyto aparatu w fizycznym urządzeniu z iOS, aby wykryć punkty orientacyjne w ciągłym strumieniu wideo. Aplikacja może też wykrywać punkty orientacyjne na obrazach i filmach z galerii urządzenia.
Możesz użyć aplikacji jako punktu wyjścia dla własnej aplikacji na iOS lub skorzystać z niej podczas modyfikowania istniejącej aplikacji. Przykładowy kod obszaru orientacyjnego 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 w taki sposób, aby używała rozproszonego procesu płatności, aby mieć tylko pliki dla przykładowej aplikacji Notatnik Handlowy:
cd mediapipe git sparse-checkout init --cone git sparse-checkout set examples/hand_landmarker/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 po konfiguracji na iOS.
Kluczowe elementy
Te pliki zawierają kluczowy kod dla przykładowej aplikacji „Hang Pointer”:
- HandLandmarkerService.swift: inicjuje narzędzie do tworzenia punktów orientacyjnych, obsługuje wybór modelu i uruchamia wnioskowanie na danych wejściowych.
- CameraViewController.swift: implementuje interfejs użytkownika w trybie przesyłania obrazu z kamery na żywo i wizualizuje wyniki.
- MediaLibraryViewController.swift: implementuje interfejs użytkownika w trybie wprowadzania plików nieruchomych obrazów i filmów oraz wizualizuje wyniki.
Konfiguracja
W tej sekcji opisujemy najważniejsze czynności, jakie należy wykonać, by skonfigurować środowisko programistyczne i projekt kodu, aby móc korzystać z Wskaźnika ręcznego. Ogólne informacje o konfigurowaniu środowiska programistycznego na potrzeby zadań MediaPipe, w tym o wymaganiach dotyczących wersji platformy, znajdziesz w przewodniku konfiguracji dla iOS.
Zależności
Narzędzie do rysowania ręczne korzysta z biblioteki MediaPipeTasksVision
, która musi być zainstalowana za pomocą CocoaPods. Biblioteka jest zgodna z aplikacjami Swift i Objective-C i nie wymaga dodatkowej konfiguracji pod kątem określonego języka.
Instrukcje instalowania CocoaPods w systemie macOS znajdziesz w przewodniku instalacji CocoaPods.
Instrukcje tworzenia Podfile
z podami niezbędnymi do działania aplikacji znajdziesz w artykule Korzystanie z CocoaPods.
Dodaj pod MediaPipeTasksVision w narzędziu Podfile
za pomocą tego kodu:
target 'MyHandLandmarkerApp' do
use_frameworks!
pod 'MediaPipeTasksVision'
end
Jeśli aplikacja zawiera cele testu jednostkowego, dodatkowe informacje o konfigurowaniu Podfile
znajdziesz w przewodniku konfiguracji na iOS.
Model
Zadanie MediaPipe Hand Pointer wymaga wytrenowanego modelu zgodnego z tym zadaniem. Więcej informacji o dostępnych wytrenowanych modelach na potrzeby ręcznego punktu orientacyjnego znajdziesz w sekcji przeglądu zadań w sekcji „Modele”.
Wybierz i pobierz model, a potem dodaj go do katalogu projektów za pomocą Xcode. Instrukcje dodawania plików do projektu Xcode znajdziesz w artykule Zarządzanie plikami i folderami w projekcie Xcode.
Aby określić ścieżkę do modelu w pakiecie aplikacji, użyj właściwości BaseOptions.modelAssetPath
. Przykładowy kod znajdziesz w następnej sekcji.
Tworzenie zadania
Zadanie Notatnika ręcznego możesz utworzyć, wywołując jeden z jego inicjatorów. Inicjator HandLandmarker(options:)
akceptuje wartości opcji konfiguracji.
Jeśli nie potrzebujesz zainicjowanego ręcznego punktu orientacyjnego zainicjowanego z niestandardowymi opcjami konfiguracji, możesz użyć inicjatora HandLandmarker(modelPath:)
, aby utworzyć ręcznie punkt orientacyjny z opcjami domyślnymi. Więcej informacji o opcjach konfiguracji znajdziesz w artykule Omówienie konfiguracji.
Zadanie tworzenia punktów orientacyjnych rąk obsługuje 3 typy danych wejściowych: obrazy, pliki wideo i strumienie wideo na żywo. Domyślnie HandLandmarker(modelPath:)
inicjuje zadanie dotyczące nieruchomych obrazów. Jeśli chcesz zainicjować zadanie na potrzeby przetwarzania plików wideo lub strumieni wideo na żywo, użyj funkcji HandLandmarker(options:)
, aby określić tryb transmisji wideo lub transmisji na żywo. Tryb transmisji na żywo wymaga też dodatkowej opcji konfiguracji handLandmarkerLiveStreamDelegate
, która umożliwia asynchronicznie ręcznemu dostarczaniu przez niego wyników punktów orientacyjnych.
Wybierz kartę odpowiadającą Twojemu trybowi biegowemu, aby zobaczyć, jak utworzyć zadanie i uruchomić wnioskowanie.
Swift
Obraz
import MediaPipeTasksVision let modelPath = Bundle.main.path(forResource: "hand_landmarker", ofType: "task") let options = HandLandmarkerOptions() options.baseOptions.modelAssetPath = modelPath options.runningMode = .image options.minHandDetectionConfidence = minHandDetectionConfidence options.minHandPresenceConfidence = minHandPresenceConfidence options.minTrackingConfidence = minHandTrackingConfidence options.numHands = numHands let handLandmarker = try HandLandmarker(options: options)
Wideo
import MediaPipeTasksVision let modelPath = Bundle.main.path(forResource: "hand_landmarker", ofType: "task") let options = HandLandmarkerOptions() options.baseOptions.modelAssetPath = modelPath options.runningMode = .video options.minHandDetectionConfidence = minHandDetectionConfidence options.minHandPresenceConfidence = minHandPresenceConfidence options.minTrackingConfidence = minHandTrackingConfidence options.numHands = numHands let handLandmarker = try HandLandmarker(options: options)
Transmisja na żywo
import MediaPipeTasksVision // Class that conforms to the `HandLandmarkerLiveStreamDelegate` protocol and // implements the method that the hand landmarker calls once it finishes // performing landmarks detection in each input frame. class HandLandmarkerResultProcessor: NSObject, HandLandmarkerLiveStreamDelegate { func handLandmarker( _ handLandmarker: HandLandmarker, didFinishDetection result: HandLandmarkerResult?, timestampInMilliseconds: Int, error: Error?) { // Process the hand landmarker result or errors here. } } let modelPath = Bundle.main.path( forResource: "hand_landmarker", ofType: "task") let options = HandLandmarkerOptions() options.baseOptions.modelAssetPath = modelPath options.runningMode = .liveStream options.minHandDetectionConfidence = minHandDetectionConfidence options.minHandPresenceConfidence = minHandPresenceConfidence options.minTrackingConfidence = minHandTrackingConfidence options.numHands = numHands // Assign an object of the class to the `handLandmarkerLiveStreamDelegate` // property. let processor = HandLandmarkerResultProcessor() options.handLandmarkerLiveStreamDelegate = processor let handLandmarker = try HandLandmarker(options: options)
Objective-C
Obraz
@import MediaPipeTasksVision; NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"hand_landmarker" ofType:@"task"]; MPPHandLandmarkerOptions *options = [[MPPHandLandmarkerOptions alloc] init]; options.baseOptions.modelAssetPath = modelPath; options.runningMode = MPPRunningModeImage; options.minHandDetectionConfidence = minHandDetectionConfidence; options.minHandPresenceConfidence = minHandPresenceConfidence; options.minTrackingConfidence = minHandTrackingConfidence; options.numHands = numHands; MPPHandLandmarker *handLandmarker = [[MPPHandLandmarker alloc] initWithOptions:options error:nil];
Wideo
@import MediaPipeTasksVision; NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"hand_landmarker" ofType:@"task"]; MPPHandLandmarkerOptions *options = [[MPPHandLandmarkerOptions alloc] init]; options.baseOptions.modelAssetPath = modelPath; options.runningMode = MPPRunningModeVideo; options.minHandDetectionConfidence = minHandDetectionConfidence; options.minHandPresenceConfidence = minHandPresenceConfidence; options.minTrackingConfidence = minHandTrackingConfidence; options.numHands = numHands; MPPHandLandmarker *handLandmarker = [[MPPHandLandmarker alloc] initWithOptions:options error:nil];
Transmisja na żywo
@import MediaPipeTasksVision; // Class that conforms to the `MPPHandLandmarkerLiveStreamDelegate` protocol // and implements the method that the hand landmarker calls once it finishes // performing landmarks detection in each input frame. @interface APPHandLandmarkerResultProcessor : NSObject@end @implementation APPHandLandmarkerResultProcessor - (void)handLandmarker:(MPPHandLandmarker *)handLandmarker didFinishDetectionWithResult:(MPPHandLandmarkerResult *)handLandmarkerResult timestampInMilliseconds:(NSInteger)timestampInMilliseconds error:(NSError *)error { // Process the hand landmarker result or errors here. } @end NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"hand_landmarker" ofType:@"task"]; MPPHandLandmarkerOptions *options = [[MPPHandLandmarkerOptions alloc] init]; options.baseOptions.modelAssetPath = modelPath; options.runningMode = MPPRunningModeLiveStream; options.minHandDetectionConfidence = minHandDetectionConfidence; options.minHandPresenceConfidence = minHandPresenceConfidence; options.minTrackingConfidence = minHandTrackingConfidence; options.numHands = numHands; // Assign an object of the class to the `handLandmarkerLiveStreamDelegate` // property. APPHandLandmarkerResultProcessor *processor = [APPHandLandmarkerResultProcessor new]; options.handLandmarkerLiveStreamDelegate = processor; MPPHandLandmarker *handLandmarker = [[MPPHandLandmarker alloc] initWithOptions:options error:nil];
Opcje konfiguracji
To zadanie ma te opcje konfiguracji w przypadku aplikacji na iOS:
Nazwa opcji | Opis | Zakres wartości | Wartość domyślna |
---|---|---|---|
running_mode |
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. W tym trybie handLandmarkerLiveStreamDelegate musi być ustawiony na instancję klasy, która implementuje HandLandmarkerLiveStreamDelegate , aby asynchronicznie otrzymywać wyniki wykrywania punktów orientacyjnych dłoni.
|
{RunningMode.image, RunningMode.video, RunningMode.liveStream } |
RunningMode.image |
numHands |
Maksymalna liczba rąk wykrytych przez detektor punktów orientacyjnych dłoni. | Any integer > 0 |
1 |
minHandDetectionConfidence |
Minimalny stopień pewności, że wykrywanie dłoni zostanie uznane za udane w modelu wykrywania dłoni. | 0.0 - 1.0 |
0.5 |
minHandPresenceConfidence |
Minimalny wskaźnik ufności dla wyniku obecności ręki w modelu wykrywania punktów orientacyjnych ręki. W trybie wideo i w trybie transmisji na żywo, jeśli wskaźnik ufności obecności ręki dla modelu punktu orientacyjnego dłoni jest poniżej tego progu, komponent wskazuje rękę ręcznie uruchamia model wykrywania dłoni. W przeciwnym razie do wykrywania punktów orientacyjnych potrzebny jest uproszczony algorytm śledzenia dłoni. | 0.0 - 1.0 |
0.5 |
minTrackingConfidence |
Minimalny wynik pewności, że śledzenie dłoni zostanie uznane za udane. Jest to próg interfejsu użytkownika w ramce ograniczającej między rękami w bieżącej a ostatniej klatce. W trybie wideo i trybie strumienia Wskaźnika ręcznego, jeśli śledzenie nie powiedzie się, wskazujesz rękę ręcznie. W przeciwnym razie pomijane jest wykrywanie rąk. | 0.0 - 1.0 |
0.5 |
result_listener |
Konfiguruje detektor wyników, aby asynchronicznie otrzymywać wyniki wykrywania, gdy punkt orientacyjny z ręczną ręką działa w trybie transmisji na żywo.
Ma zastosowanie tylko wtedy, gdy tryb działania jest ustawiony na LIVE_STREAM |
Nie dotyczy | Nie dotyczy |
Gdy tryb biegowy jest ustawiony na transmisję na żywo, Znacznik orientacyjny ręcznie wymaga dodatkowej opcji konfiguracji handLandmarkerLiveStreamDelegate
, która umożliwia asynchronicznie wyświetlanie wyników wykrywania punktów orientacyjnych dłoni. Osoba, której przekazano dostęp, musi wdrożyć metodę handLandmarker(_:didFinishDetection:timestampInMilliseconds:error:)
, która jest wywoływana przez punkt orientacyjny ręcznie po przetworzeniu wyników wykrywania punktów orientacyjnych dla każdej klatki.
Nazwa opcji | Opis | Zakres wartości | Wartość domyślna |
---|---|---|---|
handLandmarkerLiveStreamDelegate |
Umożliwia asynchronicznie odbieranie wyników wykrywania punktów orientacyjnych dłoni w trybie transmisji na żywo. Klasa, której instancja jest ustawiona na tę właściwość, musi implementować metodę handLandmarker(_:didFinishDetection:timestampInMilliseconds:error:) . |
Nie dotyczy | Nie ustawiono |
Przygotuj dane
Przed przekazaniem obrazu wejściowego lub ramki do obiektu MPImage
musisz go przekonwertować do Punktu orientacyjnego Ręka. MPImage
obsługuje różne typy formatów obrazów na iOS i może ich używać w każdym trybie działania, aby wnioskować. Więcej informacji na temat MPImage
znajdziesz w interfejsie MPImage API
Wybierz format obrazu na iOS zależnie od swojego przypadku użycia i trybu uruchamiania, którego wymaga Twoja aplikacja.MPImage
akceptuje formaty obrazów UIImage
, CVPixelBuffer
i CMSampleBuffer
(iOS).
UIImage
Format UIImage
dobrze sprawdza się w tych trybach biegania:
Obrazy: obrazy z pakietu aplikacji, galerii użytkownika lub systemu plików sformatowane jako obrazy
UIImage
można przekonwertować na obiektMPImage
.Filmy: użyj narzędzia AVAssetImageGenerator, aby wyodrębnić klatki wideo do formatu CGImage, a następnie przekonwertuj je na obrazy
UIImage
.
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];
W tym przykładzie inicjuje się MPImage
z domyślną orientacją UIImage.Orientation.Up. Możesz zainicjować MPImage
przy użyciu dowolnej z obsługiwanych wartości UIImage.Orientation. Odbicie lustrzane w orientacji poziomej nie obsługuje odbicia lustrzanego, np. .upMirrored
, .downMirrored
, .leftMirrored
i .rightMirrored
.
Więcej informacji na temat UIImage
znajdziesz w dokumentacji dla programistów Apple dotyczącej UIImage (w języku angielskim).
CVPixelBuffer
Format CVPixelBuffer
dobrze sprawdza się w aplikacjach, które generują ramki i wykorzystują do przetwarzania platformę CoreImage w systemie iOS.
Format CVPixelBuffer
dobrze sprawdza się w tych trybach biegania:
Obrazy: aplikacje generujące obrazy
CVPixelBuffer
po przetworzeniu przy użyciu platformyCoreImage
systemu iOS mogą być wysyłane do Kreatora map ręcznie w trybie wyświetlania obrazów.Filmy: klatki wideo można przekonwertować do formatu
CVPixelBuffer
w celu przetworzenia, a następnie wysłać do narzędzia ręcznego w trybie wideo.transmisja na żywo: aplikacje używające kamery systemu iOS do generowania klatek można skonwertować do formatu
CVPixelBuffer
w celu przetworzenia, zanim zostaną przesłane do narzędzia ręcznego 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 na temat CVPixelBuffer
znajdziesz w dokumentacji dla programistów Apple CVPixelBuffer.
CMSampleBuffer
Format CMSampleBuffer
przechowuje próbki multimediów jednolitego typu i dobrze sprawdza się w trybie transmisji na żywo. Klatki na żywo z kamer na iOS są przesyłane asynchronicznie w formacie CMSampleBuffer
przez system iOS AVCaptureVideoDataOutput.
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 na temat CMSampleBuffer
znajdziesz w dokumentacji dla programistów Apple CMSampleBuffer.
Uruchamianie zadania
Aby uruchomić Punkt orientacyjny ręcznie, użyj metody detect()
specyficznej dla przypisanego trybu biegowego:
- Nieruchomy obraz:
detect(image:)
- Film:
detect(videoFrame:timestampInMilliseconds:)
- Transmisja na żywo:
detectAsync(image:timestampInMilliseconds:)
Swift
Obraz
let result = try handLandmarker.detect(image: image)
Wideo
let result = try handLandmarker.detect( videoFrame: image, timestampInMilliseconds: timestamp)
Transmisja na żywo
try handLandmarker.detectAsync( image: image, timestampInMilliseconds: timestamp)
Objective-C
Obraz
MPPHandLandmarkerResult *result = [handLandmarker detectInImage:image error:nil];
Wideo
MPPHandLandmarkerResult *result = [handLandmarker detectInVideoFrame:image timestampInMilliseconds:timestamp error:nil];
Transmisja na żywo
BOOL success = [handLandmarker detectAsyncInImage:image timestampInMilliseconds:timestamp error:nil];
W przykładowym kodzie obszaru orientacyjnym „ręcznym” przedstawiono bardziej szczegółowo implementacje każdego z tych trybów. Przykładowy kod pozwala użytkownikowi przełączać się między trybami przetwarzania, które w Twoim przypadku mogą nie być wymagane.
Uwaga:
W trybie wideo lub w trybie transmisji na żywo musisz też podać sygnaturę czasową ramki wejściowej w zadaniu Wskażnika dłoni.
Gdy działa w trybie graficznym lub wideo, zadanie Wskażnik dłoni blokuje 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 za pomocą platform Dispatch lub NSOperation w systemie iOS.
W trybie transmisji na żywo zadanie wskazana ręcznie wraca od razu i nie blokuje bieżącego wątku. Po przetworzeniu każdej ramki wejściowej wywołuje metodę
handLandmarker(_:didFinishDetection:timestampInMilliseconds:error:)
z wynikiem ręcznego tworzenia punktów orientacyjnych. Znak orientacyjny wyzwala tę metodę asynchronicznie w dedykowanej szeregowej kolejce wysyłki. W przypadku wyświetlania wyników w interfejsie po ich przetworzeniu wyślij do głównej kolejki. Jeśli funkcjadetectAsync
zostanie wywołana, gdy zadanie Mapa witryny jest zajęte przetwarzaniem innej ramki, ta funkcja zignoruje nową ramkę wejściową.
Obsługa i wyświetlanie wyników
Po uruchomieniu wnioskowania zadanie Punkt orientacyjny do ręcznego zwraca wartość HandLandmarkerResult
, która zawiera punkty orientacyjne na zdjęciu, punkty orientacyjne we współrzędnych świata i ręce(lewa/prawa ręka) wykrytych rąk.
Poniżej znajdziesz przykład danych wyjściowych z tego zadania:
Dane wyjściowe HandLandmarkerResult
zawierają 3 komponenty. Każdy komponent jest tablicą, w której każdy element zawiera te wyniki dotyczące pojedynczej wykrytej dłoni:
Ręka dominująca
Ręka wskazuje, czy wykryte ręce są lewą czy prawą ręką.
Punkty orientacyjne
Jest 21 punktów orientacyjnych wskazujących dłonie, a każdy z nich składa się ze współrzędnych
x
,y
iz
. Współrzędnex
iy
są normalizowane do wartości [0,0, 1,0] odpowiednio do szerokości i wysokości obrazu. Współrzędnaz
reprezentuje głębokość punktu orientacyjnego, przy czym głębokość na nadgarstku jest punktem początkowym. Im mniejsza wartość, tym zbliża się punkt orientacyjny do aparatu. Siła działaniaz
jest mniej więcej zbliżona do skalix
.Punkty orientacyjne na świecie
We współrzędnych świata są również przedstawione 21 punktów orientacyjnych. Każdy punkt orientacyjny składa się z elementów
x
,y
iz
, które reprezentują rzeczywiste współrzędne 3D w metrach z punktem początkowym w środku geometrycznym dłoni.
HandLandmarkerResult:
Handedness:
Categories #0:
index : 0
score : 0.98396
categoryName : Left
Landmarks:
Landmark #0:
x : 0.638852
y : 0.671197
z : -3.41E-7
Landmark #1:
x : 0.634599
y : 0.536441
z : -0.06984
... (21 landmarks for a hand)
WorldLandmarks:
Landmark #0:
x : 0.067485
y : 0.031084
z : 0.055223
Landmark #1:
x : 0.063209
y : -0.00382
z : 0.020920
... (21 world landmarks for a hand)
Poniższy obraz przedstawia wizualizację wyników zadania: