Firebase Summit에서 발표된 모든 내용을 살펴보고 Firebase로 앱을 빠르게 개발하고 안심하고 앱을 실행하는 방법을 알아보세요. 자세히 알아보기

iOS에서 ML Kit로 객체 감지 및 추적

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.

ML Kit를 사용하여 비디오 프레임에서 개체를 감지하고 추적할 수 있습니다.

ML Kit 이미지를 전달하면 ML Kit는 각 이미지에 대해 최대 5개의 감지된 개체 목록과 이미지에서의 위치를 ​​반환합니다. 비디오 스트림에서 개체를 감지할 때 모든 개체에는 이미지에서 개체를 추적하는 데 사용할 수 있는 ID가 있습니다. 선택적으로 광범위한 범주 설명으로 개체에 레이블을 지정하는 대략적인 개체 분류를 활성화할 수도 있습니다.

시작하기 전에

  1. 아직 앱에 Firebase를 추가하지 않았다면 시작하기 가이드 의 단계에 따라 추가하세요.
  2. Podfile에 ML Kit 라이브러리 포함:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    프로젝트의 Pod를 설치하거나 업데이트한 후 .xcworkspace 를 사용하여 Xcode 프로젝트를 열어야 합니다.
  3. 앱에서 Firebase를 가져옵니다.

    빠른

    import Firebase

    오브젝티브-C

    @import Firebase;

1. 객체 감지기 구성

객체 감지 및 추적을 시작하려면 먼저 VisionObjectDetector 의 인스턴스를 만들고 선택적으로 기본값에서 변경하려는 감지기 설정을 지정합니다.

  1. VisionObjectDetectorOptions 개체를 사용하여 사용 사례에 대한 개체 감지기를 구성합니다. 다음 설정을 변경할 수 있습니다.

    물체 감지기 설정
    감지 모드 .stream (기본값) | .singleImage

    스트림 모드(기본값)에서 객체 감지기는 매우 짧은 대기 시간으로 실행되지만 감지기의 처음 몇 번 호출에서 불완전한 결과(예: 지정되지 않은 경계 상자 또는 범주)를 생성할 수 있습니다. 또한 스트림 모드에서 감지기는 개체에 추적 ID를 할당하여 프레임 전체에서 개체를 추적하는 데 사용할 수 있습니다. 실시간으로 비디오 스트림을 처리할 때와 같이 개체를 추적하거나 짧은 대기 시간이 중요한 경우 이 모드를 사용합니다.

    단일 이미지 모드에서 객체 감지기는 결과를 반환하기 전에 감지된 객체의 경계 상자와 (분류를 활성화한 경우) 범주를 사용할 수 있을 때까지 기다립니다. 결과적으로 탐지 대기 시간이 더 길어질 수 있습니다. 또한 단일 이미지 모드에서는 추적 ID가 할당되지 않습니다. 대기 시간이 중요하지 않고 부분적인 결과를 처리하고 싶지 않은 경우 이 모드를 사용하십시오.

    여러 개체 감지 및 추적 false (기본값) | true

    최대 5개의 개체 또는 가장 눈에 띄는 개체(기본값)만 감지하고 추적할지 여부입니다.

    개체 분류 false (기본값) | true

    감지된 개체를 대략적인 범주로 분류할지 여부입니다. 활성화되면 객체 감지기가 객체를 패션 상품, 식품, 가정용품, 장소, 식물 및 알려지지 않은 범주로 분류합니다.

    객체 감지 및 추적 API는 다음 두 가지 핵심 사용 사례에 최적화되어 있습니다.

    • 카메라 뷰파인더에서 가장 눈에 띄는 물체의 실시간 감지 및 추적
    • 정적 이미지에서 여러 물체 감지

    이러한 사용 사례에 대해 API를 구성하려면:

    빠른

    // Live detection and tracking
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .stream
    options.shouldEnableMultipleObjects = false
    options.shouldEnableClassification = true  // Optional
    
    // Multiple object detection in static images
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .singleImage
    options.shouldEnableMultipleObjects = true
    options.shouldEnableClassification = true  // Optional
    

    오브젝티브-C

    // Live detection and tracking
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeStream;
    options.shouldEnableMultipleObjects = NO;
    options.shouldEnableClassification = YES;  // Optional
    
    // Multiple object detection in static images
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeSingleImage;
    options.shouldEnableMultipleObjects = YES;
    options.shouldEnableClassification = YES;  // Optional
    
  2. FirebaseVisionObjectDetector 의 인스턴스를 가져옵니다.

    빠른

    let objectDetector = Vision.vision().objectDetector()
    
    // Or, to change the default settings:
    let objectDetector = Vision.vision().objectDetector(options: options)
    

    오브젝티브-C

    FIRVisionObjectDetector *objectDetector = [[FIRVision vision] objectDetector];
    
    // Or, to change the default settings:
    FIRVisionObjectDetector *objectDetector = [[FIRVision vision] objectDetectorWithOptions:options];
    

2. 물체 감지기 실행

물체를 감지하고 추적하려면 비디오의 각 이미지 또는 프레임에 대해 다음을 수행하십시오. 스트림 모드를 활성화한 경우 CMSampleBufferRef 에서 VisionImage 객체를 생성해야 합니다.

  1. UIImage 또는 CMSampleBufferRef 를 사용하여 VisionImage 개체를 만듭니다.

    UIImage 를 사용하려면:

    1. 필요한 경우 imageOrientation 속성이 .up 이 되도록 이미지를 회전합니다.
    2. 올바르게 회전된 UIImage 를 사용하여 VisionImage 객체를 만듭니다. 회전 메타데이터를 지정하지 마십시오. 기본값인 .topLeft 를 사용해야 합니다.

      빠른

      let image = VisionImage(image: uiImage)

      오브젝티브-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

    CMSampleBufferRef 를 사용하려면:

    1. CMSampleBufferRef 버퍼에 포함된 이미지 데이터의 방향을 지정하는 VisionImageMetadata 개체를 만듭니다.

      이미지 방향을 얻으려면:

      빠른

      func imageOrientation(
          deviceOrientation: UIDeviceOrientation,
          cameraPosition: AVCaptureDevice.Position
          ) -> VisionDetectorImageOrientation {
          switch deviceOrientation {
          case .portrait:
              return cameraPosition == .front ? .leftTop : .rightTop
          case .landscapeLeft:
              return cameraPosition == .front ? .bottomLeft : .topLeft
          case .portraitUpsideDown:
              return cameraPosition == .front ? .rightBottom : .leftBottom
          case .landscapeRight:
              return cameraPosition == .front ? .topRight : .bottomRight
          case .faceDown, .faceUp, .unknown:
              return .leftTop
          }
      }

      오브젝티브-C

      - (FIRVisionDetectorImageOrientation)
          imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                                 cameraPosition:(AVCaptureDevicePosition)cameraPosition {
        switch (deviceOrientation) {
          case UIDeviceOrientationPortrait:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationLeftTop;
            } else {
              return FIRVisionDetectorImageOrientationRightTop;
            }
          case UIDeviceOrientationLandscapeLeft:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationBottomLeft;
            } else {
              return FIRVisionDetectorImageOrientationTopLeft;
            }
          case UIDeviceOrientationPortraitUpsideDown:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationRightBottom;
            } else {
              return FIRVisionDetectorImageOrientationLeftBottom;
            }
          case UIDeviceOrientationLandscapeRight:
            if (cameraPosition == AVCaptureDevicePositionFront) {
              return FIRVisionDetectorImageOrientationTopRight;
            } else {
              return FIRVisionDetectorImageOrientationBottomRight;
            }
          default:
            return FIRVisionDetectorImageOrientationTopLeft;
        }
      }

      그런 다음 메타데이터 개체를 만듭니다.

      빠른

      let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
      let metadata = VisionImageMetadata()
      metadata.orientation = imageOrientation(
          deviceOrientation: UIDevice.current.orientation,
          cameraPosition: cameraPosition
      )

      오브젝티브-C

      FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
      AVCaptureDevicePosition cameraPosition =
          AVCaptureDevicePositionBack;  // Set to the capture device you used.
      metadata.orientation =
          [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                       cameraPosition:cameraPosition];
    2. CMSampleBufferRef 개체와 회전 메타데이터를 사용하여 VisionImage 개체를 만듭니다.

      빠른

      let image = VisionImage(buffer: sampleBuffer)
      image.metadata = metadata

      오브젝티브-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. VisionImage 를 물체 감지기의 이미지 처리 방법 중 하나로 전달합니다. 비동기식 process(image:) 메서드 또는 동기식 results() 메서드를 사용할 수 있습니다.

    객체를 비동기적으로 감지하려면:

    빠른

    objectDetector.process(image) { detectedObjects, error in
      guard error == nil else {
        // Error.
        return
      }
      guard let detectedObjects = detectedObjects, !detectedObjects.isEmpty else {
        // No objects detected.
        return
      }
    
      // Success. Get object info here.
      // ...
    }
    

    오브젝티브-C

    [objectDetector processImage:image
                      completion:^(NSArray<FIRVisionObject *> * _Nullable objects,
                                   NSError * _Nullable error) {
                        if (error == nil) {
                          return;
                        }
                        if (objects == nil | objects.count == 0) {
                          // No objects detected.
                          return;
                        }
    
                        // Success. Get object info here.
                        // ...
                      }];
    

    객체를 동기적으로 감지하려면:

    빠른

    var results: [VisionObject]? = nil
    do {
      results = try objectDetector.results(in: image)
    } catch let error {
      print("Failed to detect object with error: \(error.localizedDescription).")
      return
    }
    guard let detectedObjects = results, !detectedObjects.isEmpty else {
      print("Object detector returned no results.")
      return
    }
    
    // ...
    

    오브젝티브-C

    NSError *error;
    NSArray<FIRVisionObject *> *objects = [objectDetector resultsInImage:image
                                                                   error:&error];
    if (error == nil) {
      return;
    }
    if (objects == nil | objects.count == 0) {
      // No objects detected.
      return;
    }
    
    // Success. Get object info here.
    // ...
    
  3. 이미지 프로세서에 대한 호출이 성공하면 비동기 메서드를 호출했는지 동기 메서드를 호출했는지에 따라 VisionObject 의 목록을 완료 핸들러에 전달하거나 목록을 반환합니다.

    VisionObject 에는 다음 속성이 포함됩니다.

    frame 이미지에서 개체의 위치를 ​​나타내는 CGRect 입니다.
    trackingID 이미지에서 개체를 식별하는 정수입니다. 단일 이미지 모드에서 없음.
    classificationCategory 개체의 대략적인 범주입니다. 객체 감지기에 분류가 활성화되어 있지 않으면 항상 .unknown 입니다.
    confidence 개체 분류의 신뢰도 값입니다. 객체 감지기에 분류가 활성화되어 있지 않거나 객체가 알 수 없음으로 분류된 경우 이는 nil 입니다.

    빠른

    // detectedObjects contains one item if multiple object detection wasn't enabled.
    for obj in detectedObjects {
      let bounds = obj.frame
      let id = obj.trackingID
    
      // If classification was enabled:
      let category = obj.classificationCategory
      let confidence = obj.confidence
    }
    

    오브젝티브-C

    // The list of detected objects contains one item if multiple
    // object detection wasn't enabled.
    for (FIRVisionObject *obj in objects) {
      CGRect bounds = obj.frame;
      if (obj.trackingID) {
        NSInteger id = obj.trackingID.integerValue;
      }
    
      // If classification was enabled:
      FIRVisionObjectCategory category = obj.classificationCategory;
      float confidence = obj.confidence.floatValue;
    }
    

사용성 및 성능 향상

최상의 사용자 경험을 위해 앱에서 다음 지침을 따르세요.

  • 성공적인 물체 감지는 물체의 시각적 복잡성에 달려 있습니다. 시각적 특징이 적은 개체는 감지할 이미지의 더 많은 부분을 차지해야 할 수 있습니다. 감지하려는 개체의 종류와 잘 작동하는 입력 캡처에 대한 지침을 사용자에게 제공해야 합니다.
  • 분류를 사용할 때 지원되는 범주에 완전히 속하지 않는 개체를 감지하려면 알 수 없는 개체에 대한 특수 처리를 구현합니다.

또한 [ML Kit 머티리얼 디자인 쇼케이스 앱][showcase-link]{: .external } 및 머신 러닝 기반 기능 컬렉션을 위한 머티리얼 디자인 패턴을 확인하세요.

실시간 애플리케이션에서 스트리밍 모드를 사용할 때 최상의 프레임 속도를 얻으려면 다음 지침을 따르십시오.

  • 대부분의 장치는 적절한 프레임 속도를 생성할 수 없으므로 스트리밍 모드에서 다중 개체 감지를 사용하지 마십시오.

  • 필요하지 않은 경우 분류를 비활성화합니다.

  • 감지기에 대한 호출을 조절합니다. 감지기가 실행되는 동안 새 비디오 프레임을 사용할 수 있게 되면 프레임을 삭제합니다.
  • 감지기의 출력을 사용하여 입력 이미지에 그래픽을 오버레이하는 경우 먼저 ML Kit에서 결과를 가져온 다음 단일 단계로 이미지와 오버레이를 렌더링합니다. 이렇게 하면 각 입력 프레임에 대해 한 번만 디스플레이 표면에 렌더링됩니다. 예제는 쇼케이스 샘플 앱의 previewOverlayViewFIRDetectionOverlayView 클래스를 참조하세요.