【问题标题】:How can I get Camera Calibration Data on iOS? aka AVCameraCalibrationData如何在 iOS 上获取相机校准数据?又名 AVCameraCalibrationData
【发布时间】:2018-06-14 01:55:04
【问题描述】:

据我了解,AVCameraCalibrationData 仅可通过 AVCaptureDepthDataOutput 获得。对吗?

另一方面,AVCaptureDepthDataOutput 只能通过 iPhone X 前置摄像头或 iPhone Plus 后置摄像头访问,还是我弄错了?

我要做的是获取 AVCaptureVideoDataOutput SampleBuffer 的 FOV。特别是,它应该匹配所选的预设(全高清、照片等)。

【问题讨论】:

    标签: ios computer-vision ios11 arkit avkit


    【解决方案1】:

    不是答案,但是...

    自从我开始使用代码制作具有深度功能的 Flutter 插件以来已经过去了三周,这是对带我去工作 PoC 的痛苦试验和错误的快速回顾:

    (我为代码质量道歉,这也是我第一次使用objective-c)

    • iOS 有大量的相机(硬件组合),只有一个子集支持深度数据。当您发现自己的设备时:
          AVCaptureDeviceDiscoverySession *discoverySession = [AVCaptureDeviceDiscoverySession
              discoverySessionWithDeviceTypes:deviceTypes
                                    mediaType:AVMediaTypeVideo
                                     position:AVCaptureDevicePositionUnspecified];
    

    您可以询问他们的深度能力:

    for (AVCaptureDevice *device in devices) {
            BOOL depthDataCapable;
            if (@available(iOS 11.0, *)) {
              AVCaptureDeviceFormat *activeDepthDataFormat = [device activeDepthDataFormat];
              depthDataCapable = (activeDepthDataFormat != nil);
              NSLog(@" -- %@ supports DepthData: %s", [device localizedName],
            } else {
              depthDataCapable = false;
            }
    }
    

    在 iPhone12 上:

     -- Front TrueDepth Camera supports DepthData: true
     -- Back Dual Wide Camera supports DepthData: true
     -- Back Ultra Wide Camera supports DepthData: false
     -- Back Camera supports DepthData: false
     -- Front Camera supports DepthData: false
    

    附言从历史上看,前置摄像头的质量往往低于后置摄像头,但在深度捕捉方面,您无法击败使用红外投影仪/扫描仪的 TrueDepth 摄像头。

    既然您知道哪些相机可以完成这项工作,您需要选择功能强大的相机并启用深度:

    (空行是代码省略,这不是一个完整的例子)

      // this is in your 'post-select-camera' initialization
      _captureSession = [[AVCaptureSession alloc] init];
      // cameraName is not the localizedName
      _captureDevice = [AVCaptureDevice deviceWithUniqueID:cameraName];
    
      // this is in your camera controller initialization
      // enable depth delivery in AVCapturePhotoOutput
      _capturePhotoOutput = [AVCapturePhotoOutput new];
      [_captureSession addOutput:_capturePhotoOutput];
    
      // BOOL depthDataSupported is a property of the controller
      _depthDataSupported = [_capturePhotoOutput isDepthDataDeliverySupported];
      if (_depthDataSupported) {
        [_capturePhotoOutput setDepthDataDeliveryEnabled:YES];
      }
      [_captureSession addOutput:_capturePhotoOutput];
    
      // this is in your capture method
      // enable depth delivery in AVCapturePhotoSettings
      AVCapturePhotoSettings *settings = [AVCapturePhotoSettings photoSettings];
      
      if (@available(iOS 11.0, *) && _depthDataSupported) {
        [settings setDepthDataDeliveryEnabled:YES];
      }
    
      // Here I use a try/catch because even depth capable and enabled cameras can crash if settings are not correct. 
      // For example a very high picture resolution seems to throw an exception, and this might be a different limit for different phone models.
      // I am sure this information is somewhere I haven't looked yet. 
      @try {
        [_capturePhotoOutput capturePhotoWithSettings:settings delegate:photoDelegate];
      } @catch (NSException *e) {
        [settings setDepthDataDeliveryEnabled:NO];
        [_capturePhotoOutput capturePhotoWithSettings:settings delegate:photoDelegate];
      }
    
      // after you took a photo and
      // didFinishProcessingPhoto:(AVCapturePhoto *)photo was invoked
    
    
      AVDepthData *depthData = [photo depthData];
      if (depthData != nil) {
        AVCameraCalibrationData *calibrationData = [depthData cameraCalibrationData];
        CGFloat pixelSize = [calibrationData pixelSize];
        matrix_float3x3 intrinsicMatrix = [calibrationData intrinsicMatrix];
        CGSize referenceDimensions = [calibrationData intrinsicMatrixReferenceDimensions];
        // now do what you need to do - I need to transform that to 16bit, Grayscale, Tiff, and it starts like this... 
    
        if (depthData.depthDataType != kCVPixelFormatType_DepthFloat16) {
          depthData = [depthData depthDataByConvertingToDepthDataType:kCVPixelFormatType_DepthFloat16];
        }
    
      // DON'T FORGET HIT LIKE AND SUBSCRIBE FOR MORE BAD CODE!!! :P
    
      }
    
    
    
    
    

    【讨论】:

      【解决方案2】:

      Apple 实际上在这里有一个不错的设置说明: https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/capturing_photos_with_depth

      除了 Apple 文档之外我在其他任何地方都没有看到的重要说明:

      要捕获深度图,您需要首先选择 builtInDualCamera 或 builtInTrueDepthCamera 捕获设备作为会话的视频输入。即使 iOS 设备具有双摄像头或 TrueDepth 摄像头,选择默认的后置或前置摄像头也不会启用深度捕捉。

      【讨论】:

        【解决方案3】:

        这是 swift 5 中更完整/更新的代码示例,它是从以前的答案中汇总而成的。这将为您提供 iphone 的相机内在矩阵。

        基于:

        // session setup
        captureSession = AVCaptureSession()
        
        let captureVideoDataOutput = AVCaptureVideoDataOutput()
        
        captureSession?.addOutput(captureVideoDataOutput)
        
        // enable the flag
        if #available(iOS 11.0, *) {
            captureVideoDataOutput.connection(with: .video)?.isCameraIntrinsicMatrixDeliveryEnabled = true
        } else {
            // ...
        }
        
        // `isCameraIntrinsicMatrixDeliveryEnabled` should be set before this
        captureSession?.startRunning()
        

        现在在AVCaptureVideoDataOutputSampleBufferDelegate.captureOutput(...)

        if #available(iOS 11.0, *) {
            if let camData = CMGetAttachment(sampleBuffer, key:kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, attachmentModeOut:nil) as? Data {
                let matrix: matrix_float3x3 = camData.withUnsafeBytes { $0.pointee }
                print(matrix)
                // > simd_float3x3(columns: (SIMD3<Float>(1599.8231, 0.0, 0.0), SIMD3<Float>(0.0, 1599.8231, 0.0), SIMD3<Float>(539.5, 959.5, 1.0)))
            }
        } else {
            // ...
        }
        

        【讨论】:

        • 在 iPhone SE 上,isCameraIntrinsicMatrixDeliverySupported 一直是假的
        【解决方案4】:

        背景:当被问及相机校准时,许多堆栈溢出响应都引用了内在数据,包括这篇文章的公认答案,但校准数据通常包括内在数据、外在数据、镜头失真等。所有这些都列出了here in the iOS documentation。作者提到他们只是在寻找 FOV,它在样本缓冲区中,而不是在相机校准数据中。所以最终,我认为他的问题得到了回答。但是,如果您发现这个问题正在寻找实际的相机校准数据,这会让您失望。就像答案说的那样,您只能在特定条件下获得校准数据,我将在下面详细介绍。

        在我回答其余部分之前,我只想说,如果您正在寻找的只是内在矩阵,那么这里接受的答案很好,它可以比其他这些更容易获得(即环境不那么严格)值through the approach outlined above。如果您将它用于计算机视觉,这就是我正在使用它的目的,有时这就是所需要的。但是对于非常酷的东西,你会想要它!所以我将继续解释如何达到这个目标:

        我假设您已准备好通用相机应用程序代码。在该代码中,当拍摄照片时,您可能会调用看起来像这样的 photoOutput 函数:

        func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {...
        

        输出参数将有一个值,您可以参考以查看是否支持相机校准,称为 isCameraCalibrationDataDeliverySupported,例如,要打印出来,请使用以下内容:

        print("isCameraCalibrationDataDeliverySupported: \(output.isCameraCalibrationDataDeliverySupported)")
        

        在我链接的文档中注意,它仅在特定场景下受支持:

        "这个属性的值只有在 isDualCameraDualPhotoDeliveryEnabled 属性为 true。启用 相机校准交付,设置 照片设置中的 isCameraCalibrationDataDeliveryEnabled 属性 对象。”

        所以这很重要,请注意这一点以避免不必要的压力。使用实际值进行调试并确保启用了正确的环境。

        一切就绪后,您应该从以下位置获取实际的相机校准数据:

        photo.cameraCalibrationData
        

        只需拉出该对象即可获取您要查找的特定值,例如:

        photo.cameraCalibrationData?.extrinsicMatrix
        photo.cameraCalibrationData?.intrinsicMatrix
        photo.cameraCalibrationData?.lensDistortionCenter
        etc.
        

        基本上我在上面链接到的文档中列出的所有内容。

        【讨论】:

        • 仅供参考,截至 iOS13 cameraCalibrationDataphoto 中始终为零;你必须从photo.depthData 得到它。文档已声明不推荐使用请求校准数据的属性;只要您请求深度数据,您就会得到校准数据。
        • 嗨@darda! cameraCalibrationData 和上述方法是否仅适用于 iPhone 的渲染相机?我正在使用前置摄像头进行测试,它似乎不起作用
        • 我没有获得cameraCalibrationData 的运气——在photo 中总是nilphoto.depthData 也不提供它。它声称它受支持并且我启用它,但没有提供任何东西。有没有人有这个工作的示例应用程序?我使用的是 iPhone 12 Pro。
        【解决方案5】:

        AVCameraCalibrationData只能从深度数据输出或照片输出中获取。

        但是,如果您只需要 FOV,您只需要该课程提供的部分信息 - camera intrinsics matrix - 您可以从 AVCaptureVideoDataOutput 自行获取。

        1. AVCaptureConnection 上设置cameraIntrinsicMatrixDeliveryEnabled,将您的相机设备连接到捕获会话。 (请注意,您应该先检查cameraIntrinsicMatrixDeliverySupported;并非所有捕获格式都支持内在函数。)

        2. 当视频输出提供样本缓冲区时,请检查每个样本缓冲区的附件中是否有 kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix 键。如CMSampleBuffer.h 中所述(有人应该file a radar 将此信息放入在线文档中),该附件的值是CFData 编码matrix_float3x3,以及 (0,0) 和 (1,1 ) 该矩阵的元素是以像素为单位的水平和垂直焦距。

        【讨论】:

        • 小更新 - 看起来 cameraIntrinsicMatrixDeliveryEnabled 已更改为 isCameraIntrinsicMatrixDeliveryEnabled。 cameraIntrinsicMatrixDeliverySupported 更改为 isCameraIntrinsicMatrixDeliverySupported。
        • 我发现这篇关于如何以 matrix_float3x3 形式访问该数据的帖子也很有帮助 - stackoverflow.com/questions/48565286/… 如果您只需要内在数据(而不是其他相机校准数据),那么结合这一点是理想的情况.
        • @Bourne 嘿,伯恩!我在这里阅读了您的帖子,“假设您拥有通用相机应用程序代码”是什么意思?我正在研究访问 ARCamera 参数。你有更多的建议吗? stackoverflow.com/questions/62867707/…
        • 有没有人有一个实际工作的示例应用程序?我运气不太好。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-10-15
        • 2011-04-05
        • 1970-01-01
        • 2011-05-13
        • 1970-01-01
        • 2015-09-24
        • 2013-01-28
        相关资源
        最近更新 更多