Как я могу получить данные калибровки камеры на iOS? aka AVCameraCalibrationData
Насколько я понимаю, AVCameraCalibrationData доступна только через AVCaptureDepthDataOutput. Это верно?
С другой стороны, AVCaptureDepthDataOutput доступен только с передней камеры iPhone X или задней камеры iPhone Plus, или я ошибаюсь?
То, что я пытаюсь сделать, это получить поле зрения AVCaptureVideoDataOutput SampleBuffer. Особенно это должно соответствовать выбранной предустановке (Full HD, Фото и т. Д.).
5 ответов
Ты можешь получить AVCameraCalibrationData
только из данных о глубине или фото.
Однако, если все, что вам нужно, это FOV, вам нужна только часть информации, которую предлагает класс - матрица встроенных функций камеры - и вы можете получить ее самостоятельно из AVCaptureVideoDataOutput
,
Задавать
cameraIntrinsicMatrixDeliveryEnabled
наAVCaptureConnection
подключение вашего устройства камеры к сеансу захвата. (Обратите внимание, вы должны проверитьcameraIntrinsicMatrixDeliverySupported
первый; не все форматы захвата поддерживают встроенные функции.)Когда видеовыход отправляет буферы семплов, проверьте вложения каждого буфера семплов на наличие
kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix
ключ. Как отмечено вCMSampleBuffer.h
(кто-то должен подать радар о получении этой информации в онлайн-документации), значение для этого вложения являетсяCFData
кодированиеmatrix_float3x3
и (0,0) и (1,1) элементы этой матрицы - это горизонтальное и вертикальное фокусное расстояние в пикселях.Там нет третьего шага.
Предыстория: многие из этих ответов на переполнение стека ссылаются на внутренние данные, когда их спрашивают о калибровке камеры, включая принятый ответ для этого сообщения, но данные калибровки обычно включают внутренние данные, внешние данные, искажение объектива и т. Д. Все это перечислено здесь в Документация 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.
на основе:
- /questions/6984003/kak-ya-mogu-poluchit-dannyie-kalibrovki-kameryi-na-ios-aka-avcameracalibrationdata/6984030#6984030
- /questions/21275834/swift-3-kak-poluchit-dostup-k-znacheniyu-matrixfloat3x3-v-48-bajtovyih-cfdata/21275840#21275840
// 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, выбор задней или передней камеры по умолчанию не включает захват глубины.