การแบ่งกลุ่มเซลฟีด้วย ML Kit บน iOS

ML Kit มี SDK ที่เพิ่มประสิทธิภาพสำหรับการแบ่งกลุ่มภาพเซลฟี ชิ้นงานของ Selfie Segmenter จะลิงก์แบบคงที่กับแอปของคุณในเวลาที่สร้าง การดำเนินการนี้จะเพิ่มขนาดแอปได้สูงสุด 24 MB และเวลาในการตอบสนองของ API อาจแตกต่างกันตั้งแต่ประมาณ 7 มิลลิวินาทีถึง 12 มิลลิวินาที ขึ้นอยู่กับขนาดรูปภาพอินพุต ตามที่วัดใน iPhone X

ลองเลย

ก่อนเริ่มต้น

  1. ใส่ไลบรารี ML Kit ต่อไปนี้ใน Podfile

    pod 'GoogleMLKit/SegmentationSelfie', '8.0.0'
    
  2. หลังจากติดตั้งหรืออัปเดต Pod ของโปรเจ็กต์แล้ว ให้เปิดโปรเจ็กต์ Xcode โดยใช้ .xcworkspace ML Kit รองรับใน Xcode เวอร์ชัน 13.2.1 ขึ้นไป

1. สร้างอินสแตนซ์ของ Segmenter

หากต้องการทำการแบ่งกลุ่มในรูปภาพเซลฟี ให้สร้างอินสแตนซ์ของ Segmenter ด้วย SelfieSegmenterOptions ก่อน แล้วระบุการตั้งค่าการแบ่งกลุ่ม (ไม่บังคับ)

ตัวเลือก Segmenter

โหมด Segmenter

Segmenter ทำงานได้ 2 โหมด โปรดเลือกตัวเลือกที่ตรงกับกรณีการใช้งานของคุณ

STREAM_MODE (default)

โหมดนี้ออกแบบมาสำหรับการสตรีมเฟรมจากวิดีโอหรือกล้อง ในโหมดนี้ ตัวแบ่งส่วนจะใช้ประโยชน์จากผลลัพธ์จากเฟรมก่อนหน้าเพื่อแสดงผลการแบ่งส่วนที่ราบรื่นยิ่งขึ้น

SINGLE_IMAGE_MODE (default)

โหมดนี้ออกแบบมาสำหรับรูปภาพเดี่ยวที่ไม่เกี่ยวข้อง ในโหมดนี้ ตัวแบ่งส่วนจะประมวลผลแต่ละรูปภาพแยกกันโดยไม่มีการปรับให้เรียบในเฟรม

เปิดใช้มาสก์ขนาดดิบ

ขอให้ตัวแบ่งส่วนแสดงมาสก์ขนาดดิบที่ตรงกับขนาดเอาต์พุตของโมเดล

โดยปกติแล้วขนาดมาสก์ดิบ (เช่น 256x256) จะเล็กกว่าขนาดรูปภาพอินพุต

หากไม่ได้ระบุตัวเลือกนี้ ตัวแบ่งส่วนจะปรับขนาดมาสก์ดิบให้ตรงกับขนาดรูปภาพอินพุต ลองใช้ตัวเลือกนี้หากต้องการใช้ตรรกะการปรับขนาดที่กําหนดเอง หรือหากกรณีการใช้งานของคุณไม่จําเป็นต้องมีการปรับขนาด

ระบุตัวเลือก Segmenter ดังนี้

Swift

let options = SelfieSegmenterOptions()
options.segmenterMode = .singleImage
options.shouldEnableRawSizeMask = true

Objective-C

MLKSelfieSegmenterOptions *options = [[MLKSelfieSegmenterOptions alloc] init];
options.segmenterMode = MLKSegmenterModeSingleImage;
options.shouldEnableRawSizeMask = YES;

สุดท้าย ให้รับอินสแตนซ์ของ Segmenter ส่งตัวเลือกที่คุณระบุ

Swift

let segmenter = Segmenter.segmenter(options: options)

Objective-C

MLKSegmenter *segmenter = [MLKSegmenter segmenterWithOptions:options];

2. เตรียมรูปภาพอินพุต

หากต้องการแบ่งกลุ่มเซลฟี ให้ทำดังนี้สำหรับรูปภาพหรือเฟรมวิดีโอแต่ละรายการ หากเปิดใช้โหมดสตรีม คุณต้องสร้างออบเจ็กต์ VisionImage จาก CMSampleBuffer

สร้างออบเจ็กต์ VisionImage โดยใช้ UIImage หรือ CMSampleBuffer

หากใช้ UIImage ให้ทำตามขั้นตอนต่อไปนี้

  • สร้างออบเจ็กต์ VisionImage ด้วย UIImage อย่าลืมระบุ .orientation ที่ถูกต้อง

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

หากใช้ CMSampleBuffer ให้ทำตามขั้นตอนต่อไปนี้

  • ระบุการวางแนวของข้อมูลรูปภาพที่มีอยู่ใน CMSampleBuffer

    วิธีดูการวางแนวรูปภาพ

    Swift

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

    Objective-C

    - (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 และการวางแนวต่อไปนี้

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Objective-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. ประมวลผลรูปภาพ

ส่งออบเจ็กต์ VisionImage ไปยังเมธอดการประมวลผลรูปภาพของ Segmenter รายการใดรายการหนึ่ง คุณจะใช้วิธีprocess(image:)แบบไม่พร้อมกันหรือวิธีresults(in:)แบบพร้อมกันก็ได้

วิธีทำการแบ่งกลุ่มในรูปภาพเซลฟีแบบพร้อมกัน

Swift

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.

Objective-C

NSError *error;
MLKSegmentationMask *mask =
    [segmenter resultsInImage:image error:&error];
if (error != nil) {
  // Error.
  return;
}

// Success. Get a segmentation mask here.

หากต้องการทำการแบ่งกลุ่มในรูปเซลฟีแบบไม่พร้อมกัน ให้ทำดังนี้

Swift

segmenter.process(image) { mask, error in
  guard error == nil else {
    // Error.
    return
  }
  // Success. Get a segmentation mask here.

Objective-C

[segmenter processImage:image
             completion:^(MLKSegmentationMask * _Nullable mask,
                          NSError * _Nullable error) {
               if (error != nil) {
                 // Error.
                 return;
               }
               // Success. Get a segmentation mask here.
             }];

4. รับมาสก์การแบ่งกลุ่ม

คุณจะได้รับผลการแบ่งกลุ่มดังนี้

Swift

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
}

Objective-C

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 อย่างรวดเร็ว

เคล็ดลับในการปรับปรุงประสิทธิภาพ

คุณภาพของผลลัพธ์ขึ้นอยู่กับคุณภาพของรูปภาพอินพุต

  • รูปภาพควรมีขนาดอย่างน้อย 256x256 พิกเซลเพื่อให้ ML Kit ได้ผลการแบ่งกลุ่มที่แม่นยำ
  • หากทำการแบ่งกลุ่มเซลฟีในแอปพลิเคชันแบบเรียลไทม์ คุณอาจต้องพิจารณาขนาดโดยรวมของรูปภาพอินพุตด้วย ระบบจะประมวลผลรูปภาพขนาดเล็กได้เร็วกว่า ดังนั้นหากต้องการลดเวลาในการตอบสนอง ให้ถ่ายรูปภาพที่ความละเอียดต่ำกว่า แต่โปรดทราบข้อกำหนดด้านความละเอียดข้างต้นและตรวจสอบว่าวัตถุในภาพมีขนาดใหญ่ที่สุดเท่าที่จะเป็นไปได้
  • โฟกัสของรูปภาพที่ไม่ดีอาจส่งผลต่อความถูกต้องได้เช่นกัน หากไม่ได้รับผลลัพธ์ที่ยอมรับได้ ให้ขอให้ผู้ใช้ถ่ายภาพใหม่

หากต้องการใช้การแบ่งกลุ่มในแอปพลิเคชันแบบเรียลไทม์ ให้ทำตามหลักเกณฑ์ต่อไปนี้เพื่อให้ได้อัตราเฟรมที่ดีที่สุด

  • ใช้โหมดเครื่องมือแบ่งกลุ่ม stream
  • ลองถ่ายภาพที่ความละเอียดต่ำลง อย่างไรก็ตาม โปรดคำนึงถึงข้อกำหนดด้านขนาดรูปภาพของ API นี้ด้วย
  • หากต้องการประมวลผลเฟรมวิดีโอ ให้ใช้ API แบบซิงโครนัส results(in:) ของเครื่องแบ่งส่วน เรียกใช้เมธอดนี้จากฟังก์ชัน captureOutput(_, didOutput:from:) ของ AVCaptureVideoDataOutputSampleBufferDelegate เพื่อรับผลลัพธ์จากเฟรมวิดีโอที่ระบุแบบพร้อมกัน ตั้งค่า alwaysDiscardsLateVideoFrames ของ AVCaptureVideoDataOutput เป็นจริงเพื่อจำกัดการเรียกใช้ตัวแบ่งส่วน หากมีวิดีโอเฟรมใหม่ขณะที่ตัวแบ่งส่วนทำงานอยู่ ระบบจะทิ้งเฟรมนั้น
  • หากใช้เอาต์พุตของเครื่องแบ่งส่วนเพื่อซ้อนทับกราฟิกบนรูปภาพอินพุต ให้รับผลลัพธ์จาก ML Kit ก่อน จากนั้นจึงแสดงรูปภาพและซ้อนทับในขั้นตอนเดียว การทำเช่นนี้จะทำให้คุณแสดงผลไปยังพื้นผิวการแสดงผลเพียงครั้งเดียวสำหรับแต่ละเฟรมอินพุตที่ประมวลผลแล้ว ดูตัวอย่างได้ในคลาส previewOverlayView และ CameraViewController ในตัวอย่างการเริ่มต้นอย่างรวดเร็วของ ML Kit