iOS相机:手动曝光时间但自动ISO?

9

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

setExposureModeCustomWithDuration:ISO:completionHandler:

但这需要手动设置两个值。是否有一种方法或聪明的技巧,可以允许您手动设置曝光时间,但使ISO自行处理以尝试正确曝光图像?

3个回答

7

我不确定这个解决方案是否是最好的,因为我和你一样也遇到了困难。我的做法是监听曝光偏移量的变化,并根据它们调整ISO值,直到达到可接受的曝光水平。大部分代码都来自于苹果的示例代码。

首先,你需要监听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之间闪烁。你��没有找到一组能够平稳运行的数值? - Joe
你能否评论一下你使用了苹果的哪个示例项目? - isaac
@isaac 抱歉,我应该一开始就包含它的。感谢你指出来。请检查这个链接:https://developer.apple.com/library/ios/samplecode/AVCamManual/Introduction/Intro.html#//apple_ref/doc/uid/TP40014578-Intro-DontLinkElementID_2 - khose
1
希望能够得到一个Swift的示例。 - Brandon A
1
你们修好了快速闪烁的问题了吗? - thelearner
显示剩余2条评论

1

这是由@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: &exposureTargetOffsetContext)


它能运行,但屏幕一直在闪烁。如何修复? - jdleung
@jdleung 尽量使用自然光(窗户或户外)。如果曝光时间足够短,人工光可能会导致闪烁。 - Volodymyr Kulyk
是的,在更明亮的场景下,这会提供更好的体验。将 biasISO 更改为较小的值可以使亮度过渡更加平滑,但也需要更长的时间才能达到准确的 ISO。是否还有其他更好的解决方案?谢谢。 - jdleung
@jdleung 更好的解决方案是自己分析每个帧的亮度,并考虑设置ISO。我们花费了很多时间在C++上制作这样的算法。 - Volodymyr Kulyk
明白了。目前没有官方的API解决方案,而且自己去做也是一项艰巨的工作。我决定让用户将曝光设置为自动或单独调整持续时间和ISO。谢谢! - jdleung

1
如果光照条件发生巨大变化,接受的答案需要很长时间才能加速/减速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 没有被调用。 - jdleung

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接