Ejemplo n.º 1
0
def _read_transform_write(file_in, file_out, random_rotation):

    mesh = trimesh.load(file_in)
    bounds = mesh.extents
    if bounds.min() == 0.0:
        return

    # translate to origin
    translation = (mesh.bounds[0] + mesh.bounds[1]) * 0.5
    translation = trimesh.transformations.translation_matrix(
        direction=-translation)
    mesh.apply_transform(translation)

    # scale to unit cube
    scale = 1.0 / bounds.max()  # TODO: max(abs())?
    scale_trafo = trimesh.transformations.scale_matrix(factor=scale)
    mesh.apply_transform(scale_trafo)

    # apply random rotation
    if random_rotation:
        rnd = np.random.RandomState(file_utils.filename_to_hash(file_in))
        rand_rot = trimesh.transformations.random_rotation_matrix(rnd.rand(3))
        mesh.apply_transform(rand_rot)

    # TODO: translate to origin, rotate, scale

    mesh.export(file_out)
Ejemplo n.º 2
0
def _get_and_save_query_pts(file_in_mesh: str,
                            file_out_query_pts: str,
                            file_out_query_dist: str,
                            file_out_query_vis: str,
                            num_query_pts: int,
                            patch_radius: float,
                            far_query_pts_ratio=0.1,
                            signed_distance_batch_size=1000,
                            debug=False):

    import trimesh

    # random state for file name
    rng = np.random.RandomState(file_utils.filename_to_hash(file_in_mesh))

    in_mesh = trimesh.load(file_in_mesh)

    # get query pts
    query_pts_ms = sdf.get_query_pts_for_mesh(in_mesh, num_query_pts,
                                              patch_radius,
                                              far_query_pts_ratio, rng)
    np.save(file_out_query_pts, query_pts_ms.astype(np.float32))

    # get signed distance
    query_dist_ms = sdf.get_signed_distance(in_mesh, query_pts_ms,
                                            signed_distance_batch_size)
    # fix NaNs, Infs and truncate
    nan_ids = np.isnan(query_dist_ms)
    inf_ids = np.isinf(query_dist_ms)
    query_dist_ms[nan_ids] = 0.0
    query_dist_ms[inf_ids] = 1.0
    query_dist_ms[query_dist_ms < -1.0] = -1.0
    query_dist_ms[query_dist_ms > 1.0] = 1.0
    np.save(file_out_query_dist, query_dist_ms.astype(np.float32))

    if debug and file_out_query_vis is not None:
        # save visualization
        sdf.visualize_query_points(query_pts_ms, query_dist_ms,
                                   file_out_query_vis)
Ejemplo n.º 3
0
def sample_blensor(base_dir,
                   dataset_dir,
                   blensor_bin,
                   dir_in,
                   dir_out,
                   dir_out_vis,
                   dir_out_pcd,
                   dir_blensor_scripts,
                   num_scans_per_mesh_min,
                   num_scans_per_mesh_max,
                   num_processes,
                   min_pts_size=0,
                   scanner_noise_sigma_min=0.0,
                   scanner_noise_sigma_max=0.05):
    """
    Call Blender to use a Blensor script to sample a point cloud from a mesh
    :param base_dir:
    :param dataset_dir:
    :param dir_in:
    :param dir_out:
    :param dir_blensor_scripts:
    :param num_scans_per_mesh_min: default: 5
    :param num_scans_per_mesh_max: default: 100
    :param scanner_noise_sigma_min: default: 0.0004, rather a lot: 0.01
    :param scanner_noise_sigma_max: default: 0.0004, rather a lot: 0.01
    :return:
    """

    # test blensor scripts with: .\blender -P 00990000_6216c8dabde0a997e09b0f42_trimesh_000.py

    blender_path = os.path.join(base_dir, blensor_bin)
    dir_abs_in = os.path.join(base_dir, dataset_dir, dir_in)
    dir_abs_out = os.path.join(base_dir, dataset_dir, dir_out)
    dir_abs_out_vis = os.path.join(base_dir, dataset_dir, dir_out_vis)
    dir_abs_blensor = os.path.join(base_dir, dataset_dir, dir_blensor_scripts)
    dir_abs_pcd = os.path.join(base_dir, dataset_dir, dir_out_pcd)

    os.makedirs(dir_abs_out, exist_ok=True)
    os.makedirs(dir_abs_out_vis, exist_ok=True)
    os.makedirs(dir_abs_blensor, exist_ok=True)
    os.makedirs(dir_abs_pcd, exist_ok=True)

    with open('blensor_script_template.py', 'r') as file:
        blensor_script_template = file.read()

    blender_blensor_calls = []
    pcd_base_files = []
    pcd_noisy_files = []
    obj_locations = []
    obj_rotations = []

    obj_files = [
        f for f in os.listdir(dir_abs_in)
        if os.path.isfile(os.path.join(dir_abs_in, f)) and f[-4:] == '.ply'
    ]
    for fi, file in enumerate(obj_files):

        # gather all file names involved in the blensor scanning
        obj_file = os.path.join(dir_abs_in, file)
        blensor_script_file = os.path.join(dir_abs_blensor, file[:-4] + '.py')

        new_pcd_base_files = []
        new_pcd_noisy_files = []
        new_obj_locations = []
        new_obj_rotations = []
        rnd = np.random.RandomState(file_utils.filename_to_hash(obj_file))
        num_scans = rnd.randint(num_scans_per_mesh_min,
                                num_scans_per_mesh_max + 1)
        noise_sigma = rnd.rand() * (
            scanner_noise_sigma_max -
            scanner_noise_sigma_min) + scanner_noise_sigma_min
        for num_scan in range(num_scans):
            pcd_base_file = os.path.join(
                dir_abs_pcd, file[:-4] +
                '_{num}.numpy.gz'.format(num=str(num_scan).zfill(5)))
            pcd_noisy_file = pcd_base_file[:-9] + '00000.numpy.gz'

            obj_location = (rnd.rand(3) * 2.0 - 1.0)
            obj_location_rand_factors = np.array([0.1, 1.0, 0.1])
            obj_location *= obj_location_rand_factors
            obj_location[1] += 4.0  # offset in cam view dir
            obj_rotation = trafo.random_quaternion(rnd.rand(3))

            # extend lists of pcd output files
            new_pcd_base_files.append(pcd_base_file)
            new_pcd_noisy_files.append(pcd_noisy_file)
            new_obj_locations.append(obj_location.tolist())
            new_obj_rotations.append(obj_rotation.tolist())

        new_scan_sigmas = [noise_sigma] * num_scans

        pcd_base_files.append(new_pcd_base_files)
        pcd_noisy_files.append(new_pcd_noisy_files)
        obj_locations.append(new_obj_locations)
        obj_rotations.append(new_obj_rotations)

        # prepare blensor calls if necessary
        output_files = [
            os.path.join(dir_abs_pcd, os.path.basename(f))
            for f in new_pcd_noisy_files
        ]
        output_files += [blensor_script_file]
        if file_utils.call_necessary(obj_file, output_files):
            blensor_script = blensor_script_template.format(
                file_loc=obj_file,
                obj_locations=str(new_obj_locations),
                obj_rotations=str(new_obj_rotations),
                evd_files=str(new_pcd_base_files),
                scan_sigmas=str(new_scan_sigmas),
            )
            blensor_script = blensor_script.replace(
                '\\', '/')  # '\' would require escape sequence

            with open(blensor_script_file, "w") as text_file:
                text_file.write(blensor_script)

            # start blender with python script (-P) and close without prompt (-b)
            blender_blensor_call = '{} -P {} -b'.format(
                blender_path, blensor_script_file)
            blender_blensor_calls.append((blender_blensor_call, ))

    utils_mp.start_process_pool(utils_mp.mp_worker, blender_blensor_calls,
                                num_processes)

    def get_pcd_origin_file(pcd_file):
        origin_file = os.path.basename(pcd_file)[:-9] + '.xyz'
        origin_file = origin_file.replace('00000.xyz', '.xyz')
        origin_file = origin_file.replace('_noisy.xyz', '.xyz')
        origin_file = origin_file.replace('_00000.xyz', '.xyz')
        return origin_file

    print('### convert pcd to pts')
    call_params = []
    for fi, files in enumerate(pcd_noisy_files):
        pcd_files_abs = [
            os.path.join(dir_abs_pcd, os.path.basename(f)) for f in files
        ]
        pcd_origin = get_pcd_origin_file(files[0])
        xyz_file = os.path.join(dir_abs_out_vis, pcd_origin)
        xyz_npy_file = os.path.join(dir_abs_out, pcd_origin + '.npy')

        if file_utils.call_necessary(pcd_files_abs, [xyz_npy_file, xyz_file]):
            call_params += [
                (pcd_files_abs, xyz_npy_file, xyz_file, obj_locations[fi],
                 obj_rotations[fi], min_pts_size)
            ]

    utils_mp.start_process_pool(_pcd_files_to_pts, call_params, num_processes)
Ejemplo n.º 4
0
def sample_blensor(base_dir,
                   dataset_dir,
                   blensor_bin,
                   dir_in,
                   dir_out,
                   dir_out_vis,
                   dir_out_pcd,
                   dir_blensor_scripts,
                   num_scans_per_mesh_min,
                   num_scans_per_mesh_max,
                   num_processes,
                   min_pts_size=0,
                   scanner_noise_sigma_min=0.0,
                   scanner_noise_sigma_max=0.05):
    """
    Call Blender to use a Blensor script to sample a point cloud from a mesh
    :param base_dir:
    :param dataset_dir:
    :param dir_in:
    :param dir_out:
    :param dir_blensor_scripts:
    :param num_scans_per_mesh_min: default: 5
    :param num_scans_per_mesh_max: default: 100
    :param scanner_noise_sigma_min: default: 0.0004, rather a lot: 0.01
    :param scanner_noise_sigma_max: default: 0.0004, rather a lot: 0.01
    :return:
    """

    # test blensor scripts with: .\blender -P 00990000_6216c8dabde0a997e09b0f42_trimesh_000.py

    blender_path = os.path.join(base_dir, blensor_bin)
    dir_abs_in = os.path.join(base_dir, dataset_dir, dir_in)
    dir_abs_out = os.path.join(base_dir, dataset_dir, dir_out)
    dir_abs_out_vis = os.path.join(base_dir, dataset_dir, dir_out_vis)
    dir_abs_blensor = os.path.join(base_dir, dataset_dir, dir_blensor_scripts)
    dir_abs_pcd = os.path.join(base_dir, dataset_dir, dir_out_pcd)

    os.makedirs(dir_abs_out, exist_ok=True)
    os.makedirs(dir_abs_out_vis, exist_ok=True)
    os.makedirs(dir_abs_blensor, exist_ok=True)
    os.makedirs(dir_abs_pcd, exist_ok=True)

    blensor_script_template = \
        '''
import bpy
from bpy import data as D
from bpy import context as C
from mathutils import *
from math import *

import blensor


evd_files = {evd_files}
obj_locations = {obj_locations}
obj_rotations = {obj_rotations}
scan_sigmas = {scan_sigmas}

# delete default mesh
bpy.ops.object.select_all(action="DESELECT")
bpy.data.objects["Cube"].select = True
bpy.ops.object.delete()

# load our mesh
file_loc = '{file_loc}'
imported_object = bpy.ops.import_mesh.ply(filepath=file_loc)
obj_object = bpy.context.selected_objects[0]
obj_object.rotation_mode = 'QUATERNION'

"""If the scanner is the default camera it can be accessed 
for example by bpy.data.objects["Camera"]"""
scanner = bpy.data.objects["Camera"]
scanner.rotation_mode = 'QUATERNION'
scanner.local_coordinates = False
scanner.location = Vector([0.0, 0.0, 0.0])

# Kinect settings
# https://github.com/mgschwan/blensor/blob/master/release/scripts/addons/blensor/kinect.py
# scanner.kinect_max_dist=6.0
# scanner.kinect_min_dist=0.7 
# scanner.kinect_noise_mu=0.0  # default 0.0
# scanner.kinect_noise_sigma=0.0  # default 0.0
# scanner.kinect_xres=640 
# scanner.kinect_yres=480 
# scanner.kinect_flength=0.73 
# scanner.kinect_enable_window=False  # experimental
# scanner.kinect_ref_dist=0.0
# scanner.kinect_ref_limit=0.01
# scanner.kinect_ref_slope=0.16 
# scanner.kinect_noise_scale=0.25  # default 0.25
# scanner.kinect_noise_smooth=1.5  # default 1.5
# scanner.kinect_inlier_distance=0.05
        
for i in range(len(evd_files)):
    
    def do_scan(scanner, pcd_file_out):
        """Scan the scene with the Velodyne scanner and save it 
        to the file "/tmp/scan.pcd"
        Note: The data will actually be saved to /tmp/scan00000.pcd 
        and /tmp/scan_noisy00000.pcd
        """
        # blensor.blendodyne.scan_advanced(
        #     scanner,
        #     rotation_speed=10.0,
        #     simulation_fps=24,
        #     angle_resolution=0.1728,
        #     max_distance=120,
        #     evd_file=pcd_file_out,
        #     noise_mu=0.0,
        #     noise_sigma=0.03,
        #     start_angle=0.0,
        #     end_angle=360.0,
        #     evd_last_scan=True,
        #     add_blender_mesh=False,
        #     add_noisy_blender_mesh=False)
            
        # blensor.kinect.scan_advanced(
        #     scanner,
        #     evd_file=pcd_file_out,
        #     evd_last_scan=True
        #     )
        
        # TOF settings
        # https://github.com/mgschwan/blensor/blob/master/release/scripts/addons/blensor/tof.py
        # Blensor 1.0.18 RC 10 Windows has a bug in evd.py: https://github.com/mgschwan/blensor/issues/30
        blensor.tof.scan_advanced(
            scanner,
            evd_file=pcd_file_out,
            evd_last_scan=True,
            max_distance=10.0,
            add_blender_mesh = False,
            add_noisy_blender_mesh = False,
            tof_res_x=176,
            tof_res_y=144,
            lens_angle_w=43.6,
            lens_angle_h=34.6,
            flength=10.0,
            noise_mu=0.0,
            # noise_sigma=scanner_noise_sigma,  # default 0.0004
            noise_sigma=scan_sigmas[i],  # default 0.0004
            backfolding=False,
            )
        
    evd_file = evd_files[i]
    obj_object.location = Vector(obj_locations[i])
    obj_object.rotation_quaternion = Quaternion(obj_rotations[i])
    do_scan(scanner, evd_file)
    
bpy.ops.wm.quit_blender()
        '''

    blender_blensor_calls = []
    pcd_base_files = []
    pcd_noisy_files = []
    obj_locations = []
    obj_rotations = []

    obj_files = [
        f for f in os.listdir(dir_abs_in)
        if os.path.isfile(os.path.join(dir_abs_in, f)) and f[-4:] == '.ply'
    ]
    for fi, file in enumerate(obj_files):

        # gather all file names involved in the blensor scanning
        obj_file = os.path.join(dir_abs_in, file)
        blensor_script_file = os.path.join(dir_abs_blensor, file[:-4] + '.py')

        new_pcd_base_files = []
        new_pcd_noisy_files = []
        new_obj_locations = []
        new_obj_rotations = []
        rnd = np.random.RandomState(file_utils.filename_to_hash(obj_file))
        num_scans = rnd.randint(num_scans_per_mesh_min,
                                num_scans_per_mesh_max + 1)
        noise_sigma = rnd.rand() * (
            scanner_noise_sigma_max -
            scanner_noise_sigma_min) + scanner_noise_sigma_min
        for num_scan in range(num_scans):
            pcd_base_file = os.path.join(
                dir_abs_pcd, file[:-4] +
                '_{num}.numpy.gz'.format(num=str(num_scan).zfill(5)))
            pcd_noisy_file = pcd_base_file[:-9] + '00000.numpy.gz'

            obj_location = (rnd.rand(3) * 2.0 - 1.0)
            obj_location_rand_factors = np.array([0.1, 1.0, 0.1])
            obj_location *= obj_location_rand_factors
            obj_location[1] += 4.0  # offset in cam view dir
            obj_rotation = trafo.random_quaternion(rnd.rand(3))

            # extend lists of pcd output files
            new_pcd_base_files.append(pcd_base_file)
            new_pcd_noisy_files.append(pcd_noisy_file)
            new_obj_locations.append(obj_location.tolist())
            new_obj_rotations.append(obj_rotation.tolist())

        new_scan_sigmas = [noise_sigma] * num_scans

        pcd_base_files.append(new_pcd_base_files)
        pcd_noisy_files.append(new_pcd_noisy_files)
        obj_locations.append(new_obj_locations)
        obj_rotations.append(new_obj_rotations)

        # prepare blensor calls if necessary
        output_files = [
            os.path.join(dir_abs_pcd, os.path.basename(f))
            for f in new_pcd_noisy_files
        ]
        output_files += [blensor_script_file]
        if file_utils.call_necessary(obj_file, output_files):
            blensor_script = blensor_script_template.format(
                file_loc=obj_file,
                obj_locations=str(new_obj_locations),
                obj_rotations=str(new_obj_rotations),
                evd_files=str(new_pcd_base_files),
                scan_sigmas=str(new_scan_sigmas),
            )
            blensor_script = blensor_script.replace(
                '\\', '/')  # '\' would require escape sequence

            with open(blensor_script_file, "w") as text_file:
                text_file.write(blensor_script)

            # start blender with python script (-P) and close without prompt (-b)
            blender_blensor_call = '{} -P {} -b'.format(
                blender_path, blensor_script_file)
            blender_blensor_calls.append((blender_blensor_call, ))

    utils_mp.start_process_pool(utils_mp.mp_worker, blender_blensor_calls,
                                num_processes)

    def get_pcd_origin_file(pcd_file):
        origin_file = os.path.basename(pcd_file)[:-9] + '.xyz'
        origin_file = origin_file.replace('00000.xyz', '.xyz')
        origin_file = origin_file.replace('_noisy.xyz', '.xyz')
        origin_file = origin_file.replace('_00000.xyz', '.xyz')
        return origin_file

    print('### convert pcd to pts')
    call_params = []
    for fi, files in enumerate(pcd_noisy_files):
        pcd_files_abs = [
            os.path.join(dir_abs_pcd, os.path.basename(f)) for f in files
        ]
        pcd_origin = get_pcd_origin_file(files[0])
        xyz_file = os.path.join(dir_abs_out_vis, pcd_origin)
        xyz_npy_file = os.path.join(dir_abs_out, pcd_origin + '.npy')

        if file_utils.call_necessary(pcd_files_abs, [xyz_npy_file, xyz_file]):
            call_params += [
                (pcd_files_abs, xyz_npy_file, xyz_file, obj_locations[fi],
                 obj_rotations[fi], min_pts_size)
            ]

    utils_mp.start_process_pool(_pcd_files_to_pts, call_params, num_processes)