Giriş
Bu Hello World! eğiticisi, iOS'te MediaPipe grafiği çalıştıran bir iOS uygulaması geliştirmek için MediaPipe Framework'ü kullanır.
Ne oluşturacaksınız?
iOS cihazdaki canlı video akışına gerçek zamanlı Sobel kenar algılama özelliği sağlayan basit bir kamera uygulamasıdır.
Kurulum
- Sisteminize MediaPipe Framework'ü yükleyin. Ayrıntılar için Çerçeve kurulum kılavuzuna bakın.
- iOS cihazınızı geliştirme için ayarlayın.
- iOS uygulamasını derlemek ve dağıtmak için sisteminizde Bazel'i kurun.
Kenar algılama grafiği
Kullanılacak 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"
}
Aşağıda grafikle ilgili bir görsel yer almaktadır:
Bu grafikte, cihazınızın kamerası tarafından sağlanacak tüm gelen kareler için input_video
adlı tek bir giriş akışı vardır.
Grafikteki ilk düğüm (LuminanceCalculator
), tek bir paket (görüntü çerçevesi) alır ve OpenGL gölgelendirici kullanarak parlaklık değişikliği uygular. Elde edilen resim çerçevesi, luma_video
çıkış akışına gönderilir.
İkinci düğüm olan SobelEdgesCalculator
, luma_video
akışındaki gelen paketlere uç algılama uygular ve çıkışta output_video
çıkış akışı oluşturur.
iOS uygulamamız, output_video
akışının çıktı resim çerçevelerini gösterir.
Minimum minimum uygulama kurulumu
İlk olarak basit bir iOS uygulamasıyla başlıyor ve bu uygulamayı oluşturmak için bazel
hizmetinin nasıl kullanıldığını gösteriyoruz.
İlk olarak, Dosya > Yeni > Tek Görünüm Uygulaması yoluyla bir XCode projesi oluşturun.
Ürün adını "HelloWorld" olarak ayarlayın ve com.google.mediapipe
gibi uygun bir kuruluş tanımlayıcısı kullanın. Kuruluş tanımlayıcısı, ürün adıyla birlikte uygulamanın bundle_id
öğesi olacaktır (ör. com.google.mediapipe.HelloWorld
).
Dili Objective-C olarak ayarlayın.
Projeyi uygun bir konuma kaydedin. Buna
$PROJECT_TEMPLATE_LOC
adını verelim. Bu durumda projeniz $PROJECT_TEMPLATE_LOC/HelloWorld
dizininde olur. Bu dizin, HelloWorld
adlı başka bir dizin ve bir HelloWorld.xcodeproj
dosyası içerecek.
iOS uygulamasını oluştururken Bazel kullanacağımızdan, HelloWorld.xcodeproj
bu eğiticide faydalı olmayacaktır. $PROJECT_TEMPLATE_LOC/HelloWorld/HelloWorld
dizininin içeriği aşağıda listelenmiştir:
AppDelegate.h
veAppDelegate.m
ViewController.h
veViewController.m
main.m
Info.plist
Main.storyboard
veLaunch.storyboard
Assets.xcassets
dizini.
Bu dosyaları HelloWorld
adlı bir dizine, MediaPipe Çerçevesi kaynak koduna erişebilen bir konuma kopyalayın. Örneğin, bu eğiticide oluşturacağımız uygulamanın kaynak kodu mediapipe/examples/ios/HelloWorld
konumundadır. Codelab genelinde bu yol $APPLICATION_PATH
olarak adlandırılacaktır.
$APPLICATION_PATH
içinde bir BUILD
dosyası oluşturun ve aşağıdaki derleme kurallarını ekleyin:
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 = [],
)
objc_library
kuralı; AppDelegate
ve ViewController
sınıfları, main.m
ve uygulama resimli taslakları için bağımlılık ekler. Şablonlu uygulama yalnızca UIKit
SDK'sına bağlıdır.
ios_application
kuralı, iOS cihazınıza yüklenmek üzere bir iOS uygulaması oluşturmak için oluşturulan HelloWorldAppLibrary
Objective-C kitaplığını kullanır.
Uygulamayı derlemek için bir terminalde aşağıdaki komutu kullanın:
bazel build -c opt --config=ios_arm64 <$APPLICATION_PATH>:HelloWorldApp'
Örneğin, mediapipe/examples/ios/helloworld
içinde HelloWorldApp
uygulamasını derlemek için aşağıdaki komutu kullanın:
bazel build -c opt --config=ios_arm64 mediapipe/examples/ios/helloworld:HelloWorldApp
Ardından XCode'a geri dönün, Pencere > Cihazlar ve Simülatörler'i açın, cihazınızı seçin ve yukarıdaki komut tarafından oluşturulan .ipa
dosyasını cihazınıza ekleyin.
iOS Framework uygulamalarını kurma ve derleme ile ilgili dokümanı burada bulabilirsiniz.
Cihazınızda uygulamayı açın. Boş olduğu için boş bir beyaz ekran göstermesi gerekir.
Canlı görüntüleme feed'i için kamerayı kullanma
Bu eğiticide, kameradan karelere erişmek ve kareleri yakalamak için MPPCameraInputSource
sınıfını kullanacağız. Bu sınıf, kareleri kameradan almak için AVCaptureSession
API'sini kullanır.
Ancak, bu sınıfı kullanmadan önce uygulamada kamera kullanımını desteklemek için Info.plist
dosyasını değiştirin.
ViewController.m
dosyasına aşağıdaki içe aktarma satırını ekleyin:
#import "mediapipe/objc/MPPCameraInputSource.h"
Bir nesne _cameraSource
oluşturmak için uygulama bloğuna aşağıdaki kodu ekleyin:
@implementation ViewController {
// Handles camera access via AVCaptureSession library.
MPPCameraInputSource* _cameraSource;
}
Şu kodu viewDidLoad()
hesabına ekleyin:
-(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;
}
Kod; _cameraSource
özelliğini başlatır, çekim oturumu hazır ayarını ve hangi kameranın kullanılacağını belirler.
Görüntülemek için _cameraSource
kaynağından ViewController
uygulamamıza kareler almamız gerekir. MPPCameraInputSource
, yetki verdikleri kullanıcılar için bir protokol sağlayan MPPInputSourceDelegate
adlı MPPInputSource
alt sınıfıdır. Yani ViewController
uygulamamız, _cameraSource
kullanıcısının temsilcisi olabilir.
ViewController
arayüz tanımını uygun şekilde güncelleyin:
@interface ViewController () <MPPInputSourceDelegate>
Kamera kurulumunu işlemek ve gelen kareleri işlemek için ana sıradan farklı bir sıra kullanmamız gerekir. ViewController
öğesinin uygulama bloğuna aşağıdaki kodu ekleyin:
// Process camera frames on this queue.
dispatch_queue_t _videoQueue;
viewDidLoad()
aracında, _cameraSource
nesnesini başlattıktan sonra aşağıdaki satırı ekleyin:
[_cameraSource setDelegate:self queue:_videoQueue];
Ayrıca _cameraSource
nesnesini ayarlamadan önce sırayı başlatmak için aşağıdaki kodu ekleyin:
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);
Kamera çerçevelerini işlemek için önceliği QOS_CLASS_USER_INTERACTIVE
olan bir seri sıra kullanırız.
Dosyanın üst kısmında başlık içe aktarıldıktan sonra, ViewController
arayüzü/uygulaması öncesinde aşağıdaki satırı ekleyin:
static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";
MPPInputSourceDelegate
protokolünden herhangi bir yöntem uygulamadan önce kamera karelerini görüntüleyecek bir yöntem belirlememiz gerekir. Mediapipe Framework, görüntüleri ekranda göstermek için MPPLayerRenderer
adlı başka bir yardımcı program sunar. Bu yardımcı program, MPPCameraInputSource
tarafından yetki verilmiş kullanıcılara sağlanan görüntü türü olan CVPixelBufferRef
nesnelerini görüntülemek için kullanılabilir.
ViewController.m
dosyasına aşağıdaki içe aktarma satırını ekleyin:
#import "mediapipe/objc/MPPLayerRenderer.h"
Ekranın görüntülerini görüntülemek için ViewController
öğesine _liveView
adlı yeni bir UIView
nesnesi eklememiz gerekir.
ViewController
öğesinin uygulama bloğuna aşağıdaki satırları ekleyin:
// Display the camera preview frames.
IBOutlet UIView* _liveView;
// Render frames in a layer.
MPPLayerRenderer* _renderer;
Main.storyboard
bölümüne gidin ve nesne kitaplığından ViewController
sınıfının View
öğesine UIView
nesnesi ekleyin. ViewController
sınıfına yeni eklediğiniz _liveView
nesnesine bu görünümden bir referans prizi ekleyin. Görünümü ortalanıp tüm uygulama ekranını kaplayacak şekilde yeniden boyutlandırın.
_renderer
nesnesini başlatmak için ViewController.m
öğesine geri dönün ve aşağıdaki kodu viewDidLoad()
öğesine ekleyin:
_renderer = [[MPPLayerRenderer alloc] init];
_renderer.layer.frame = _liveView.layer.bounds;
[_liveView.layer addSublayer:_renderer.layer];
_renderer.frameScaleMode = MPPFrameScaleModeFillAndCrop;
Kameradan kareleri almak için aşağıdaki yöntemi uygulayacağız:
// 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);
});
}
Bu, MPPInputSource
için yetki verme yöntemidir. İlk olarak, kareleri doğru kaynaktan, yani _cameraSource
'den aldığımızı kontrol ederiz. Ardından, kameradan alınan kareyi _renderer
üzerinden ana sırada görüntüleriz.
Şimdi, kareleri görüntülemek üzere görünümün belirmesi için
kamerayı hemen başlatmamız gerekiyor. Bunun için viewWillAppear:(BOOL)animated
işlevini uygulayacağız:
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
}
Kamerayı çalıştırmaya başlamadan önce, kameraya erişmek için kullanıcının iznini almamız gerekir.
MPPCameraInputSource
, kullanıcı yanıt verdiğinde kameraya erişim izni istemek ve bazı işlemler yapmak için requestCameraAccessWithCompletionHandler:(void (^_Nullable)(BOOL
granted))handler
işlevi sunar. Şu kodu viewWillAppear:animated
hesabına ekleyin:
[_cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
if (granted) {
dispatch_async(_videoQueue, ^{
[_cameraSource start];
});
}
}];
Uygulamayı derlemeden önce aşağıdaki bağımlılıkları BUILD
dosyanıza ekleyin:
sdk_frameworks = [
"AVFoundation",
"CoreGraphics",
"CoreMedia",
],
deps = [
"//mediapipe/objc:mediapipe_framework_ios",
"//mediapipe/objc:mediapipe_input_sources_ios",
"//mediapipe/objc:mediapipe_layer_renderer",
],
Şimdi uygulamayı iOS cihazınızda derleyip çalıştırın. Kamera izinlerini kabul ettikten sonra canlı kamera görüntü feed'i görürsünüz.
Artık kamera çerçevelerini MediaPipe grafiğinde kullanmaya hazırız.
iOS'te MediaPipe grafiği kullanma
Alakalı bağımlılıkları ekleyin
MediaPipe grafiğini kullanmak için iOS API'yi içeren MediaPipe çerçeve kodunun bağımlılıklarını ekledik. Bir MediaPipe grafiği kullanmak için, uygulamamızda kullanmayı amaçladığımız grafiğe bir bağımlılık eklememiz gerekir. Aşağıdaki satırı BUILD
dosyanızdaki data
listesine ekleyin:
"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",
Şimdi bağımlılığı, bu grafikte kullanılan hesaplayıcılara BUILD
dosyasındaki deps
alanında ekleyin:
"//mediapipe/graphs/edge_detection:mobile_calculators",
Son olarak, ViewController.m
dosyasını Objective-C++ özelliğini desteklemek için ViewController.mm
olarak yeniden adlandırın.
Grafiği ViewController
aracında kullanın
ViewController.m
dosyasına aşağıdaki içe aktarma satırını ekleyin:
#import "mediapipe/objc/MPPGraph.h"
Grafik adı, giriş akışı ve çıkış akışıyla statik bir sabit belirtin:
static NSString* const kGraphName = @"mobile_gpu";
static const char* kInputStream = "input_video";
static const char* kOutputStream = "output_video";
Aşağıdaki özelliği ViewController
arayüzüne ekleyin:
// The MediaPipe graph currently in use. Initialized in viewDidLoad, started in viewWillAppear: and
// sent video frames on _videoQueue.
@property(nonatomic) MPPGraph* mediapipeGraph;
Yukarıdaki yorumda açıklandığı gibi önce bu grafiği viewDidLoad
ile başlatacağız. Bunun için aşağıdaki işlevi kullanarak grafiği .pbtxt
dosyasından yüklememiz gerekir:
+ (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;
}
viewDidLoad
içindeki grafiği aşağıdaki gibi başlatmak için bu işlevi kullanın:
self.mediapipeGraph = [[self class] loadGraphFromResource:kGraphName];
Grafik, kamera karelerinin işlenmesinin sonuçlarını ViewController
hedefine geri gönderecektir. Grafik başlatıldıktan sonra ViewController
öğesini mediapipeGraph
nesnesinin temsilcisi olarak ayarlamak için aşağıdaki satırı ekleyin:
self.mediapipeGraph.delegate = self;
Canlı video feed'indeki kareleri işlerken bellek çakışmasını önlemek için aşağıdaki satırı ekleyin:
// Set maxFramesInFlight to a small value to avoid memory contention for real-time processing.
self.mediapipeGraph.maxFramesInFlight = 2;
Kullanıcı, uygulamamızda kamerayı kullanma izni verdiğinde grafiği başlatın:
[_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];
});
}
}];
Daha önce processVideoFrame
işlevinde kameradan kareler aldığımızda bu kareleri _renderer
kullanarak _liveView
içinde gösteriyorduk. Şimdi, bu kareleri grafiğe göndermemiz ve bunun yerine sonuçları oluşturmamız gerekiyor. Bu işlevin uygulamasında değişiklik yaparak aşağıdakileri yapabilirsiniz:
- (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];
}
imageBuffer
öğesini self.mediapipeGraph
alanına, kInputStream
giriş akışına MPPPacketTypePixelBuffer
türünde bir paket olarak göndeririz (ör. "input_video").
Grafik, bu giriş paketiyle çalışacak ve kOutputStream
sonucunu (yani "output_video") verir. Bu çıkış akışında paket almak ve bunları ekranda görüntülemek için aşağıdaki yetki verme yöntemini uygulayabiliriz:
- (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);
});
}
}
ViewController
arayüz tanımını MPPGraphDelegate
ile güncelleyin:
@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>
Hepsi bu kadar! Uygulamayı iOS cihazınızda derleyip çalıştırın. Canlı video feed'inde kenar algılama grafiğini çalıştırmanın sonuçlarını görürsünüz. Tebrikler!
iOS örneklerinde artık common bir şablon uygulaması kullanıldığını lütfen unutmayın. Bu eğitimdeki kod, common şablon uygulamasında kullanılır. helloworld uygulaması, kenar algılama grafiği için uygun BUILD
dosyası bağımlılıklarına sahiptir.