[nodes/blender] update documentation

This commit is contained in:
Loïc Vital 2023-05-30 11:07:59 +02:00
parent 3eaea1ff6f
commit 1e74c00d8b
2 changed files with 36 additions and 31 deletions

View file

@ -10,9 +10,11 @@ class RenderAnimatedCamera(desc.CommandLineNode):
category = 'Visualization' category = 'Visualization'
documentation = ''' documentation = '''
This node makes a rendering of the sfmData scene through an animated camera using the Blender rendering engine. This node uses Blender to visualize a 3D model from a given set of cameras.
It supports both Point Clouds (.abc) and Meshes (.obj). The cameras must be a SfMData file in JSON format.
''' For the 3D model it supports both point clouds in Alembic format and meshes in OBJ format.
One frame per viewpoint will be rendered, and the undistorted views can optionally be used as background.
'''
inputs = [ inputs = [
desc.File( desc.File(
@ -25,7 +27,7 @@ class RenderAnimatedCamera(desc.CommandLineNode):
), ),
desc.File( desc.File(
name='script', name='script',
label='Script Path', label='Script',
description='Path to the internal script for rendering in Blender', description='Path to the internal script for rendering in Blender',
value=os.path.join(currentDir, 'scripts' ,'renderAnimatedCameraInBlender.py'), value=os.path.join(currentDir, 'scripts' ,'renderAnimatedCameraInBlender.py'),
uid=[], uid=[],
@ -34,15 +36,15 @@ class RenderAnimatedCamera(desc.CommandLineNode):
), ),
desc.File( desc.File(
name='cameras', name='cameras',
label='SfmData with Animated Camera', label='Cameras',
description='SfmData with the animated camera to render (in json format)', description='SfmData with the views, poses and intrinsics to use (in JSON format)',
value='', value='',
uid=[0], uid=[0],
), ),
desc.File( desc.File(
name='model', name='model',
label='Model', label='Model',
description='Point Cloud or Mesh to render', description='Point cloud (.abc) or mesh (.obj) to render',
value='', value='',
uid=[0], uid=[0],
), ),
@ -55,8 +57,8 @@ class RenderAnimatedCamera(desc.CommandLineNode):
), ),
desc.File( desc.File(
name='undistortedImages', name='undistortedImages',
label='Undistorted Images Folder', label='Undistorted Images',
description='Input folder with the undistorted images', description='Folder containing the undistorted images',
value='', value='',
uid=[0], uid=[0],
enabled=lambda node: node.useBackground.value, enabled=lambda node: node.useBackground.value,
@ -66,12 +68,12 @@ class RenderAnimatedCamera(desc.CommandLineNode):
label="Point Cloud Settings", label="Point Cloud Settings",
group=None, group=None,
enabled=lambda node: node.model.value.lower().endswith('.abc'), enabled=lambda node: node.model.value.lower().endswith('.abc'),
description="Settings of the render if we use a Point Cloud", description="Settings for point cloud rendering",
groupDesc=[ groupDesc=[
desc.FloatParam( desc.FloatParam(
name='particleSize', name='particleSize',
label='Particle Size', label='Particle Size',
description='Scale of particles used to show the point cloud', description='Scale of particles used for the point cloud',
value=0.01, value=0.01,
range=(0.01, 1.0, 0.01), range=(0.01, 1.0, 0.01),
uid=[0], uid=[0],
@ -79,12 +81,11 @@ class RenderAnimatedCamera(desc.CommandLineNode):
desc.ChoiceParam( desc.ChoiceParam(
name='particleColor', name='particleColor',
label='Particle Color', label='Particle Color',
description='Color of particles used to show the point cloud', description='Color of particles used for the point cloud',
value='Red', value='Red',
values=['Grey', 'White', 'Red', 'Green', 'Magenta'], values=['Grey', 'White', 'Red', 'Green', 'Magenta'],
exclusive=True, exclusive=True,
uid=[0], uid=[0],
joinChar=',',
), ),
] ]
), ),
@ -93,7 +94,7 @@ class RenderAnimatedCamera(desc.CommandLineNode):
label="Mesh Settings", label="Mesh Settings",
group=None, group=None,
enabled=lambda node: node.model.value.lower().endswith('.obj'), enabled=lambda node: node.model.value.lower().endswith('.obj'),
description="Setting of the render if we use a Mesh", description="Setting for mesh rendering",
groupDesc=[ groupDesc=[
desc.ChoiceParam( desc.ChoiceParam(
name='shading', name='shading',
@ -107,7 +108,7 @@ class RenderAnimatedCamera(desc.CommandLineNode):
desc.ChoiceParam( desc.ChoiceParam(
name='edgeColor', name='edgeColor',
label='Edge Color', label='Edge Color',
description='Color of the edges of the rendered object', description='Color of the mesh edges',
value='Red', value='Red',
values=['Grey', 'White', 'Red', 'Green', 'Magenta'], values=['Grey', 'White', 'Red', 'Green', 'Magenta'],
exclusive=True, exclusive=True,
@ -120,17 +121,17 @@ class RenderAnimatedCamera(desc.CommandLineNode):
outputs = [ outputs = [
desc.File( desc.File(
name='output', name='output',
label='Output Folder', label='Output',
description='Output Folder', description='Output folder',
value=desc.Node.internalFolder, value=desc.Node.internalFolder,
uid=[], uid=[],
), ),
desc.File( desc.File(
name='render', name='frames',
label='Render', label='Frames',
description='Frames rendered in Blender', description='Frames rendered in Blender',
semantic='image', semantic='image',
value=desc.Node.internalFolder + '<VIEW_ID>.png', value=desc.Node.internalFolder + '<VIEW_ID>.jpg',
uid=[], uid=[],
group='', group='',
), ),

View file

@ -9,6 +9,7 @@ import glob
def createParser(): def createParser():
'''Create command line interface.'''
# When --help or no args are given, print this help # When --help or no args are given, print this help
usage_text = ( usage_text = (
"Run blender in background mode with this script:" "Run blender in background mode with this script:"
@ -67,10 +68,10 @@ def parseSfMCameraFile(filepath):
return views, intrinsics, poses return views, intrinsics, poses
def getFromId(data, key, id): def getFromId(data, key, identifier):
'''Utility function to retrieve view, intrinsic or pose using their IDs.''' '''Utility function to retrieve view, intrinsic or pose using their IDs.'''
for item in data: for item in data:
if item[key] == id: if item[key] == identifier:
return item return item
return None return None
@ -109,14 +110,16 @@ def setupCamera(intrinsic, pose):
def initScene(): def initScene():
'''Initialize Blender scene.'''
# Clear current scene (keep default camera) # Clear current scene (keep default camera)
bpy.data.objects.remove(bpy.data.objects['Cube']) bpy.data.objects.remove(bpy.data.objects['Cube'])
bpy.data.objects.remove(bpy.data.objects['Light']) bpy.data.objects.remove(bpy.data.objects['Light'])
# Set output format
bpy.context.scene.render.image_settings.file_format = 'JPEG'
def initCompositing(): def initCompositing():
'''Initialize Blender compositing graph for adding background image to render.''' '''Initialize Blender compositing graph for adding background image to render.'''
bpy.context.scene.render.image_settings.file_format = 'PNG'
bpy.context.scene.render.film_transparent = True bpy.context.scene.render.film_transparent = True
bpy.context.scene.use_nodes = True bpy.context.scene.use_nodes = True
bpy.context.scene.node_tree.nodes.new(type="CompositorNodeAlphaOver") bpy.context.scene.node_tree.nodes.new(type="CompositorNodeAlphaOver")
@ -135,11 +138,11 @@ def initCompositing():
def setupRender(view, intrinsic, pose, outputDir): def setupRender(view, intrinsic, pose, outputDir):
'''Setup rendering in Blender for a given view.''' '''Setup rendering in Blender for a given view.'''
setupCamera(intrinsic, pose) setupCamera(intrinsic, pose)
bpy.context.scene.render.filepath = os.path.abspath(outputDir + '/' + view['viewId'] + '.png') bpy.context.scene.render.filepath = os.path.abspath(outputDir + '/' + view['viewId'] + '.jpg')
def setupBackground(view, folderUndistorted): def setupBackground(view, folderUndistorted):
'''TODO''' '''Retrieve undistorted image corresponding to view and use it as background.'''
baseImgName = os.path.splitext(os.path.basename(view['path']))[0] baseImgName = os.path.splitext(os.path.basename(view['path']))[0]
undistortedImgPath = glob.glob(folderUndistorted + '/*' + baseImgName + "*")[0] undistortedImgPath = glob.glob(folderUndistorted + '/*' + baseImgName + "*")[0]
bpy.ops.image.open(filepath=undistortedImgPath) bpy.ops.image.open(filepath=undistortedImgPath)
@ -147,8 +150,8 @@ def setupBackground(view, folderUndistorted):
bpy.context.scene.node_tree.nodes["Image"].image = bpy.data.images[undistortedImgName] bpy.context.scene.node_tree.nodes["Image"].image = bpy.data.images[undistortedImgName]
def loadScene(filename): def loadModel(filename):
'''TODO''' '''Load model in Alembic of OBJ format. Make sure orientation matches camera orientation.'''
if filename.lower().endswith('.obj'): if filename.lower().endswith('.obj'):
bpy.ops.import_scene.obj(filepath=filename, axis_forward='Y', axis_up='Z') bpy.ops.import_scene.obj(filepath=filename, axis_forward='Y', axis_up='Z')
return bpy.data.objects['mesh'], bpy.data.meshes['mesh'] return bpy.data.objects['mesh'], bpy.data.meshes['mesh']
@ -160,7 +163,7 @@ def loadScene(filename):
def setupWireframeShading(mesh, color): def setupWireframeShading(mesh, color):
'''TODO''' '''Setup material for wireframe shading.'''
# Initialize wireframe material # Initialize wireframe material
material = bpy.data.materials.new('Wireframe') material = bpy.data.materials.new('Wireframe')
material.use_backface_culling = True material.use_backface_culling = True
@ -191,7 +194,7 @@ def setupWireframeShading(mesh, color):
def setupLineArtShading(obj, mesh, color): def setupLineArtShading(obj, mesh, color):
'''TODO''' '''Setup materials and Solidify modifier for line art shading.'''
# Transparent filling material # Transparent filling material
matFill = bpy.data.materials.new('Fill') matFill = bpy.data.materials.new('Fill')
matFill.use_backface_culling = True matFill.use_backface_culling = True
@ -225,7 +228,7 @@ def setupLineArtShading(obj, mesh, color):
def setupPointCloudShading(obj, color, size): def setupPointCloudShading(obj, color, size):
'''TODO''' '''Setup material and geometry nodes for point cloud shading.'''
# Colored filling material # Colored filling material
material = bpy.data.materials.new('PointCloud_Mat') material = bpy.data.materials.new('PointCloud_Mat')
material.use_nodes = True material.use_nodes = True
@ -278,6 +281,7 @@ def main():
parser.print_help() parser.print_help()
return -1 return -1
# Color palette (common for point cloud and mesh visualization)
palette={ palette={
'Grey':(0.2, 0.2, 0.2, 1), 'Grey':(0.2, 0.2, 0.2, 1),
'White':(1, 1, 1, 1), 'White':(1, 1, 1, 1),
@ -297,7 +301,7 @@ def main():
views, intrinsics, poses = parseSfMCameraFile(args.cameras) views, intrinsics, poses = parseSfMCameraFile(args.cameras)
print("Load scene objects") print("Load scene objects")
sceneObj, sceneMesh = loadScene(args.model) sceneObj, sceneMesh = loadModel(args.model)
print("Setup shading") print("Setup shading")
if args.model.lower().endswith('.obj'): if args.model.lower().endswith('.obj'):