scripts/render/render.py (129 lines of code) (raw):

#!/usr/bin/env python3 # Copyright 2004-present Facebook. All Rights Reserved. # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. """Entrypoint for distributed render back-end. The render script currently supports three modes of operation: - Single node (i.e. master/worker are the same machine) - LAN farms (i.e. master directly communicates to worker nodes in manually configured farm) - AWS farms (i.e. master sets up workers via kubernetes) For users who wish to get results rather than modify the internals, we suggest using the front-end to this render pipeline by using run.py. Example: To run a single node render: $ python render.py \ --input_root=/path/to/data \ --output_root=/path/to/data/output \ --rig=/path/to/data/rigs/rig_calibrated.json \ --first=001700 \ --last=001700 To run on LAN: $ python render.py \ --input_root=smb://192.168.1.100/example \ --output_root=smb://192.168.1.100/example/output \ --rig=smb://192.168.1.100/example/rigs/rig_calibrated.json \ --first=001700 \ --last=001700 \ --master=192.168.1.100 \ --workers=192.168.1.100,192.168.1.101 To run on AWS: $ python render.py \ --input_root=s3://example/data \ --output_root=s3://example/data/output \ --rig=s3://example/data/rigs/rig_calibrated.json \ --first=001700 \ --last=001700 Attributes: FLAGS (absl.flags._flagvalues.FlagValues): Globally defined flags for render.py. """ import logging import os import sys from absl import app, flags dir_scripts = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) dir_root = os.path.dirname(dir_scripts) sys.path.append(dir_root) sys.path.append(os.path.join(dir_scripts, "util")) import config import glog_check as glog import setup from network import Address, get_frame_name from pipeline import Pipeline from scripts.util.system_util import image_type_paths requests_logger = logging.getLogger("pika") requests_logger.propagate = False # prevents pika logger from being displayed FLAGS = flags.FLAGS def verify_inputs(): """Verifies that all the command line flags are valid.""" glog.check_ne(FLAGS.input_root, "", "Input_root cannot be empty") glog.check_ne(FLAGS.output_root, "", "Output_root cannot be empty") if not FLAGS.rig: FLAGS.rig = os.path.join(FLAGS.input_root, "rig.json") if not FLAGS.color: FLAGS.color = os.path.join(FLAGS.input_root, image_type_paths["color"]) if not FLAGS.background_disp: FLAGS.background_disp = os.path.join( FLAGS.input_root, image_type_paths["background_disp"] ) if not FLAGS.background_color: FLAGS.background_color = os.path.join( FLAGS.input_root, image_type_paths["background_color"] ) if not FLAGS.foreground_masks: FLAGS.foreground_masks = os.path.join( FLAGS.input_root, image_type_paths["foreground_masks"] ) FLAGS.workers = config.LOCALHOST if not FLAGS.workers else FLAGS.workers FLAGS.master = config.LOCALHOST if not FLAGS.master else FLAGS.master # Check flag values glog.check_ge(FLAGS.random_proposals, 0, "Random_proposals must be > 1") glog.check_le(FLAGS.first, FLAGS.last, "First must be <= last") if FLAGS.run_depth_estimation and FLAGS.do_temporal_filter: glog.check_gt(FLAGS.time_radius, 0, "Temporal filter radius must be > 0") num_frames = int(FLAGS.last) - int(FLAGS.first) + 1 # Ignore temporal filter if we do not have enough frames if num_frames < 2 * int(FLAGS.time_radius) - 1: FLAGS.do_temporal_filter = False FLAGS.time_radius = 0 glog.check_gt( num_frames, FLAGS.time_radius, f"Number of frames ({num_frames}) must be greater than temporal range ({FLAGS.time_radius})", ) def set_input_param(base_params, image_type): """Updates paths to the flags to reflect those constructed inside Docker. Args: base_params (dict[str, _]): Map of all the FLAGS defined in render.py. image_type (str): Name of an image type (re: source/util/ImageTypes.h). """ base_params[image_type] = os.path.join( config.DOCKER_INPUT_ROOT, image_type_paths[image_type] ) def main(): """Runs the main render pipeline with the parameters passed in through command line args.""" base_params = { flag: value for flag, value in FLAGS.flag_values_dict().items() if flag in setup.flag_names } if not FLAGS.skip_setup and FLAGS.cloud == "": setup.setup_workers(base_params) # If the address is a Samba or local endpoint, we have the addresses mapped in Docker # Access to the externally visible endpoint is only necessary in remote cases input_protocol = Address(FLAGS.input_root).protocol base_params["num_levels"] = len(config.WIDTHS) if input_protocol is None or input_protocol == "smb": base_params["input_root"] = config.DOCKER_INPUT_ROOT base_params["output_root"] = config.DOCKER_OUTPUT_ROOT base_params["rig"] = FLAGS.rig.replace( FLAGS.input_root, base_params["input_root"] ) input_image_types = { "background_color", "background_disp", "color", "foreground_masks", } for image_type in input_image_types: set_input_param(base_params, image_type) # frame_chunks use the Python range standard where first is included but last excluded frame_chunks = [ { "first": get_frame_name(frame), "last": get_frame_name(min(int(FLAGS.last), frame + FLAGS.chunk_size - 1)), } for frame in range(int(FLAGS.first), int(FLAGS.last) + 1, FLAGS.chunk_size) ] if FLAGS.background_frame == "": background_frame = None else: background_frame = [ { "first": get_frame_name(int(FLAGS.background_frame)), "last": get_frame_name(int(FLAGS.background_frame)), } ] pipeline = Pipeline( FLAGS.master, base_params, frame_chunks, background_frame, FLAGS.force_recompute ) # We need resized colors to compute foreground masks pipeline_stages = [ (pipeline.precompute_resizes, FLAGS.run_precompute_resizes), (pipeline.generate_foreground_masks, FLAGS.run_generate_foreground_masks), ] if FLAGS.use_foreground_masks: # Resize foreground masks pipeline_stages.append( ( pipeline.precompute_resizes_foreground, FLAGS.run_precompute_resizes_foreground, ) ) pipeline_stages.append((pipeline.depth_estimation, FLAGS.run_depth_estimation)) if FLAGS.format == "6dof": pipeline_stages += [ (pipeline.convert_to_binary, FLAGS.run_convert_to_binary), (pipeline.fusion, FLAGS.run_fusion), ] else: pipeline_stages.append( (pipeline.simple_mesh_renderer, FLAGS.run_simple_mesh_renderer) ) pipeline.run(pipeline_stages) setup.cleanup_workers() def main_loop(argv): """Validates and logs flags and renders with them if determined to be valid. Args: argv (list[str]): List of arguments (used interally by abseil). """ setup.init_facebook360_dep(FLAGS) setup.log_flags() verify_inputs() main() if __name__ == "__main__": # Abseil entry point app.run() expects all flags to be already defined setup.define_flags() app.run(main_loop)