iOS 相机:手动曝光时间但自动 ISO?
iOS camera: manual exposure duration but auto ISO?
我正在使用相机视频源进行一些图像处理,并希望针对最快的快门速度进行优化。我知道您可以使用
手动设置曝光持续时间和 ISO
setExposureModeCustomWithDuration:ISO:completionHandler:
但这需要手动设置两个值。是否有一种方法或巧妙的技巧可以让您手动设置曝光持续时间,但让 ISO 自行处理以尝试正确曝光图像?
我不确定这个解决方案是否是最好的解决方案,因为我和你一样一直在努力解决这个问题。我所做的是倾听曝光偏移的变化,并根据它们调整 ISO,直到达到可接受的曝光水平。大部分代码取自 Apple 示例代码
因此,首先,您监听 ExposureTargetOffset 的变化。添加您的 class 声明:
static void *ExposureTargetOffsetContext = &ExposureTargetOffsetContext;
然后,在您正确完成设备设置后:
[self addObserver:self forKeyPath:@"captureDevice.exposureTargetOffset" options:NSKeyValueObservingOptionNew context:ExposureTargetOffsetContext];
(而不是 captureDevice,使用您的 属性 作为设备)
然后在你的 class 中实现 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 将进行调整以使图像不会曝光不足或曝光过度。
不要忘记在需要时移除观察者。希望这适合你。
干杯!
代码示例由 @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)
如果光照条件发生较大变化,接受的答案将需要很长时间才能提升 up/down ISO。这是一个根据曝光偏移量按比例更改 ISO 值的示例 (Swift 4)。
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()
}
我正在使用相机视频源进行一些图像处理,并希望针对最快的快门速度进行优化。我知道您可以使用
手动设置曝光持续时间和 ISOsetExposureModeCustomWithDuration:ISO:completionHandler:
但这需要手动设置两个值。是否有一种方法或巧妙的技巧可以让您手动设置曝光持续时间,但让 ISO 自行处理以尝试正确曝光图像?
我不确定这个解决方案是否是最好的解决方案,因为我和你一样一直在努力解决这个问题。我所做的是倾听曝光偏移的变化,并根据它们调整 ISO,直到达到可接受的曝光水平。大部分代码取自 Apple 示例代码
因此,首先,您监听 ExposureTargetOffset 的变化。添加您的 class 声明:
static void *ExposureTargetOffsetContext = &ExposureTargetOffsetContext;
然后,在您正确完成设备设置后:
[self addObserver:self forKeyPath:@"captureDevice.exposureTargetOffset" options:NSKeyValueObservingOptionNew context:ExposureTargetOffsetContext];
(而不是 captureDevice,使用您的 属性 作为设备) 然后在你的 class 中实现 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 将进行调整以使图像不会曝光不足或曝光过度。
不要忘记在需要时移除观察者。希望这适合你。
干杯!
代码示例由 @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)
如果光照条件发生较大变化,接受的答案将需要很长时间才能提升 up/down ISO。这是一个根据曝光偏移量按比例更改 ISO 值的示例 (Swift 4)。
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()
}