NCTCMumbai's picture
Upload 2571 files
0b8359d
raw
history blame
8.59 kB
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# =============================================================================
"""Script to render object views from ShapeNet obj models.
Example usage:
blender -b --python render.py -- -m model.obj -o output/ -s 128 -n 120 -fov 5
"""
from __future__ import print_function
import argparse
import itertools
import json
from math import pi
import os
import random
import sys
from mathutils import Vector
import math
import mathutils
import time
import copy
import bpy
sys.path.append(os.path.dirname(__file__))
BG_LUMINANCE = 0
def look_at(obj_camera, point):
loc_camera = obj_camera.location
direction = point - loc_camera
# point the cameras '-Z' and use its 'Y' as up
rot_quat = direction.to_track_quat('-Z', 'Y')
obj_camera.rotation_euler = rot_quat.to_euler()
def roll_camera(obj_camera):
roll_rotate = mathutils.Euler(
(0, 0, random.random() * math.pi - math.pi * 0.5), 'XYZ')
obj_camera.rotation_euler = (obj_camera.rotation_euler.to_matrix() *
roll_rotate.to_matrix()).to_euler()
def norm(x):
return math.sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2])
def normalize(x):
n = norm(x)
x[0] /= n
x[1] /= n
x[2] /= n
def random_top_sphere():
xyz = [random.normalvariate(0, 1) for x in range(3)]
normalize(xyz)
if xyz[2] < 0:
xyz[2] *= -1
return xyz
def perturb_sphere(loc, size):
while True:
xyz = [random.normalvariate(0, 1) for x in range(3)]
normalize(xyz)
nloc = [loc[i] + xyz[i] * random.random() * size for i in range(3)]
normalize(nloc)
if nloc[2] >= 0:
return nloc
def perturb(loc, size):
while True:
nloc = [loc[i] + random.random() * size * 2 - size for i in range(3)]
if nloc[2] >= 0:
return nloc
bpy.ops.object.mode_set()
def delete_all_objects():
bpy.ops.object.select_by_type(type="MESH")
bpy.ops.object.delete(use_global=False)
def set_scene(render_size, fov, alpha=False):
"""Set up default scene properties."""
delete_all_objects()
cam = bpy.data.cameras["Camera"]
cam.angle = fov * pi / 180
light = bpy.data.objects["Lamp"]
light.location = (0, 0, 1)
look_at(light, Vector((0.0, 0, 0)))
bpy.data.lamps['Lamp'].type = "HEMI"
bpy.data.lamps['Lamp'].energy = 1
bpy.data.lamps['Lamp'].use_specular = False
bpy.data.lamps['Lamp'].use_diffuse = True
bpy.context.scene.world.horizon_color = (
BG_LUMINANCE, BG_LUMINANCE, BG_LUMINANCE)
bpy.context.scene.render.resolution_x = render_size
bpy.context.scene.render.resolution_y = render_size
bpy.context.scene.render.resolution_percentage = 100
bpy.context.scene.render.use_antialiasing = True
bpy.context.scene.render.antialiasing_samples = '5'
def get_modelview_matrix():
cam = bpy.data.objects["Camera"]
bpy.context.scene.update()
# when apply to object with CV coordinate i.e. to_blender * obj
# this gives object in blender coordinate
to_blender = mathutils.Matrix(
((1., 0., 0., 0.),
(0., 0., -1., 0.),
(0., 1., 0., 0.),
(0., 0., 0., 1.)))
return cam.matrix_world.inverted() * to_blender
def print_matrix(f, mat):
for i in range(4):
for j in range(4):
f.write("%lf " % mat[i][j])
f.write("\n")
def mul(loc, v):
return [loc[i] * v for i in range(3)]
def merge_all():
bpy.ops.object.select_by_type(type="MESH")
bpy.context.scene.objects.active = bpy.context.selected_objects[0]
bpy.ops.object.join()
obj = bpy.context.scene.objects.active
bpy.ops.object.origin_set(type="ORIGIN_CENTER_OF_MASS")
return obj
def insert_frame(obj, frame_number):
obj.keyframe_insert(data_path="location", frame=frame_number)
obj.keyframe_insert(data_path="rotation_euler", frame=frame_number)
obj.keyframe_insert(data_path="scale", frame=frame_number)
def render(output_prefix):
bpy.context.scene.render.filepath = output_prefix
bpy.context.scene.render.image_settings.file_format = "PNG"
bpy.context.scene.render.alpha_mode = "TRANSPARENT"
bpy.context.scene.render.image_settings.color_mode = "RGBA"
bpy.ops.render.render(write_still=True, animation=True)
def render_obj(
obj_fn, save_dir, n, perturb_size, rotate=False, roll=False, scale=1.0):
# Load object.
bpy.ops.import_scene.obj(filepath=obj_fn)
cur_obj = merge_all()
scale = 2.0 / max(cur_obj.dimensions) * scale
cur_obj.scale = (scale, scale, scale)
# Using the center of mass as the origin doesn't really work, because Blender
# assumes the object is a solid shell. This seems to generate better-looking
# rotations.
bpy.ops.object.origin_set(type='ORIGIN_GEOMETRY', center='BOUNDS')
# bpy.ops.mesh.primitive_cube_add(location=(0, 0, 1))
# cube = bpy.data.objects["Cube"]
# cube.scale = (0.2, 0.2, 0.2)
for polygon in cur_obj.data.polygons:
polygon.use_smooth = True
bpy.ops.object.select_all(action="DESELECT")
camera = bpy.data.objects["Camera"]
# os.system("mkdir " + save_dir)
for i in range(n):
fo = open(save_dir + "/%06d.txt" % i, "w")
d = 30
shift = 0.2
if rotate:
t = 1.0 * i / (n-1) * 2 * math.pi
loc = [math.sin(t), math.cos(t), 1]
normalize(loc)
camera.location = mul(loc, d)
look_at(camera, Vector((0.0, 0, 0)))
print_matrix(fo, get_modelview_matrix())
print_matrix(fo, get_modelview_matrix())
insert_frame(camera, 2 * i)
insert_frame(camera, 2 * i + 1)
else:
loc = random_top_sphere()
camera.location = mul(loc, d)
look_at(camera, Vector((0.0, 0, 0)))
if roll:
roll_camera(camera)
camera.location = perturb(mul(loc, d), shift)
print_matrix(fo, get_modelview_matrix())
insert_frame(camera, 2 * i)
if perturb_size > 0:
loc = perturb_sphere(loc, perturb_size)
else:
loc = random_top_sphere()
camera.location = mul(loc, d)
look_at(camera, Vector((0.0, 0, 0)))
if roll:
roll_camera(camera)
camera.location = perturb(mul(loc, d), shift)
print_matrix(fo, get_modelview_matrix())
insert_frame(camera, 2 * i + 1)
fo.close()
# Create a bunch of views of the object
bpy.context.scene.frame_start = 0
bpy.context.scene.frame_end = 2 * n - 1
stem = os.path.join(save_dir, '######')
render(stem)
def main():
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--model', dest='model',
required=True,
help='Path to model obj file.')
parser.add_argument('-o', '--output_dir', dest='output_dir',
required=True,
help='Where to output files.')
parser.add_argument('-s', '--output_size', dest='output_size',
required=True,
help='Width and height of output in pixels, e.g. 32x32.')
parser.add_argument('-n', '--num_frames', dest='n', type=int,
required=True,
help='Number of frames to generate per clip.')
parser.add_argument('-scale', '--scale', dest='scale', type=float,
help='object scaling', default=1)
parser.add_argument('-perturb', '--perturb', dest='perturb', type=float,
help='sphere perturbation', default=0)
parser.add_argument('-rotate', '--rotate', dest='rotate', action='store_true',
help='render rotating test set')
parser.add_argument('-roll', '--roll', dest='roll', action='store_true',
help='add roll')
parser.add_argument(
'-fov', '--fov', dest='fov', type=float, required=True,
help='field of view')
if '--' not in sys.argv:
parser.print_help()
exit(1)
argv = sys.argv[sys.argv.index('--') + 1:]
args, _ = parser.parse_known_args(argv)
random.seed(args.model + str(time.time()) + str(os.getpid()))
# random.seed(0)
set_scene(int(args.output_size), args.fov)
render_obj(
args.model, args.output_dir, args.n, args.perturb, args.rotate,
args.roll, args.scale)
exit()
if __name__ == '__main__':
main()