परिचय
नमस्ते दुनिया! ट्यूटोरियल, iOS बनाने के लिए MediaPipe Framework का इस्तेमाल करता है ऐसा ऐप्लिकेशन जो iOS पर MediaPipe ग्राफ़ चलाता है.
आपको क्या बनाना होगा
लाइव वीडियो में, रीयल-टाइम में सोबेल एज डिटेक्शन करने के लिए एक आसान कैमरा ऐप्लिकेशन स्ट्रीम करते हैं.
सेटअप
- अपने सिस्टम पर MediaPipe Framework इंस्टॉल करें, फ़्रेमवर्क इंस्टॉलेशन देखें गाइड पढ़ें.
- डेवलपमेंट के लिए अपना iOS डिवाइस सेटअप करें.
- 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"
}
ग्राफ़ का विज़ुअलाइज़ेशन नीचे दिखाया गया है:
इस ग्राफ़ में, आने वाले सभी फ़्रेम के लिए 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
डायरेक्ट्री नीचे दी गई है:
AppDelegate.h
औरAppDelegate.m
ViewController.h
औरViewController.m
main.m
Info.plist
Main.storyboard
औरLaunch.storyboard
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 डिवाइस पर ऐप्लिकेशन बनाएं और चलाएं. आपको के नतीजे भी मिलते हैं. बधाई हो!
कृपया ध्यान दें कि iOS के उदाहरणों में अब सामान्य टेंप्लेट ऐप्लिकेशन का इस्तेमाल किया जाता है. इसमें मौजूद कोड
इस ट्यूटोरियल का इस्तेमाल सामान्य टेंप्लेट ऐप्लिकेशन में किया गया है. helloworld ऐप्लिकेशन में
किनारे की पहचान वाले ग्राफ़ के लिए सही BUILD
फ़ाइल डिपेंडेंसी.