नमस्ते! iOS पर

शुरुआती जानकारी

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

आप क्या बनाएंगे

रीयल-टाइम में Sobel किनारे की पहचान करने वाला एक आसान कैमरा ऐप्लिकेशन है. इसे iOS डिवाइस पर लाइव वीडियो स्ट्रीम के लिए लागू किया गया है.

edge_detection_ios_gpu_gif

सेटअप

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

किनारे की पहचान के लिए ग्राफ़

हम नीचे दिए गए ग्राफ़ का इस्तेमाल करेंगे, 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.

भाषा को ऑब्जेक्टिव-सी पर सेट करें.

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

इस ट्यूटोरियल के लिए, HelloWorld.xcodeproj काम का नहीं होगा, क्योंकि हम iOS ऐप्लिकेशन बनाने के लिए bazel का इस्तेमाल करेंगे. $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 फ़्रेमवर्क सोर्स कोड को ऐक्सेस कर सके. उदाहरण के लिए, इस ट्यूटोरियल में बनाए जाने वाले ऐप्लिकेशन का सोर्स कोड 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 नियम, आपके iOS डिवाइस पर iOS ऐप्लिकेशन बनाने के लिए जनरेट की गई HelloWorldAppLibrary ऑब्जेक्टिव-सी लाइब्रेरी का इस्तेमाल करता है.

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

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 पर वापस जाएं और विंडो > डिवाइस और सिम्युलेटर खोलें. इसके बाद, अपना डिवाइस चुनें और ऊपर दिए गए निर्देश से जनरेट की गई .ipa फ़ाइल को अपने डिवाइस में जोड़ें. यहां iOS फ़्रेमवर्क ऐप्लिकेशन को सेट अप और कंपाइल करने के बारे में जानकारी दी गई है.

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

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

इस ट्यूटोरियल में, हम कैमरे से फ़्रेम ऐक्सेस करने और ऐक्सेस करने के लिए, MPPCameraInputSource क्लास का इस्तेमाल करेंगे. यह क्लास, कैमरे से फ़्रेम पाने के लिए AVCaptureSession API का इस्तेमाल करती है.

हालांकि, इस क्लास का इस्तेमाल करने से पहले, 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 में फ़्रेम दिखाने के लिए 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 प्रोटोकॉल से किसी भी तरीके को लागू करने से पहले, हमें पहले कैमरे के फ़्रेम दिखाने का तरीका सेट अप करना होगा. MediaPipe Framework, स्क्रीन पर इमेज दिखाने के लिए MPPLayerRenderer नाम की एक और सुविधा उपलब्ध कराता है. इस सुविधा का इस्तेमाल, CVPixelBufferRef ऑब्जेक्ट दिखाने के लिए किया जा सकता है. यह ऐसे इमेज हैं जो MPPCameraInputSource अपने प्रतिनिधियों को देते हैं.

ViewController.m में, नीचे दी गई इंपोर्ट लाइन जोड़ें:

#import "mediapipe/objc/MPPLayerRenderer.h"

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

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

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

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

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

_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 फ़्रेमवर्क कोड की डिपेंडेंसी जोड़ दी है, जिसमें MediaPipe ग्राफ़ का इस्तेमाल करने के लिए iOS API शामिल है. MediaPipe ग्राफ़ का इस्तेमाल करने के लिए, हमें उस ग्राफ़ पर निर्भरता जोड़ना होगा जिसे हम अपने ऐप्लिकेशन में इस्तेमाल करना चाहते हैं. अपनी BUILD फ़ाइल में मौजूद data सूची में यह लाइन जोड़ें:

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

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

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

आखिर में, Objective-C++ के साथ काम करने के लिए, ViewController.m फ़ाइल का नाम बदलकर ViewController.mm करें.

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 पर भेजना चाहिए. ViewController को mediapipeGraph ऑब्जेक्ट के प्रतिनिधि के तौर पर सेट करने के लिए, ग्राफ़ शुरू करने के बाद यहां दी गई लाइन जोड़ें:

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

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

ग्राफ़ इस इनपुट पैकेट के साथ चलेगा और आउटपुट के तौर पर kOutputStream, जैसे कि " callout_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 वाले उदाहरणों में अब common टेंप्लेट ऐप्लिकेशन का इस्तेमाल किया जाता है. इस ट्यूटोरियल में मौजूद कोड का इस्तेमाल common टेंप्लेट ऐप्लिकेशन में किया गया है. helloworld ऐप्लिकेशन में, एज डिटेक्शन ग्राफ़ के लिए BUILD फ़ाइल डिपेंडेंसी है.