Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[nodes] Use bindings for HDR brackets estimation #2554

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 14 additions & 86 deletions meshroom/nodes/aliceVision/LdrToHdrCalibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import os
from collections import Counter

from pyalicevision import sfmData as avsfmdata
from pyalicevision import hdr as avhdr

from meshroom.core import desc
from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL

Expand Down Expand Up @@ -205,7 +208,7 @@ def update(cls, node):
node.nbBrackets.value = 0
return

inputs = []
inputs = avhdr.vectorli()
for viewpoint in viewpoints:
jsonMetadata = viewpoint.metadata.value
if not jsonMetadata:
Expand All @@ -232,97 +235,22 @@ def update(cls, node):
# We assume that there is no multi-bracketing, so nothing to do.
node.nbBrackets.value = 1
return
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
inputs.sort()

exposureGroups = []
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)

# 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)
exposure = LdrToHdrCalibration.getExposure((float(fnumber), float(shutterSpeed), float(iso)))

prevPath = os.path.dirname(path)
prevExposure = currentExposure
newGroup = False
obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
inputs.append(obj)

exposureGroups.append(exposures)
obj = avhdr.estimateGroups(inputs)

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
else:
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
if len(obj) == 0:
node.nbBrackets.value = 0
return

bestBracketSize = bestTuple[0]
node.nbBrackets.value = bestBracketSize
node.nbBrackets.value = len(obj[0])

@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 = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)

return shutterSpeed * expIncrease
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
return obj.getExposure()
100 changes: 14 additions & 86 deletions meshroom/nodes/aliceVision/LdrToHdrMerge.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import math
from collections import Counter

from pyalicevision import sfmData as avsfmdata
from pyalicevision import hdr as avhdr

from meshroom.core import desc
from meshroom.core.utils import COLORSPACES, EXR_STORAGE_DATA_TYPE, VERBOSE_LEVEL

Expand Down Expand Up @@ -278,7 +281,7 @@ def update(cls, node):
node.nbBrackets.value = 0
return

inputs = []
inputs = avhdr.vectorli()
for viewpoint in viewpoints:
jsonMetadata = viewpoint.metadata.value
if not jsonMetadata:
Expand All @@ -305,100 +308,25 @@ def update(cls, node):
# We assume that there is no multi-bracketing, so nothing to do.
node.nbBrackets.value = 1
return
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
inputs.sort()

exposureGroups = []
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)

# 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)
exposure = LdrToHdrMerge.getExposure((float(fnumber), float(shutterSpeed), float(iso)))

prevPath = os.path.dirname(path)
prevExposure = currentExposure
newGroup = False
obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
inputs.append(obj)

exposureGroups.append(exposures)
obj = avhdr.estimateGroups(inputs)

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
else:
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
if len(obj) == 0:
node.nbBrackets.value = 0
return

bestBracketSize = bestTuple[0]
node.nbBrackets.value = bestBracketSize
node.nbBrackets.value = len(obj[0])

@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 = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)

return shutterSpeed * expIncrease
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
return obj.getExposure()

def processChunk(self, chunk):
# Trick to avoid sending --nbBrackets to the command line when the bracket detection is automatic.
Expand Down
104 changes: 17 additions & 87 deletions meshroom/nodes/aliceVision/LdrToHdrSampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
import os
from collections import Counter

from pyalicevision import sfmData as avsfmdata
from pyalicevision import hdr as avhdr

from meshroom.core import desc
from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL

Expand Down Expand Up @@ -231,7 +234,7 @@ def update(cls, node):
node.nbBrackets.value = 0
return

inputs = []
inputs = avhdr.vectorli()
for viewpoint in viewpoints:
jsonMetadata = viewpoint.metadata.value
if not jsonMetadata:
Expand All @@ -258,99 +261,26 @@ def update(cls, node):
# We assume that there is no multi-bracketing, so nothing to do.
node.nbBrackets.value = 1
return
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
inputs.sort()

exposureGroups = []
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)

# 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)
exposure = LdrToHdrSampling.getExposure((float(fnumber), float(shutterSpeed), float(iso)))

prevPath = os.path.dirname(path)
prevExposure = currentExposure
newGroup = False
obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
inputs.append(obj)

exposureGroups.append(exposures)
obj = avhdr.estimateGroups(inputs)

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(obj) == 0:
node.nbBrackets.value = 0
return

if len(bracketSizes) == 0:
node.nbBrackets.value = 0
else:
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
bracketSize = len(obj[0])
bracketCount = len(obj)

bestBracketSize = bestTuple[0]
bestCount = bestTuple[1]
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
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 = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)

return shutterSpeed * expIncrease
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
return obj.getExposure()
Loading