Как захват данных с камеры в iOS 11 и Swift 4?
Я пытаюсь получить данные глубины с камеры в iOS 11 С AVDepthData, tho, когда я настраиваю photoOutput с AVCapturePhotoCaptureDelegate фотографию.depthData равна нулю.
поэтому я попытался настроить AVCaptureDepthDataOutputDelegate с помощью AVCaptureDepthDataOutput, то я не знаю, как захватить фотографию глубины?
у кого-нибудь есть картинка из AVDepthData?
Edit:
вот код I попробовал:
// 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 answer, вот что вам нужно сделать, чтобы получить данные глубины для каждого пикселя, я написал несколько комментариев, Надеюсь, это поможет!
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:)
чтобы увидеть, если данные глубины отбрасываются по какой-то причине.
то, как вы init устройство захвата не правильно.
вы должны использовать режим двойной камеры.
что касается oc, как следует:
AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];