Skip to content

Commit acd820b

Browse files
committed
v0.1 of the TDG
1 parent 6861166 commit acd820b

7 files changed

+770
-1
lines changed

README.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
1-
# Surface-Inspection-Training-Data-Generator
1+
# Surface-Inspection-Training-Data-Generator
2+
## Quickstart Guide
3+
1) Install BlenderProc 1.12
4+
2) In main.py set the BLENDER_PATH constant to BlenderProc's blender path
5+
3) Move the TDG folder into the BlenderProc directory
6+
4) Set the number of defects per part in generatedefects.py and the for-loop iterations in the main.py files
7+
5) Run main.py

TDG/Inspektionspfad_links_blenderready.txt

+145
Large diffs are not rendered by default.

TDG/defaultscene.blend

10.2 MB
Binary file not shown.

TDG/generatedefects.py

+209
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#region IMPORTS
2+
import bpy
3+
import random
4+
import os
5+
# from randomizetexture import MAIN_OBJ_NAME
6+
#endregion
7+
8+
#region CONSTANTS
9+
DEFECTS_PER_PART = 101
10+
DEFECT_NAME = 'Sphere'
11+
BLOWHOLE_ID = 2
12+
MAIN_OBJ_NAME = 'Turbosupercharger'
13+
14+
#cloud noise texture randomization values
15+
CLOUD_TXTR_MIN_NOISE_STR = -0.8
16+
CLOUD_TXTR_MAX_NOISE_STR = 0.8
17+
CLOUD_TXTR_MIN_NOISE_SCALE = 2
18+
CLOUD_TXTR_MAX_NOISE_SCALE = 10
19+
CLOUD_TXTR_MIN_NOISE_DEPTH = 0
20+
CLOUD_TXTR_MAX_NOISE_DEPTH = 20
21+
#endregion
22+
23+
#region OBJECTS
24+
def get_active_object():
25+
return bpy.context.active_object
26+
27+
def get_object(ref=None):
28+
objref = None
29+
if ref is None:
30+
objref = get_active_object()
31+
else:
32+
if type(ref) == str:
33+
objref = bpy.data.objects[ref]
34+
else:
35+
objref = ref
36+
return objref
37+
38+
def copy_object(tocopy, col = None):
39+
new_obj = None
40+
to_copy = get_object(tocopy)
41+
col_ref = None
42+
43+
if col == None:
44+
col_ref = get_active_collection()
45+
elif type(col) == str:
46+
if collection_exists(col):
47+
col_ref = col
48+
else:
49+
col_ref = create_collection(col)
50+
else:
51+
col_ref = col
52+
53+
new_obj = to_copy.copy()
54+
if new_obj.data is not None:
55+
new_obj.data = to_copy.data.copy()
56+
col_ref.objects.link(new_obj)
57+
return new_obj
58+
59+
#endregion
60+
61+
#region MESHES
62+
def get_vertices(ref):
63+
if type(ref) == str:
64+
return get_object(ref).data.vertices
65+
else:
66+
return ref.data.vertices
67+
#endregion
68+
69+
#region COLLECTIONS
70+
def get_active_collection():
71+
return bpy.context.view_layer.active_layer_collection.collection
72+
73+
def collection_exists(col):
74+
if type(col) == str:
75+
return col in bpy.data.collections
76+
return col.name in bpy.data.collections
77+
78+
def create_collection(name):
79+
bpy.data.collections.new(name)
80+
colref = bpy.data.collections[name]
81+
bpy.context.scene.collection.children.link(colref)
82+
return colref
83+
#endregion
84+
85+
#region MODIFIERS
86+
def make_solid(ref = None, tar_thickness = 0.01):
87+
target = get_object(ref)
88+
tar_mods = bpy.data.objects[target.name].modifiers
89+
if any([m for m in tar_mods if m.name == "Solid"]):
90+
change_thickness(target, tar_thickness)
91+
else:
92+
solid_mod = tar_mods.new(name='Solid', type ='SOLIDIFY')
93+
solid_mod.thickness = tar_thickness
94+
95+
#doesn't work as expected: setting thickness to 0 makes the object hollow!
96+
def change_thickness(ref=None, thickness = 0.001):
97+
target = get_object(ref)
98+
#this is not a great solution, since the mod name is hard coded
99+
bpy.data.objects[target.name].modifiers['Solid'].thickness = thickness
100+
101+
def make_boolean_difference(main_ref = None, tool_ref = None):
102+
main_obj = get_object(main_ref)
103+
tool_obj = get_object(tool_ref)
104+
bool_mod = main_obj.modifiers.new(name='Bool_Diff', type='BOOLEAN')
105+
bool_mod.object = tool_obj
106+
bool_mod.operation = 'DIFFERENCE'
107+
#TODO: could add if condition here (don't want to apply immediately)
108+
bpy.ops.object.modifier_apply({"object": main_obj}, modifier=bool_mod.name)
109+
110+
def make_boolean_intersection(main_ref = None, tool_ref = None):
111+
main_obj = get_object(main_ref)
112+
tool_obj = get_object(tool_ref)
113+
bool_mod = main_obj.modifiers.new(name='Bool_Inter', type='BOOLEAN')
114+
bool_mod.object = tool_obj
115+
bool_mod.operation = 'INTERSECT'
116+
#TODO: could add if condition here (don't want to apply immediately)
117+
bpy.ops.object.modifier_apply({"object": main_obj}, modifier=bool_mod.name)
118+
119+
def add_noisy_displacement(main_ref = None, strength = 0, noise_depth = 4, noise_scale = 6):
120+
main_obj = get_object(main_ref)
121+
displace = main_obj.modifiers.new(name="Displace", type='DISPLACE')
122+
displace.texture = add_noise_texture(noise_depth, noise_scale)
123+
displace.strength = strength
124+
#endregion
125+
126+
#region TEXTURES
127+
def add_noise_texture(noise_depth = 4, noise_scale = 6):
128+
noise_texture = bpy.data.textures.new("Noise", 'CLOUDS')
129+
noise_texture.noise_basis = 'BLENDER_ORIGINAL'
130+
noise_texture.noise_type = 'SOFT_NOISE'
131+
noise_texture.noise_depth = noise_depth
132+
noise_texture.noise_scale = noise_scale
133+
return noise_texture
134+
#endregion
135+
136+
#region CUSTOM_METHODS
137+
def create_defect(main_obj_ref, tool_ref, vert, category_id = -1):
138+
tool_ref.location = vert.co - random.uniform(0,0.3)*vert.normal
139+
140+
#TODO: move the randomization elsewhere
141+
noise_strength = random.uniform(CLOUD_TXTR_MIN_NOISE_STR, CLOUD_TXTR_MAX_NOISE_STR)
142+
noise_depth = int(random.uniform(CLOUD_TXTR_MIN_NOISE_DEPTH, CLOUD_TXTR_MAX_NOISE_DEPTH))
143+
noise_scale = random.uniform(CLOUD_TXTR_MIN_NOISE_SCALE, CLOUD_TXTR_MAX_NOISE_SCALE)
144+
add_noisy_displacement(tool_ref, noise_strength, noise_depth, noise_scale)
145+
146+
main_obj_copy = copy_object(main_obj_ref)
147+
add_category_id(main_obj_copy,category_id)
148+
149+
make_boolean_difference(main_obj_ref,tool_ref)
150+
151+
make_solid(tool_ref)
152+
153+
make_boolean_intersection(main_obj_copy, tool_ref)
154+
tool_ref.modifiers.clear()
155+
156+
def select_defect_verts(mo_vertices, number_of_defects=DEFECTS_PER_PART):
157+
vertex_count = len(mo_vertices)
158+
#avoid intersecting defects
159+
step_size = int(vertex_count/(1.3*number_of_defects))
160+
starting_vert_number = random.randint(0,step_size)
161+
v_index = starting_vert_number
162+
defect_vert_indices = []
163+
while(v_index + step_size < (1/1.3)*vertex_count):
164+
defect_vert_indices.append(v_index)
165+
v_index += step_size
166+
return defect_vert_indices
167+
168+
def add_category_id(obj_ref, category_id = -1):
169+
obj_ref['category_id'] = category_id
170+
#endregion
171+
172+
#region CREATE DEFECTS
173+
174+
#object names must match names in the .blend file
175+
main_obj_ref = get_object(MAIN_OBJ_NAME)
176+
177+
tool_ref = get_object(DEFECT_NAME)
178+
179+
mo_vertices = get_vertices(main_obj_ref)
180+
defect_vert_indices = select_defect_verts(mo_vertices)
181+
182+
for v in defect_vert_indices:
183+
create_defect(main_obj_ref, tool_ref, mo_vertices[v], BLOWHOLE_ID)
184+
185+
#remove defect tool
186+
bpy.ops.object.select_all(action='DESELECT')
187+
bpy.data.objects[DEFECT_NAME].select_set(True)
188+
bpy.ops.object.delete()
189+
190+
#smooth objects to remove artifacts
191+
ctx = bpy.context.copy()
192+
all_objects = [o for o in bpy.data.objects if o.type == "MESH"]
193+
o = all_objects[0]
194+
ctx['object'] = o
195+
ctx['active_object'] = o
196+
ctx['selected_objects'] = all_objects
197+
ctx['selected_editable_objects'] = all_objects
198+
bpy.ops.object.shade_smooth(ctx)
199+
for m in bpy.data.meshes:
200+
m.use_auto_smooth = True
201+
202+
203+
#save the scene
204+
wd = os.getcwd()
205+
scene_path = os.path.join(wd,'scene.blend')
206+
print("Defects were created. Will now be saved")
207+
bpy.ops.wm.save_mainfile(filepath=scene_path)
208+
209+
#endregion

0 commit comments

Comments
 (0)