|
1 | 1 | import os |
2 | 2 | import sys |
3 | | -import argparse |
4 | | -import yaml |
5 | | - |
6 | | -import bpy |
7 | 3 |
|
8 | 4 | SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) |
| 5 | +BASE_DIR = os.path.dirname(SCRIPT_DIR) |
9 | 6 | sys.path.append(SCRIPT_DIR) |
10 | 7 |
|
11 | | -from utils import * |
12 | | - |
13 | | - |
14 | | -class Generator(): |
15 | | - def parse_images(self): |
16 | | - self.img_names = os.listdir(self.src) |
17 | | - |
18 | | - def _parse_config(self, config_path): |
19 | | - with open(config_path, 'r') as f: |
20 | | - cfg = yaml.safe_load(f) |
21 | | - |
22 | | - # Required parameters |
23 | | - if 'source_folder' in cfg: |
24 | | - self.src = cfg['source_folder'] |
25 | | - else: |
26 | | - raise KeyError('"source_folder" is not found in {config_path}') |
27 | | - |
28 | | - if 'destination_folder' in cfg: |
29 | | - self.dest = cfg['destination_folder'] |
30 | | - else: |
31 | | - raise KeyError('"destination_folder" is not found in {config_path}') |
32 | | - |
33 | | - # Not required parameters |
34 | | - if 'camera_angles_range' in cfg: |
35 | | - self.camera_angles_range = [[b for b in a] for a in cfg['camera_angles_range']] |
36 | | - else: |
37 | | - self.camera_angles_range = ((90, 0), (90, 0)) |
38 | | - |
39 | | - if 'camera_distance_range' in cfg: |
40 | | - self.camera_distance_range = cfg['camera_distance_range'] |
41 | | - else: |
42 | | - self.camera_distance_range = (5, 5) |
43 | | - |
44 | | - if 'render_per_input' in cfg: |
45 | | - self.render_per_input = cfg['render_per_input'] |
46 | | - else: |
47 | | - self.render_per_input = 1 |
48 | | - |
49 | | - self.parse_images() |
50 | | - |
51 | | - def __init__(self, config_path) -> None: |
52 | | - self._parse_config(config_path) |
53 | | - |
54 | | - def _clear_scene(self): |
55 | | - while(len(bpy.data.objects)): |
56 | | - obj = bpy.data.objects[-1] |
57 | | - obj.select = True |
58 | | - bpy.ops.object.delete(use_global=False) |
59 | | - |
60 | | - def _add_camera(self): |
61 | | - ang = self.camera_angles_range |
62 | | - first_min, first_max = ang[0][0], ang[1][0] |
63 | | - second_min, second_max = ang[0][1], ang[1][1] |
64 | | - first_ang = np.random.random() * (first_max - first_min) + first_min |
65 | | - second_ang = np.random.random() * (second_max - second_min) + second_min |
66 | | - |
67 | | - d_max, d_min = self.camera_distance_range |
68 | | - dist = np.random.random() * (d_max - d_min) + d_min |
69 | | - |
70 | | - loc = spherical2cartesian((deg2rad(first_ang), deg2rad(second_ang)), dist) |
71 | | - |
72 | | - bpy.ops.object.camera_add( |
73 | | - view_align=True, |
74 | | - enter_editmode=False, |
75 | | - location=loc, |
76 | | - rotation=((0., 0., 0.))) |
77 | | - |
78 | | - cam = bpy.data.objects['Camera'] |
79 | | - constraint = cam.constraints.new(type='LIMIT_LOCATION') |
80 | | - constraint.use_min_z = True |
81 | | - |
82 | | - constraint = cam.constraints.new('TRACK_TO') |
83 | | - constraint.target = self.image |
84 | | - constraint.track_axis = 'TRACK_NEGATIVE_Z' |
85 | | - constraint.up_axis = 'UP_Y' |
86 | | - |
87 | | - def _add_lights(self): |
88 | | - bpy.ops.object.lamp_add(type='POINT', |
89 | | - radius=1, view_align=False, |
90 | | - location=(2, -2, 2)) |
91 | | - |
92 | | - def _add_objects(self, img_name): |
93 | | - bpy.ops.import_image.to_plane( |
94 | | - files=[{"name": img_name}], |
95 | | - directory=self.src, |
96 | | - filter_image=True, filter_movie=True, filter_glob="", relative=False, |
97 | | - location=(0,0,0)) |
98 | | - name = img_name.split('.')[0] |
99 | | - self.image = bpy.data.objects[name] |
100 | | - self.image.rotation_euler[0] = deg2rad(90) |
101 | | - self.image.rotation_euler[2] = deg2rad(90) |
102 | | - |
103 | | - # Wall |
104 | | - bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False, |
105 | | - location=(-0.01, 0, 0)) |
106 | | - plane = bpy.data.objects['Plane'] |
107 | | - y_scale = np.random.random() * 3 + 1 |
108 | | - plane.scale[0] = y_scale |
109 | | - plane.rotation_euler[0] = deg2rad(90) |
110 | | - plane.rotation_euler[2] = deg2rad(90) |
111 | | - |
112 | | - bpy.ops.material.new() |
113 | | - plane.data.materials.append(bpy.data.materials[0]) |
114 | | - plane.data.materials[0].diffuse_color = np.random.random(3) |
115 | | - |
116 | | - # Floor |
117 | | - bpy.ops.mesh.primitive_plane_add(radius=1, view_align=False, enter_editmode=False, |
118 | | - location=(0, 0, -0.7)) |
119 | | - plane = bpy.data.objects['Plane.001'] |
120 | | - plane.scale[0] = 10 |
121 | | - plane.scale[1] = 10 |
122 | | - |
123 | | - bpy.ops.material.new() |
124 | | - plane.data.materials.append(bpy.data.materials[1]) |
125 | | - plane.data.materials[0].diffuse_color = np.random.random(3) |
126 | | - |
| 8 | +from datagen import Generator |
127 | 9 |
|
128 | | - def _arrange_scene(self, img_name): |
129 | | - self._clear_scene() |
130 | | - self._add_lights() |
131 | | - self._add_objects(img_name) |
132 | | - self._add_camera() |
133 | 10 |
|
134 | | - def _render(self, index, img_name): |
135 | | - bpy.context.scene.camera = bpy.data.objects['Camera'] |
| 11 | +# The folder with images to render |
| 12 | +source_folder = os.path.join(BASE_DIR, "src") |
136 | 13 |
|
137 | | - base, ext = img_name.split(os.extsep) |
138 | | - img_name = base + "_{0:0>5d}".format(index) |
139 | | - '.'.join((img_name, ext)) |
| 14 | +# The folder to place renders |
| 15 | +destination_folder = os.path.join(BASE_DIR, "dst") |
140 | 16 |
|
141 | | - bpy.context.scene.render.filepath = os.path.join(self.dest, img_name) |
142 | | - bpy.ops.render.render('INVOKE_DEFAULT', write_still=True) |
| 17 | +# The angles and distance are the spherical coordinates of the camera |
| 18 | +# given that the position of the object in the scene is (0,0,0) |
| 19 | +# render_per_input times different angles and distances are sampled |
| 20 | +# from these ranges |
| 21 | +camera_angles_range = [[70, -60], [110, 60]] |
143 | 22 |
|
144 | | - def mainloop(self): |
145 | | - for name in self.img_names: |
146 | | - for i in range(self.render_per_input): |
147 | | - self._arrange_scene(name) |
148 | | - self._render(i, name) |
| 23 | +camera_distance_range = [3, 5] |
149 | 24 |
|
| 25 | +render_per_input = 2 |
150 | 26 |
|
151 | | -argv = sys.argv[sys.argv.index('--') + 1:] |
152 | | -parser = argparse.ArgumentParser() |
153 | | -parser.add_argument('-c', '--cfg', type=str) |
154 | | -args = parser.parse_known_args(argv)[0] |
155 | 27 |
|
156 | | -if __name__ == '__main__': |
157 | | - gen = Generator(args.cfg) |
158 | | - gen.mainloop() |
| 28 | +if __name__ == "__main__": |
| 29 | + gen = Generator( |
| 30 | + src=source_folder, |
| 31 | + dest=destination_folder, |
| 32 | + camera_angles_range=camera_angles_range, |
| 33 | + camera_distance_range=camera_distance_range, |
| 34 | + render_per_input=render_per_input, |
| 35 | + ).run() |
0 commit comments