fix: Move minExposure/maxExposure from format into device (#2211)

* fix: Move `minExposure`/`maxExposure` into `device`

* Update docs

* chore: Remove unneeded dependency

* chore: Update code
This commit is contained in:
Marc Rousavy 2023-11-24 18:20:56 +01:00 committed by GitHub
parent bb3a42e6bc
commit cad5240420
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 25 additions and 36 deletions

View File

@ -12,7 +12,7 @@ To adjust the exposure of the Camera, you can use the Camera's [`exposure`](/doc
<Camera {...props} exposure={-1} /> <Camera {...props} exposure={-1} />
``` ```
Values for the `exposure` prop range from [`format.minExposure`](/docs/api/interfaces/CameraDeviceFormat#maxExposure) to [`format.maxExposure`](/docs/api/interfaces/CameraDeviceFormat#minExposure), inclusively. By default (`undefined`), it is set to neutral auto exposure. Values for the `exposure` prop range from [`device.minExposure`](/docs/api/interfaces/CameraDevice#minexposure) to [`device.maxExposure`](/docs/api/interfaces/CameraDevice#maxexposure), inclusively. By default (`undefined`), it is set to neutral auto exposure.
Instead of manually adjusting ISO and Exposure-Duration, this acts as an "exposure compensation bias", meaning the Camera will still continuously automatically adjust exposure as it goes, but premultiplies the given exposure value to it's ISO and Exposure Duration settings. Instead of manually adjusting ISO and Exposure-Duration, this acts as an "exposure compensation bias", meaning the Camera will still continuously automatically adjust exposure as it goes, but premultiplies the given exposure value to it's ISO and Exposure Duration settings.
@ -44,12 +44,12 @@ Just like [`zoom`](zooming), this property can be animated using Reanimated.
// 2. map slider to [minExposure, 0, maxExposure] // 2. map slider to [minExposure, 0, maxExposure]
const exposureValue = useDerivedValue(() => { const exposureValue = useDerivedValue(() => {
if (format == null) return 0 if (device == null) return 0
return interpolate(exposureSlider.value, return interpolate(exposureSlider.value,
[-1, 0, 1], [-1, 0, 1],
[format.minExposure, 0, format.maxExposure]) [device.minExposure, 0, device.maxExposure])
}, [exposureSlider, format]) }, [exposureSlider, device])
// 3. pass it as an animated prop // 3. pass it as an animated prop
const animatedProps = useAnimatedProps(() => ({ const animatedProps = useAnimatedProps(() => ({

View File

@ -177,8 +177,6 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, private val
map.putInt("videoWidth", videoSize.width) map.putInt("videoWidth", videoSize.width)
map.putInt("minISO", isoRange.lower) map.putInt("minISO", isoRange.lower)
map.putInt("maxISO", isoRange.upper) map.putInt("maxISO", isoRange.upper)
map.putDouble("minExposure", exposureRange.lower.toDouble() / exposureStep.toDouble())
map.putDouble("maxExposure", exposureRange.upper.toDouble() / exposureStep.toDouble())
map.putInt("minFps", fpsRange.lower) map.putInt("minFps", fpsRange.lower)
map.putInt("maxFps", fpsRange.upper) map.putInt("maxFps", fpsRange.upper)
map.putDouble("maxZoom", maxZoom) map.putDouble("maxZoom", maxZoom)
@ -209,6 +207,8 @@ class CameraDeviceDetails(private val cameraManager: CameraManager, private val
map.putDouble("minZoom", minZoom) map.putDouble("minZoom", minZoom)
map.putDouble("maxZoom", maxZoom) map.putDouble("maxZoom", maxZoom)
map.putDouble("neutralZoom", 1.0) // Zoom is always relative to 1.0 on Android map.putDouble("neutralZoom", 1.0) // Zoom is always relative to 1.0 on Android
map.putDouble("minExposure", exposureRange.lower.toDouble() / exposureStep.toDouble())
map.putDouble("maxExposure", exposureRange.upper.toDouble() / exposureStep.toDouble())
map.putString("hardwareLevel", hardwareLevel.unionValue) map.putString("hardwareLevel", hardwareLevel.unionValue)
map.putString("sensorOrientation", Orientation.fromRotationDegrees(sensorOrientation).unionValue) map.putString("sensorOrientation", Orientation.fromRotationDegrees(sensorOrientation).unionValue)
map.putArray("formats", getFormats()) map.putArray("formats", getFormats())

View File

@ -13,8 +13,6 @@ data class CameraDeviceFormat(
val maxFps: Double, val maxFps: Double,
val minISO: Double, val minISO: Double,
val maxISO: Double, val maxISO: Double,
val minExposure: Double,
val maxExposure: Double,
val fieldOfView: Double, val fieldOfView: Double,
val maxZoom: Double, val maxZoom: Double,
val videoStabilizationModes: List<VideoStabilizationMode>, val videoStabilizationModes: List<VideoStabilizationMode>,
@ -48,8 +46,6 @@ data class CameraDeviceFormat(
value.getDouble("maxFps"), value.getDouble("maxFps"),
value.getDouble("minISO"), value.getDouble("minISO"),
value.getDouble("maxISO"), value.getDouble("maxISO"),
value.getDouble("minExposure"),
value.getDouble("maxExposure"),
value.getDouble("fieldOfView"), value.getDouble("fieldOfView"),
value.getDouble("maxZoom"), value.getDouble("maxZoom"),
videoStabilizationModes, videoStabilizationModes,

View File

@ -187,14 +187,14 @@ extension CameraSession {
ReactLogger.log(level: .info, message: "Configuring Format (\(targetFormat))...") ReactLogger.log(level: .info, message: "Configuring Format (\(targetFormat))...")
let currentFormat = CameraDeviceFormat(fromFormat: device.activeFormat, forDevice: device) let currentFormat = CameraDeviceFormat(fromFormat: device.activeFormat)
if currentFormat == targetFormat { if currentFormat == targetFormat {
ReactLogger.log(level: .info, message: "Already selected active format, no need to configure.") ReactLogger.log(level: .info, message: "Already selected active format, no need to configure.")
return return
} }
// Find matching format (JS Dictionary -> strongly typed Swift class) // Find matching format (JS Dictionary -> strongly typed Swift class)
let format = device.formats.first { targetFormat.isEqualTo(format: $0, device: device) } let format = device.formats.first { targetFormat.isEqualTo(format: $0) }
guard let format else { guard let format else {
throw CameraError.format(.invalidFormat) throw CameraError.format(.invalidFormat)
} }
@ -295,7 +295,7 @@ extension CameraSession {
return return
} }
let clamped = max(min(exposure, device.maxExposureTargetBias), device.minExposureTargetBias) let clamped = min(max(exposure, device.minExposureTargetBias), device.maxExposureTargetBias)
device.setExposureTargetBias(clamped) device.setExposureTargetBias(clamped)
} }

View File

@ -10,7 +10,7 @@ import AVFoundation
extension AVCaptureDevice { extension AVCaptureDevice {
func toDictionary() -> [String: Any] { func toDictionary() -> [String: Any] {
let formats = formats.map { CameraDeviceFormat(fromFormat: $0, forDevice: self) } let formats = formats.map { CameraDeviceFormat(fromFormat: $0) }
return [ return [
"id": uniqueID, "id": uniqueID,
@ -20,8 +20,10 @@ extension AVCaptureDevice {
"hasFlash": hasFlash, "hasFlash": hasFlash,
"hasTorch": hasTorch, "hasTorch": hasTorch,
"minZoom": minAvailableVideoZoomFactor, "minZoom": minAvailableVideoZoomFactor,
"neutralZoom": neutralZoomFactor,
"maxZoom": maxAvailableVideoZoomFactor, "maxZoom": maxAvailableVideoZoomFactor,
"neutralZoom": neutralZoomFactor,
"minExposure": minExposureTargetBias,
"maxExposure": maxExposureTargetBias,
"isMultiCam": isMultiCam, "isMultiCam": isMultiCam,
"supportsRawCapture": false, // TODO: supportsRawCapture "supportsRawCapture": false, // TODO: supportsRawCapture
"supportsLowLightBoost": isLowLightBoostSupported, "supportsLowLightBoost": isLowLightBoostSupported,

View File

@ -22,9 +22,6 @@ struct CameraDeviceFormat: Equatable, CustomStringConvertible {
let minFps: Double let minFps: Double
let maxFps: Double let maxFps: Double
let minExposure: Float
let maxExposure: Float
let minISO: Float let minISO: Float
let maxISO: Float let maxISO: Float
@ -41,15 +38,13 @@ struct CameraDeviceFormat: Equatable, CustomStringConvertible {
let supportsDepthCapture: Bool let supportsDepthCapture: Bool
init(fromFormat format: AVCaptureDevice.Format, forDevice device: AVCaptureDevice) { init(fromFormat format: AVCaptureDevice.Format) {
videoWidth = Int(format.videoDimensions.width) videoWidth = Int(format.videoDimensions.width)
videoHeight = Int(format.videoDimensions.height) videoHeight = Int(format.videoDimensions.height)
photoWidth = Int(format.photoDimensions.width) photoWidth = Int(format.photoDimensions.width)
photoHeight = Int(format.photoDimensions.height) photoHeight = Int(format.photoDimensions.height)
minFps = format.minFps minFps = format.minFps
maxFps = format.maxFps maxFps = format.maxFps
minExposure = device.minExposureTargetBias
maxExposure = device.maxExposureTargetBias
minISO = format.minISO minISO = format.minISO
maxISO = format.maxISO maxISO = format.maxISO
fieldOfView = format.videoFieldOfView fieldOfView = format.videoFieldOfView
@ -72,8 +67,6 @@ struct CameraDeviceFormat: Equatable, CustomStringConvertible {
maxFps = jsValue["maxFps"] as! Double maxFps = jsValue["maxFps"] as! Double
minISO = jsValue["minISO"] as! Float minISO = jsValue["minISO"] as! Float
maxISO = jsValue["maxISO"] as! Float maxISO = jsValue["maxISO"] as! Float
minExposure = jsValue["minExposure"] as! Float
maxExposure = jsValue["maxExposure"] as! Float
fieldOfView = jsValue["fieldOfView"] as! Float fieldOfView = jsValue["fieldOfView"] as! Float
maxZoom = jsValue["maxZoom"] as! Double maxZoom = jsValue["maxZoom"] as! Double
let jsVideoStabilizationModes = jsValue["videoStabilizationModes"] as! [String] let jsVideoStabilizationModes = jsValue["videoStabilizationModes"] as! [String]
@ -88,8 +81,8 @@ struct CameraDeviceFormat: Equatable, CustomStringConvertible {
// swiftlint:enable force_cast // swiftlint:enable force_cast
} }
func isEqualTo(format other: AVCaptureDevice.Format, device otherDevice: AVCaptureDevice) -> Bool { func isEqualTo(format other: AVCaptureDevice.Format) -> Bool {
let other = CameraDeviceFormat(fromFormat: other, forDevice: otherDevice) let other = CameraDeviceFormat(fromFormat: other)
return self == other return self == other
} }
@ -103,8 +96,6 @@ struct CameraDeviceFormat: Equatable, CustomStringConvertible {
"videoWidth": videoWidth, "videoWidth": videoWidth,
"minISO": minISO, "minISO": minISO,
"maxISO": maxISO, "maxISO": maxISO,
"minExposure": minExposure,
"maxExposure": maxExposure,
"fieldOfView": fieldOfView, "fieldOfView": fieldOfView,
"maxZoom": maxZoom, "maxZoom": maxZoom,
"supportsVideoHdr": supportsVideoHdr, "supportsVideoHdr": supportsVideoHdr,

View File

@ -80,14 +80,6 @@ export interface CameraDeviceFormat {
* Minimum supported ISO value * Minimum supported ISO value
*/ */
minISO: number minISO: number
/**
* The minimum Exposure-Bias value this format supports. When setting the `exposure` to this value, the image is almost completely dark (under-exposed).
*/
minExposure: number
/**
* The maximum Exposure-Bias value this format supports. When setting the `exposure` to this value, the image is almost completely bright (over-exposed).
*/
maxExposure: number
/** /**
* The video field of view in degrees * The video field of view in degrees
*/ */
@ -204,6 +196,14 @@ export interface CameraDevice {
* })) * }))
*/ */
neutralZoom: number neutralZoom: number
/**
* The minimum Exposure-Bias value this format supports. When setting the `exposure` to this value, the image is almost completely dark (under-exposed).
*/
minExposure: number
/**
* The maximum Exposure-Bias value this format supports. When setting the `exposure` to this value, the image is almost completely bright (over-exposed).
*/
maxExposure: number
/** /**
* All available formats for this camera device. Use this to find the best format for your use case and set it to the Camera's {@linkcode CameraProps.format | Camera's .format} property. * All available formats for this camera device. Use this to find the best format for your use case and set it to the Camera's {@linkcode CameraProps.format | Camera's .format} property.
* *

View File

@ -116,7 +116,7 @@ export interface CameraProps extends ViewProps {
* *
* The Camera will still continue to auto-adjust exposure and focus, but will premultiply the exposure setting with the provided value here. * The Camera will still continue to auto-adjust exposure and focus, but will premultiply the exposure setting with the provided value here.
* *
* This values ranges from {@linkcode CameraDeviceFormat.minExposure format.minExposure} to {@linkcode CameraDeviceFormat.maxExposure format.maxExposure}. * This values ranges from {@linkcode CameraDevice.minExposure device.minExposure} to {@linkcode CameraDevice.maxExposure device.maxExposure}.
* *
* The value between min- and max supported exposure is considered the default, neutral value. * The value between min- and max supported exposure is considered the default, neutral value.
*/ */