توفّر حزمة ML Kit حزمة تطوير برامج (SDK) محسّنة لتقسيم الصور الذاتية. يتم ربط مواد عرض أداة "تقسيم الصور الذاتية" بشكل ثابت بتطبيقك في وقت الإنشاء. سيؤدي ذلك إلى زيادة حجم تطبيقك بما يصل إلى 24 ميغابايت، ويمكن أن يتراوح وقت استجابة واجهة برمجة التطبيقات بين 7 و12 ملي ثانية تقريبًا حسب حجم الصورة المُدخلة، كما تم قياسه على هاتف iPhone X.
جرّبه الآن
- يمكنك تجربة نموذج التطبيق لاطلاع على مثال على استخدام واجهة برمجة التطبيقات هذه.
قبل البدء
أدرِج مكتبات ML Kit التالية في ملف Podfile:
pod 'GoogleMLKit/SegmentationSelfie', '7.0.0'
بعد تثبيت حِزم Pods في مشروعك أو تعديلها، افتح مشروع Xcode باستخدام ملف
xcworkspace
.. تتوفّر حزمة ML Kit في الإصدار 13.2.1 من Xcode أو الإصدارات الأحدث.
1. إنشاء مثيل لفئة Segmenter
لإجراء عملية تقسيم على صورة ذاتية، عليك أولاً إنشاء مثيل من Segmenter
باستخدام SelfieSegmenterOptions
وتحديد إعدادات التقسيم اختياريًا.
خيارات أداة التقسيم
وضع "المُقسِّم"
يعمل Segmenter
بوضعَين. احرص على اختيار الإعدادات التي تناسب حالة الاستخدام.
STREAM_MODE (default)
تم تصميم هذا الوضع لبث اللقطات من الفيديو أو الكاميرا. في هذا الوضع، سيستفيد مُقسِّم الفيديو من النتائج من اللقطات السابقة لعرض نتائج أكثر سلاسة.
SINGLE_IMAGE_MODE (default)
تم تصميم هذا الوضع للصور الفردية غير ذات الصلة. في هذا الوضع، ستعالج أداة تقسيم الفيديو كل صورة بشكل مستقل، بدون تمويه اللقطات.
تفعيل قناع الحجم الأصلي
يطلب من أداة التقسيم عرض قناع الحجم الأوّلي الذي يتطابق مع حجم إخراج النموذج.
يكون حجم القناع الأوّلي (مثلاً 256x256) عادةً أصغر من حجم الصورة المُدخلة.
في حال عدم تحديد هذا الخيار، ستعيد أداة التقسيم قياس القناع الأوّلي لمطابقة حجم الصورة المُدخلة. ننصحك باستخدام هذا الخيار إذا كنت تريد تطبيق منطق مخصّص لإعادة القياس أو إذا لم تكن إعادة القياس مطلوبة لحالة الاستخدام.
حدِّد خيارات أداة التقسيم:
let options = SelfieSegmenterOptions() options.segmenterMode = .singleImage options.shouldEnableRawSizeMask = true
MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init]; options.segmenterMode = MLKSegmenterModeSingleImage; options.shouldEnableRawSizeMask = YES;
أخيرًا، احصل على مثيل من Segmenter
. نقْل الخيارات التي حدّدتها:
let segmenter = Segmenter.segmenter(options: options)
MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];
2. تجهيز صورة الإدخال
لتقسيم صورك الذاتية، اتّبِع الخطوات التالية لكل صورة أو لقطة فيديو.
إذا فعّلت وضع البث، عليك إنشاء عناصر VisionImage
من
CMSampleBuffer
.
أنشئ عنصر VisionImage
باستخدام UIImage
أو
CMSampleBuffer
.
إذا كنت تستخدم UIImage
، اتّبِع الخطوات التالية:
- أنشئ عنصر
VisionImage
باستخدامUIImage
. احرص على تحديد.orientation
الصحيح.let image = VisionImage(image: UIImage) visionImage.orientation = image.imageOrientation
MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image]; visionImage.orientation = image.imageOrientation;
إذا كنت تستخدم CMSampleBuffer
، اتّبِع الخطوات التالية:
-
حدِّد اتجاه بيانات الصورة الواردة في العنصر
CMSampleBuffer
.للحصول على اتجاه الصورة:
func imageOrientation( deviceOrientation: UIDeviceOrientation, cameraPosition: AVCaptureDevice.Position ) -> UIImage.Orientation { switch deviceOrientation { case .portrait: return cameraPosition == .front ? .leftMirrored : .right case .landscapeLeft: return cameraPosition == .front ? .downMirrored : .up case .portraitUpsideDown: return cameraPosition == .front ? .rightMirrored : .left case .landscapeRight: return cameraPosition == .front ? .upMirrored : .down case .faceDown, .faceUp, .unknown: return .up } }
- (UIImageOrientation) imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation cameraPosition:(AVCaptureDevicePosition)cameraPosition { switch (deviceOrientation) { case UIDeviceOrientationPortrait: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored : UIImageOrientationRight; case UIDeviceOrientationLandscapeLeft: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored : UIImageOrientationUp; case UIDeviceOrientationPortraitUpsideDown: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored : UIImageOrientationLeft; case UIDeviceOrientationLandscapeRight: return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored : UIImageOrientationDown; case UIDeviceOrientationUnknown: case UIDeviceOrientationFaceUp: case UIDeviceOrientationFaceDown: return UIImageOrientationUp; } }
- أنشئ كائن
VisionImage
باستخدام كائنCMSampleBuffer
واتجاهه:let image = VisionImage(buffer: sampleBuffer) image.orientation = imageOrientation( deviceOrientation: UIDevice.current.orientation, cameraPosition: cameraPosition)
MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer]; image.orientation = [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation cameraPosition:cameraPosition];
3- معالجة الصورة
نقْل عنصر VisionImage
إلى إحدى طرق معالجة الصور في Segmenter
. يمكنك استخدام الطريقة غير المتزامنة process(image:)
أو الطريقة المتزامنة results(in:)
.
لإجراء عملية تقسيم على صورة سيلفي بشكل متزامن:
var mask: [SegmentationMask] do { mask = try segmenter.results(in: image) } catch let error { print("Failed to perform segmentation with error: \(error.localizedDescription).") return } // Success. Get a segmentation mask here.
NSError *error; MLKSegmentationMask *mask = [segmenter resultsInImage:image error:&error]; if (error != nil) { // Error. return; } // Success. Get a segmentation mask here.
لتنفيذ عملية تقسيم على صورة سيلفي بشكل غير متزامن:
segmenter.process(image) { mask, error in guard error == nil else { // Error. return } // Success. Get a segmentation mask here.
[segmenter processImage:image completion:^(MLKSegmentationMask * _Nullable mask, NSError * _Nullable error) { if (error != nil) { // Error. return; } // Success. Get a segmentation mask here. }];
4. الحصول على قناع التقسيم
يمكنك الحصول على نتيجة التقسيم على النحو التالي:
let maskWidth = CVPixelBufferGetWidth(mask.buffer) let maskHeight = CVPixelBufferGetHeight(mask.buffer) CVPixelBufferLockBaseAddress(mask.buffer, CVPixelBufferLockFlags.readOnly) let maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer) var maskAddress = CVPixelBufferGetBaseAddress(mask.buffer)!.bindMemory( to: Float32.self, capacity: maskBytesPerRow * maskHeight) for _ in 0...(maskHeight - 1) { for col in 0...(maskWidth - 1) { // Gets the confidence of the pixel in the mask being in the foreground. let foregroundConfidence: Float32 = maskAddress[col] } maskAddress += maskBytesPerRow / MemoryLayout<Float32>.size }
size_t width = CVPixelBufferGetWidth(mask.buffer); size_t height = CVPixelBufferGetHeight(mask.buffer); CVPixelBufferLockBaseAddress(mask.buffer, kCVPixelBufferLock_ReadOnly); size_t maskBytesPerRow = CVPixelBufferGetBytesPerRow(mask.buffer); float *maskAddress = (float *)CVPixelBufferGetBaseAddress(mask.buffer); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { // Gets the confidence of the pixel in the mask being in the foreground. float foregroundConfidence = maskAddress[col]; } maskAddress += maskBytesPerRow / sizeof(float); }
للحصول على مثال كامل على كيفية استخدام نتائج التجزئة، يُرجى الاطّلاع على نموذج البدء السريع لمجموعة ML Kit.
نصائح لتحسين الأداء
تعتمد جودة النتائج على جودة الصورة المُدخلة:
- لكي تحصل حزمة ML Kit على نتيجة تقسيم دقيقة، يجب أن تكون الصورة بدقة 256×256 بكسل على الأقل.
- إذا كنت تُجري عملية تقسيم للصور الذاتية في تطبيق يعمل في الوقت الفعلي، ننصحك أيضًا بالتفكير في الأبعاد العامة للصور المُدخلة. يمكن معالجة الصور الأصغر حجمًا بشكل أسرع، لذا لتقليل وقت الاستجابة، يمكنك التقاط الصور بدقة أقل، ولكن يجب مراعاة متطلبات الدقة المذكورة أعلاه والتأكّد من أنّ الهدف يشغل أكبر قدر ممكن من الصورة.
- يمكن أن يؤثر أيضًا عدم تركيز الصورة في الدقة. إذا لم تحصل على نتائج مقبولة، اطلب من المستخدم إعادة التقاط الصورة.
إذا كنت تريد استخدام التقسيم في تطبيق يعمل في الوقت الفعلي، اتّبِع الإرشادات التالية لتحقيق أفضل معدّلات عرض اللقطات:
- استخدِم وضع
stream
لإنشاء الشرائح. - ننصحك بالتقاط الصور بدرجة دقة أقل. ومع ذلك، يجب أيضًا مراعاة متطلبات أبعاد الصورة في واجهة برمجة التطبيقات هذه.
- لمعالجة لقطات الفيديو، استخدِم واجهة برمجة التطبيقات
results(in:)
المتزامنة لفاصل الفيديو. يمكنك استدعاء هذه الطريقة من الدالة captureOutput(_, didOutput:from:) في AVCaptureVideoDataOutputSampleBufferDelegate للحصول على النتائج بشكل متزامن من إطار الفيديو المحدَّد. اضبط القيمة alwaysDiscardsLateVideoFrames في AVCaptureVideoDataOutput على true لتقليل عدد عمليات طلب البيانات إلى أداة تقسيم الفيديو. إذا أصبح إطار فيديو جديد متاحًا أثناء تشغيل أداة تقسيم الفيديوهات، سيتم تجاهله. - إذا كنت تستخدم مخرجات أداة التقسيم لوضع الرسومات على صورة الإدخال، يمكنك أولاً الحصول على النتيجة من ML Kit، ثم عرض الصورة والعنصر المتراكب في خطوة واحدة. وبذلك، يتم عرض المحتوى على سطح العرض مرة واحدة فقط لكل إطار إدخال تمت معالجته. يمكنك الاطّلاع على فئتَي previewOverlayView وCameraViewController في نموذج البدء السريع لمجموعة أدوات تعلُّم الآلة للحصول على مثال.