def render_shadeless(blender_objects, path='flat.png'):
    """
  Render a version of the scene with shading disabled and unique materials
  assigned to all objects, and return a set of all colors that should be in the
  rendered image. The image itself is written to path. This is used to ensure
  that all objects will be visible in the final rendered scene.
  """
    render_args = bpy.context.scene.render

    # Cache the render args we are about to clobber
    old_filepath = render_args.filepath
    old_engine = render_args.engine
    old_use_antialiasing = render_args.use_antialiasing

    # Override some render settings to have flat shading
    render_args.filepath = path
    render_args.engine = 'BLENDER_RENDER'
    render_args.use_antialiasing = False

    # Move the lights and ground to layer 2 so they don't render
    # utils.set_layer(bpy.data.objects['Lamp_Key'], 2)
    # utils.set_layer(bpy.data.objects['Lamp_Fill'], 2)
    # utils.set_layer(bpy.data.objects['Lamp_Back'], 2)
    utils.set_layer(bpy.data.objects['Ground'], 2)

    # Add random shadeless materials to all objects
    object_colors = set()
    old_materials = []
    for i, obj in enumerate(blender_objects):
        old_materials.append(obj.data.materials[0])
        bpy.ops.material.new()
        mat = bpy.data.materials['Material']
        mat.name = 'Material_%d' % i
        while True:
            r, g, b = [random.random() for _ in range(3)]
            if (r, g, b) not in object_colors: break
        object_colors.add((r, g, b))
        mat.diffuse_color = [r, g, b]
        mat.use_shadeless = True
        obj.data.materials[0] = mat

    # Render the scene
    bpy.ops.render.render(write_still=True)

    # Undo the above; first restore the materials to objects
    for mat, obj in zip(old_materials, blender_objects):
        obj.data.materials[0] = mat

    # Move the lights and ground back to layer 0
    # utils.set_layer(bpy.data.objects['Lamp_Key'], 0)
    # utils.set_layer(bpy.data.objects['Lamp_Fill'], 0)
    # utils.set_layer(bpy.data.objects['Lamp_Back'], 0)
    utils.set_layer(bpy.data.objects['Ground'], 0)

    # Set the render settings back to what they were
    render_args.filepath = old_filepath
    render_args.engine = old_engine
    render_args.use_antialiasing = old_use_antialiasing

    return object_colors
Ejemplo n.º 2
0
def generate_images(folder, blender_objects):
    if args.use_gpu == 1:
        # Blender changed the API for enabling CUDA at some point
        if bpy.app.version < (2, 78, 0):
            bpy.context.user_preferences.system.compute_device_type = 'CUDA'
            bpy.context.user_preferences.system.compute_device = 'CUDA_0'
        else:
            cycles_prefs = bpy.context.user_preferences.addons['cycles'].preferences
            cycles_prefs.compute_device_type = 'CUDA'
        bpy.context.scene.cycles.device = 'GPU'
    render_args = bpy.context.scene.render
    old_filepath = render_args.filepath
    ground = bpy.data.objects['Ground']
    ground.location[0] = ground.location[1] = 0
    ground.rotation_euler[2] = 0
    ground.scale[0] = ground.scale[1] = 100
    ground.scale[2] = 0
    ground_extra = ground.copy()
    ground_extra.name = 'Ground_extra'
    ground_extra.rotation_euler[2] = np.pi
    bpy.context.scene.objects.link(ground_extra)
    for idx in range(args.num_views):
        theta = 2 * np.pi * np.random.uniform(args.min_theta, args.max_theta)
        phi = 0.5 * np.pi * np.random.uniform(args.min_phi, args.max_phi)
        rho = np.random.uniform(args.min_rho, args.max_rho)
        cos_theta = np.cos(theta)
        sin_theta = np.sin(theta)
        cos_phi = np.cos(phi)
        sin_phi = np.sin(phi)
        bpy.context.scene.camera.location[0] = rho * cos_phi * cos_theta
        bpy.context.scene.camera.location[1] = rho * cos_phi * sin_theta
        bpy.context.scene.camera.location[2] = rho * sin_phi
        with open(os.path.join(folder, 'view_{}.json'.format(idx)), 'w') as f:
            json.dump({'theta': theta, 'phi': phi, 'rho': rho}, f)
        # Scene
        render_args.filepath = os.path.join(folder, 'image_{}.png'.format(idx))
        bpy.ops.render.render(write_still=True)
        # Segmentation
        generate_mask(os.path.join(folder, 'segment_{}.png'.format(idx)), blender_objects)
        # Background
        for i, obj in enumerate(blender_objects):
            utils.set_layer(obj, 2)
#         render_args.filepath = os.path.join(folder, 'back_{}.png'.format(idx))
#         bpy.ops.render.render(write_still=True)
        # Objects
        for i, obj in enumerate(blender_objects):
            utils.set_layer(obj, 0)
#             render_args.filepath = os.path.join(folder, 'object_{}_{}.png'.format(idx, i))
#             bpy.ops.render.render(write_still=True)
            generate_mask(os.path.join(folder, 'mask_{}_{}.png'.format(idx, i)), blender_objects)
            utils.set_layer(obj, 2)
        for i, obj in enumerate(blender_objects):
            utils.set_layer(obj, 0)
    render_args.filepath = old_filepath
    return
Ejemplo n.º 3
0
def generate_images(folder, blender_objects):
    if args.use_gpu == 1:
        # Blender changed the API for enabling CUDA at some point
        if bpy.app.version < (2, 78, 0):
            bpy.context.user_preferences.system.compute_device_type = 'CUDA'
            bpy.context.user_preferences.system.compute_device = 'CUDA_0'
        else:
            cycles_prefs = bpy.context.user_preferences.addons[
                'cycles'].preferences
            cycles_prefs.compute_device_type = 'CUDA'
        bpy.context.scene.cycles.device = 'GPU'
    render_args = bpy.context.scene.render
    old_filepath = render_args.filepath
    # Scene
    render_args.filepath = os.path.join(folder, 'image.png')
    bpy.ops.render.render(write_still=True)
    # Segmentation
    generate_mask(os.path.join(folder, 'segment.png'), blender_objects)
    # Background
    for i, obj in enumerate(blender_objects):
        utils.set_layer(obj, 2)


#     render_args.filepath = os.path.join(folder, 'back.png')
#     bpy.ops.render.render(write_still=True)
# Objects
    for i, obj in enumerate(blender_objects):
        utils.set_layer(obj, 0)
        #         render_args.filepath = os.path.join(folder, 'object_{}.png'.format(i))
        #         bpy.ops.render.render(write_still=True)
        generate_mask(os.path.join(folder, 'mask_{}.png'.format(i)),
                      blender_objects)
        utils.set_layer(obj, 2)
    render_args.filepath = old_filepath
    return
Ejemplo n.º 4
0
def render_shadeless(blender_objects, path='flat.png'):
    """
  Render a version of the scene with shading disabled and unique materials
  assigned to all objects, and return a set of all colors that should be in the
  rendered image. The image itself is written to path. This is used to ensure
  that all objects will be visible in the final rendered scene.
  """
    render_args = bpy.context.scene.render

    # Cache the render args we are about to clobber
    old_filepath = render_args.filepath
    old_engine = render_args.engine
    if bpy.app.version <= (2, 79, 0):
        old_use_antialiasing = render_args.use_antialiasing
    old_view_transform = bpy.context.scene.view_settings.view_transform

    # Override some render settings to have flat shading
    render_args.filepath = path
    if bpy.app.version <= (2, 79, 0):
        render_args.engine = 'BLENDER_RENDER'  # default rendering engine
        render_args.use_antialiasing = False
    else:
        render_args.engine = 'BLENDER_EEVEE'  # default rendering engine

    # Move the lights and ground to layer 2 so they don't render
    if bpy.app.version <= (2, 79, 0):
        utils.set_layer(bpy.data.objects['Lamp_Key'], 2)
        utils.set_layer(bpy.data.objects['Lamp_Fill'], 2)
        utils.set_layer(bpy.data.objects['Lamp_Back'], 2)
        utils.set_layer(bpy.data.objects['Ground'], 2)
    else:
        # coll = bpy.data.collections.new("Collection")
        # bpy.context.scene.collection.children.link(coll)
        # print(bpy.data.objects.keys())
        # coll.objects.link(bpy.data.objects['Plane'])
        # coll.objects.link(bpy.data.objects['Lamp_Fill'])
        # coll.objects.link(bpy.data.objects['Lamp_Key'])
        # coll.objects.link(bpy.data.objects['Lamp_Back'])

        # bpy.context.window.view_layer.layer_collection.children["Collection"].exclude = True
        # # Update view layer
        # layer = bpy.context.view_layer
        # layer.update()

        bpy.context.scene.objects['Plane'].hide_render = True
        bpy.context.scene.objects['Lamp_Fill'].hide_render = True
        bpy.context.scene.objects['Lamp_Key'].hide_render = True
        bpy.context.scene.objects['Lamp_Back'].hide_render = True

        # v1 = bpy.context.scene.view_layers.new(name="Lights layer")
        # v1.active_layer_collection(coll)
        # bpy.context.window.view_layer = v1

        # # bpy.data.objects['Plane'].hide_set(False)
        # # bpy.data.objects['Lamp_Key'].hide_set(False)
        # # bpy.data.objects['Lamp_Fill'].hide_set(False)
        # # bpy.data.objects['Lamp_Back'].hide_set(False)

    # Add random shadeless materials to all objects
    object_colors = set()
    old_materials = []
    for i, obj in enumerate(blender_objects):
        old_materials.append(obj.data.materials[0])
        bpy.ops.material.new()
        mat = bpy.data.materials['Material']
        mat.name = 'Material_%d' % i
        while True:
            r, g, b = [random.random() for _ in range(3)]
            if (r, g, b) not in object_colors: break
        object_colors.add((r, g, b))
        if bpy.app.version <= (2, 79, 0):
            mat.use_shadeless = True
            mat.diffuse_color = [r, g, b]
        else:
            mat.use_nodes = True
            mat.node_tree.nodes.remove(
                mat.node_tree.nodes.get(
                    'Principled BSDF'))  # Remove default node
            material_output = mat.node_tree.nodes.get('Material Output')
            emission = mat.node_tree.nodes.new('ShaderNodeEmission')
            emission.inputs['Strength'].default_value = 1
            emission.inputs['Color'].default_value = [r, g, b, 1.]
            print(r, g, b)
            mat.node_tree.links.new(material_output.inputs[0],
                                    emission.outputs[0])
            obj.active_material = mat
            bpy.context.scene.view_settings.view_transform = 'Standard'
        obj.data.materials[0] = mat

    # Render the scene
    bpy.ops.render.render(write_still=True)

    # Undo the above; first restore the materials to objects
    for mat, obj in zip(old_materials, blender_objects):
        obj.data.materials[0] = mat
        obj.active_material = mat

    # Move the lights and ground back to layer 0
    if bpy.app.version <= (2, 79, 0):
        utils.set_layer(bpy.data.objects['Lamp_Key'], 0)
        utils.set_layer(bpy.data.objects['Lamp_Fill'], 0)
        utils.set_layer(bpy.data.objects['Lamp_Back'], 0)
        utils.set_layer(bpy.data.objects['Ground'], 0)
    else:
        bpy.context.scene.objects['Plane'].hide_render = False
        bpy.context.scene.objects['Lamp_Fill'].hide_render = False
        bpy.context.scene.objects['Lamp_Key'].hide_render = False
        bpy.context.scene.objects['Lamp_Back'].hide_render = False

        bpy.context.scene.view_settings.view_transform = old_view_transform

    # Set the render settings back to what they were
    render_args.filepath = old_filepath
    render_args.engine = old_engine
    if bpy.app.version <= (2, 79, 0):
        render_args.use_antialiasing = old_use_antialiasing

    return object_colors
Ejemplo n.º 5
0
def render_masks(blender_objects, path_prefix):

  render_args = bpy.context.scene.render

  # Cache the render args we are about to clobber
  old_filepath = render_args.filepath
  old_engine = render_args.engine
  old_use_antialiasing = render_args.use_antialiasing

  # Override some render settings to have flat shading
  render_args.filepath = path_prefix
  render_args.engine = 'BLENDER_RENDER'
  render_args.use_antialiasing = False
  render_args.layers['RenderLayer'].use_pass_object_index = True

  bpy.context.scene.use_nodes = True
  tree = bpy.context.scene.node_tree
  render_layers = tree.nodes.new('CompositorNodeRLayers')
  index_buffer = render_layers.outputs['IndexOB']

  masker_nodes = []
  output_nodes = []
  masker_to_output_links = []
  for i, obj in enumerate(blender_objects):
    obj.pass_index = i + 1
    masker = tree.nodes.new('CompositorNodeIDMask')
    masker.index = i + 1
    masker_nodes.append(masker)
    tree.links.new(index_buffer, masker.inputs[0])
    mask_file_output = tree.nodes.new("CompositorNodeOutputFile")
    mask_file_output.file_slots[0].path = 'mask_{:02}_modal_###.png'.format(i + 1)
    output_nodes.append(mask_file_output)
    masker_to_output_links.append(tree.links.new(masker.outputs[0], mask_file_output.inputs[0]))

  # Render all the modal masks
  bpy.ops.render.render(write_still=False)

  for i, obj in enumerate(blender_objects):
    maybe_crop(os.path.join(path_prefix, output_nodes[i].file_slots[0].path.replace('###', '001')))
    utils.set_layer(obj, 2)
    tree.links.remove(masker_to_output_links[i])
    tree.nodes.remove(output_nodes[i])

  # Render each amodal mask
  for i, obj in enumerate(blender_objects):
    utils.set_layer(obj, 0)
    output_node = tree.nodes.new("CompositorNodeOutputFile")
    output_node.file_slots[0].path = 'mask_{:02}_amodal_###.png'.format(i + 1)
    masker_to_output = tree.links.new(masker_nodes[i].outputs[0], output_node.inputs[0])
    bpy.ops.render.render(write_still=False)
    maybe_crop(os.path.join(path_prefix, output_node.file_slots[0].path.replace('###', '001')))
    tree.links.remove(masker_to_output)
    tree.nodes.remove(output_node)
    utils.set_layer(obj, 2)

  for i, obj in enumerate(blender_objects):
    utils.set_layer(obj, 0)

  # Set the render settings back to what they were
  render_args.filepath = old_filepath
  render_args.engine = old_engine
  render_args.use_antialiasing = old_use_antialiasing
Ejemplo n.º 6
0
def render_scene(args,
    num_objects=5,
    output_index=0,
    output_split='none',
    output_image='render.png',
    output_scene='render_json',
    output_masks=None,
    output_blendfile=None,
  ):

  # Load the main blendfile
  bpy.ops.wm.open_mainfile(filepath=args.base_scene_blendfile)

  # Load materials
  utils.load_materials(args.material_dir)

  # Set render arguments so we can get pixel coordinates later.
  # We use functionality specific to the CYCLES renderer so BLENDER_RENDER
  # cannot be used.
  render_args = bpy.context.scene.render
  render_args.engine = "CYCLES"
  render_args.filepath = output_image
  render_args.resolution_x = args.width
  render_args.resolution_y = args.height
  render_args.resolution_percentage = 100
  render_args.tile_x = args.render_tile_size
  render_args.tile_y = args.render_tile_size
  if args.use_gpu == 1:
    # Blender changed the API for enabling CUDA at some point
    if bpy.app.version < (2, 78, 0):
      bpy.context.user_preferences.system.compute_device_type = 'CUDA'
      bpy.context.user_preferences.system.compute_device = 'CUDA_0'
    else:
      cycles_prefs = bpy.context.user_preferences.addons['cycles'].preferences
      cycles_prefs.compute_device_type = 'CUDA'

  # Some CYCLES-specific stuff
  bpy.data.worlds['World'].cycles.sample_as_light = True
  bpy.context.scene.cycles.blur_glossy = 2.0
  bpy.context.scene.cycles.samples = args.render_num_samples
  bpy.context.scene.cycles.transparent_min_bounces = args.render_min_bounces
  bpy.context.scene.cycles.transparent_max_bounces = args.render_max_bounces
  if args.use_gpu == 1:
    bpy.context.scene.cycles.device = 'GPU'

  if args.output_depth:
    # Following is based on stanford-shapenet-renderer
    bpy.context.scene.use_nodes = True
    tree = bpy.context.scene.node_tree
    render_layers = tree.nodes.new('CompositorNodeRLayers')
    depth_file_output = tree.nodes.new(type="CompositorNodeOutputFile")
    depth_file_output.label = 'Depth Output'
    depth_file_output.file_slots[0].path = '../../' + output_image + '_depth'
    map = tree.nodes.new(type="CompositorNodeNormalize")  # thus, most distant points have pixel intensity of one, and nearest zero
    tree.links.new(render_layers.outputs['Depth'], map.inputs[0])
    tree.links.new(map.outputs[0], depth_file_output.inputs[0])

  # This will give ground-truth information about the scene and its objects
  scene_struct = {
      'split': output_split,
      'image_index': output_index,
      'image_filename': os.path.basename(output_image),
      'objects': [],
      'directions': {},
  }

  # Put a plane on the ground so we can compute cardinal directions
  bpy.ops.mesh.primitive_plane_add(radius=5)
  plane = bpy.context.object

  def rand(L):
    return 2.0 * L * (random.random() - 0.5)

  # Add random jitter to camera position
  if args.camera_jitter > 0:
    for i in range(3):
      bpy.data.objects['Camera'].location[i] += rand(args.camera_jitter)

  # Figure out the left, up, and behind directions along the plane and record
  # them in the scene structure
  camera = bpy.data.objects['Camera']
  plane_normal = plane.data.vertices[0].normal
  cam_behind = camera.matrix_world.to_quaternion() * Vector((0, 0, -1))
  cam_left = camera.matrix_world.to_quaternion() * Vector((-1, 0, 0))
  cam_up = camera.matrix_world.to_quaternion() * Vector((0, 1, 0))
  plane_behind = (cam_behind - cam_behind.project(plane_normal)).normalized()
  plane_left = (cam_left - cam_left.project(plane_normal)).normalized()
  plane_up = cam_up.project(plane_normal).normalized()

  # Delete the plane; we only used it for normals anyway. The base scene file
  # contains the actual ground plane.
  utils.delete_object(plane)

  # Save all six axis-aligned directions in the scene struct
  scene_struct['directions']['behind'] = tuple(plane_behind)
  scene_struct['directions']['front'] = tuple(-plane_behind)
  scene_struct['directions']['left'] = tuple(plane_left)
  scene_struct['directions']['right'] = tuple(-plane_left)
  scene_struct['directions']['above'] = tuple(plane_up)
  scene_struct['directions']['below'] = tuple(-plane_up)

  # Add random jitter to lamp positions
  if args.key_light_jitter > 0:
    for i in range(3):
      bpy.data.objects['Lamp_Key'].location[i] += rand(args.key_light_jitter)
  if args.back_light_jitter > 0:
    for i in range(3):
      bpy.data.objects['Lamp_Back'].location[i] += rand(args.back_light_jitter)
  if args.fill_light_jitter > 0:
    for i in range(3):
      bpy.data.objects['Lamp_Fill'].location[i] += rand(args.fill_light_jitter)

  # Now make some random objects
  objects, blender_objects = add_random_objects(scene_struct, num_objects, args, camera)

  if args.no_background:
    # This must come after add_random_objects, as that also changes the ground layer
    utils.set_layer(bpy.data.objects['Ground'], 2)
  else:
    # Note that in base_scene, the ground has no material (hence uses blender's default)
    bpy.data.materials.new(name='Ground_Material')
    ground_mat = bpy.data.materials['Ground_Material']
    background_intensity = args.background_intensities[random.randrange(len(args.background_intensities))]
    ground_mat.diffuse_color = [background_intensity] * 3
    bpy.data.objects['Ground'].data.materials.append(ground_mat)

  # Render the scene and dump the scene data structure
  scene_struct['objects'] = objects
  scene_struct['relationships'] = compute_all_relationships(scene_struct)
  while True:
    try:
      bpy.ops.render.render(write_still=True)
      break
    except Exception as e:
      print(e)

  if args.crop:
    maybe_crop(output_image)
    if args.output_depth:
      raise NotImplementedError

  if output_masks is not None:
    render_masks(blender_objects, output_masks)

  with open(output_scene, 'w') as f:
    json.dump(scene_struct, f, indent=2)

  if output_blendfile is not None:
    bpy.ops.wm.save_as_mainfile(filepath=output_blendfile)
Ejemplo n.º 7
0
def generate_mask(path, blender_objects, channel_split=3, antialiasing=True):
    render_args = bpy.context.scene.render
    # Cache the render args we are about to clobber
    old_filepath = render_args.filepath
    old_engine = render_args.engine
    old_use_antialiasing = render_args.use_antialiasing
    old_alpha_mode = render_args.alpha_mode
    old_color_mode = render_args.image_settings.color_mode
    # Override some render settings to have flat shading
    render_args.filepath = path
    render_args.engine = 'BLENDER_RENDER'
    render_args.use_antialiasing = antialiasing
    render_args.alpha_mode = 'TRANSPARENT'
    render_args.image_settings.color_mode = 'RGBA'
    # Move the lights and ground to layer 2 so they don't render
    utils.set_layer(bpy.data.objects['Lamp_Key'], 2)
    utils.set_layer(bpy.data.objects['Lamp_Fill'], 2)
    utils.set_layer(bpy.data.objects['Lamp_Back'], 2)
    utils.set_layer(bpy.data.objects['Ground'], 2)
    # Add random shadeless materials to all objects
    old_materials = []
    for i, obj in enumerate(blender_objects):
        old_materials.append(obj.data.materials[0])
        bpy.ops.material.new()
        mat = bpy.data.materials['Material']
        mat.name = 'Material_%d' % i
        r = (i % channel_split) / (channel_split - 1)
        idx = i // channel_split
        g = (idx % channel_split) / (channel_split - 1)
        idx //= channel_split
        b = (idx % channel_split) / (channel_split - 1)
        mat.diffuse_color = [r, g, b]
        mat.use_shadeless = True
        obj.data.materials[0] = mat
    # Render the scene
    bpy.ops.render.render(write_still=True)
    # Undo the above; first restore the materials to objects
    for mat, obj in zip(old_materials, blender_objects):
        obj.data.materials[0] = mat
    # Move the lights and ground back to layer 0
    utils.set_layer(bpy.data.objects['Lamp_Key'], 0)
    utils.set_layer(bpy.data.objects['Lamp_Fill'], 0)
    utils.set_layer(bpy.data.objects['Lamp_Back'], 0)
    utils.set_layer(bpy.data.objects['Ground'], 0)
    # Set the render settings back to what they were
    render_args.filepath = old_filepath
    render_args.engine = old_engine
    render_args.use_antialiasing = old_use_antialiasing
    render_args.alpha_mode = old_alpha_mode
    render_args.image_settings.color_mode = old_color_mode
    return
Ejemplo n.º 8
0
def render_shadeless(blender_objects, path='flat.png'):
    """
  Render a version of the scene with shading disabled and unique materials
  assigned to all objects, and return a set of all colors that should be in the
  rendered image. The image itself is written to path. This is used to ensure
  that all objects will be visible in the final rendered scene.
  """
    render_args = bpy.context.scene.render

    # Cache the render args we are about to clobber
    old_filepath = render_args.filepath
    old_engine = render_args.engine
    old_use_antialiasing = render_args.use_antialiasing

    # Override some render settings to have flat shading
    render_args.filepath = path
    render_args.engine = 'BLENDER_RENDER'
    render_args.use_antialiasing = False

    # Move the lights and ground to layer 2 so they don't render
    utils.set_layer(bpy.data.objects['Lamp_Key'], 2)
    utils.set_layer(bpy.data.objects['Lamp_Fill'], 2)
    utils.set_layer(bpy.data.objects['Lamp_Back'], 2)
    utils.set_layer(bpy.data.objects['Ground'], 2)

    # Add random shadeless materials to all objects
    object_colors = set()
    text_colors = {}
    old_materials = []

    for i, obj in enumerate(blender_objects):
        old_materials.append(obj.data.materials[0])
        bpy.ops.material.new()
        mat = bpy.data.materials['Material']
        mat.name = 'Material_%d' % i
        r, g, b = i * .01, i * .01, i * .01

        if "Mesh" in obj.data.name or "CUText" in obj.data.name:
            text_colors[obj.data.name] = (r, g, b)
        else:
            object_colors.add((r, g, b))
        mat.diffuse_color = [r, g, b]
        mat.use_shadeless = True
        obj.data.materials[0] = mat

    # Need to do this to not transform the colors too much during rendering
    bpy.context.scene.render.image_settings.view_settings.view_transform = "Raw"
    bpy.context.scene.render.image_settings.file_format = 'OPEN_EXR'
    bpy.context.scene.render.image_settings.color_mode = 'RGB'

    # Render the scene
    bpy.ops.render.render(write_still=True)

    # Change these settings back so we render out the PNG images
    bpy.context.scene.render.image_settings.view_settings.view_transform = "Default"
    bpy.context.scene.render.image_settings.file_format = 'JPEG'
    bpy.context.scene.render.image_settings.color_mode = 'RGB'

    # Undo the above; first restore the materials to objects
    for mat, obj in zip(old_materials, blender_objects):
        obj.data.materials[0] = mat

    # Move the lights and ground back to layer data
    utils.set_layer(bpy.data.objects['Lamp_Key'], 0)
    utils.set_layer(bpy.data.objects['Lamp_Fill'], 0)
    utils.set_layer(bpy.data.objects['Lamp_Back'], 0)
    utils.set_layer(bpy.data.objects['Ground'], 0)

    # Set the render settings back to what they were
    render_args.filepath = old_filepath
    render_args.engine = old_engine
    render_args.use_antialiasing = old_use_antialiasing

    return object_colors, text_colors