Как я могу получить данные калибровки камеры на iOS? aka AVCameraCalibrationData

Насколько я понимаю, AVCameraCalibrationData доступна только через AVCaptureDepthDataOutput. Это верно?

С другой стороны, AVCaptureDepthDataOutput доступен только с передней камеры iPhone X или задней камеры iPhone Plus, или я ошибаюсь?

То, что я пытаюсь сделать, это получить поле зрения AVCaptureVideoDataOutput SampleBuffer. Особенно это должно соответствовать выбранной предустановке (Full HD, Фото и т. Д.).

5 ответов

Решение

Ты можешь получить AVCameraCalibrationData только из данных о глубине или фото.

Однако, если все, что вам нужно, это FOV, вам нужна только часть информации, которую предлагает класс - матрица встроенных функций камеры - и вы можете получить ее самостоятельно из AVCaptureVideoDataOutput,

  1. Задавать cameraIntrinsicMatrixDeliveryEnabled на AVCaptureConnection подключение вашего устройства камеры к сеансу захвата. (Обратите внимание, вы должны проверить cameraIntrinsicMatrixDeliverySupported первый; не все форматы захвата поддерживают встроенные функции.)

  2. Когда видеовыход отправляет буферы семплов, проверьте вложения каждого буфера семплов на наличие kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix ключ. Как отмечено в CMSampleBuffer.h (кто-то должен подать радар о получении этой информации в онлайн-документации), значение для этого вложения является CFData кодирование matrix_float3x3 и (0,0) и (1,1) элементы этой матрицы - это горизонтальное и вертикальное фокусное расстояние в пикселях.

  3. Там нет третьего шага.

Предыстория: многие из этих ответов на переполнение стека ссылаются на внутренние данные, когда их спрашивают о калибровке камеры, включая принятый ответ для этого сообщения, но данные калибровки обычно включают внутренние данные, внешние данные, искажение объектива и т. Д. Все это перечислено здесь в Документация iOS. Автор упомянул, что они просто искали FOV, которое находится в буфере образца, а не в данных калибровки камеры. В конце концов, я думаю, что на его вопрос был дан ответ. НО, если вы нашли этот вопрос в поисках реальных данных калибровки камеры, это вас сбивает. И, как сказано в ответе, вы можете получить данные калибровки только при определенных условиях, о которых я подробнее расскажу ниже.

Прежде чем я отвечу на остальное, я бы просто сказал, что принятый ответ здесь великолепен, если вы ищете ТОЛЬКО внутреннюю матрицу, которую можно получить намного проще (т.е. не так жестко для среды), чем остальные эти значения с помощью подход, изложенный выше. Если вы используете это для компьютерного зрения, для чего я это использую, иногда это все, что нужно. Но для действительно крутых вещей вам понадобится все! Итак, я продолжу объяснять, как этого добиться:

Я предполагаю, что у вас есть общий код приложения камеры. В этом коде, когда делается снимок, вы, вероятно, собираетесь вызвать функцию 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.

В основном все, что указано в документации, на которую я ссылался выше.

Вот более полный / обновленный пример кода в 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 {
    // ...
}

Не ответ, но ...

Прошло три недели с тех пор, как я начал экспериментировать с кодом, чтобы создать плагин флаттера с поддержкой глубины, и это краткий обзор болезненных проб и ошибок, которые привели меня к работающему 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

ps Исторически сложилось так, что фронтальные камеры, как правило, имеют худшее качество по сравнению с их задними аналогами, но для захвата глубины вы не можете превзойти камеру 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

  }




У Apple на самом деле есть достойные инструкции по настройке:https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/capturing_photos_with_depth

Важное примечание, которое я не видел нигде, кроме документации Apple:

Для захвата карт глубины вам необходимо сначала выбрать устройство захвата builtInDualCamera или builtInTrueDepthCamera в качестве видеовхода сеанса. Даже если на устройстве iOS есть двойная камера или камера TrueDepth, выбор задней или передней камеры по умолчанию не включает захват глубины.

Другие вопросы по тегам