Как получить данные о глубине с камеры в iOS 11 и Swift 4?
Я пытаюсь получить данные о глубине с камеры в iOS 11 с помощью AVDepthData, хотя при настройке вывода фото с помощью AVCapturePhotoCaptureDelegate photo.depthData равен нулю.
Поэтому я попытался настроить AVCaptureDepthDataOutputDelegate с AVCaptureDepthDataOutput, хотя я не знаю, как сделать снимок глубины?
Кто-нибудь когда-нибудь получал изображение от AVDepthData?
Редактировать:
Вот код, который я попробовал:
// delegates: AVCapturePhotoCaptureDelegate & AVCaptureDepthDataOutputDelegate
@IBOutlet var image_view: UIImageView!
@IBOutlet var capture_button: UIButton!
var captureSession: AVCaptureSession?
var sessionOutput: AVCapturePhotoOutput?
var depthOutput: AVCaptureDepthDataOutput?
var previewLayer: AVCaptureVideoPreviewLayer?
@IBAction func capture(_ sender: Any) {
self.sessionOutput?.capturePhoto(with: AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg]), delegate: self)
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
self.previewLayer?.removeFromSuperlayer()
self.image_view.image = UIImage(data: photo.fileDataRepresentation()!)
let depth_map = photo.depthData?.depthDataMap
print("depth_map:", depth_map) // is nil
}
func depthDataOutput(_ output: AVCaptureDepthDataOutput, didOutput depthData: AVDepthData, timestamp: CMTime, connection: AVCaptureConnection) {
print("depth data") // never called
}
override func viewDidLoad() {
super.viewDidLoad()
self.captureSession = AVCaptureSession()
self.captureSession?.sessionPreset = .photo
self.sessionOutput = AVCapturePhotoOutput()
self.depthOutput = AVCaptureDepthDataOutput()
self.depthOutput?.setDelegate(self, callbackQueue: DispatchQueue(label: "depth queue"))
do {
let device = AVCaptureDevice.default(for: .video)
let input = try AVCaptureDeviceInput(device: device!)
if(self.captureSession?.canAddInput(input))!{
self.captureSession?.addInput(input)
if(self.captureSession?.canAddOutput(self.sessionOutput!))!{
self.captureSession?.addOutput(self.sessionOutput!)
if(self.captureSession?.canAddOutput(self.depthOutput!))!{
self.captureSession?.addOutput(self.depthOutput!)
self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession!)
self.previewLayer?.frame = self.image_view.bounds
self.previewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.previewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
self.image_view.layer.addSublayer(self.previewLayer!)
}
}
}
} catch {}
self.captureSession?.startRunning()
}
Я пробую две вещи: одну, где данные глубины равны нулю, и одну, где я пытаюсь вызвать метод делегата глубины.
Кто-нибудь знает, что мне не хватает?
4 ответа
Во-первых, вам нужно использовать двойную камеру, иначе вы не получите никаких данных о глубине.
let device = AVCaptureDevice.default(.builtInDualCamera, for: .video, position: .back)
И держи ссылку на свою очередь
let dataOutputQueue = DispatchQueue(label: "data queue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
Вы также, вероятно, захотите синхронизировать видео и данные о глубине.
var outputSynchronizer: AVCaptureDataOutputSynchronizer?
Затем вы можете синхронизировать два выхода в вашем методе viewDidLoad() следующим образом
if sessionOutput?.isDepthDataDeliverySupported {
sessionOutput?.isDepthDataDeliveryEnabled = true
depthDataOutput?.connection(with: .depthData)!.isEnabled = true
depthDataOutput?.isFilteringEnabled = true
outputSynchronizer = AVCaptureDataOutputSynchronizer(dataOutputs: [sessionOutput!, depthDataOutput!])
outputSynchronizer!.setDelegate(self, queue: self.dataOutputQueue)
}
Я бы порекомендовал посмотреть сеанс WWDC 507 - они также предоставляют полный пример приложения, которое делает именно то, что вы хотите.
Чтобы дать более подробную информацию в ответе @klinger, вот что вам нужно сделать, чтобы получить данные глубины для каждого пикселя, я написал несколько комментариев, надеюсь, это поможет!
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
//## Convert Disparity to Depth ##
let depthData = (photo.depthData as AVDepthData!).converting(toDepthDataType: kCVPixelFormatType_DepthFloat32)
let depthDataMap = depthData.depthDataMap //AVDepthData -> CVPixelBuffer
//## Data Analysis ##
// Useful data
let width = CVPixelBufferGetWidth(depthDataMap) //768 on an iPhone 7+
let height = CVPixelBufferGetHeight(depthDataMap) //576 on an iPhone 7+
CVPixelBufferLockBaseAddress(depthDataMap, CVPixelBufferLockFlags(rawValue: 0))
// Convert the base address to a safe pointer of the appropriate type
let floatBuffer = unsafeBitCast(CVPixelBufferGetBaseAddress(depthDataMap), to: UnsafeMutablePointer<Float32>.self)
// Read the data (returns value of type Float)
// Accessible values : (width-1) * (height-1) = 767 * 575
let distanceAtXYPoint = floatBuffer[Int(x * y)]
}
Есть два способа сделать это, и вы пытаетесь сделать оба одновременно:
- Захват данных о глубине вместе с изображением. Это делается с помощью
photo.depthData
объект изphotoOutput(_:didFinishProcessingPhoto:error:)
, Я объясню, почему это не работает для вас ниже. - Использовать
AVCaptureDepthDataOutput
и реализоватьdepthDataOutput(_:didOutput:timestamp:connection:)
, Я не уверен, почему это не сработало для вас, но реализацияdepthDataOutput(_:didOutput:timestamp:connection:)
может помочь вам понять, почему.
Я думаю, что #1 - лучший вариант, потому что он соединяет данные глубины с изображением. Вот как бы вы это сделали:
@IBAction func capture(_ sender: Any) {
let settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
settings.isDepthDataDeliveryEnabled = true
self.sessionOutput?.capturePhoto(with: settings, delegate: self)
}
// ...
override func viewDidLoad() {
// ...
self.sessionOutput = AVCapturePhotoOutput()
self.sessionOutput.isDepthDataDeliveryEnabled = true
// ...
}
Затем, depth_map
не должно быть nil
, Обязательно прочитайте и то, и это (отдельные, но похожие страницы) для получения дополнительной информации о получении данных глубины.
Для № 2, я не совсем уверен, почему depthDataOutput(_:didOutput:timestamp:connection:)
не вызывается, но вы должны реализовать depthDataOutput(_:didDrop:timestamp:connection:reason:)
чтобы увидеть, удаляются ли данные глубины по какой-то причине.
То, как вы запускаете устройство захвата, неверно.
Вы должны использовать режим двойной камеры.
что касается oc, то вроде:
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];