Einführung
Dies ist Hello World! Tutorial verwendet MediaPipe Framework zur Entwicklung eines iOS- die ein MediaPipe-Diagramm unter iOS ausführt.
Umfang
Eine einfache Kamera-App für die Sobel-Kantenerkennung in Echtzeit, die auf ein Live-Video angewendet wird auf einem iOS-Gerät streamen.
Einrichtung
- MediaPipe Framework auf Ihrem System installieren (siehe Framework-Installation) .
- Richten Sie Ihr iOS-Gerät für die Entwicklung ein.
- Richten Sie Bazel auf Ihrem System ein, um die iOS-App zu erstellen und bereitzustellen.
Grafik für die Kantenerkennung
Wir verwenden die folgende Grafik, edge_detection_mobile_gpu.pbtxt
:
# MediaPipe graph that performs GPU Sobel edge detection on a live video stream.
# Used in the examples
# mediapipe/examples/android/src/java/com/google/mediapipe/apps/basic:helloworld
# and mediapipe/examples/ios/helloworld.
# Images coming into and out of the graph.
input_stream: "input_video"
output_stream: "output_video"
# Converts RGB images into luminance images, still stored in RGB format.
node: {
calculator: "LuminanceCalculator"
input_stream: "input_video"
output_stream: "luma_video"
}
# Applies the Sobel filter to luminance images stored in RGB format.
node: {
calculator: "SobelEdgesCalculator"
input_stream: "luma_video"
output_stream: "output_video"
}
Im Folgenden sehen Sie eine Visualisierung des Diagramms:
Dieses Diagramm enthält einen einzelnen Eingabestream namens input_video
für alle eingehenden Frames
die von der Kamera Ihres Geräts bereitgestellt wird.
Der erste Knoten im Diagramm, LuminanceCalculator
, verwendet ein einzelnes Paket (Bild
Frame) und wendet eine Änderung der Leuchtdichte mithilfe eines OpenGL-Shaders an. Das Ergebnis
Bildframe an den luma_video
-Ausgabestream gesendet.
Der zweite Knoten, SobelEdgesCalculator
, wendet die Edge-Erkennung auf eingehende Anfragen an
Pakete im luma_video
-Stream und Ausgaben führen zu einer output_video
-Ausgabe
.
In der iOS-App werden die Ausgabe-Bildframes von output_video
angezeigt.
.
Minimale anfängliche Einrichtung der Anwendung
Wir beginnen mit einer einfachen iOS-App und zeigen, wie bazel
verwendet wird
um sie zu erstellen.
Erstellen Sie zuerst ein XCode-Projekt über „File“ > Neu > Einzelansicht-App.
Legen Sie als Produktnamen „HelloWorld“ fest und verwenden Sie eine geeignete Organisation.
Kennung wie com.google.mediapipe
. Die Organisations-ID
zusammen mit dem Produktnamen die bundle_id
für die Anwendung, z. B.:
com.google.mediapipe.HelloWorld
.
Setzen Sie die Sprache auf Objective-C.
Speichern Sie das Projekt an einem geeigneten Speicherort. Nennen wir sie
$PROJECT_TEMPLATE_LOC
Ihr Projekt befindet sich
$PROJECT_TEMPLATE_LOC/HelloWorld
-Verzeichnis. Dieses Verzeichnis enthält
ein weiteres Verzeichnis namens HelloWorld
und eine HelloWorld.xcodeproj
-Datei.
Das HelloWorld.xcodeproj
ist für diese Anleitung nicht hilfreich, da wir
bazel, um die iOS-App zu erstellen. Der Inhalt der
Das Verzeichnis $PROJECT_TEMPLATE_LOC/HelloWorld/HelloWorld
ist unten aufgeführt:
AppDelegate.h
undAppDelegate.m
ViewController.h
undViewController.m
main.m
Info.plist
Main.storyboard
undLaunch.storyboard
Assets.xcassets
befinden.
Kopieren Sie diese Dateien in das Verzeichnis „HelloWorld
“ an einen Speicherort mit Zugriffsberechtigung.
des MediaPipe Framework-Quellcodes. Der Quellcode der
die wir in diesem Tutorial erstellen werden,
mediapipe/examples/ios/HelloWorld
Wir bezeichnen diesen Pfad als
$APPLICATION_PATH
im gesamten Codelab.
Erstellen Sie eine BUILD
-Datei in $APPLICATION_PATH
und fügen Sie den folgenden Build hinzu:
Regeln:
MIN_IOS_VERSION = "11.0"
load(
"@build_bazel_rules_apple//apple:ios.bzl",
"ios_application",
)
ios_application(
name = "HelloWorldApp",
bundle_id = "com.google.mediapipe.HelloWorld",
families = [
"iphone",
"ipad",
],
infoplists = ["Info.plist"],
minimum_os_version = MIN_IOS_VERSION,
provisioning_profile = "//mediapipe/examples/ios:developer_provisioning_profile",
deps = [":HelloWorldAppLibrary"],
)
objc_library(
name = "HelloWorldAppLibrary",
srcs = [
"AppDelegate.m",
"ViewController.m",
"main.m",
],
hdrs = [
"AppDelegate.h",
"ViewController.h",
],
data = [
"Base.lproj/LaunchScreen.storyboard",
"Base.lproj/Main.storyboard",
],
sdk_frameworks = [
"UIKit",
],
deps = [],
)
Die Regel objc_library
fügt Abhängigkeiten für AppDelegate
und
ViewController
-Klassen, main.m
und die Anwendungs-Storyboards. Die
Vorlagen-App hängt nur vom UIKit
SDK ab.
Die Regel ios_application
verwendet die Objective-C-Bibliothek HelloWorldAppLibrary
wird generiert, um eine iOS-App zur Installation auf Ihrem iOS-Gerät zu erstellen.
Führen Sie den folgenden Befehl in einem Terminal aus, um die App zu erstellen:
bazel build -c opt --config=ios_arm64 <$APPLICATION_PATH>:HelloWorldApp'
Um die Anwendung HelloWorldApp
beispielsweise in
mediapipe/examples/ios/helloworld
, verwenden Sie den folgenden Befehl:
bazel build -c opt --config=ios_arm64 mediapipe/examples/ios/helloworld:HelloWorldApp
Gehen Sie dann zurück zu XCode, öffnen Sie Window > „Geräte und Simulatoren“. Wählen Sie Ihre
Gerät und fügen Sie Ihrem Gerät die mit dem obigen Befehl generierte Datei .ipa
hinzu.
Hier finden Sie das Dokument zum Einrichten und Kompilieren von iOS-Framework-Apps.
Öffnen Sie die App auf Ihrem Gerät. Da das Feld leer ist, sollte ein leerer weißer Bildschirm.
Kamera für Livestream-Feed verwenden
In dieser Anleitung verwenden wir die Klasse MPPCameraInputSource
, um auf
Bilder von der Kamera nehmen. Diese Klasse verwendet die AVCaptureSession
API, um
die Bilder von der Kamera.
Bevor Sie diese Klasse verwenden, ändern Sie die Datei Info.plist
so, dass sie Kamera unterstützt
Nutzung in der App.
Fügen Sie in ViewController.m
die folgende Importzeile hinzu:
#import "mediapipe/objc/MPPCameraInputSource.h"
Fügen Sie dem Implementierungsblock Folgendes hinzu, um ein Objekt zu erstellen
_cameraSource
:
@implementation ViewController {
// Handles camera access via AVCaptureSession library.
MPPCameraInputSource* _cameraSource;
}
Fügen Sie den folgenden Code zu viewDidLoad()
hinzu:
-(void)viewDidLoad {
[super viewDidLoad];
_cameraSource = [[MPPCameraInputSource alloc] init];
_cameraSource.sessionPreset = AVCaptureSessionPresetHigh;
_cameraSource.cameraPosition = AVCaptureDevicePositionBack;
// The frame's native format is rotated with respect to the portrait orientation.
_cameraSource.orientation = AVCaptureVideoOrientationPortrait;
}
Mit dem Code wird _cameraSource
initialisiert, die Voreinstellung für die Aufnahmesitzung wird festgelegt und die Einstellung,
Kamera verwenden.
Die Frames aus dem _cameraSource
müssen in unsere App übertragen werden.
ViewController
, um sie anzuzeigen. MPPCameraInputSource
ist eine abgeleitete Klasse von
MPPInputSource
, das ein Protokoll für seine Bevollmächtigten bereitstellt, nämlich die
MPPInputSourceDelegate
Unsere Anwendung ViewController
kann also ein Bevollmächtigter
von _cameraSource
.
Aktualisieren Sie die Schnittstellendefinition von ViewController
entsprechend:
@interface ViewController () <MPPInputSourceDelegate>
Für die Kameraeinrichtung und die Verarbeitung eingehender Bilder sollte eine Warteschlange verwendet werden.
die sich von der Hauptwarteschlange unterscheiden. Fügen Sie Folgendes in den Implementierungsblock von
ViewController
:
// Process camera frames on this queue.
dispatch_queue_t _videoQueue;
Fügen Sie in viewDidLoad()
die folgende Zeile hinzu, nachdem Sie das
_cameraSource
-Objekt:
[_cameraSource setDelegate:self queue:_videoQueue];
Fügen Sie den folgenden Code hinzu, um die Warteschlange zu initialisieren, bevor Sie die
Objekt _cameraSource
:
dispatch_queue_attr_t qosAttribute = dispatch_queue_attr_make_with_qos_class(
DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INTERACTIVE, /*relative_priority=*/0);
_videoQueue = dispatch_queue_create(kVideoQueueLabel, qosAttribute);
Wir verwenden eine serielle Warteschlange mit der Priorität QOS_CLASS_USER_INTERACTIVE
für
Bilderrahmen zu verarbeiten.
Fügen Sie nach den Header-Importen am Anfang der Datei die folgende Zeile hinzu, bevor
die Schnittstelle/Implementierung von ViewController
:
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
Bevor wir eine Methode aus dem MPPInputSourceDelegate
-Protokoll implementieren, müssen wir
eine Möglichkeit zur Darstellung der Kamerabilder eingerichtet. Das Mediapipe-Framework bietet
ein weiteres Dienstprogramm namens MPPLayerRenderer
, mit dem Bilder auf dem Bildschirm angezeigt werden. Dieses
können Sie CVPixelBufferRef
-Objekte anzeigen lassen. Dabei handelt es sich um den Typ
die Bilder, die von MPPCameraInputSource
den Bevollmächtigten zur Verfügung gestellt werden.
Fügen Sie in ViewController.m
die folgende Importzeile hinzu:
#import "mediapipe/objc/MPPLayerRenderer.h"
Zum Anzeigen von Bildschirmbildern müssen wir ein neues UIView
-Objekt namens
_liveView
zu ViewController
.
Fügen Sie dem Implementierungsblock von ViewController
die folgenden Zeilen hinzu:
// Display the camera preview frames.
IBOutlet UIView* _liveView;
// Render frames in a layer.
MPPLayerRenderer* _renderer;
Rufen Sie Main.storyboard
auf und fügen Sie ein UIView
-Objekt aus der Objektbibliothek zum
View
der Klasse ViewController
. Fügen Sie in dieser Ansicht ein verweisendes Outlet zum
Das _liveView
-Objekt, das Sie der Klasse ViewController
gerade hinzugefügt haben. Ändern Sie die Größe des
sodass sie zentriert wird und den gesamten App-Bildschirm einnimmt.
Kehren Sie zu ViewController.m
zurück und fügen Sie den folgenden Code in viewDidLoad()
ein:
Initialisieren Sie das Objekt _renderer
:
_renderer = [[MPPLayerRenderer alloc] init];
_renderer.layer.frame = _liveView.layer.bounds;
[_liveView.layer addSublayer:_renderer.layer];
_renderer.frameScaleMode = MPPFrameScaleModeFillAndCrop;
Um Frames von der Kamera abzurufen, implementieren wir die folgende Methode:
// Must be invoked on _videoQueue.
- (void)processVideoFrame:(CVPixelBufferRef)imageBuffer
timestamp:(CMTime)timestamp
fromSource:(MPPInputSource*)source {
if (source != _cameraSource) {
NSLog(@"Unknown source: %@", source);
return;
}
// Display the captured image on the screen.
CFRetain(imageBuffer);
dispatch_async(dispatch_get_main_queue(), ^{
[_renderer renderPixelBuffer:imageBuffer];
CFRelease(imageBuffer);
});
}
Dies ist eine Delegatmethode von MPPInputSource
. Zunächst überprüfen wir, ob wir
Frames aus der richtigen Quelle beziehen, d.h. dem _cameraSource
. Dann zeigen wir
den Frame, den die Kamera über _renderer
in der Hauptwarteschlange empfangen hat.
Jetzt müssen wir die Kamera starten, sobald sich die Ansicht zur Anzeige der Frames
angezeigt werden. Hierzu implementieren wir die
viewWillAppear:(BOOL)animated
-Funktion:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
Bevor wir die Kamera ausführen können, benötigen wir die Erlaubnis des Nutzers, um darauf zuzugreifen.
MPPCameraInputSource
stellt eine Funktion bereit.
requestCameraAccessWithCompletionHandler:(void (^_Nullable)(BOOL
granted))handler
, um den Zugriff auf die Kamera anzufordern und etwas zu tun, wenn der Nutzer
haben geantwortet. Fügen Sie viewWillAppear:animated
den folgenden Code hinzu:
[_cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
if (granted) {
dispatch_async(_videoQueue, ^{
[_cameraSource start];
});
}
}];
Bevor Sie die Anwendung erstellen, fügen Sie die folgenden Abhängigkeiten zu BUILD
hinzu.
Datei:
sdk_frameworks = [
"AVFoundation",
"CoreGraphics",
"CoreMedia",
],
deps = [
"//mediapipe/objc:mediapipe_framework_ios",
"//mediapipe/objc:mediapipe_input_sources_ios",
"//mediapipe/objc:mediapipe_layer_renderer",
],
Erstellen Sie nun die Anwendung und führen Sie sie auf Ihrem iOS-Gerät aus. Es sollte eine Live-Anzeige Kamera-Ansicht-Feed nach dem Akzeptieren der Kameraberechtigungen.
Jetzt können Kameraframes in einem MediaPipe-Diagramm verwendet werden.
MediaPipe-Diagramm unter iOS verwenden
Relevante Abhängigkeiten hinzufügen
Wir haben bereits die Abhängigkeiten des MediaPipe-Framework-Codes hinzugefügt, der
um ein MediaPipe-Diagramm zu verwenden. Um ein MediaPipe-Diagramm zu verwenden, müssen wir
Abhängigkeit von der Grafik,
die wir in unserer Anwendung verwenden wollen. Folgendes hinzufügen
Zeile zur data
-Liste in der Datei BUILD
hinzu:
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
Fügen Sie nun die Abhängigkeit zu den in diesem Diagramm verwendeten Rechnern im Feld deps
hinzu.
in der Datei BUILD
:
"//mediapipe/graphs/edge_detection:mobile_calculators",
Benennen Sie abschließend die Datei ViewController.m
in ViewController.mm
um.
Objective-C++.
Das Diagramm in ViewController
verwenden
Fügen Sie in ViewController.m
die folgende Importzeile hinzu:
#import "mediapipe/objc/MPPGraph.h"
Geben Sie eine statische Konstante mit dem Namen der Grafik, dem Eingabestream und dem Ausgabestream:
static NSString* const kGraphName = @"mobile_gpu";
static const char* kInputStream = "input_video";
static const char* kOutputStream = "output_video";
Fügen Sie der Schnittstelle von ViewController
das folgende Attribut hinzu:
// The MediaPipe graph currently in use. Initialized in viewDidLoad, started in viewWillAppear: and
// sent video frames on _videoQueue.
@property(nonatomic) MPPGraph* mediapipeGraph;
Wie im Kommentar oben erläutert, initialisieren wir diese Grafik
Zuerst viewDidLoad
. Dazu müssen wir das Diagramm aus der Datei .pbtxt
laden.
mithilfe der folgenden Funktion:
+ (MPPGraph*)loadGraphFromResource:(NSString*)resource {
// Load the graph config resource.
NSError* configLoadError = nil;
NSBundle* bundle = [NSBundle bundleForClass:[self class]];
if (!resource || resource.length == 0) {
return nil;
}
NSURL* graphURL = [bundle URLForResource:resource withExtension:@"binarypb"];
NSData* data = [NSData dataWithContentsOfURL:graphURL options:0 error:&configLoadError];
if (!data) {
NSLog(@"Failed to load MediaPipe graph config: %@", configLoadError);
return nil;
}
// Parse the graph config resource into mediapipe::CalculatorGraphConfig proto object.
mediapipe::CalculatorGraphConfig config;
config.ParseFromArray(data.bytes, data.length);
// Create MediaPipe graph with mediapipe::CalculatorGraphConfig proto object.
MPPGraph* newGraph = [[MPPGraph alloc] initWithGraphConfig:config];
[newGraph addFrameOutputStream:kOutputStream outputPacketType:MPPPacketTypePixelBuffer];
return newGraph;
}
Initialisieren Sie mit dieser Funktion die Grafik in viewDidLoad
so:
self.mediapipeGraph = [[self class] loadGraphFromResource:kGraphName];
Die Grafik sollte die Ergebnisse der Verarbeitung von Kamerabildern an die
ViewController
Fügen Sie nach der Initialisierung des Diagramms die folgende Zeile hinzu, um den
ViewController
als Bevollmächtigter des mediapipeGraph
-Objekts:
self.mediapipeGraph.delegate = self;
Um Speicherkonflikte bei der Verarbeitung von Frames aus dem Live-Videofeed zu vermeiden, fügen Sie folgende Zeile:
// Set maxFramesInFlight to a small value to avoid memory contention for real-time processing.
self.mediapipeGraph.maxFramesInFlight = 2;
Starte nun das Diagramm, wenn der Benutzer die Berechtigung zur Verwendung der Kamera erteilt hat. in unserer App:
[_cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
if (granted) {
// Start running self.mediapipeGraph.
NSError* error;
if (![self.mediapipeGraph startWithError:&error]) {
NSLog(@"Failed to start graph: %@", error);
}
else if (![self.mediapipeGraph waitUntilIdleWithError:&error]) {
NSLog(@"Failed to complete graph initial run: %@", error);
}
dispatch_async(_videoQueue, ^{
[_cameraSource start];
});
}
}];
Als wir zuvor Frames von der Kamera im processVideoFrame
wurden sie mithilfe der _renderer
-Funktion im _liveView
angezeigt. Jetzt
müssen diese Frames an die Grafik
senden und stattdessen die Ergebnisse rendern. Ändern
die Implementierung dieser Funktion, um Folgendes zu erreichen:
- (void)processVideoFrame:(CVPixelBufferRef)imageBuffer
timestamp:(CMTime)timestamp
fromSource:(MPPInputSource*)source {
if (source != _cameraSource) {
NSLog(@"Unknown source: %@", source);
return;
}
[self.mediapipeGraph sendPixelBuffer:imageBuffer
intoStream:kInputStream
packetType:MPPPacketTypePixelBuffer];
}
Wir senden die imageBuffer
als Paket vom Typ an self.mediapipeGraph
MPPPacketTypePixelBuffer
in den Eingabestream kInputStream
, d.h.
„input_video“.
Die Grafik wird mit diesem Eingabepaket ausgeführt und gibt ein Ergebnis in
kOutputStream
, z.B. „output_video“. Wir können den folgenden Bevollmächtigten
-Methode, um Pakete in diesem Ausgabestream zu empfangen und auf dem Bildschirm anzuzeigen:
- (void)mediapipeGraph:(MPPGraph*)graph
didOutputPixelBuffer:(CVPixelBufferRef)pixelBuffer
fromStream:(const std::string&)streamName {
if (streamName == kOutputStream) {
// Display the captured image on the screen.
CVPixelBufferRetain(pixelBuffer);
dispatch_async(dispatch_get_main_queue(), ^{
[_renderer renderPixelBuffer:pixelBuffer];
CVPixelBufferRelease(pixelBuffer);
});
}
}
Aktualisieren Sie die Schnittstellendefinition von ViewController
mit MPPGraphDelegate
:
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
Das ist alles! Erstellen Sie die App und führen Sie sie auf Ihrem iOS-Gerät aus. Das Feld Ergebnisse der Ausführung des Kantenerkennungsdiagramms in einem Live-Videofeed. Glückwunsch!
Für die iOS-Beispiele wird jetzt eine gängige Vorlagen-App verwendet. Der Code in
Diese Anleitung wird in der allgemeinen Vorlagen-App verwendet. Die App helloworld enthält die
geeignete BUILD
-Dateiabhängigkeiten für den Edge-Erkennungsdiagramm.