예제 #1
0
    def add_transforms_object(self,
                              record: ModelRecord,
                              position: Dict[str, float],
                              rotation: Dict[str, float],
                              o_id: Optional[int] = None) -> dict:
        """
        This is a wrapper for `Controller.get_add_object()` and the `add_object` command.
        This caches the ID of the object so that it can be easily cleaned up later.

        :param record: The model record.
        :param position: The initial position of the object.
        :param rotation: The initial rotation of the object, in Euler angles.
        :param o_id: The unique ID of the object. If None, a random ID is generated.

        :return: An `add_object` command.
        """

        if o_id is None:
            o_id: int = Controller.get_unique_id()

        # Log the static data.
        self.object_ids = np.append(self.object_ids, o_id)

        return {
            "$type": "add_object",
            "name": record.name,
            "url": record.get_url(),
            "scale_factor": record.scale_factor,
            "position": position,
            "rotation": rotation,
            "category": record.wcategory,
            "id": o_id
        }
예제 #2
0
    def get_image(self, record: ModelRecord):
        o_id = Controller.get_unique_id()
        self.communicate({"$type": "add_object",
                          "name": record.name,
                          "url": record.get_url(),
                          "scale_factor": record.scale_factor,
                          "rotation": record.canonical_rotation,
                          "id": o_id})

        s = TDWUtils.get_unit_scale(record) * 2

        # Scale the model and get an image.
        # Look at the model's centroid.
        resp = self.communicate([{"$type": "scale_object",
                                  "id": o_id,
                                  "scale_factor": {"x": s, "y": s, "z": s}},
                                 {"$type": "look_at",
                                  "avatar_id": "a",
                                  "object_id": o_id,
                                  "use_centroid": True}])
        # Destroy the model and unload the asset bundle.
        self.communicate([{"$type": "destroy_object",
                          "id": o_id},
                          {"$type": "unload_asset_bundles"}])
        return Images(resp[0]), resp[-1]
예제 #3
0
    def trial(self, scene: Scene, record: ModelRecord, output_path: Path,
              scene_index: int) -> None:
        """
        Run a trial in a scene that has been initialized.

        :param scene: Data for the current scene.
        :param record: The model's metadata record.
        :param output_path: Write the .wav file to this path.
        :param scene_index: The scene identifier.
        """

        self.py_impact.reset(initial_amp=0.05)

        # Initialize the scene, positioning objects, furniture, etc.
        resp = self.communicate(scene.initialize_scene(self))
        center = scene.get_center(self)

        max_y = scene.get_max_y()

        # The object's initial position.
        o_x = RNG.uniform(center["x"] - 0.15, center["x"] + 0.15)
        o_y = RNG.uniform(max_y - 0.5, max_y)
        o_z = RNG.uniform(center["z"] - 0.15, center["z"] + 0.15)
        # Physics values.
        mass = self.object_info[record.name].mass + RNG.uniform(
            self.object_info[record.name].mass * -0.15,
            self.object_info[record.name].mass * 0.15)
        static_friction = RNG.uniform(0.1, 0.3)
        dynamic_friction = RNG.uniform(0.7, 0.9)
        # Angles of rotation.
        yaw = RNG.uniform(-30, 30)
        pitch = RNG.uniform(0, 45)
        roll = RNG.uniform(-45, 45)
        # The force applied to the object.
        force = RNG.uniform(0, 5)
        # The avatar's position.
        a_r = RNG.uniform(1.5, 2.2)
        a_x = center["x"] + a_r
        a_y = RNG.uniform(1.5, 3)
        a_z = center["z"] + a_r
        cam_angle_min, cam_angle_max = scene.get_camera_angles()
        theta = np.radians(RNG.uniform(cam_angle_min, cam_angle_max))
        a_x = np.cos(theta) * (a_x - center["x"]) - np.sin(theta) * (
            a_z - center["z"]) + center["x"]
        a_z = np.sin(theta) * (a_x - center["x"]) + np.cos(theta) * (
            a_z - center["z"]) + center["z"]

        o_id = 0
        # Create the object and apply a force.
        commands = [{
            "$type": "add_object",
            "name": record.name,
            "url": record.get_url(),
            "scale_factor": record.scale_factor,
            "position": {
                "x": o_x,
                "y": o_y,
                "z": o_z
            },
            "category": record.wcategory,
            "id": o_id
        }, {
            "$type": "set_mass",
            "id": o_id,
            "mass": mass
        }, {
            "$type": "set_physic_material",
            "id": o_id,
            "bounciness": self.object_info[record.name].bounciness,
            "static_friction": static_friction,
            "dynamic_friction": dynamic_friction
        }, {
            "$type": "rotate_object_by",
            "angle": yaw,
            "id": o_id,
            "axis": "yaw",
            "is_world": True
        }, {
            "$type": "rotate_object_by",
            "angle": pitch,
            "id": o_id,
            "axis": "pitch",
            "is_world": True
        }, {
            "$type": "rotate_object_by",
            "angle": roll,
            "id": o_id,
            "axis": "roll",
            "is_world": True
        }, {
            "$type": "apply_force_magnitude_to_object",
            "magnitude": force,
            "id": o_id
        }, {
            "$type": "send_rigidbodies",
            "frequency": "always"
        }, {
            "$type": "send_collisions",
            "enter": True,
            "exit": False,
            "stay": False,
            "collision_types": ["obj", "env"]
        }, {
            "$type": "send_transforms",
            "frequency": "always"
        }]
        # Parse bounds data to get the centroid of all objects currently in the scene.
        bounds = Bounds(resp[0])
        if bounds.get_num() == 0:
            look_at = {"x": center["x"], "y": 0.1, "z": center["z"]}
        else:
            centers = []
            for i in range(bounds.get_num()):
                centers.append(bounds.get_center(i))
            centers_x, centers_y, centers_z = zip(*centers)
            centers_len = len(centers_x)
            look_at = {
                "x": sum(centers_x) / centers_len,
                "y": sum(centers_y) / centers_len,
                "z": sum(centers_z) / centers_len
            }
        # Add the avatar.
        # Set the position at a given distance (r) from the center of the scene.
        # Rotate around that position to a random angle constrained by the scene's min and max angles.
        commands.extend([{
            "$type": "teleport_avatar_to",
            "position": {
                "x": a_x,
                "y": a_y,
                "z": a_z
            }
        }, {
            "$type": "look_at_position",
            "position": look_at
        }])

        # Send the commands.
        resp = self.communicate(commands)

        AudioUtils.start(output_path=output_path, until=(0, 10))

        # Loop until all objects are sleeping.
        done = False
        while not done and AudioUtils.is_recording():
            commands = []
            collisions, environment_collisions, rigidbodies = PyImpact.get_collisions(
                resp)
            # Create impact sounds from object-object collisions.
            for collision in collisions:
                if PyImpact.is_valid_collision(collision):
                    # Get the audio material and amp.
                    collider_id = collision.get_collider_id()
                    collider_material, collider_amp = self._get_object_info(
                        collider_id, Scene.OBJECT_IDS, record.name)
                    collidee_id = collision.get_collider_id()
                    collidee_material, collidee_amp = self._get_object_info(
                        collidee_id, Scene.OBJECT_IDS, record.name)
                    impact_sound_command = self.py_impact.get_impact_sound_command(
                        collision=collision,
                        rigidbodies=rigidbodies,
                        target_id=collidee_id,
                        target_amp=collidee_amp,
                        target_mat=collidee_material.name,
                        other_id=collider_id,
                        other_mat=collider_material.name,
                        other_amp=collider_amp,
                        play_audio_data=False)
                    commands.append(impact_sound_command)
            # Create impact sounds from object-environment collisions.
            for collision in environment_collisions:
                collider_id = collision.get_object_id()
                if self._get_velocity(rigidbodies, collider_id) > 0:
                    collider_material, collider_amp = self._get_object_info(
                        collider_id, Scene.OBJECT_IDS, record.name)
                    surface_material = scene.get_surface_material()
                    impact_sound_command = self.py_impact.get_impact_sound_command(
                        collision=collision,
                        rigidbodies=rigidbodies,
                        target_id=collider_id,
                        target_amp=collider_amp,
                        target_mat=collider_material.name,
                        other_id=-1,
                        other_amp=0.01,
                        other_mat=surface_material.name,
                        play_audio_data=False)
                    commands.append(impact_sound_command)
            # If there were no collisions, check for movement. If nothing is moving, the trial is done.
            if len(commands) == 0:
                transforms = AudioDataset._get_transforms(resp)
                done = True
                for i in range(rigidbodies.get_num()):
                    if self._is_moving(rigidbodies.get_id(i), transforms,
                                       rigidbodies):
                        done = False
                        break
            # Continue the trial.
            if not done:
                resp = self.communicate(commands)

        # Stop listening for anything except audio data..
        resp = self.communicate([{
            "$type": "send_rigidbodies",
            "frequency": "never"
        }, {
            "$type": "send_transforms",
            "frequency": "never"
        }, {
            "$type": "send_collisions",
            "enter": False,
            "exit": False,
            "stay": False,
            "collision_types": []
        }, {
            "$type": "send_audio_sources",
            "frequency": "always"
        }])
        # Wait for the audio to finish.
        done = False
        while not done and AudioUtils.is_recording():
            done = True
            for r in resp[:-1]:
                if OutputData.get_data_type_id(r) == "audi":
                    audio_sources = AudioSources(r)
                    for i in range(audio_sources.get_num()):
                        if audio_sources.get_is_playing(i):
                            done = False
            if not done:
                resp = self.communicate([])
        # Cleanup.
        commands = [{
            "$type": "send_audio_sources",
            "frequency": "never"
        }, {
            "$type": "destroy_object",
            "id": o_id
        }]
        for scene_object_id in Scene.OBJECT_IDS:
            commands.append({"$type": "destroy_object", "id": scene_object_id})
        self.communicate(commands)

        # Insert the trial's values into the database.
        self.db_c.execute(
            "INSERT INTO sound20k VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)",
            (output_path.name, scene_index, a_x, a_y, a_z, o_x, o_y, o_z, mass,
             static_friction, dynamic_friction, yaw, pitch, roll, force))
        self.conn.commit()
예제 #4
0
    def process_model(self, record: ModelRecord, a: str, envs: list,
                      train_count: int, val_count: int, root_dir: str,
                      wnid: str) -> float:
        """
        Capture images of a model.

        :param record: The model record.
        :param a: The ID of the avatar.
        :param envs: All environment data.
        :param train_count: Number of train images.
        :param val_count: Number of val images.
        :param root_dir: The root directory for saving images.
        :param wnid: The wnid of the record.
        :return The time elapsed.
        """

        image_count = 0

        # Get the filename index. If we shouldn't overwrite any images, start after the last image.
        if self.no_overwrite:
            # Check if any images exist.
            wnid_dir = Path(root_dir).joinpath(f"train/{wnid}")
            if wnid_dir.exists():
                max_file_index = -1
                for image in wnid_dir.iterdir():
                    if not image.is_file() or image.suffix != ".jpg" \
                            or not image.stem.startswith("img_") or image.stem[4:-5] != record.name:
                        continue
                    image_index = int(image.stem[-4:])
                    if image_index > max_file_index:
                        max_file_index = image_index
                file_index = max_file_index + 1
            else:
                file_index = 0
        else:
            file_index = 0

        image_positions = []
        o_id = self.get_unique_id()

        s = TDWUtils.get_unit_scale(record)

        # Add the object.
        # Set the screen size to 32x32 (to make the build run faster; we only need the average grayscale values).
        # Toggle off pass masks.
        # Set render quality to minimal.
        # Scale the object to "unit size".
        self.communicate([{
            "$type": "add_object",
            "name": record.name,
            "url": record.get_url(),
            "scale_factor": record.scale_factor,
            "category": record.wcategory,
            "id": o_id
        }, {
            "$type": "set_screen_size",
            "height": 32,
            "width": 32
        }, {
            "$type": "set_pass_masks",
            "avatar_id": a,
            "pass_masks": []
        }, {
            "$type": "set_render_quality",
            "render_quality": 0
        }, {
            "$type": "scale_object",
            "id": o_id,
            "scale_factor": {
                "x": s,
                "y": s,
                "z": s
            }
        }])

        # The index in the HDRI records array.
        hdri_index = 0
        # The number of iterations on this skybox so far.
        skybox_count = 0
        if self.skyboxes:
            # The number of iterations per skybox for this model.
            its_per_skybox = round(
                (train_count + val_count) / len(self.skyboxes))

            # Set the first skybox.
            hdri_index, skybox_count, command = self.set_skybox(
                self.skyboxes, its_per_skybox, hdri_index, skybox_count)
            self.communicate(command)
        else:
            its_per_skybox = 0

        while len(image_positions) < train_count + val_count:
            e = RNG.choice(envs)

            # Get the real grayscale.
            g_r, d, a_p, o_p, o_rot, cam_rot = self.get_real_grayscale(
                o_id, a, e)

            if g_r > 0:
                # Get the optimal grayscale.
                g_o = self.get_optimal_grayscale(o_id, a, o_p, a_p)

                if g_o > 0 and g_r / g_o > self.grayscale_threshold:
                    # Cache the position.
                    image_positions.append(
                        ImagePosition(a_p, cam_rot, o_p, o_rot))

        # Send images.
        # Set the screen size.
        # Set render quality to maximum.
        commands = [{
            "$type": "send_images",
            "frequency": "always"
        }, {
            "$type": "set_pass_masks",
            "avatar_id": a,
            "pass_masks": ["_img", "_id"] if self.id_pass else ["_img"]
        }, {
            "$type": "set_screen_size",
            "height": self.screen_size,
            "width": self.screen_size
        }, {
            "$type": "set_render_quality",
            "render_quality": 5
        }]
        # Hide the object maybe.
        if not self.show_objects:
            commands.append({"$type": "hide_object", "id": o_id})

        self.communicate(commands)

        t0 = time()

        # Generate images from the cached spatial data.
        train = 0
        for p in image_positions:
            # Teleport the avatar.
            # Rotate the avatar's camera.
            # Teleport the object.
            # Rotate the object.
            # Get the response.
            commands = [{
                "$type": "teleport_avatar_to",
                "avatar_id": a,
                "position": p.avatar_position
            }, {
                "$type": "rotate_sensor_container_to",
                "avatar_id": a,
                "rotation": p.camera_rotation
            }, {
                "$type": "teleport_object",
                "id": o_id,
                "position": p.object_position
            }, {
                "$type": "rotate_object_to",
                "id": o_id,
                "rotation": p.object_rotation
            }]
            # Set the visual materials.
            if self.materials is not None:
                if record.name not in self.substructures:
                    self.substructures.update(
                        {record.name: record.substructure})
                for sub_object in self.substructures[record.name]:
                    for i in range(
                            len(self.substructures[record.name][
                                sub_object["name"]])):
                        material_name = self.materials[RNG.randint(
                            0, len(self.materials))].name
                        commands.extend([
                            self.get_add_material(material_name), {
                                "$type": "set_visual_material",
                                "id": o_id,
                                "material_name": material_name,
                                "object_name": sub_object["name"],
                                "material_index": i
                            }
                        ])
            # Maybe set a new skybox.
            # Rotate the skybox.
            if self.skyboxes:
                hdri_index, skybox_count, command = self.set_skybox(
                    self.skyboxes, its_per_skybox, hdri_index, skybox_count)
                if command:
                    commands.append(command)
                commands.append({
                    "$type": "rotate_hdri_skybox_by",
                    "angle": RNG.uniform(0, 360)
                })

            resp = self.communicate(commands)
            train += 1

            # Create a thread to save the image.
            t = Thread(target=self.save_image,
                       args=(resp, record, file_index, root_dir, wnid, train,
                             train_count))
            t.daemon = True
            t.start()
            file_index += 1
            image_count += 1
        t1 = time()

        # Stop sending images.
        # Destroy the object.
        # Unload asset bundles.
        self.communicate([{
            "$type": "send_images",
            "frequency": "never"
        }, {
            "$type": "destroy_object",
            "id": o_id
        }, {
            "$type": "unload_asset_bundles"
        }])
        return t1 - t0