【问题标题】:iOS camera: manual exposure duration but auto ISO?iOS相机:手动曝光时间但自动ISO?
【发布时间】:2015-07-01 09:45:34
【问题描述】:

我正在使用相机视频源进行一些图像处理,并希望优化以获得最快的快门速度。我知道你可以手动设置曝光时间和 ISO 使用

setExposureModeCustomWithDuration:ISO:completionHandler:

但这需要手动设置这两个值。有没有一种方法或巧妙的技巧可以让您手动设置曝光持续时间但让 ISO 句柄本身尝试正确曝光图像?

【问题讨论】:

    标签: ios camera avcapturesession iso manual


    【解决方案1】:

    我不确定这个解决方案是否是最好的,因为我和你一样在这个问题上苦苦挣扎。我所做的是聆听曝光偏移的变化,并从中调整 ISO,直到达到可接受的曝光水平。此代码大部分取自 Apple 示例代码

    所以,首先,您要监听 ExposureTargetOffset 的变化。添加你的类声明:

    static void *ExposureTargetOffsetContext = &ExposureTargetOffsetContext;

    然后,一旦您正确完成了设备设置:

    [self addObserver:self forKeyPath:@"captureDevice.exposureTargetOffset" options:NSKeyValueObservingOptionNew context:ExposureTargetOffsetContext];

    (而不是 captureDevice,使用您的设备属性) 然后在你的类中实现 KVO 的回调:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{
    
    if (context == ExposureTargetOffsetContext){
            float newExposureTargetOffset = [change[NSKeyValueChangeNewKey] floatValue];
            NSLog(@"Offset is : %f",newExposureTargetOffset);
    
            if(!self.device) return;
    
            CGFloat currentISO = self.device.ISO;
            CGFloat biasISO = 0;
    
            //Assume 0,3 as our limit to correct the ISO
            if(newExposureTargetOffset > 0.3f) //decrease ISO
                biasISO = -50;
            else if(newExposureTargetOffset < -0.3f) //increase ISO
                biasISO = 50;
    
            if(biasISO){
                //Normalize ISO level for the current device
                CGFloat newISO = currentISO+biasISO;
                newISO = newISO > self.device.activeFormat.maxISO? self.device.activeFormat.maxISO : newISO;
                newISO = newISO < self.device.activeFormat.minISO? self.device.activeFormat.minISO : newISO;
    
                NSError *error = nil;
                if ([self.device lockForConfiguration:&error]) {
                    [self.device setExposureModeCustomWithDuration:AVCaptureExposureDurationCurrent ISO:newISO completionHandler:^(CMTime syncTime) {}];
                    [self.device unlockForConfiguration];
                }
            }
        }
    }
    

    使用此代码,快门速度将保持不变,并且将调整 ISO 以使图像不会过曝或曝光过度。

    不要忘记在需要时移除观察者。希望这适合你。

    干杯!

    【讨论】:

    • 非常感谢!我不得不去了解 KVO,但这可能是值得花的时间。这似乎在大多数情况下运行良好,尽管在某些照明情况下,我会在两个 ISO 之间快速来回闪烁。你有没有制定出一套适合你的价值观?
    • 您能评论一下您使用的是 Apple 的哪个示例项目吗?
    • @isaac 对不起,我一开始就应该包含它。感谢您指出了这一点。请检查这个link
    • 如果能在 swift 中找到一个例子会很好。
    • 快闪问题你们解决了吗?
    【解决方案2】:

    @khose 在 swift 上提供的代码示例:

    private var device: AVCaptureDevice?
    private var exposureTargetOffsetContext = 0

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if device == nil {
            return
        }
        if keyPath == "exposureTargetOffset" {
            let newExposureTargetOffset = change?[NSKeyValueChangeKey.newKey] as! Float
            print("Offset is : \(newExposureTargetOffset)")
    
            let currentISO = device?.iso
            var biasISO = 0
    
            //Assume 0,01 as our limit to correct the ISO
            if newExposureTargetOffset > 0.01 { //decrease ISO
                biasISO = -50
            } else if newExposureTargetOffset < -0.01 { //increase ISO
                biasISO = 50
            }
    
            if biasISO != Int(0) {
                //Normalize ISO level for the current device
                var newISO = currentISO! + Float(biasISO)
                newISO = newISO > (device?.activeFormat.maxISO)! ? (device?.activeFormat.maxISO)! : newISO
                newISO = newISO < (device?.activeFormat.minISO)! ? (device?.activeFormat.minISO)! : newISO
    
                try? device?.lockForConfiguration()
                device?.setExposureModeCustom(duration: AVCaptureDevice.currentExposureDuration, iso: newISO, completionHandler: nil)
                device?.unlockForConfiguration()
            }
        }
    }
    


    用法:
    device?.addObserver(self, forKeyPath: "exposureTargetOffset", options: NSKeyValueObservingOptions.new, context: &amp;exposureTargetOffsetContext)

    【讨论】:

    • 它可以工作,但屏幕一直闪烁。如何解决?
    • @jdleung 尝试使用自然光(窗户或室外)。如果曝光时间足够短,可能会导致人造光闪烁。
    • 是的,在更明亮的场景下它可以提供更好的体验。将biasISO更改为较小的值可以使亮度过渡更顺畅,但也需要更多时间才能达到准确的ISO。还有其他更好的解决方案吗?谢谢。
    • @jdleung 更好的解决方案是自己分析每一帧的亮度并考虑它设置ISO。我们花了很多时间在 C++ 上制作这样的算法。
    • 知道了。没有官方的 API 解决方案,我自己做这件事是一项艰巨的工作。我决定让用户将曝光设置为自动或分别调整持续时间和 ISO。谢谢!
    【解决方案3】:

    如果照明条件发生较大变化,则接受的答案将需要很长时间才能提高/降低 ISO。这是一个示例(Swift 4),它根据曝光偏移量的比例改变 ISO 的值​​。

    fileprivate var settingISO = false
    @objc func exposureTargetOffsetChanged(notification: Notification) {
        guard !settingISO, self.device.exposureMode == .custom, let exposureTargetOffset = notification.userInfo?["newValue"] as? Float else {
            return
        }
        var isoChange = Float(0.0)
        let limit = Float(0.05)
        let isoChangeStep: Float
        if abs(exposureTargetOffset) > 1 {
            isoChangeStep = 500
        } else if abs(exposureTargetOffset) > 0.5 {
            isoChangeStep = 200
        } else if abs(exposureTargetOffset) > 0.2 {
            isoChangeStep = 50
        } else if abs(exposureTargetOffset) > 0.1 {
            isoChangeStep = 20
        } else {
            isoChangeStep = 5
        }
    
        if exposureTargetOffset > limit {
            isoChange -= isoChangeStep
        } else if exposureTargetOffset < -limit {
            isoChange += isoChangeStep
        } else {
            return
        }
        var newiso = self.device.iso + isoChange
        newiso = max(self.device.activeFormat.minISO, newiso)
        newiso = min(self.device.activeFormat.maxISO, newiso)
    
        guard newiso != self.device.iso, (try? self.device.lockForConfiguration()) != nil else { return }
        self.settingISO = true
        Camera.log("exposureTargetOffset=\(exposureTargetOffset), isoChange=\(isoChange), newiso=\(newiso)")
    
        self.device.setExposureModeCustom(duration: self.customDuration ?? AVCaptureDevice.currentExposureDuration, iso: newiso) { (_) in
            self.settingISO = false
        }
        self.device.unlockForConfiguration()
    }
    

    【讨论】:

    • 我尝试这样实现:NotificationCenter.default.addObserver(self, selector:#selector(exposureTargetOffsetChanged), name: Notification.Name(rawValue: "exposureTargetOffset"), object: nil),但 exposureTargetOffsetChanged 没有被调用。
    猜你喜欢
    • 1970-01-01
    • 2012-05-06
    • 2016-04-08
    • 1970-01-01
    • 2017-06-18
    • 2018-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多