Skip to content

Commit

Permalink
[BoundingBox] add visualization for automatically computed bounding box
Browse files Browse the repository at this point in the history
  • Loading branch information
almarouk committed Aug 28, 2023
1 parent 2bea35d commit e748cc1
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 2 deletions.
20 changes: 18 additions & 2 deletions meshroom/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from meshroom.core import desc, stats, hashValue, nodeVersion, Version
from meshroom.core.attribute import attributeFactory, ListAttribute, GroupAttribute, Attribute
from meshroom.core.exception import NodeUpgradeError, UnknownNodeTypeError
from meshroom.nodes.aliceVision.Meshing import boundingBoxMonitor


def getWritingFilepath(filepath):
Expand Down Expand Up @@ -1314,6 +1315,19 @@ def _updateChunks(self):
else:
self._chunks[0].range = desc.Range()

class MeshingNode(Node):
def __init__(self, nodeType, position=None, parent=None, **kwargs):
super().__init__(nodeType, position, parent, **kwargs)
self.internalFolderChanged.connect(self.checkBBox)
self.globalStatusChanged.connect(self.checkBBox)

def checkBBox(self):
"""Load automatic bounding box if needed."""
if self.useBoundingBox.value:
return
self.automaticBBoxValid.value = False
with boundingBoxMonitor(self, checkOnce=True) as thread:
pass

class CompatibilityIssue(Enum):
"""
Expand Down Expand Up @@ -1555,7 +1569,8 @@ def upgrade(self):
# store internal attributes that could be used during node upgrade
commonInternalAttributes.append(attrName)

node = Node(self.nodeType, position=self.position)
cl = MeshingNode if self.nodeType=="Meshing" else Node
node = cl(self.nodeType, position=self.position)
# convert attributes from a list of tuples into a dict
attrValues = {key: value for (key, value) in self.inputs.items()}
intAttrValues = {key: value for (key, value) in self.internalInputs.items()}
Expand Down Expand Up @@ -1676,7 +1691,8 @@ def nodeFactory(nodeDict, name=None, template=False, uidConflict=False):
break

if compatibilityIssue is None:
node = Node(nodeType, position, **inputs)
cl = MeshingNode if nodeType=="Meshing" else Node
node = cl(nodeType, position, **inputs)
node.setInternalAttributeValues(internalInputs)
else:
logging.warning("Compatibility issue detected for node '{}': {}".format(name, compatibilityIssue.name))
Expand Down
103 changes: 103 additions & 0 deletions meshroom/nodes/aliceVision/Meshing.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
__version__ = "7.0"

from meshroom.core import desc
import os
import threading
import psutil
import time
from contextlib import contextmanager


class Meshing(desc.AVCommandLineNode):
Expand Down Expand Up @@ -526,4 +531,102 @@ class Meshing(desc.AVCommandLineNode):
value="{cache}/{nodeType}/{uid0}/densePointCloud.abc",
uid=[],
),
desc.BoolParam(
name="automaticBBoxValid",
label="",
description="Indicates if the Bounding Box has been "
"automatically computed and loaded.\n"
"Note that this param is always not enabled (not exposed in the UI) and "
"only used to indicate if the automatic bounding box should be displayed.",
value=False,
enabled=False,
uid=[],
),
]

def processChunk(self, chunk):
with boundingBoxMonitor(chunk.node):
super(Meshing, self).processChunk(chunk)

@contextmanager
def boundingBoxMonitor(node, checkOnce=False):
"""
Context manager to load the automatic bounding box.
Inputs
------
node: MeshingNode
The considered meshing node
checkOnce: bool
If `True`, the bounding box file will be checked continuously
till created; if already exists, it will be ignored.
Otherwise, the file is checked only once and it will not be
ignored if already created.
Returns
-------
BoundingBoxThread
"""
bboxThread = None
try:
if not node.useBoundingBox.value:
bboxThread = BoundingBoxThread(node, checkOnce)
bboxThread.start()
yield bboxThread
finally:
if bboxThread is not None:
bboxThread.stopRequest()
bboxThread.join()

class BoundingBoxThread(threading.Thread):
"""Thread that loads the bounding box."""
def __init__(self, node, checkOnce):
threading.Thread.__init__(self)
self.node = node
self.checkOnce = checkOnce
self.parentProc = psutil.Process() # by default current process pid
self._stopFlag = threading.Event()
self.interval = 5 # wait duration before rechecking for bounding box file

def run(self):
self.startTime = time.time() if not self.checkOnce else -1
try:
while True:
updated = self.updateBoundingBox()
if updated or self.checkOnce:
return
if self._stopFlag.wait(self.interval):
# stopFlag has been set
# try to update boundingBox one last time and exit main loop
if self.parentProc.is_running():
self.updateBoundingBox()
return
except (KeyboardInterrupt, SystemError, GeneratorExit, psutil.NoSuchProcess):
pass

def updateBoundingBox(self) -> bool:
"""Tries to load the bounding box.
Returns
-------
bool: indicates if loading was successful
"""
file = os.path.join(os.path.dirname(self.node.outputMesh.value), "boundingBox.txt")
if not os.path.exists(file) or os.path.getmtime(file) < self.startTime:
return False
with open(file, 'r') as stream:
# file contains (in order, one value per line):
# translation: x, y, z ; rotation: x, y, z ; scale: x, y, z
data = list(map(float, stream.read().strip().splitlines()))
i = 0
for vec in self.node.boundingBox.value:
for x in vec.value:
x.value = data[i]
i += 1
self.node.automaticBBoxValid.value = True
return True

def stopRequest(self):
""" Request the thread to exit as soon as possible. """
self._stopFlag.set()
1 change: 1 addition & 0 deletions meshroom/ui/qml/Viewer3D/EntityWithGizmo.qml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Entity {
property Layer frontLayerComponent
property var window
property alias uniformScale: transformGizmo.uniformScale // By default, if not set, the value is: false
property alias gizmoEnabled : transformGizmo.enabled
property TransformGizmo transformGizmo: TransformGizmo {
id: transformGizmo
camera: root.camera
Expand Down
4 changes: 4 additions & 0 deletions meshroom/ui/qml/Viewer3D/MediaLibrary.qml
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,11 @@ Entity {
// Specific properties to the MESHING node (declared and initialized for every Entity anyway)
property bool hasBoundingBox: {
if(nodeType === "Meshing" && currentNode.attribute("useBoundingBox")) // Can have a BoundingBox
{
if(currentNode.attribute("automaticBBoxValid"))
return currentNode.attribute("useBoundingBox").value || currentNode.attribute("automaticBBoxValid").value
return currentNode.attribute("useBoundingBox").value
}
return false
}
onHasBoundingBoxChanged: model.hasBoundingBox = hasBoundingBox
Expand Down
1 change: 1 addition & 0 deletions meshroom/ui/qml/Viewer3D/MeshingBoundingBox.qml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Entity {

EntityWithGizmo {
id: boundingBoxEntity
gizmoEnabled: root.currentMeshingNode ? root.currentMeshingNode.attribute("useBoundingBox").value : true
sceneCameraController: root.sceneCameraController
frontLayerComponent: root.frontLayerComponent
window: root.window
Expand Down

0 comments on commit e748cc1

Please sign in to comment.