Skip to content
Snippets Groups Projects
global_script.py 9.97 KiB
import random
import glob
import json
import os
import sys
import shutil
from collections import OrderedDict

import bpy
from mathutils import Vector

print('#' * 30)

WORKING_DIR = r"D:\Mingming\synthe_dripe"
os.chdir(WORKING_DIR)
sys.path.append(WORKING_DIR)

from scripts import random_pose
from scripts import utils
from scripts import camera_proj
from scripts import human

import importlib

importlib.reload(random_pose)
importlib.reload(utils)
importlib.reload(camera_proj)
importlib.reload(human)

from scripts.human import HumanLoader


def abs_path(rel_path):
    return os.path.join(os.path.dirname(os.path.dirname(__file__)), rel_path)


random.seed()

C = bpy.context
D = bpy.data

CONTINUE = False

# Clean scene
try:
    bpy.ops.object.mode_set(mode='OBJECT')
    utils.unselect_all()
except RuntimeError:
    pass

for col in D.collections:
    D.collections.remove(col)

for bpy_data_iter in (
        D.objects,
        D.meshes,
        D.lights,
        D.cameras,
        D.armatures,
        D.images,
):
    for id_data in bpy_data_iter:
        bpy_data_iter.remove(id_data)

for ob in D.objects:
    C.scene.collection.objects.unlink(ob)

# Import car
car_collection = D.collections.new("Cars")
C.scene.collection.children.link(car_collection)

cars = []

for src_path, name in {
    r"car_models\suv_car\car.blend": 'SUV',
    # r"car_models\red_car\red.blend": 'Red',
    # r"car_models\pickup_car\pickup.blend": 'PickUp',
    # r"car_models\family_car\family_car.blend": 'Family',
    # r"car_models\coupe_car\coupe_car.blend": 'Coupe',
    # r"car_models\truck\truck_open.blend": 'Truck',

}.items():
    with D.libraries.load(abs_path(src_path)) as (data_from, data_to):
        data_to.objects = data_from.objects

    for obj in data_to.objects:
        car_collection.objects.link(obj)

    D.objects['Car'].name = name
    cars.append(D.objects[name])
car_picker = utils.Randomizer(cars)

# import humans
human_loader = HumanLoader('mh_models/exports')

# Creation scene
# add camera
C.scene.render.engine = 'BLENDER_EEVEE'

camera_data = D.cameras.new(name='Camera')
camera_data.type = 'PERSP'
camera_data.lens_unit = 'FOV'
camera_data.angle = utils.r(68)

C.scene.render.resolution_x = 640
C.scene.render.resolution_y = 480

camera_object = D.objects.new('Camera', camera_data)
C.scene.collection.objects.link(camera_object)

camera_object.location = [-.97, -0.11, 0.68]
camera_object.rotation_euler = utils.r([72, 8, -82])

# set background
back_folder = abs_path("backgrounds")
back_imgs = {}
for key in ['night', 'day']:
    list_imgs = glob.glob(os.path.join(back_folder, f'{key}_*'))
    back_imgs[key] = []
    for img in list_imgs:
        img_name = os.path.basename(img)
        bpy.ops.image.open(filepath=img, directory=back_folder,
                           files=[{"name": img_name}], relative_path=False, show_multiview=False)
        back_imgs[key].append(img_name)

# Create image holder
bpy.ops.import_image.to_plane(
    directory=back_folder,
    files=[{"name": "default_green.png"}],
    shader='SHADELESS',
    use_transparency=False,
    offset=False,
    height=round(10 / 2.25, 1),
    align_axis="X-"
)

image_holder = C.active_object
image_holder.name = 'Image_holder'
image_holder.location = (4, 1.5, 1.3)
image_holder.rotation_euler.z = utils.r(-100)
image_holder.active_material.shadow_method = 'NONE'

# add light
sun_collection = D.collections.new("Sun")
C.scene.collection.children.link(sun_collection)

light_params = {
    'day': {
        'energy_bounds': (10, 35),
        'back_color_bounds': (0.4, 0.65),
        'sun_color': (1, 1, 1)
    },
    'night': {
        'energy_bounds': (5, 15),
        'back_color_bounds': (0.05, 0.15),
        'sun_color': (1, 0.5, 0.2)
    }

}

light_data = D.lights.new(name="Sun", type='SUN')
light_data.energy = 20
light_object = D.objects.new(name="Sun", object_data=light_data)

sun_collection.objects.link(light_object)
light_object.location = (5, 0, 3)

# Sun position
C.scene.sun_pos_properties.usage_mode = 'NORMAL'
C.scene.sun_pos_properties.sun_object = light_object
C.scene.sun_pos_properties.object_collection = sun_collection
C.scene.sun_pos_properties.object_collection_type = 'DIURNAL'
C.scene.sun_pos_properties.co_parser = "41°22′14″N 2°09′00″E"
C.scene.sun_pos_properties.sun_distance = 3
C.scene.sun_pos_properties.use_day_of_year = True
C.scene.sun_pos_properties.year = 2022
C.scene.sun_pos_properties.day_of_year = 182

fp = abs_path(r"output")
fp_img = os.path.join(fp, 'images')
fp_ann_2D = os.path.join(fp, 'annots_2D')
fp_ann_3D = os.path.join(fp, 'annots_3D')

info_path = os.path.join(fp, 'infos.json')
scenes_ids = OrderedDict()
if not CONTINUE or not os.path.isfile(info_path):
    if os.path.isdir(fp):
        shutil.rmtree(fp)
    os.mkdir(fp)

    os.mkdir(fp_img)
    os.mkdir(fp_ann_2D)
    os.mkdir(fp_ann_3D)

frame_rate = 25
nb_scene = 1
nb_pose = 10
human_loader.max_len = min(human_loader.max_len, nb_scene)
ratio_conf_man = int(nb_scene / len(human_loader.human_paths))
if CONTINUE:
    with open(info_path) as f_info:
        scene_ids = json.load(f_info, object_pairs_hook=OrderedDict)['id_max_scenes']

    human_loader.human_paths = [hp for hp in human_loader.human_paths if hp not in scene_ids]

man = None
for sc in range(nb_scene):
    # Random car
    car = car_picker()
    car_targets = {side: [ch for ch in car.children if f'Target_{side.upper()}' in ch.name] for side in 'lr'}
    nb_targets = sum([len(v) for v in car_targets.values()])
    # Random personne
    if ratio_conf_man < 1:
        if not sc % 10:
            human_loader.load_next()
        man = human_loader(car=car)
    else:
        if not sc % ratio_conf_man:
            man = human_loader.next(car=car)
        else:
            human.set_bounds(man, car)

    human_path = human_loader.paths[man]
    scenes_ids.setdefault(human_path, -1)
    scenes_ids[human_path] += 1

    # Random time
    C.scene.sun_pos_properties.north_offset = utils.r(random.randint(-179, 180))
    time_day = random.randint(0, 23)
    if 6 <= time_day <= 21:
        day_night = 'day'
        C.scene.sun_pos_properties.time = time_day
    else:
        day_night = 'night'
        C.scene.sun_pos_properties.time = (time_day + 12) % 24

    light_param = light_params[day_night]
    light_data.energy = random.randint(*light_param['energy_bounds'])
    light_data.color = light_param['sun_color']
    back_val = random.uniform(*light_param['back_color_bounds'])
    bpy.data.worlds["World"].node_tree.nodes["Background.001"].inputs[0].default_value = \
        (back_val, back_val, back_val, 1)

    # Random background
    back_img = random.choice(back_imgs[day_night])
    image_holder.active_material.node_tree.nodes['Image Texture'].image = D.images[back_img]
    image_holder.location.y = 1.5 + random.uniform(-0.3, 0.3)

    # Camera movement
    camera_object.rotation_euler = utils.r([72 + random.randint(-2, 2), 8, -82])

    C.scene.render.filepath = fp
    C.scene.render.image_settings.file_format = 'PNG'
    C.scene.camera = camera_object

    P, K, RT = camera_proj.get_3x4_P_matrix_from_blender(camera_object)

    file_root_name = f'{list(scenes_ids).index(human_path)}_{scenes_ids[human_path]}'

    with open(os.path.join(fp, 'cameras.json'), 'a+') as f_cam:
        previous_cameras = f_cam.read()
        if previous_cameras:
            previous_cameras = json.loads(previous_cameras)
        else:
            previous_cameras = {}
        previous_cameras[file_root_name] = {
            'P': utils.mat_to_list(P),
            'K': utils.mat_to_list(K),
            'RT': utils.mat_to_list(RT),
        }
        json.dump(previous_cameras, f_cam, indent=4)

    man.animation_data_clear()

    # Exemple: 150k / 200 / 2 = 1500 poses
    with open(os.path.join(fp_ann_2D, f'annotations_{file_root_name}.csv'), 'w') as annot_file_2D, \
            open(os.path.join(fp_ann_3D, f'annotations_{file_root_name}.csv'), 'w') as annot_file_3D:
        bone_lbls = list(man.pose.bones.keys())
        annot_file_2D.write(';'.join([lbl for bone in bone_lbls for lbl in [bone + k for k in ['_x', '_y']]]) + '\n')
        annot_file_3D.write(
            ';'.join([lbl for bone in bone_lbls for lbl in [bone + k for k in ['_X', '_Y', '_Z']]]) + '\n')

        for po in range(nb_pose):
            C.scene.frame_set(po * frame_rate)
            use_targets = nb_pose - po <= (nb_targets // 2) ** 2
            # use_targets = False
            human.switch_constraints(man, enable=not use_targets)
            random_pose.random_pose_ik(man, targets=car_targets if use_targets else None)

            bpy.ops.object.mode_set(mode='OBJECT')

            man.keyframe_insert(data_path="location", index=-1)
            man.keyframe_insert(data_path="rotation_euler", index=-1)

            bpy.ops.object.mode_set(mode='POSE')

            for bone in man.pose.bones:
                bone.keyframe_insert(data_path="rotation_euler", index=-1)
                if bone.name[-3:] == '_IK':
                    bone.keyframe_insert(data_path="location", index=-1)

            bpy.ops.object.mode_set(mode='OBJECT')

            # set output path so render won't get overwritten
            C.scene.render.filepath = os.path.join(fp_img, f"{file_root_name}_{po}" + f'_drive' if use_targets else '')
            bpy.ops.render.render(write_still=True)  # render still

            annotations_2D = []
            annotations_3D = []
            for lbl in bone_lbls:
                bone_3d = utils.get_head_pose(lbl, man)
                annotations_3D.append(f"{bone_3d[0]:.3f};{bone_3d[1]:.3f};{bone_3d[2]:.3f}")

                bone_2d = P @ bone_3d
                bone_2d /= bone_2d[-1]
                annotations_2D.append(f"{bone_2d[0]:.2f};{bone_2d[1]:.2f}")
            annot_file_2D.write(';'.join(annotations_2D) + '\n')
            annot_file_3D.write(';'.join(annotations_3D) + '\n')

C.scene.frame_end = int(frame_rate * (nb_pose - 0.5))
utils.select_only(man)
bpy.ops.object.mode_set(mode='POSE')

with open(info_path, 'w') as f:
    json.dump({
        'models': list(scenes_ids),
        'id_max_scenes': scenes_ids
    }, f, indent=4)

print('Done', '#' * 25)