Skip to content

Commit 8a46cad

Browse files
authored
Merge pull request #2554 from alicevision/dev/brackets
[nodes] Use bindings for HDR brackets estimation
2 parents ebcbf9d + c78834b commit 8a46cad

File tree

3 files changed

+45
-259
lines changed

3 files changed

+45
-259
lines changed

meshroom/nodes/aliceVision/LdrToHdrCalibration.py

Lines changed: 14 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import os
66
from collections import Counter
77

8+
from pyalicevision import sfmData as avsfmdata
9+
from pyalicevision import hdr as avhdr
10+
811
from meshroom.core import desc
912
from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL
1013

@@ -205,7 +208,7 @@ def update(cls, node):
205208
node.nbBrackets.value = 0
206209
return
207210

208-
inputs = []
211+
inputs = avhdr.vectorli()
209212
for viewpoint in viewpoints:
210213
jsonMetadata = viewpoint.metadata.value
211214
if not jsonMetadata:
@@ -232,97 +235,22 @@ def update(cls, node):
232235
# We assume that there is no multi-bracketing, so nothing to do.
233236
node.nbBrackets.value = 1
234237
return
235-
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
236-
inputs.sort()
237-
238-
exposureGroups = []
239-
exposures = []
240-
prevFnumber = 0.0
241-
prevShutterSpeed = 0.0
242-
prevIso = 0.0
243-
prevPath = None # Stores the dirname of the previous parsed image
244-
prevExposure = None
245-
newGroup = False # True if a new exposure group needs to be created (useful when there are several datasets)
246-
for path, exp in inputs:
247-
# If the dirname of the previous image and the dirname of the current image do not match, this means that the
248-
# dataset that is being parsed has changed. A new group needs to be created but will fail to be detected in the
249-
# next "if" statement if the new dataset's exposure levels are different. Setting "newGroup" to True prevents this
250-
# from happening.
251-
if prevPath is not None and prevPath != os.path.dirname(path):
252-
newGroup = True
253-
254-
currentExposure = LdrToHdrCalibration.getExposure(exp)
255238

256-
# Create a new group if the current image's exposure level is smaller than the previous image's, or
257-
# if a new dataset has been detected (with a change in the path of the images).
258-
if prevExposure and currentExposure < prevExposure or newGroup:
259-
exposureGroups.append(exposures)
260-
exposures = [exp]
261-
else:
262-
exposures.append(exp)
239+
exposure = LdrToHdrCalibration.getExposure((float(fnumber), float(shutterSpeed), float(iso)))
263240

264-
prevPath = os.path.dirname(path)
265-
prevExposure = currentExposure
266-
newGroup = False
241+
obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
242+
inputs.append(obj)
267243

268-
exposureGroups.append(exposures)
244+
obj = avhdr.estimateGroups(inputs)
269245

270-
exposures = None
271-
bracketSizes = Counter()
272-
if len(exposureGroups) == 1:
273-
if len(set(exposureGroups[0])) == 1:
274-
# Single exposure and multiple views
275-
node.nbBrackets.value = 1
276-
else:
277-
# Single view and multiple exposures
278-
node.nbBrackets.value = len(exposureGroups[0])
279-
else:
280-
for expGroup in exposureGroups:
281-
bracketSizes[len(expGroup)] += 1
282-
283-
if len(bracketSizes) == 0:
284-
node.nbBrackets.value = 0
285-
else:
286-
bestTuple = None
287-
for tuple in bracketSizes.most_common():
288-
if bestTuple is None or tuple[1] > bestTuple[1]:
289-
bestTuple = tuple
290-
elif tuple[1] == bestTuple[1]:
291-
bestTuple = tuple if tuple[0] > bestTuple[0] else bestTuple
246+
if len(obj) == 0:
247+
node.nbBrackets.value = 0
248+
return
292249

293-
bestBracketSize = bestTuple[0]
294-
node.nbBrackets.value = bestBracketSize
250+
node.nbBrackets.value = len(obj[0])
295251

296252
@staticmethod
297253
def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
298254
fnumber, shutterSpeed, iso = exp
299-
300-
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed)
301-
validFnumber = fnumber > 0.0 and math.isfinite(fnumber)
302-
303-
if not validShutterSpeed and not validFnumber:
304-
return -1.0
305-
306-
validRefFnumber = refFnumber > 0.0 and math.isfinite(refFnumber)
307-
308-
if not validShutterSpeed:
309-
shutterSpeed = 1.0 / 200.0
310-
311-
if not validFnumber:
312-
if validRefFnumber:
313-
fnumber = refFnumber
314-
else:
315-
fnumber = 2.0
316-
317-
lRefFnumber = refFnumber
318-
if not validRefFnumber:
319-
lRefFnumber = fnumber
320-
321-
isoToAperture = 1.0
322-
if iso > 1e-6 and refIso > 1e-6:
323-
isoToAperture = math.sqrt(iso / refIso)
324-
325-
newFnumber = fnumber * isoToAperture
326-
expIncrease = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)
327-
328-
return shutterSpeed * expIncrease
255+
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
256+
return obj.getExposure()

meshroom/nodes/aliceVision/LdrToHdrMerge.py

Lines changed: 14 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import math
66
from collections import Counter
77

8+
from pyalicevision import sfmData as avsfmdata
9+
from pyalicevision import hdr as avhdr
10+
811
from meshroom.core import desc
912
from meshroom.core.utils import COLORSPACES, EXR_STORAGE_DATA_TYPE, VERBOSE_LEVEL
1013

@@ -278,7 +281,7 @@ def update(cls, node):
278281
node.nbBrackets.value = 0
279282
return
280283

281-
inputs = []
284+
inputs = avhdr.vectorli()
282285
for viewpoint in viewpoints:
283286
jsonMetadata = viewpoint.metadata.value
284287
if not jsonMetadata:
@@ -305,100 +308,25 @@ def update(cls, node):
305308
# We assume that there is no multi-bracketing, so nothing to do.
306309
node.nbBrackets.value = 1
307310
return
308-
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
309-
inputs.sort()
310-
311-
exposureGroups = []
312-
exposures = []
313-
prevFnumber = 0.0
314-
prevShutterSpeed = 0.0
315-
prevIso = 0.0
316-
prevPath = None # Stores the dirname of the previous parsed image
317-
prevExposure = None
318-
newGroup = False # True if a new exposure group needs to be created (useful when there are several datasets)
319-
for path, exp in inputs:
320-
# If the dirname of the previous image and the dirname of the current image do not match, this means that the
321-
# dataset that is being parsed has changed. A new group needs to be created but will fail to be detected in the
322-
# next "if" statement if the new dataset's exposure levels are different. Setting "newGroup" to True prevents this
323-
# from happening.
324-
if prevPath is not None and prevPath != os.path.dirname(path):
325-
newGroup = True
326-
327-
currentExposure = LdrToHdrMerge.getExposure(exp)
328311

329-
# Create a new group if the current image's exposure level is smaller than the previous image's, or
330-
# if a new dataset has been detected (with a change in the path of the images).
331-
if prevExposure and currentExposure < prevExposure or newGroup:
332-
exposureGroups.append(exposures)
333-
exposures = [exp]
334-
else:
335-
exposures.append(exp)
312+
exposure = LdrToHdrMerge.getExposure((float(fnumber), float(shutterSpeed), float(iso)))
336313

337-
prevPath = os.path.dirname(path)
338-
prevExposure = currentExposure
339-
newGroup = False
314+
obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
315+
inputs.append(obj)
340316

341-
exposureGroups.append(exposures)
317+
obj = avhdr.estimateGroups(inputs)
342318

343-
exposures = None
344-
bracketSizes = Counter()
345-
if len(exposureGroups) == 1:
346-
if len(set(exposureGroups[0])) == 1:
347-
# Single exposure and multiple views
348-
node.nbBrackets.value = 1
349-
else:
350-
# Single view and multiple exposures
351-
node.nbBrackets.value = len(exposureGroups[0])
352-
else:
353-
for expGroup in exposureGroups:
354-
bracketSizes[len(expGroup)] += 1
355-
356-
if len(bracketSizes) == 0:
357-
node.nbBrackets.value = 0
358-
else:
359-
bestTuple = None
360-
for tuple in bracketSizes.most_common():
361-
if bestTuple is None or tuple[1] > bestTuple[1]:
362-
bestTuple = tuple
363-
elif tuple[1] == bestTuple[1]:
364-
bestTuple = tuple if tuple[0] > bestTuple[0] else bestTuple
319+
if len(obj) == 0:
320+
node.nbBrackets.value = 0
321+
return
365322

366-
bestBracketSize = bestTuple[0]
367-
node.nbBrackets.value = bestBracketSize
323+
node.nbBrackets.value = len(obj[0])
368324

369325
@staticmethod
370326
def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
371327
fnumber, shutterSpeed, iso = exp
372-
373-
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed)
374-
validFnumber = fnumber > 0.0 and math.isfinite(fnumber)
375-
376-
if not validShutterSpeed and not validFnumber:
377-
return -1.0
378-
379-
validRefFnumber = refFnumber > 0.0 and math.isfinite(refFnumber)
380-
381-
if not validShutterSpeed:
382-
shutterSpeed = 1.0 / 200.0
383-
384-
if not validFnumber:
385-
if validRefFnumber:
386-
fnumber = refFnumber
387-
else:
388-
fnumber = 2.0
389-
390-
lRefFnumber = refFnumber
391-
if not validRefFnumber:
392-
lRefFnumber = fnumber
393-
394-
isoToAperture = 1.0
395-
if iso > 1e-6 and refIso > 1e-6:
396-
isoToAperture = math.sqrt(iso / refIso)
397-
398-
newFnumber = fnumber * isoToAperture
399-
expIncrease = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)
400-
401-
return shutterSpeed * expIncrease
328+
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
329+
return obj.getExposure()
402330

403331
def processChunk(self, chunk):
404332
# Trick to avoid sending --nbBrackets to the command line when the bracket detection is automatic.

meshroom/nodes/aliceVision/LdrToHdrSampling.py

Lines changed: 17 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
import os
66
from collections import Counter
77

8+
from pyalicevision import sfmData as avsfmdata
9+
from pyalicevision import hdr as avhdr
10+
811
from meshroom.core import desc
912
from meshroom.core.utils import COLORSPACES, VERBOSE_LEVEL
1013

@@ -231,7 +234,7 @@ def update(cls, node):
231234
node.nbBrackets.value = 0
232235
return
233236

234-
inputs = []
237+
inputs = avhdr.vectorli()
235238
for viewpoint in viewpoints:
236239
jsonMetadata = viewpoint.metadata.value
237240
if not jsonMetadata:
@@ -258,99 +261,26 @@ def update(cls, node):
258261
# We assume that there is no multi-bracketing, so nothing to do.
259262
node.nbBrackets.value = 1
260263
return
261-
inputs.append((viewpoint.path.value, (float(fnumber), float(shutterSpeed), float(iso))))
262-
inputs.sort()
263-
264-
exposureGroups = []
265-
exposures = []
266-
prevFnumber = 0.0
267-
prevShutterSpeed = 0.0
268-
prevIso = 0.0
269-
prevPath = None # Stores the dirname of the previous parsed image
270-
prevExposure = None
271-
newGroup = False # True if a new exposure group needs to be created (useful when there are several datasets)
272-
for path, exp in inputs:
273-
# If the dirname of the previous image and the dirname of the current image do not match, this means that the
274-
# dataset that is being parsed has changed. A new group needs to be created but will fail to be detected in the
275-
# next "if" statement if the new dataset's exposure levels are different. Setting "newGroup" to True prevents this
276-
# from happening.
277-
if prevPath is not None and prevPath != os.path.dirname(path):
278-
newGroup = True
279-
280-
currentExposure = LdrToHdrSampling.getExposure(exp)
281264

282-
# Create a new group if the current image's exposure level is smaller than the previous image's, or
283-
# if a new dataset has been detected (with a change in the path of the images).
284-
if prevExposure and currentExposure < prevExposure or newGroup:
285-
exposureGroups.append(exposures)
286-
exposures = [exp]
287-
else:
288-
exposures.append(exp)
265+
exposure = LdrToHdrSampling.getExposure((float(fnumber), float(shutterSpeed), float(iso)))
289266

290-
prevPath = os.path.dirname(path)
291-
prevExposure = currentExposure
292-
newGroup = False
267+
obj = avhdr.LuminanceInfo(viewpoint.viewId.value,viewpoint.path.value, exposure)
268+
inputs.append(obj)
293269

294-
exposureGroups.append(exposures)
270+
obj = avhdr.estimateGroups(inputs)
295271

296-
exposures = None
297-
bracketSizes = Counter()
298-
if len(exposureGroups) == 1:
299-
if len(set(exposureGroups[0])) == 1:
300-
# Single exposure and multiple views
301-
node.nbBrackets.value = 1
302-
else:
303-
# Single view and multiple exposures
304-
node.nbBrackets.value = len(exposureGroups[0])
305-
else:
306-
for expGroup in exposureGroups:
307-
bracketSizes[len(expGroup)] += 1
272+
if len(obj) == 0:
273+
node.nbBrackets.value = 0
274+
return
308275

309-
if len(bracketSizes) == 0:
310-
node.nbBrackets.value = 0
311-
else:
312-
bestTuple = None
313-
for tuple in bracketSizes.most_common():
314-
if bestTuple is None or tuple[1] > bestTuple[1]:
315-
bestTuple = tuple
316-
elif tuple[1] == bestTuple[1]:
317-
bestTuple = tuple if tuple[0] > bestTuple[0] else bestTuple
276+
bracketSize = len(obj[0])
277+
bracketCount = len(obj)
318278

319-
bestBracketSize = bestTuple[0]
320-
bestCount = bestTuple[1]
321-
node.outliersNb = len(inputs) - (bestBracketSize * bestCount) # Compute number of outliers
322-
node.nbBrackets.value = bestBracketSize
279+
node.nbBrackets.value = bracketSize
280+
node.outliersNb = len(inputs) - (bracketSize * bracketCount)
323281

324282
@staticmethod
325283
def getExposure(exp, refIso = 100.0, refFnumber = 1.0):
326284
fnumber, shutterSpeed, iso = exp
327-
328-
validShutterSpeed = shutterSpeed > 0.0 and math.isfinite(shutterSpeed)
329-
validFnumber = fnumber > 0.0 and math.isfinite(fnumber)
330-
331-
if not validShutterSpeed and not validFnumber:
332-
return -1.0
333-
334-
validRefFnumber = refFnumber > 0.0 and math.isfinite(refFnumber)
335-
336-
if not validShutterSpeed:
337-
shutterSpeed = 1.0 / 200.0
338-
339-
if not validFnumber:
340-
if validRefFnumber:
341-
fnumber = refFnumber
342-
else:
343-
fnumber = 2.0
344-
345-
lRefFnumber = refFnumber
346-
if not validRefFnumber:
347-
lRefFnumber = fnumber
348-
349-
isoToAperture = 1.0
350-
if iso > 1e-6 and refIso > 1e-6:
351-
isoToAperture = math.sqrt(iso / refIso)
352-
353-
newFnumber = fnumber * isoToAperture
354-
expIncrease = (lRefFnumber / newFnumber) * (lRefFnumber / newFnumber)
355-
356-
return shutterSpeed * expIncrease
285+
obj = avsfmdata.ExposureSetting(shutterSpeed, fnumber, iso)
286+
return obj.getExposure()

0 commit comments

Comments
 (0)