[nodes] HDR Fusion: Align bracket detection with AliceVision's

The bracket detection performed in Meshroom used to differ from the one
performed in AliceVision.

The metadata that were retrieved to perform the exposure comparisons were
not the same, and where AliceVision was actually computing an exposure
value, Meshroom was just performing a comparison between the shutter
speed, fnumber and ISO values, which resulted in less accurate groups.

The `getExposure` static method that is added to the `LdrToHdrSampling`,
`LdrToHdrCalibration` and `LdrToHdrMerge` node is the pythonic version
of the `getExposure` method from the View class in AliceVision.
This commit is contained in:
Candice Bentéjac 2023-08-16 16:57:23 +02:00
parent 9db9337d66
commit ea8d619fb1
3 changed files with 168 additions and 33 deletions

View file

@ -1,6 +1,7 @@
__version__ = "4.0"
import json
import math
import os
from collections import Counter
@ -231,9 +232,20 @@ Sample pixels from Low range images for HDR creation.
node.nbBrackets.value = 0
return
d = json.loads(jsonMetadata)
fnumber = findMetadata(d, ["FNumber", "Exif:ApertureValue", "ApertureValue", "Aperture"], "")
shutterSpeed = findMetadata(d, ["Exif:ShutterSpeedValue", "ShutterSpeedValue", "ShutterSpeed"], "")
iso = findMetadata(d, ["Exif:ISOSpeedRatings", "ISOSpeedRatings", "ISO"], "")
# Find Fnumber
fnumber = findMetadata(d, ["FNumber"], "")
if fnumber == "":
aperture = findMetadata(d, ["Exif:ApertureValue", "ApertureValue", "Aperture"], "")
if aperture == "":
fnumber = -1.0
else:
fnumber = pow(2.0, aperture / 2.0)
# Get shutter speed and ISO
shutterSpeed = findMetadata(d, ["ExposureTime", "Exif:ShutterSpeedValue", "ShutterSpeedValue", "ShutterSpeed"], -1.0)
iso = findMetadata(d, ["Exif:PhotographicSensitivity", "PhotographicSensitivity", "Photographic Sensitivity", "ISO"], -1.0)
if not fnumber and not shutterSpeed:
# If one image without shutter or fnumber, we cannot found the number of brackets.
# We assume that there is no multi-bracketing, so nothing to do.
@ -248,6 +260,7 @@ Sample pixels from Low range images for HDR creation.
prevShutterSpeed = 0.0
prevIso = 0.0
prevPath = None # Stores the dirname of the previous parsed image
prevExposure = None
newGroup = False # True if a new exposure group needs to be created (useful when there are several datasets)
for path, exp in inputs:
# If the dirname of the previous image and the dirname of the current image do not match, this means that the
@ -257,20 +270,18 @@ Sample pixels from Low range images for HDR creation.
if prevPath is not None and prevPath != os.path.dirname(path):
newGroup = True
# A new group is created if the current image's exposure level is larger than the previous image's, if there
# were any changes in the ISO or aperture value, or if a new dataset has been detected with the path.
# Since the input images are ordered, the shutter speed should always be decreasing, so a shutter speed larger
# than the previous one indicates the start of a new exposure group.
fnumber, shutterSpeed, iso = exp
if exposures:
prevFnumber, prevShutterSpeed, prevIso = exposures[-1]
if exposures and len(exposures) > 1 and (fnumber > prevFnumber or shutterSpeed > prevShutterSpeed or iso < prevIso) or newGroup:
currentExposure = LdrToHdrSampling.getExposure(exp)
# Create a new group if the current image's exposure level is smaller than the previous image's, or
# if a new dataset has been detected (with a change in the path of the images).
if prevExposure and currentExposure < prevExposure or newGroup:
exposureGroups.append(exposures)
exposures = [exp]
else:
exposures.append(exp)
prevPath = os.path.dirname(path)
prevExposure = currentExposure
newGroup = False
exposureGroups.append(exposures)
@ -302,3 +313,37 @@ Sample pixels from Low range images for HDR creation.
bestCount = bestTuple[1]
node.outliersNb = len(inputs) - (bestBracketSize * bestCount) # Compute number of outliers
node.nbBrackets.value = bestBracketSize
@staticmethod
def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
fnumber, shutterSpeed, iso = exp
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed)
validFnumber = fnumber > 0.0 and math.isfinite(fnumber)
if not validShutterSpeed and not validFnumber:
return -1.0
validRefFnumber = refFnumber > 0.0 and math.isfinite(refFnumber)
if not validShutterSpeed:
shutterSpeed = 1.0 / 200.0
if not validFnumber:
if validRefFnumber:
fnumber = refFnumber
else:
fnumber = 2.0
lRefFnumber = refFnumber
if not validRefFnumber:
lRefFnumber = fnumber
isoToAperture = 1.0
if iso > 1e-6 and refIso > 1e-6:
isoToAperture = math.sqrt(iso / refIso)
newFnumber = fnumber * isoToAperture
expIncrease = (newFnumber / lRefFnumber) * (newFnumber / lRefFnumber)
return shutterSpeed * expIncrease