Skip to content

Commit cffaab6

Browse files
authored
Merge pull request #2 from Oxid15/develop
v0.2.0
2 parents 6fd360e + 0798ba5 commit cffaab6

File tree

6 files changed

+158
-66
lines changed

6 files changed

+158
-66
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ git clone https://github.com/Oxid15/ArtworkRetrievalDataGen.git
3232

3333
4. Review the results in the destination folder
3434

35-
![Alt text](./examples/00000.png)
35+
![Example](./examples/00000.jpg)

datagen/generator.py

Lines changed: 127 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1+
import csv
2+
import json
3+
import logging
14
import os
5+
from typing import Tuple, Union
26

37
import bpy
48

@@ -8,40 +12,49 @@
812
class Generator:
913
def __init__(
1014
self,
11-
src,
12-
dest,
13-
camera_angles_range=((90, 0), (90, 0)),
14-
camera_distance_range=(5, 5),
15-
render_per_input=1,
15+
src: str,
16+
dst: str,
17+
camera_angles_range_h: Tuple[float, float] = (0, 0),
18+
camera_angles_range_v: Tuple[float, float] = (90, 90),
19+
camera_distance_range: Tuple[float, float] = (5, 5),
20+
render_per_input: int = 1,
21+
max_images_to_render: Union[int, None] = None,
22+
generate_table: bool = False,
23+
generate_meta: bool = False,
1624
) -> None:
1725
self.src = src
18-
self.dest = dest
19-
self.camera_angles_range = camera_angles_range
26+
self.dst = dst
27+
self.camera_angles_range_h = camera_angles_range_h
28+
self.camera_angles_range_v = camera_angles_range_v
2029
self.camera_distance_range = camera_distance_range
2130
self.render_per_input = render_per_input
31+
self.max_images_to_render = max_images_to_render
32+
self.generate_table = generate_table
33+
self.generate_meta = generate_meta
2234

2335
self.parse_images()
2436

25-
def parse_images(self):
26-
self.img_names = os.listdir(self.src)
37+
def parse_images(self) -> None:
38+
self.img_names = sorted(os.listdir(self.src))
2739

28-
def _clear_scene(self):
40+
def _clear_scene(self) -> None:
2941
while len(bpy.data.objects):
3042
obj = bpy.data.objects[-1]
3143
obj.select_set(True)
3244
bpy.ops.object.delete(use_global=False)
3345

34-
def _add_camera(self):
35-
ang = self.camera_angles_range
36-
first_min, first_max = ang[0][0], ang[1][0]
37-
second_min, second_max = ang[0][1], ang[1][1]
38-
first_ang = np.random.random() * (first_max - first_min) + first_min
39-
second_ang = np.random.random() * (second_max - second_min) + second_min
46+
# bpy.data.materials.remove(bpy.data.materials['Wall-Material'])
47+
48+
def _add_camera(self) -> None:
49+
v_min, v_max = self.camera_angles_range_v
50+
h_min, h_max = self.camera_angles_range_h
51+
v_ang = np.random.random() * (v_max - v_min) + v_min
52+
h_ang = np.random.random() * (h_max - h_min) + h_min
4053

4154
d_max, d_min = self.camera_distance_range
4255
dist = np.random.random() * (d_max - d_min) + d_min
4356

44-
loc = spherical2cartesian((deg2rad(first_ang), deg2rad(second_ang)), dist)
57+
loc = spherical2cartesian((deg2rad(v_ang), deg2rad(h_ang)), dist)
4558

4659
bpy.ops.object.camera_add(
4760
align="VIEW",
@@ -51,20 +64,21 @@ def _add_camera(self):
5164
)
5265

5366
cam = bpy.data.objects["Camera"]
54-
constraint = cam.constraints.new(type="LIMIT_LOCATION")
55-
constraint.use_min_z = True
67+
# constraint = cam.constraints.new(type="LIMIT_LOCATION")
68+
# constraint.use_min_z = True
5669

5770
constraint = cam.constraints.new("TRACK_TO")
5871
constraint.target = self.image
5972
constraint.track_axis = "TRACK_NEGATIVE_Z"
6073
constraint.up_axis = "UP_Y"
6174

62-
def _add_lights(self):
75+
def _add_lights(self) -> None:
6376
bpy.ops.object.light_add(
6477
type="POINT", radius=1, align="VIEW", location=(2, -2, 2)
6578
)
79+
bpy.data.objects["Point"].data.energy = 1000
6680

67-
def _add_objects(self, img_name):
81+
def _add_objects(self, img_name: str) -> None:
6882
bpy.ops.import_image.to_plane(
6983
files=[{"name": img_name}],
7084
directory=self.src,
@@ -90,43 +104,117 @@ def _add_objects(self, img_name):
90104
plane.rotation_euler[2] = deg2rad(90)
91105

92106
bpy.ops.material.new()
93-
bpy.data.materials[-1].node_tree.nodes["Principled BSDF"].inputs[
107+
mat = bpy.data.materials["Material.001"]
108+
mat.name = "Wall-Material"
109+
bpy.data.materials["Wall-Material"].node_tree.nodes["Principled BSDF"].inputs[
94110
0
95111
].default_value = np.random.random(4)
96-
plane.data.materials.append(bpy.data.materials[-1])
112+
plane.data.materials.append(bpy.data.materials["Wall-Material"])
97113

98114
# Floor
99115
bpy.ops.mesh.primitive_plane_add(
100-
size=1, align="VIEW", enter_editmode=False, location=(0, 0, -0.7)
116+
size=1, align="VIEW", enter_editmode=False, location=(0, 0, -1)
101117
)
102118
plane = bpy.data.objects["Plane.001"]
103119
plane.scale[0] = 100
104120
plane.scale[1] = 100
105121

106122
bpy.ops.material.new()
107-
bpy.data.materials[-1].node_tree.nodes["Principled BSDF"].inputs[
123+
mat = bpy.data.materials["Material.001"]
124+
mat.name = "Floor-Material"
125+
bpy.data.materials["Floor-Material"].node_tree.nodes["Principled BSDF"].inputs[
108126
0
109127
].default_value = np.random.random(4)
110-
plane.data.materials.append(bpy.data.materials[-1])
128+
plane.data.materials.append(bpy.data.materials["Floor-Material"])
111129

112-
def _arrange_scene(self, img_name):
130+
def _arrange_scene(self, img_name: str):
113131
self._clear_scene()
114132
self._add_lights()
115133
self._add_objects(img_name)
116134
self._add_camera()
117135

118-
def _render(self, index, img_name):
136+
def _render(self, render_name: str):
119137
bpy.context.scene.camera = bpy.data.objects["Camera"]
120-
121-
base, ext = img_name.split(os.extsep)
122-
img_name = base + "_{0:0>5d}".format(index)
123-
".".join((img_name, ext))
124-
125-
bpy.context.scene.render.filepath = os.path.join(self.dest, img_name)
138+
bpy.context.scene.render.filepath = os.path.join(
139+
self.dst, "images", render_name
140+
)
141+
bpy.context.scene.render.image_settings.file_format = "JPEG"
126142
bpy.ops.render.render("INVOKE_DEFAULT", write_still=True)
127143

128-
def run(self):
129-
for name in self.img_names:
130-
for i in range(self.render_per_input):
131-
self._arrange_scene(name)
132-
self._render(i, name)
144+
def _write_meta(self) -> None:
145+
meta = {
146+
"src": self.src,
147+
"dst": self.dst,
148+
"camera_angles_range_h": self.camera_angles_range_h,
149+
"camera_angles_range_v": self.camera_angles_range_v,
150+
"camera_distance_range": self.camera_distance_range,
151+
"render_per_input": self.render_per_input,
152+
"size": self._names,
153+
}
154+
155+
with open(os.path.join(self.dst, "meta.json"), "w") as f:
156+
json.dump(meta, f, indent=2)
157+
158+
def _rendering_loop(self, writer: Union[csv.DictWriter, None]) -> None:
159+
if writer:
160+
writer.writeheader()
161+
162+
self._names = []
163+
for i, base_name in enumerate(self.img_names):
164+
name, _ = os.path.splitext(base_name)
165+
for j in range(self.render_per_input):
166+
render_name = f"{name}_{j:0>5d}.jpg"
167+
try:
168+
self._arrange_scene(base_name)
169+
self._render(render_name)
170+
except Exception as e:
171+
logging.exception(f"Error with {i}: ", e)
172+
else:
173+
self._names.append(
174+
[
175+
base_name,
176+
os.path.abspath(
177+
os.path.join(self.dst, "images", render_name)
178+
),
179+
]
180+
)
181+
if writer:
182+
writer.writerow(
183+
{"base_img": base_name, "query_img": render_name}
184+
)
185+
186+
if (
187+
self.max_images_to_render is not None
188+
and len(self._names) > self.max_images_to_render
189+
):
190+
logging.info("Finished at max images")
191+
break
192+
193+
logging.info(f"Generated images for {i + 1} inputs")
194+
195+
def run(self) -> None:
196+
os.makedirs(os.path.join(self.dst, "images"))
197+
198+
csv_file = None
199+
writer = None
200+
201+
if self.generate_table:
202+
csv_file = open(os.path.join(self.dst, "map.csv"), "w")
203+
writer = csv.DictWriter(csv_file, ["base_img", "query_img"])
204+
205+
logging.info("Starting generation process")
206+
try:
207+
self._rendering_loop(writer)
208+
except Exception as e:
209+
logging.exception(e)
210+
logging.info("Exception occured while rendering, stopping...")
211+
212+
if csv_file:
213+
csv_file.close()
214+
logging.info("Map file closed")
215+
216+
if self.generate_meta:
217+
logging.info("Writing meta")
218+
self._write_meta()
219+
220+
logging.info("Done!")

datagen/utils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
from typing import Tuple
2+
13
import numpy as np
24

35

4-
def deg2rad(deg):
6+
def deg2rad(deg: float):
57
return np.pi * deg / 180.
68

7-
def spherical2cartesian(two_angles, radius):
9+
def spherical2cartesian(two_angles: Tuple[float, float], radius: float) -> Tuple[float, float, float]:
810
alpha, beta = two_angles
911
return (radius * np.sin(alpha) * np.cos(beta),
1012
radius * np.sin(alpha) * np.sin(beta),

examples/00000.jpg

132 KB
Loading

examples/00000.png

-2.13 MB
Binary file not shown.

generate.py

Lines changed: 26 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import os
2+
import logging
23
import sys
34

45
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
@@ -7,29 +8,30 @@
78

89
from datagen import Generator
910

10-
11-
# The folder with images to render
12-
source_folder = os.path.join(BASE_DIR, "src")
13-
14-
# The folder to place renders
15-
destination_folder = os.path.join(BASE_DIR, "dst")
16-
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]]
22-
23-
camera_distance_range = [3, 5]
24-
25-
render_per_input = 2
26-
11+
logging.basicConfig(
12+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
13+
)
14+
15+
args = {
16+
# The folder with images to render
17+
"src": os.path.join(BASE_DIR, "src"),
18+
# The folder to place renders
19+
# The "image" folder will be created there
20+
"dst": os.path.join(BASE_DIR, "dst"),
21+
# The angles and distance are the spherical coordinates of the camera
22+
# given that the position of the object in the scene is (0,0,0)
23+
# render_per_input times different angles and distances are sampled
24+
# from these ranges
25+
"camera_angles_range_v": (90, 90),
26+
"camera_angles_range_h": (-60, 60),
27+
"camera_distance_range": (3, 5),
28+
"render_per_input": 2,
29+
"max_images_to_render": 100,
30+
# Creates the table linking original images with rendered
31+
"generate_table": True,
32+
# Creates the file with metadata of dataset
33+
"generate_meta": True,
34+
}
2735

2836
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()
37+
gen = Generator(**args).run()

0 commit comments

Comments
 (0)