नमस्ते! iOS पर

परिचय

नमस्ते दुनिया! ट्यूटोरियल, iOS बनाने के लिए MediaPipe Framework का इस्तेमाल करता है ऐसा ऐप्लिकेशन जो iOS पर MediaPipe ग्राफ़ चलाता है.

आपको क्या बनाना होगा

लाइव वीडियो में, रीयल-टाइम में सोबेल एज डिटेक्शन करने के लिए एक आसान कैमरा ऐप्लिकेशन स्ट्रीम करते हैं.

edge_detection_ios_gpu_gif

सेटअप

  1. अपने सिस्टम पर MediaPipe Framework इंस्टॉल करें, फ़्रेमवर्क इंस्टॉलेशन देखें गाइड पढ़ें.
  2. डेवलपमेंट के लिए अपना iOS डिवाइस सेटअप करें.
  3. iOS ऐप्लिकेशन बनाने और डिप्लॉय करने के लिए, अपने सिस्टम पर Baaz सेटअप करें.

किनारे का पता लगाने के लिए ग्राफ़

हम इस ग्राफ़ का इस्तेमाल करेंगे, 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"
}

ग्राफ़ का विज़ुअलाइज़ेशन नीचे दिखाया गया है:

edge_detection_mobile_gpu

इस ग्राफ़ में, आने वाले सभी फ़्रेम के लिए input_video नाम की एक इनपुट स्ट्रीम मौजूद है जिसे आपके डिवाइस के कैमरे से मिलेगा.

ग्राफ़ के पहले नोड, LuminanceCalculator, में एक पैकेट (इमेज) होता है फ़्रेम) के आकार के साथ दिखाया जाता है और OpenGL शेडर का इस्तेमाल करके ल्यूमिनेंस में बदलाव लागू करता है. नतीजे के तौर पर मिला नतीजा इमेज फ़्रेम को luma_video आउटपुट स्ट्रीम पर भेजा जाता है.

दूसरा नोड, SobelEdgesCalculator किनारे की पहचान करने की सुविधा को इनकमिंग पर लागू करता है luma_video स्ट्रीम में पैकेट और आउटपुट के नतीजे में output_video आउटपुट मिलता है स्ट्रीम.

हमारा iOS ऐप्लिकेशन, output_video के आउटपुट इमेज फ़्रेम दिखाएगा स्ट्रीम.

शुरुआती कम से कम ऐप्लिकेशन सेटअप

हम सबसे पहले एक आसान iOS ऐप्लिकेशन से शुरुआत करते हैं. इसके बाद, हम यह बताते हैं कि bazel को कैसे इस्तेमाल किया जाता है उसे बनाने के लिए.

सबसे पहले, फ़ाइल के ज़रिए XCode प्रोजेक्ट बनाएं > नया > सिंगल व्यू ऐप्लिकेशन.

प्रॉडक्ट के नाम को "helloWorld" पर सेट करें और सही संगठन का इस्तेमाल करें आइडेंटिफ़ायर, जैसे कि com.google.mediapipe. संगठन का आइडेंटिफ़ायर ऐप्लिकेशन के लिए, प्रॉडक्ट के नाम के साथ bundle_id होगा, जैसे कि com.google.mediapipe.HelloWorld.

भाषा को Objective-C पर सेट करें.

प्रोजेक्ट को सही जगह पर सेव करें. इसे कॉल करते हैं $PROJECT_TEMPLATE_LOC. तो आपका प्रोजेक्ट $PROJECT_TEMPLATE_LOC/HelloWorld डायरेक्ट्री. इस डायरेक्ट्री में ये चीज़ें शामिल होंगी HelloWorld नाम की दूसरी डायरेक्ट्री और HelloWorld.xcodeproj फ़ाइल.

इस ट्यूटोरियल के लिए HelloWorld.xcodeproj काम का नहीं होगा, क्योंकि हम इसका इस्तेमाल करेंगे basel का इस्तेमाल करें. इसका कॉन्टेंट $PROJECT_TEMPLATE_LOC/HelloWorld/HelloWorld डायरेक्ट्री नीचे दी गई है:

  1. AppDelegate.h और AppDelegate.m
  2. ViewController.h और ViewController.m
  3. main.m
  4. Info.plist
  5. Main.storyboard और Launch.storyboard
  6. Assets.xcassets डायरेक्ट्री.

इन फ़ाइलों को HelloWorld नाम की डायरेक्ट्री में कॉपी करें, ताकि उन्हें ऐक्सेस किया जा सके MediaPipe Framework का सोर्स कोड. उदाहरण के लिए, इस ट्यूटोरियल में बनाया जाने वाला ऐप्लिकेशन mediapipe/examples/ios/HelloWorld. हम इस पाथ को इस तौर पर देखेंगे पूरे कोडलैब में $APPLICATION_PATH.

$APPLICATION_PATH में BUILD फ़ाइल बनाएं और यह बिल्ड जोड़ें नियम:

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 नियम, AppDelegate और ViewController क्लास, main.m और ऐप्लिकेशन के स्टोरीबोर्ड. कॉन्टेंट बनाने टेंप्लेट वाला ऐप्लिकेशन सिर्फ़ UIKit SDK टूल पर निर्भर करता है.

ios_application नियम, HelloWorldAppLibrary के Objective-C लाइब्रेरी का इस्तेमाल करता है को आपके iOS डिवाइस पर इंस्टॉल करने के लिए iOS ऐप्लिकेशन बनाने के लिए जनरेट किया गया है.

ऐप्लिकेशन बनाने के लिए, टर्मिनल में इस कमांड का इस्तेमाल करें:

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

उदाहरण के लिए, HelloWorldApp ऐप्लिकेशन को mediapipe/examples/ios/helloworld, इस निर्देश का इस्तेमाल करें:

bazel build -c opt --config=ios_arm64 mediapipe/examples/ios/helloworld:HelloWorldApp

इसके बाद, XCode पर वापस जाएं. इसके बाद, Window खोलें > डिवाइसों और सिम्युलेटर में से, अपने और ऊपर दिए गए निर्देश से जनरेट हुई .ipa फ़ाइल को अपने डिवाइस में जोड़ें. iOS फ़्रेमवर्क ऐप्लिकेशन सेट अप और कंपाइल करने के बारे में जानकारी देने वाला दस्तावेज़ यहां दिया गया है.

अपने डिवाइस पर ऐप्लिकेशन खोलें. यह खाली है, इसलिए इसे खाली सफ़ेद स्क्रीन पर टैप करें.

लाइव व्यू फ़ीड के लिए कैमरे का इस्तेमाल करना

इस ट्यूटोरियल में, हम MPPCameraInputSource क्लास का इस्तेमाल करके, कैमरे से फ़्रेम लें. यह क्लास, ये डेटा पाने के लिए AVCaptureSession एपीआई का इस्तेमाल करती है में से फ़्रेम चुनें.

हालाँकि, इस क्लास का इस्तेमाल करने से पहले, Info.plist फ़ाइल को इस तरह बदलें कि वह कैमरे के साथ काम कर सके के बारे में ज़्यादा जानें.

ViewController.m में, यह इंपोर्ट लाइन जोड़ें:

#import "mediapipe/objc/MPPCameraInputSource.h"

कोई ऑब्जेक्ट बनाने के लिए, इसे लागू करने वाले ब्लॉक में इन्हें जोड़ें _cameraSource:

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

viewDidLoad() में यह कोड जोड़ें:

-(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;
}

यह कोड _cameraSource को शुरू करता है और कैप्चर सेशन के प्रीसेट को सेट करता है. इसके बाद, आसान नहीं है.

हमें अपने ऐप्लिकेशन में, _cameraSource से फ़्रेम लेने होंगे उन्हें दिखाने के लिए ViewController. MPPCameraInputSource, इसकी सब-क्लास है MPPInputSource, जो अपने डेलिगेट के लिए एक प्रोटोकॉल उपलब्ध कराता है. इसका नाम है MPPInputSourceDelegate. इसलिए, हमारा ऐप्लिकेशन ViewController, आपको ईमेल खाते का ऐक्सेस दे सकता है कुल _cameraSource.

ViewController की इंटरफ़ेस परिभाषा को ज़रूरत के हिसाब से अपडेट करें:

@interface ViewController () <MPPInputSourceDelegate>

कैमरा सेटअप करने और आने वाले फ़्रेम को प्रोसेस करने के लिए, हमें सूची का इस्तेमाल करना चाहिए जो मुख्य सूची से अलग है. इसके लागू होने की ब्लॉक में नीचे दी गई चीज़ें जोड़ें ViewController:

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

viewDidLoad() में, शुरू करने के बाद यह लाइन जोड़ें _cameraSource ऑब्जेक्ट:

[_cameraSource setDelegate:self queue:_videoQueue];

इसके बाद, सेट अप करने से पहले सूची शुरू करने के लिए, नीचे दिया गया कोड जोड़ें _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);

हम इसके लिए QOS_CLASS_USER_INTERACTIVE प्राथमिकता वाली सीरियल क्यू का इस्तेमाल करेंगे कैमरा फ़्रेम प्रोसेस कर रही हूँ.

फ़ाइल में सबसे ऊपर हेडर इंपोर्ट होने के बाद, यह लाइन जोड़ें ViewController का इंटरफ़ेस/लागू करना:

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

MPPInputSourceDelegate प्रोटोकॉल में बताए गए किसी भी तरीके को लागू करने से पहले, हमें ये काम करने होंगे पहले कैमरे के फ़्रेम दिखाने का तरीका सेट अप करते हैं. मीडियापाइप फ़्रेमवर्क स्क्रीन पर इमेज दिखाने के लिए, MPPLayerRenderer नाम की एक दूसरी सुविधा का इस्तेमाल किया गया है. यह यूटिलिटी का इस्तेमाल CVPixelBufferRef ऑब्जेक्ट को दिखाने के लिए किया जा सकता है, जो कि वे इमेज जो MPPCameraInputSource ने अपने डेलिगेट के लोगों को उपलब्ध कराई हैं.

ViewController.m में, यह इंपोर्ट लाइन जोड़ें:

#import "mediapipe/objc/MPPLayerRenderer.h"

स्क्रीन की इमेज दिखाने के लिए, हमें नाम का एक नया UIView ऑब्जेक्ट जोड़ना होगा _liveView से ViewController के लिए.

ViewController के लागू करने के ब्लॉक में ये लाइनें जोड़ें:

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

Main.storyboard पर जाएं और ऑब्जेक्ट लाइब्रेरी से UIView ऑब्जेक्ट को ViewController क्लास में से View. इस व्यू से रेफ़रंस देने वाला आउटलेट जोड़ें _liveView ऑब्जेक्ट, जिसे आपने अभी-अभी ViewController क्लास में जोड़ा है. साइज़ ताकि वह बीच में आ जाए और ऐप्लिकेशन की पूरी स्क्रीन को कवर कर सके.

ViewController.m पर वापस जाएं और viewDidLoad() में नीचे दिया गया कोड जोड़ें _renderer ऑब्जेक्ट शुरू करें:

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

कैमरे से फ़्रेम पाने के लिए, हम यह तरीका इस्तेमाल करेंगे:

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

यह MPPInputSource का डेलिगेट तरीका है. हम पहले जांच करते हैं कि हम सही सोर्स से फ़्रेम पाना, जैसे कि _cameraSource. इसके बाद, हम दिखाते हैं कि मुख्य सूची में मौजूद _renderer के ज़रिए, कैमरे से मिले फ़्रेम को दिखाता है.

अब, हमें फ़्रेम दिखाने के लिए व्यू के तुरंत बाद कैमरा चालू करना होगा दिखने वाला है. ऐसा करने के लिए, हम viewWillAppear:(BOOL)animated फ़ंक्शन:

-(void)viewWillAppear:(BOOL)animated {
  [super viewWillAppear:animated];
}

कैमरा इस्तेमाल करने से पहले, हमें इसे ऐक्सेस करने के लिए उपयोगकर्ता की अनुमति चाहिए. MPPCameraInputSource फ़ंक्शन उपलब्ध कराता है कैमरे का ऐक्सेस मांगने के लिए requestCameraAccessWithCompletionHandler:(void (^_Nullable)(BOOL granted))handler. साथ ही, कुछ काम तब करें, जब उपयोगकर्ता के पास जवाब दिया. viewWillAppear:animated में यह कोड जोड़ें:

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

ऐप्लिकेशन बनाने से पहले, अपने BUILD में ये डिपेंडेंसी जोड़ें फ़ाइल:

sdk_frameworks = [
    "AVFoundation",
    "CoreGraphics",
    "CoreMedia",
],
deps = [
    "//mediapipe/objc:mediapipe_framework_ios",
    "//mediapipe/objc:mediapipe_input_sources_ios",
    "//mediapipe/objc:mediapipe_layer_renderer",
],

अब अपने iOS डिवाइस पर यह ऐप्लिकेशन बनाएं और चलाएं. आपको एक लाइव कैमरा ऐक्सेस करने की अनुमतियां स्वीकार करने के बाद, कैमरा व्यू फ़ीड दिखेगा.

अब हम MediaPipe ग्राफ़ में कैमरा फ़्रेम का इस्तेमाल करने के लिए तैयार हैं.

iOS में MediaPipe ग्राफ़ का इस्तेमाल करना

काम की डिपेंडेंसी जोड़ें

हमने पहले ही MediaPipe फ़्रेमवर्क कोड की डिपेंडेंसी जोड़ दी है, जिसमें iOS API का इस्तेमाल करके MediaPipe ग्राफ़ का इस्तेमाल करें. MediaPipe ग्राफ़ का इस्तेमाल करने के लिए, हमें उस ग्राफ़ पर निर्भर होना चाहिए जिसका इस्तेमाल हम अपने ऐप्लिकेशन में करना चाहते हैं. इन्हें जोड़ें आपकी BUILD फ़ाइल में data सूची की पंक्ति:

"//mediapipe/graphs/edge_detection:mobile_gpu_binary_graph",

अब deps फ़ील्ड में, इस ग्राफ़ में इस्तेमाल किए गए कैलकुलेटर पर निर्भरता जोड़ें BUILD फ़ाइल में:

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

आखिर में, इससे जुड़ी सहायता देने के लिए, ViewController.m फ़ाइल का नाम बदलकर ViewController.mm करें Objective-C++.

ViewController में ग्राफ़ का इस्तेमाल करें

ViewController.m में, यह इंपोर्ट लाइन जोड़ें:

#import "mediapipe/objc/MPPGraph.h"

ग्राफ़, इनपुट स्ट्रीम, और आउटपुट स्ट्रीम:

static NSString* const kGraphName = @"mobile_gpu";

static const char* kInputStream = "input_video";
static const char* kOutputStream = "output_video";

ViewController के इंटरफ़ेस में इस प्रॉपर्टी को जोड़ें:

// The MediaPipe graph currently in use. Initialized in viewDidLoad, started in viewWillAppear: and
// sent video frames on _videoQueue.
@property(nonatomic) MPPGraph* mediapipeGraph;

जैसा कि ऊपर दी गई टिप्पणी में बताया गया है, हम इस ग्राफ़ को viewDidLoad पहले. ऐसा करने के लिए, हमें .pbtxt फ़ाइल से ग्राफ़ लोड करना होगा इसके लिए, नीचे दिए गए फ़ंक्शन का इस्तेमाल करें:

+   (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 में ग्राफ़ शुरू करने के लिए, इस फ़ंक्शन का इस्तेमाल इस तरह करें:

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

ग्राफ़ को कैमरे फ़्रेम को प्रोसेस करने के नतीजों को ViewController. ग्राफ़ को सेट करने के लिए, mediapipeGraph ऑब्जेक्ट के प्रतिनिधि के तौर पर ViewController:

self.mediapipeGraph.delegate = self;

लाइव वीडियो फ़ीड से फ़्रेम प्रोसेस करते समय मेमोरी में होने वाले विवाद से बचने के लिए, नीचे दी गई लाइन:

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

अब, ग्राफ़ को तब शुरू करें, जब उपयोगकर्ता ने कैमरे का इस्तेमाल करने की अनुमति दी हो हमारे ऐप्लिकेशन में:

[_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];
    });
  }
}];

इससे पहले, जब हमें processVideoFrame में कैमरे से फ़्रेम मिले थे फ़ंक्शन का इस्तेमाल करके, हमने उन्हें _liveView में _renderer का इस्तेमाल करके दिखाया है. अब, हम उन फ़्रेम को ग्राफ़ पर भेजने और नतीजों को रेंडर करने की ज़रूरत होती है. बदलाव करें इस फ़ंक्शन को लागू करके ये काम किए जा सकते हैं:

-   (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];
}

हम self.mediapipeGraph को imageBuffer की जानकारी एक पैकेट के तौर पर भेजते हैं MPPPacketTypePixelBuffer इनपुट स्ट्रीम kInputStream में डालें, यानी "input_video" शामिल है.

ग्राफ़ इस इनपुट पैकेट के साथ चलेगा और आउटपुट के रूप में दिखेगा kOutputStream, उदाहरण के लिए, "Output_video". हम यहां दिए गए ऐक्सेस लेवल को लागू कर सकते हैं इस तरीके से पाएं:

-   (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 की इंटरफ़ेस की परिभाषा को MPPGraphDelegate की मदद से अपडेट करें:

@interface ViewController () <MPPGraphDelegate, MPPInputSourceDelegate>

बस इतना ही! अपने iOS डिवाइस पर ऐप्लिकेशन बनाएं और चलाएं. आपको के नतीजे भी मिलते हैं. बधाई हो!

edge_detection_ios_gpu_gif

कृपया ध्यान दें कि iOS के उदाहरणों में अब सामान्य टेंप्लेट ऐप्लिकेशन का इस्तेमाल किया जाता है. इसमें मौजूद कोड इस ट्यूटोरियल का इस्तेमाल सामान्य टेंप्लेट ऐप्लिकेशन में किया गया है. helloworld ऐप्लिकेशन में किनारे की पहचान वाले ग्राफ़ के लिए सही BUILD फ़ाइल डिपेंडेंसी.