จะจับภาพความลึกจากกล้องใน iOS 11 และ Swift 4 ได้อย่างไร

ฉันกำลังพยายามรับข้อมูลเชิงลึกจากกล้องใน iOS 11 ด้วย AVDepthData เมื่อฉันตั้งค่า photoOutput ด้วย AVCapturePhotoCaptureDelegate photo. allowanceData จะเป็นศูนย์

ดังนั้นฉันจึงลองตั้งค่า 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()

}

ฉันกำลังลองสองสิ่ง อย่างแรกโดยที่ข้อมูลเชิงลึกเป็นศูนย์ และอีกอันที่ฉันพยายามเรียกวิธีการมอบหมายเชิงลึก

มีใครรู้บ้างว่าฉันขาดอะไรไป?


person Heestand XYZ    schedule 12.06.2017    source แหล่งที่มา
comment
คุณช่วยกรุณาระบุรหัสที่คุณลองแล้วได้ไหม?   -  person Coder-256    schedule 12.06.2017
comment
แน่นอน. ฉันกำลังลองสองสิ่งในสิ่งเดียว ฉันจะพยายามอธิบายเพิ่มเติมด้วยโค้ด   -  person Heestand XYZ    schedule 13.06.2017
comment
คุณใช้ iPhone 7 หรือไม่? ฉันคิดว่าคุณต้องมีกล้องคู่เพื่อที่จะได้ระยะชัดลึก   -  person Guig    schedule 13.06.2017
comment
ใช่มี iPhone 7 ที่มีกล้องคู่ แต่ AVDepthData ยังคงเป็นศูนย์   -  person Heestand XYZ    schedule 14.06.2017
comment
มีใครได้รับสิ่งนี้ไปทำงานไหม? @ Coder256 คุณใช้ฮาร์ดแวร์อะไร?   -  person eyeApps LLC    schedule 21.06.2017
comment
@eyeAppsLLC ฉันไม่สามารถทดสอบได้ ฉันแค่ดูเอกสาร สำหรับแผนที่เชิงลึก คุณจะต้องมี iPhone 7+ (หรืออาจเป็น iPhone 8 หรือ 7s+ คุณต้องมี โหมดแนวตั้ง ตามข้อมูลของ Apple) ที่ใช้ iOS 11 (ซึ่งขณะนี้อยู่ในรุ่นเบต้าสำหรับนักพัฒนาเท่านั้น) และฉันไม่ใช่สมาชิกของ Apple Developer Program ดังนั้นฉันจึงไม่สามารถดาวน์โหลดได้)   -  person Coder-256    schedule 21.06.2017


คำตอบ (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 - พวกเขายังมีแอปตัวอย่างเต็มรูปแบบที่ทำสิ่งที่คุณต้องการอย่างแน่นอน

https://developer.apple.com/videos/play/wwdc2017/507/< /ก>

person klinger    schedule 14.07.2017

มีสองวิธีในการทำเช่นนี้ และคุณกำลังพยายามทำทั้งสองอย่างพร้อมกัน:

  1. บันทึกความลึกพร้อมกับภาพ ซึ่งทำได้โดยใช้วัตถุ photo.depthData จาก photoOutput(_:didFinishProcessingPhoto:error:) ฉันอธิบายว่าทำไมสิ่งนี้ถึงไม่เหมาะกับคุณด้านล่าง
  2. ใช้ 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:) เพื่อดูว่าข้อมูลเชิงลึกถูกทิ้งด้วยเหตุผลบางประการหรือไม่

person Coder-256    schedule 14.06.2017
comment
ขอบคุณ! แม้ว่าฉันจะประสบปัญหาเมื่อตั้งค่า .isDepthDataDeliveryEnabled ให้เป็นจริง: [AVCapturePhotoOutput setDepthDataDeliveryEnabled:] Depth data delivery is not support in the current configuration' จากนั้นฉันก็อ่านลิงก์ทั้งสองที่คุณโพสต์แล้วลอง: self.sessionOutput.isDepthDataDeliverySupported แม้ว่าจะเป็นเท็จก็ตาม สำหรับ iPhone 7 Plus ของฉัน ไม่แน่ใจว่าทำไม บางทีนี่อาจเป็นผลิตภัณฑ์บางอย่างของ iPhone 8: macrumors.com/2017/06/14/apple-camera-lens-supplier-3d/ - person Heestand XYZ; 14.06.2017
comment
ดีใจที่สามารถช่วยได้! แม้ว่าปัญหาอาจเป็นไปได้ว่าคุณใช้ iOS 10 แทนที่จะเป็น iOS 11 บน iPhone ของคุณ - person Coder-256; 15.06.2017
comment
วิธีนี้ทำงานได้อย่างไร? สิ่งนี้ไม่จำเป็นต้องใช้โทรศัพท์ที่มีฮาร์ดแวร์พิเศษใช่ไหม - person eyeApps LLC; 21.06.2017

เพื่อให้รายละเอียดเพิ่มเติมแก่คำตอบของ @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)]

}
person Oscar Falmer    schedule 20.10.2017

วิธีที่คุณเริ่มต้นอุปกรณ์จับภาพของคุณไม่ถูกต้อง

คุณควรใช้โหมดกล้องคู่

สำหรับ oc ดังต่อไปนี้:

AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInDualCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
person 周景锦    schedule 26.06.2017