├── .idea
├── blender-deep-learning.iml
├── inspectionProfiles
│ └── Project_Default.xml
├── modules.xml
├── vcs.xml
└── workspace.xml
├── Dockerfile
├── README.md
├── deploy
├── config.json
└── docker-compose.yaml
├── images
├── depth_map.png
└── sphere.png
├── models
├── default.blend
└── default.blend1
└── src
├── dataset_creation.py
├── entrypoint.sh
├── rendering.py
├── requirements.txt
└── utils.py
/.idea/blender-deep-learning.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
137 |
138 |
139 |
140 | root
141 | base_folder
142 | update
143 | home
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 |
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 | $USER_HOME$/.subversion
239 |
240 |
241 |
242 |
243 | 1528172191166
244 |
245 |
246 | 1528172191166
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM python:3.6
2 |
3 | RUN apt-get update && \
4 | apt-get install -y \
5 | bzip2 \
6 | git \
7 | git-core \
8 | libfontconfig1 \
9 | libgconf-2-4 \
10 | libglu1 \
11 | libsm6 \
12 | libxext6 \
13 | libxrender1 \
14 | vim \
15 | wget
16 |
17 | WORKDIR /root/
18 | RUN wget -c --quiet download.blender.org/release/Blender2.79/blender-2.79a-linux-glibc219-x86_64.tar.bz2 && \
19 | tar -xf blender-2.79a-linux-glibc219-x86_64.tar.bz2 && \
20 | rm blender-2.79a-linux-glibc219-x86_64.tar.bz2 && \
21 | mv blender-2.79a-linux-glibc219-x86_64/ blender/ && \
22 | cp -r blender /usr/lib/blender && \
23 | echo "export PATH="/usr/lib/blender:$PATH"" >> /root/.bashrc
24 |
25 |
26 | COPY /src/requirements.txt /root
27 | RUN pip3 install -r /root/requirements.txt
28 |
29 | RUN pip3 install --user git+https://github.com/cocodataset/cocoapi.git#subdirectory=PythonAPI
30 |
31 | COPY models /root/models/
32 | COPY src /root/
33 | COPY deploy/config.json /root/
34 |
35 | CMD ["python3", "dataset_creation.py"]
36 | # ENTRYPOINT ["/root/entrypoint.sh"]
37 | #EXPOSE 8889
38 | #CMD ["jupyter notebook", "--ip=0.0.0.0", "--allow-root", "--port=8889"]
39 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Synthetic data generator using Blender #
2 |
3 | Repo to generate synthetic data using Blender to train object detection, instance segmentation
4 | and depth mapping algorithms.
5 |
6 | 
7 |
8 | 
9 |
10 |
11 |
12 | ## Getting Started
13 | You will need [Docker](https://docs.docker.com/install/).
14 | First, clone this repo
15 | ```
16 | git clone https://github.com/thomashossler/synthetic-data-generator.git```
17 | cd synthetic-data-generator
18 | ```
19 |
20 | Second, build the docker image
21 | ```
22 | docker build -t synthetic-data-generator .
23 | ```
24 | Then run (careful, the dataset will be created at LOCAL on your host machine):
25 | ```
26 | docker run -v /LOCAL/:/data/ -ti synthetic-data-generator
27 | ```
28 |
29 | ## Config file
30 | The config file should be modified before running the docker container.
31 |
32 |
33 | ## Download a blender model
34 | **not ready yet!**
35 |
36 | You can either create or download a blender file containing the model. A lot of free stuff exist
37 | online (for example [here](https://www.turbosquid.com/Search/3D-Models/free/blend)) but you will have
38 |
39 |
40 |
41 | ## PROGRESS ##
42 | * ~~Write Dockerfile for a blender deep learning environment~~
43 | * Write Python API to generate synthetic data for object detection
44 | * Write Python API to generate synthetic data for object segmentation
45 | * GPU support for faster rendering
46 | * user able to load any blender model
47 | * Add parameters in the config file
48 |
49 | ## Authors ##
50 |
51 | * **Thomas Hossler** - [thomashossler](https://github.com/thomashossler)
52 |
--------------------------------------------------------------------------------
/deploy/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "BASE_DIR": "/data/",
3 | "RESOLUTION_X": 1024,
4 | "RESOLUTION_Y": 512,
5 | "DEPTH_MAP_DIR": "/data/depth_maps/",
6 | "IMAGES_DIR": "/data/images/",
7 | "TILE_SIZE": 256,
8 | "N_SAMPLES": 1,
9 | "DATASET_NAME": "sphere"
10 | }
11 |
--------------------------------------------------------------------------------
/deploy/docker-compose.yaml:
--------------------------------------------------------------------------------
1 | version: '3.1'
2 | services:
3 | synthetic-data-generator:
4 | build: ../
5 | image: synthetic-data-generator
6 | container_name: synthetic-data-generator
7 | volumes:
8 | - /data/:/root/data/
9 | environment:
10 | - CONFIG_PATH=/root/config.json
11 | logging:
12 | driver: journald
13 | options:
14 | tag: "synthetic-data-generator"
15 | restart: always
16 |
--------------------------------------------------------------------------------
/images/depth_map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defqoon/synthetic-data-generator/a1ecffdd555323a89c63d6fd6520575ff625a768/images/depth_map.png
--------------------------------------------------------------------------------
/images/sphere.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defqoon/synthetic-data-generator/a1ecffdd555323a89c63d6fd6520575ff625a768/images/sphere.png
--------------------------------------------------------------------------------
/models/default.blend:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defqoon/synthetic-data-generator/a1ecffdd555323a89c63d6fd6520575ff625a768/models/default.blend
--------------------------------------------------------------------------------
/models/default.blend1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/defqoon/synthetic-data-generator/a1ecffdd555323a89c63d6fd6520575ff625a768/models/default.blend1
--------------------------------------------------------------------------------
/src/dataset_creation.py:
--------------------------------------------------------------------------------
1 | import json
2 | import os
3 |
4 | from utils import post_processing
5 |
6 |
7 | def create_dataset():
8 | """read the config file"""
9 | with open("/root/config.json", "r") as f:
10 | config = json.load(f)
11 |
12 | # create environmental variables
13 | for (key, value) in config.items():
14 | os.environ[key] = str(value)
15 |
16 | # run blender
17 | command = '/usr/lib/blender/blender {} --python {} --background'.\
18 | format("/root/models/default.blend", "/root/rendering.py")
19 | os.system(command)
20 |
21 | # post processing
22 | post_processing()
23 |
24 |
25 | if __name__ == '__main__':
26 | create_dataset()
--------------------------------------------------------------------------------
/src/entrypoint.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | python3 -u /root/dataset_creation.py
--------------------------------------------------------------------------------
/src/rendering.py:
--------------------------------------------------------------------------------
1 | from math import pi
2 | import os
3 | import json
4 |
5 | import bmesh
6 | import bpy
7 | import numpy as np
8 |
9 |
10 | # useful shortcut
11 | scene = bpy.context.scene
12 | bpy.data.scenes["Scene"].use_nodes = True
13 | tree = bpy.data.scenes["Scene"].node_tree
14 | links = tree.links
15 |
16 | # set up couple things
17 | base_folder = os.environ["BASE_DIR"]
18 | bpy.context.scene.render.filepath = base_folder
19 | bpy.context.scene.unit_settings.system ='METRIC'
20 |
21 | # stereo stuff
22 | bpy.data.scenes["Scene"].render.use_multiview = True
23 | bpy.data.scenes["Scene"].render.views_format = "STEREO_3D"
24 | bpy.data.cameras['cam'].sensor_fit = "HORIZONTAL"
25 |
26 | # create the nodes
27 | rl = tree.nodes.new(type="CompositorNodeRLayers")
28 | rl.location = 200, 200
29 | rl.select = False
30 |
31 | dm_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
32 | dm_output_node.select = False
33 | dm_output_node.base_path = os.environ["DEPTH_MAP_DIR"]
34 | dm_output_node.format.file_format = "OPEN_EXR"
35 | dm_output_node.location = 400, 400
36 |
37 | st_output_node = tree.nodes.new(type="CompositorNodeOutputFile")
38 | st_output_node.select = False
39 | st_output_node.base_path = os.environ["IMAGES_DIR"]
40 | st_output_node.location = 400, 200
41 | st_output_node.format.compression = 0
42 |
43 | # set the render engine
44 | bpy.data.scenes["Scene"].render.engine = "CYCLES"
45 | bpy.data.scenes["Scene"].cycles.samples = 16 # int(os.environ["SAMPLES"]) # decrease if too slow
46 | bpy.data.scenes["Scene"].cycles.device = "CPU"
47 | bpy.data.scenes["Scene"].render.tile_x = int(os.environ["TILE_SIZE"])
48 | bpy.data.scenes["Scene"].render.tile_y = int(os.environ["TILE_SIZE"])
49 | bpy.data.scenes["Scene"].render.resolution_x = int(os.environ["RESOLUTION_X"])
50 | bpy.data.scenes["Scene"].render.resolution_y = int(os.environ["RESOLUTION_Y"])
51 | bpy.data.scenes["Scene"].render.image_settings.color_mode = "RGB"
52 | bpy.data.scenes["Scene"].render.use_persistent_data = True
53 |
54 | dataset_name = os.environ["DATASET_NAME"]
55 | n_samples = int(os.environ["N_SAMPLES"])
56 | r_boundaries = [0.5, 2]
57 | x_boundaries = [-10, 10]
58 | y_boundaries = [-3, 5]
59 | z_boundaries = [-1, 7]
60 |
61 | # create material
62 | mat = bpy.data.materials.new('Sphere')
63 | mat.diffuse_color = (1, 0, 0)
64 |
65 | for i in range(n_samples):
66 | # create a random number of sphere
67 | nsphere = np.random.randint(0, 10)
68 | for j in range(nsphere):
69 | r = np.random.uniform(r_boundaries[0], r_boundaries[1])
70 | x = np.random.uniform(x_boundaries[0], x_boundaries[1])
71 | y = np.random.uniform(y_boundaries[0], y_boundaries[1])
72 | z = np.random.uniform(z_boundaries[0], z_boundaries[1])
73 | bpy.ops.mesh.primitive_ico_sphere_add(size=r, location=(x, y, z))
74 | bpy.ops.object.material_slot_add()
75 | bpy.context.object.material_slots[0].material = mat
76 |
77 | # deactivate multiview, save depth map
78 | dm_output_node.file_slots[0].path = '{}_{}_#'.format(dataset_name, i)
79 | bpy.data.scenes["Scene"].render.use_multiview = False
80 | l1 = links.new(rl.outputs['Depth'], dm_output_node.inputs['Image'])
81 | bpy.ops.render.render(write_still=False)
82 | links.remove(l1)
83 |
84 | # activate multiview, save stereo images
85 | st_output_node.file_slots[0].path = '{}_{}_#'.format(dataset_name, i)
86 | bpy.data.scenes["Scene"].render.use_multiview = True
87 | l2 = links.new(rl.outputs['Image'], st_output_node.inputs['Image'])
88 | bpy.ops.render.render(write_still=False)
89 | links.remove(l2)
90 |
91 | # delete all objects
92 | for o in bpy.data.objects:
93 | if o.type == 'MESH' and o.name != "background":
94 | o.select = True
95 | else:
96 | o.select = False
97 | bpy.ops.object.delete()
98 | bpy.context.scene.update()
--------------------------------------------------------------------------------
/src/requirements.txt:
--------------------------------------------------------------------------------
1 | boto==2.48.0
2 | Cython==0.28.1
3 | Keras==2.1.5
4 | opencv-python==3.4.0.12
5 | OpenEXR==1.3.2
6 | scikit-image==0.13.1
7 | SQLAlchemy==1.2.5
--------------------------------------------------------------------------------
/src/utils.py:
--------------------------------------------------------------------------------
1 | import array
2 | import glob
3 | import Imath
4 | import os
5 |
6 | import OpenEXR
7 | import numpy as np
8 |
9 |
10 | def post_processing():
11 | """couple postprocessing steps
12 | - clean images name
13 | - change depth to npy file format"""
14 | # rename images
15 | paths = glob.glob(os.environ["IMAGES_DIR"] + "*.png")
16 | for p in paths:
17 | new_name = os.path.basename(p).replace("_28", "")
18 | destination = os.path.join(os.path.dirname(p), new_name)
19 | os.rename(p, destination)
20 |
21 | # changing depth map to npy format
22 | # from https://www.excamera.com/sphinx/articles-openexr.html
23 | paths = glob.glob(os.environ["DEPTH_MAP_DIR"] + "*.exr")
24 | for p in paths:
25 | file = OpenEXR.InputFile(p)
26 |
27 | # get the size
28 | dw = file.header()['dataWindow']
29 | sz = (dw.max.x - dw.min.x + 1, dw.max.y - dw.min.y + 1)
30 |
31 | # only need one channel, they are all equal
32 | float = Imath.PixelType(Imath.PixelType.FLOAT)
33 | R = array.array('f', file.channel("R", float)).tolist()
34 | R = np.array(R)
35 | R = R.reshape(sz, order="F")
36 |
37 | output_file = os.path.join(os.environ["DEPTH_MAP_DIR"],
38 | 'depth_map_' + os.path.basename(p).replace("_28.exr", ""))
39 | np.save(output_file, R)
40 | os.remove(p)
41 |
--------------------------------------------------------------------------------