iOS'te Hello World!

Giriş

Merhaba Dünya! eğiticisi, iOS'i geliştirmek için MediaPipe Framework'ü kullanır iOS'te MediaPipe grafiği çalıştıran bir uygulamadır.

Ne oluşturacaksınız?

Canlı bir videoya gerçek zamanlı Sobel kenar algılaması için uygulanan basit bir kamera uygulaması akışla aktarmanızı sağlar.

edge_detection_ios_gpu_gif

Kurulum

  1. Sisteminize MediaPipe Framework'ü yükleme için Çerçeve yüklemesi kurulumuna bakın rehberini inceleyin.
  2. Geliştirme için iOS cihazınızı ayarlayın.
  3. iOS uygulamasını oluşturmak ve dağıtmak için sisteminizde Bazel'i kurun.

Kenar algılama grafiği

Şu grafiği kullanacağız: 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"
}

Grafik görselleştirmesi aşağıda gösterilmektedir:

edge_detection_mobile_gpu

Bu grafikte, tüm gelen kareler için input_video adlı tek bir giriş akışı bulunuyor bir veri feed'i seçin.

Grafikteki ilk düğüm olan LuminanceCalculator, tek bir paket alır (görüntü çerçeve) ve bir OpenGL gölgelendirici kullanarak parlaklık değişikliği uygular. Bunun sonucunda resim çerçevesi, luma_video çıkış akışına gönderilir.

İkinci düğüm olan SobelEdgesCalculator, gelen içeriğe uç algılamayı uygular luma_video akışındaki paketler ve çıkışlar, output_video çıkışıyla sonuçlanır akış şeklinde gösterilir.

iOS uygulamamız, output_video çıktısının resim çerçevelerini görüntüler akış şeklinde gösterilir.

İlk minimum uygulama kurulumu

Öncelikle basit bir iOS uygulamasıyla başlayıp bazel ürününün nasıl kullanılacağını göstereceğiz. sahip olacaksınız.

İlk olarak Dosya > üzerinden bir XCode projesi oluşturun Yeni > Tek Görünüm Uygulaması.

Ürün adını "HelloWorld" olarak ayarlayın ve uygun bir kuruluş kullanın com.google.mediapipe gibi bir tanımlayıcı içerir. Kuruluş tanımlayıcısı ile birlikte ürün adı, uygulama için bundle_id olacaktır. Örneğin, com.google.mediapipe.HelloWorld.

Dili Objective-C olarak ayarlayın.

Projeyi uygun bir konuma kaydedin. Buna şöyle diyelim: $PROJECT_TEMPLATE_LOC Bu nedenle projeniz, $PROJECT_TEMPLATE_LOC/HelloWorld dizini. Bu dizinde şunlar yer alır: HelloWorld adlı başka bir dizin ve bir HelloWorld.xcodeproj dosyası.

HelloWorld.xcodeproj, daha önce belirttiğimiz gibi bu eğitimde yararlı olmayacak bazel kullanarak iOS uygulamasını oluşturabilirsiniz. The content of the $PROJECT_TEMPLATE_LOC/HelloWorld/HelloWorld dizini aşağıda listelenmiştir:

  1. AppDelegate.h ve AppDelegate.m
  2. ViewController.h ve ViewController.m
  3. main.m
  4. Info.plist
  5. Main.storyboard ve Launch.storyboard
  6. Assets.xcassets dizini.
ziyaret edin.

Bu dosyaları HelloWorld adlı dizine kopyalayıp erişimi olan bir konuma kopyalayın MediaPipe Framework kaynak kodu kullanılır. Örneğin, bu eğitimde oluşturacağımız uygulama şuradadır: mediapipe/examples/ios/HelloWorld Bu yolu Codelab boyunca $APPLICATION_PATH.

$APPLICATION_PATH içinde bir BUILD dosyası oluşturun ve aşağıdaki derlemeyi ekleyin kurallarınız:

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 dersler, main.m ve uygulamanın resimli taslakları. İlgili içeriği oluşturmak için kullanılan şablonlu uygulama yalnızca UIKit SDK'sına bağlıdır.

ios_application kuralı HelloWorldAppLibrary Objective-C kitaplığını kullanır iOS cihazınıza yüklenmek üzere bir iOS uygulaması oluşturmak için oluşturulan bir web sitesini ziyaret edin.

Uygulamayı derlemek için bir terminalde aşağıdaki komutu kullanın:

bazel build -c opt --config=ios_arm64 <$APPLICATION_PATH>:HelloWorldApp'

Örneğin HelloWorldApp uygulamasını mediapipe/examples/ios/helloworld 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, Window'u açın > Cihazlar ve Simülatörler, cihazınıza yükleyin ve yukarıdaki komutla oluşturulan .ipa dosyasını cihazınıza ekleyin. iOS Framework uygulamalarını kurma ve derleme ile ilgili belgeyi aşağıda bulabilirsiniz.

Cihazınızda uygulamayı açın. Boş olduğu için boş beyaz ekran.

Canlı görüntüleme feed'i için kamerayı kullanma

Bu eğitimde, MPPCameraInputSource sınıfını kullanarak çerçeveleri seçin. Bu sınıf, aşağıdaki işlemleri yapmak için AVCaptureSession API'sini kullanır: otomatik olarak oluşturulur.

Ancak bu sınıfı kullanmadan önce Info.plist dosyasını kamerayı destekleyecek şekilde değiştirin elde etti.

ViewController.m içine aşağıdaki içe aktarma satırını ekleyin:

#import "mediapipe/objc/MPPCameraInputSource.h"

Nesne oluşturmak için aşağıdaki kodu uygulama bloğuna ekleyin _cameraSource:

@implementation ViewController {
  // Handles camera access via AVCaptureSession library.
  MPPCameraInputSource* _cameraSource;
}

Şu kodu viewDidLoad() alanı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 işlemini başlatır, yakalama oturumu hazır ayarını ayarlar ve bulun.

_cameraSource içinden kareleri uygulamamıza almamız gerekiyor Bunları görüntülemek için ViewController. MPPCameraInputSource, şunun bir alt sınıfıdır: Yetki verilmiş kullanıcıları için bir protokol sağlayan MPPInputSource MPPInputSourceDelegate. Bu durumda ViewController uygulamamız, yetki verilmiş kullanıcı / _cameraSource.

ViewController arayüzünün tanımını uygun şekilde güncelleyin:

@interface ViewController () <MPPInputSourceDelegate>

Kamera kurulumunu yönetmek ve gelen kareleri işlemek için bir ana sıradan farklıdır. Aşağıdakini ViewController:

// Process camera frames on this queue.
dispatch_queue_t _videoQueue;

viewDidLoad() içinde, başlatıldıktan sonra aşağıdaki satırı ekleyin _cameraSource nesne:

[_cameraSource setDelegate:self queue:_videoQueue];

Kurulumu ayarlamadan önce sırayı başlatmak için aşağıdaki kodu ekleyin _cameraSource nesne:

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);

Şunun için önceliği QOS_CLASS_USER_INTERACTIVE olan bir seri sırası kullanacağız: fotoğraf makinesi çerçeveleri işleniyor.

Dosyanın üst kısmına başlık içe aktarıldıktan sonra, aşağıdaki satırı arayüzü/uygulaması ViewController:

static const char* kVideoQueueLabel = "com.google.mediapipe.example.videoQueue";

MPPInputSourceDelegate protokolündeki herhangi bir yöntemi uygulamadan önce önce kamera çerçevelerini görüntüleyecek bir yöntem ayarlayın. Mediapipe çerçevesi MPPLayerRenderer adlı başka bir yardımcı programı kullanır. Bu yardımcı programı CVPixelBufferRef nesnelerini görüntülemek için kullanılabilir. MPPCameraInputSource tarafından yetki verilen kullanıcılara sağlanan resimler.

ViewController.m içine aşağıdaki içe aktarma satırını ekleyin:

#import "mediapipe/objc/MPPLayerRenderer.h"

Ekranın resimlerini görüntülemek için adlı yeni bir UIView nesnesi eklememiz gerekir. ViewController - _liveView.

Aşağıdaki satırları ViewController uygulama bloğuna ekleyin:

// Display the camera preview frames.
IBOutlet UIView* _liveView;
// Render frames in a layer.
MPPLayerRenderer* _renderer;

Main.storyboard menüsüne gidin, nesne kitaplığındanUIView ViewController sınıfından View. Bu görünümden şuna bir referans çıkışı ekleyin: ViewController sınıfına eklediğiniz _liveView nesnesi. Yeniden boyutlandır ortalanmış olacak ve uygulama ekranının tamamını kaplayacak şekilde görünür.

ViewController.m sayfasına dönün ve aşağıdaki kodu viewDidLoad() içine ekleyin: _renderer nesnesini başlatın:

_renderer = [[MPPLayerRenderer alloc] init];
_renderer.layer.frame = _liveView.layer.bounds;
[_liveView.layer addSublayer:_renderer.layer];
_renderer.frameScaleMode = MPPFrameScaleModeFillAndCrop;

Kameradan kare 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 bir yetki verme yöntemidir. İlk olarak, hesabın doğru doğru kaynaktan (_cameraSource) kareler alınıyor. Daha sonra ana sıradaki _renderer aracılığıyla kameradan alınan kare.

Şimdi, kareleri görüntülemek için görünüm açılır açılmaz kamerayı başlatmamız gerekiyor. az sonra görünür. Bunu yapmak için, viewWillAppear:(BOOL)animated işlevi:

-(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, bir işlev sağlar Kullanıcı aşağıdaki durumda kamera erişimi istemek ve bir işlem yapmak için requestCameraAccessWithCompletionHandler:(void (^_Nullable)(BOOL granted))handler yanıt verdi. Şu kodu viewWillAppear:animated alanına ekleyin:

[_cameraSource requestCameraAccessWithCompletionHandler:^void(BOOL granted) {
  if (granted) {
    dispatch_async(_videoQueue, ^{
      [_cameraSource start];
    });
  }
}];

Uygulamayı derlemeden önce BUILD dosyanıza aşağıdaki bağımlılıkları ekleyin. dosya:

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. Burada canlı bir kamera görüntüleme feed'ini etkinleştirin.

Artık kamera çerçevelerini MediaPipe grafiğinde kullanmaya hazırız.

iOS'te bir MediaPipe grafiği kullanma

İlgili bağımlılıkları ekleyin

Şunu içeren bir dosya içeren MediaPipe çerçevesi kodunun bağımlılıklarını MediaPipe grafiği kullanmak için iOS API'ye bakalım. Bir MediaPipe grafiğini kullanmak için, bir kullanmayı planladığımız grafiğe bağımlılığımızı söyleyebiliriz. Aşağıdakileri ekleyin: 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ıların deps alanına ekleyin. BUILD dosyasında:

"//mediapipe/graphs/edge_detection:mobile_calculators",

Son olarak, desteği desteklemek için ViewController.m dosyasını ViewController.mm olarak yeniden adlandırın Amaç-C++.

ViewController uygulamasında grafiği kullanın

ViewController.m içine aşağıdaki içe aktarma satırını ekleyin:

#import "mediapipe/objc/MPPGraph.h"

Grafiğin adı, giriş akışı ve çıkış akışı:

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, bu grafiği Önce viewDidLoad. Bunu yapmak için grafiği .pbtxt dosyasından yüklememiz gerekir. kullanarak aşağıdaki işlevi kullanabilirsiniz:

+   (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 şu şekilde başlatmak için bu işlevi kullanın:

self.mediapipeGraph = [[self class] loadGraphFromResource:kGraphName];

Grafik, kamera çerçevelerini işlemenin sonuçlarını ViewController Grafiği başlattıktan sonra mediapipeGraph nesnesinin yetki verilmiş kullanıcısı olarak ViewController:

self.mediapipeGraph.delegate = self;

Canlı video feed'indeki kareleri işlerken bellek çakışmasını önlemek için şu satırı ekleyin:

// Set maxFramesInFlight to a small value to avoid memory contention for real-time processing.
self.mediapipeGraph.maxFramesInFlight = 2;

Şimdi, kullanıcı kamerayı kullanma izni verdiğinde grafiği başlatın inceleyebilirsiniz:

[_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çindeki kameradan kareler aldığımızda işlevinde, _renderer kullanarak bunları _liveView içinde görüntüledik. Artık ve bunun yerine bu kareleri grafiğe gönderip sonuçları oluşturmanız gerekir. Değiştir işlevini kullanarak şunları 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 adresine tür bir paket olarak göndeririz MPPPacketTypePixelBuffer, kInputStream giriş akışına eklenir. "input_video".

Grafik, bu giriş paketiyle çalışır ve şu sonucu verir: kOutputStream, ör. "çıkış_video". Aşağıdaki temsilciyi uygulayabiliriz yöntemlerini kullanın:

-   (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 öğesinin 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. Burada sonuçlarını görebilirsiniz. Tebrikler!

edge_detection_ios_gpu_gif

iOS örneklerinin artık yaygın bir şablon uygulaması kullandığını unutmayın. Bu işlemde bu eğitim, common şablon uygulamasında kullanılır. helloworld uygulamasında bağımlılık grafiği için uygun BUILD dosya bağımlılıklarını