Use bindings for hdr brackets estimation

This commit is contained in:
Fabien Servant 2024-09-30 14:18:18 +02:00 committed by Candice Bentéjac
parent ebcbf9dace
commit c78834b6d2
3 changed files with 45 additions and 259 deletions

View file

@ -5,6 +5,9 @@ import math
import os import os
from collections import Counter from collections import Counter
from pyalicevision import sfmData as avsfmdata
from pyalicevision import hdr as avhdr
from meshroom.core import desc from meshroom.core import desc
from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL
@ -205,7 +208,7 @@ Calibrate LDR to HDR response curve from samples.
node.nbBrackets.value = 0 node.nbBrackets.value = 0
return return
inputs = [] inputs = avhdr.vectorli()
for viewpoint in viewpoints: for viewpoint in viewpoints:
jsonMetadata = viewpoint.metadata.value jsonMetadata = viewpoint.metadata.value
if not jsonMetadata: if not jsonMetadata:
@ -232,97 +235,22 @@ Calibrate LDR to HDR response curve from samples.
# We assume that there is no multi-bracketing, so nothing to do. # We assume that there is no multi-bracketing, so nothing to do.
node.nbBrackets.value = 1 node.nbBrackets.value = 1
return return
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
inputs.sort()
exposureGroups = [] exposure = LdrToHdrCalibration.getExposure((float(fnumber), float(shutterSpeed), float(iso)))
exposures = []
prevFnumber = 0.0
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
# dataset that is being parsed has changed. A new group needs to be created but will fail to be detected in the
# next "if" statement if the new dataset's exposure levels are different. Setting "newGroup" to True prevents this
# from happening.
if prevPath is not None and prevPath != os.path.dirname(path):
newGroup = True
currentExposure = LdrToHdrCalibration.getExposure(exp) obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
inputs.append(obj)
# Create a new group if the current image's exposure level is smaller than the previous image's, or obj = avhdr.estimateGroups(inputs)
# 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) if len(obj) == 0:
prevExposure = currentExposure
newGroup = False
exposureGroups.append(exposures)
exposures = None
bracketSizes = Counter()
if len(exposureGroups) == 1:
if len(set(exposureGroups[0])) == 1:
# Single exposure and multiple views
node.nbBrackets.value = 1
else:
# Single view and multiple exposures
node.nbBrackets.value = len(exposureGroups[0])
else:
for expGroup in exposureGroups:
bracketSizes[len(expGroup)] += 1
if len(bracketSizes) == 0:
node.nbBrackets.value = 0 node.nbBrackets.value = 0
else: return
bestTuple = None
for tuple in bracketSizes.most_common():
if bestTuple is None or tuple[1] > bestTuple[1]:
bestTuple = tuple
elif tuple[1] == bestTuple[1]:
bestTuple = tuple if tuple[0] > bestTuple[0] else bestTuple
bestBracketSize = bestTuple[0] node.nbBrackets.value = len(obj[0])
node.nbBrackets.value = bestBracketSize
@staticmethod @staticmethod
def getExposure(exp, refIso = 100.0, refFnumber = 1.0): def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
fnumber, shutterSpeed, iso = exp fnumber, shutterSpeed, iso = exp
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed) return obj.getExposure()
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 = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)
return shutterSpeed * expIncrease

View file

@ -5,6 +5,9 @@ import os
import math import math
from collections import Counter from collections import Counter
from pyalicevision import sfmData as avsfmdata
from pyalicevision import hdr as avhdr
from meshroom.core import desc from meshroom.core import desc
from meshroom.core.utils import COLORSPACES, EXR_STORAGE_DATA_TYPE, VERBOSE_LEVEL from meshroom.core.utils import COLORSPACES, EXR_STORAGE_DATA_TYPE, VERBOSE_LEVEL
@ -278,7 +281,7 @@ Merge LDR images into HDR images.
node.nbBrackets.value = 0 node.nbBrackets.value = 0
return return
inputs = [] inputs = avhdr.vectorli()
for viewpoint in viewpoints: for viewpoint in viewpoints:
jsonMetadata = viewpoint.metadata.value jsonMetadata = viewpoint.metadata.value
if not jsonMetadata: if not jsonMetadata:
@ -305,100 +308,25 @@ Merge LDR images into HDR images.
# We assume that there is no multi-bracketing, so nothing to do. # We assume that there is no multi-bracketing, so nothing to do.
node.nbBrackets.value = 1 node.nbBrackets.value = 1
return return
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
inputs.sort()
exposureGroups = [] exposure = LdrToHdrMerge.getExposure((float(fnumber), float(shutterSpeed), float(iso)))
exposures = []
prevFnumber = 0.0
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
# dataset that is being parsed has changed. A new group needs to be created but will fail to be detected in the
# next "if" statement if the new dataset's exposure levels are different. Setting "newGroup" to True prevents this
# from happening.
if prevPath is not None and prevPath != os.path.dirname(path):
newGroup = True
currentExposure = LdrToHdrMerge.getExposure(exp) obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
inputs.append(obj)
# Create a new group if the current image's exposure level is smaller than the previous image's, or obj = avhdr.estimateGroups(inputs)
# 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) if len(obj) == 0:
prevExposure = currentExposure
newGroup = False
exposureGroups.append(exposures)
exposures = None
bracketSizes = Counter()
if len(exposureGroups) == 1:
if len(set(exposureGroups[0])) == 1:
# Single exposure and multiple views
node.nbBrackets.value = 1
else:
# Single view and multiple exposures
node.nbBrackets.value = len(exposureGroups[0])
else:
for expGroup in exposureGroups:
bracketSizes[len(expGroup)] += 1
if len(bracketSizes) == 0:
node.nbBrackets.value = 0 node.nbBrackets.value = 0
else: return
bestTuple = None
for tuple in bracketSizes.most_common():
if bestTuple is None or tuple[1] > bestTuple[1]:
bestTuple = tuple
elif tuple[1] == bestTuple[1]:
bestTuple = tuple if tuple[0] > bestTuple[0] else bestTuple
bestBracketSize = bestTuple[0] node.nbBrackets.value = len(obj[0])
node.nbBrackets.value = bestBracketSize
@staticmethod @staticmethod
def getExposure(exp, refIso = 100.0, refFnumber = 1.0): def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
fnumber, shutterSpeed, iso = exp fnumber, shutterSpeed, iso = exp
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed) return obj.getExposure()
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 = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)
return shutterSpeed * expIncrease
def processChunk(self, chunk): def processChunk(self, chunk):
# Trick to avoid sending --nbBrackets to the command line when the bracket detection is automatic. # Trick to avoid sending --nbBrackets to the command line when the bracket detection is automatic.

View file

@ -5,6 +5,9 @@ import math
import os import os
from collections import Counter from collections import Counter
from pyalicevision import sfmData as avsfmdata
from pyalicevision import hdr as avhdr
from meshroom.core import desc from meshroom.core import desc
from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL
@ -231,7 +234,7 @@ Sample pixels from Low range images for HDR creation.
node.nbBrackets.value = 0 node.nbBrackets.value = 0
return return
inputs = [] inputs = avhdr.vectorli()
for viewpoint in viewpoints: for viewpoint in viewpoints:
jsonMetadata = viewpoint.metadata.value jsonMetadata = viewpoint.metadata.value
if not jsonMetadata: if not jsonMetadata:
@ -258,99 +261,26 @@ Sample pixels from Low range images for HDR creation.
# We assume that there is no multi-bracketing, so nothing to do. # We assume that there is no multi-bracketing, so nothing to do.
node.nbBrackets.value = 1 node.nbBrackets.value = 1
return return
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
inputs.sort()
exposureGroups = [] exposure = LdrToHdrSampling.getExposure((float(fnumber), float(shutterSpeed), float(iso)))
exposures = []
prevFnumber = 0.0
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
# dataset that is being parsed has changed. A new group needs to be created but will fail to be detected in the
# next "if" statement if the new dataset's exposure levels are different. Setting "newGroup" to True prevents this
# from happening.
if prevPath is not None and prevPath != os.path.dirname(path):
newGroup = True
currentExposure = LdrToHdrSampling.getExposure(exp) obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
inputs.append(obj)
# Create a new group if the current image's exposure level is smaller than the previous image's, or obj = avhdr.estimateGroups(inputs)
# 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) if len(obj) == 0:
prevExposure = currentExposure
newGroup = False
exposureGroups.append(exposures)
exposures = None
bracketSizes = Counter()
if len(exposureGroups) == 1:
if len(set(exposureGroups[0])) == 1:
# Single exposure and multiple views
node.nbBrackets.value = 1
else:
# Single view and multiple exposures
node.nbBrackets.value = len(exposureGroups[0])
else:
for expGroup in exposureGroups:
bracketSizes[len(expGroup)] += 1
if len(bracketSizes) == 0:
node.nbBrackets.value = 0 node.nbBrackets.value = 0
else: return
bestTuple = None
for tuple in bracketSizes.most_common():
if bestTuple is None or tuple[1] > bestTuple[1]:
bestTuple = tuple
elif tuple[1] == bestTuple[1]:
bestTuple = tuple if tuple[0] > bestTuple[0] else bestTuple
bestBracketSize = bestTuple[0] bracketSize = len(obj[0])
bestCount = bestTuple[1] bracketCount = len(obj)
node.outliersNb = len(inputs) - (bestBracketSize * bestCount) # Compute number of outliers
node.nbBrackets.value = bestBracketSize node.nbBrackets.value = bracketSize
node.outliersNb = len(inputs) - (bracketSize * bracketCount)
@staticmethod @staticmethod
def getExposure(exp, refIso = 100.0, refFnumber = 1.0): def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
fnumber, shutterSpeed, iso = exp fnumber, shutterSpeed, iso = exp
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed) return obj.getExposure()
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 = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)
return shutterSpeed * expIncrease