//  [python]
def process_metadata(self, config: Dict[str, Any], update_config=False) -> None:
        """Preprocesses self.render_metadata to a format that can be ingested in CONFIG training based on the network config.

        Reads metadata inputs that are to be used from the config.
        If update_config is specified, the method updates the input dimensionality in the config.
        Updates dictionary of metadata input vectors, each vector corresponds to a different metadata type (hair style, texture, etc).
        """

        self.metadata_inputs = {}
        self.metadata_input_distributions = {}
        self.metadata_input_labels = {}

        def fit_distribution(data, distr_type):
            if distr_type == "GMM":
                distr = mixture.GaussianMixture()
                distr.fit(data)
            elif distr_type == "one_hot":
                distr = OneHotDistribution()
                distr.fit(data)
            elif distr_type == "exemplar":
                distr = ExemplarDistribution()
                distr.fit(data)

            return distr

        for input_name in config["facemodel_inputs"].keys():
            nested_dict_keys = input_name.split(":")
            metadata_values = self.render_metadata
            for key in nested_dict_keys:
                metadata_values = [metadata[key] for metadata in metadata_values]

            # Some string values can be set to none, we'll replace that with a string for consistency
            for i, metadata_value in enumerate(metadata_values):
                if metadata_value is None:
                    metadata_values[i] = "none"

            assert all([type(metadata_value) == type(metadata_values[0]) for metadata_value in metadata_values])

            if type(metadata_values[0]) == str:
                unique_vals, unique_inverse = np.unique(metadata_values, return_inverse=True)
                n_unique_vals = unique_vals.shape[0]
                one_hot = np.zeros((len(metadata_values), n_unique_vals))
                one_hot[np.arange(len(metadata_values)), unique_inverse] = 1
                self.metadata_inputs[input_name] = one_hot
                self.metadata_input_distributions[input_name] = fit_distribution(one_hot, "one_hot")
                self.metadata_input_labels[input_name] = unique_vals.tolist()
                if update_config:
                    config["facemodel_inputs"][input_name] = (int(n_unique_vals), config["facemodel_inputs"][input_name][1])
            elif type(metadata_values[0]) == list:
                assert all([len(value) == len(metadata_values[0]) for value in metadata_values])

                metadata_values = np.array(metadata_values, dtype=np.float32)
                self.metadata_inputs[input_name] = metadata_values
                self.metadata_input_distributions[input_name] = fit_distribution(metadata_values, "exemplar")
                self.metadata_input_labels[input_name] = None
                if update_config:
                    config["facemodel_inputs"][input_name] = (metadata_values.shape[1], config["facemodel_inputs"][input_name][1])
            elif type(metadata_values[0]) == dict:
                assert all([metadata_value.keys() == metadata_values[0].keys() for metadata_value in metadata_values])

                metadata_values = [OrderedDict(sorted(metadata_value.items(), key=lambda t: t[0])) for metadata_value in metadata_values]

                self.metadata_input_labels[input_name] = list(metadata_values[0].keys())
                metadata_values = np.array([list(metadata_value.values()) for metadata_value in metadata_values], dtype=np.float32)
                if input_name == "blendshape_values":
                    jaw_opening_values = np.array([metadata["bone_rotations"]["jaw"][0] for metadata in self.render_metadata])
                    metadata_values = np.hstack((metadata_values, jaw_opening_values[:, np.newaxis]))
                    self.metadata_input_labels[input_name].append("jaw_opening")

                self.metadata_inputs[input_name] = metadata_values
                self.metadata_input_distributions[input_name] = fit_distribution(metadata_values, "exemplar")
                if update_config:
                    config["facemodel_inputs"][input_name] = (metadata_values.shape[1], config["facemodel_inputs"][input_name][1])

        # Extract rotation angles
        rotation_values = [metadata["bone_rotations"]["head"] for metadata in self.render_metadata]
        self.metadata_inputs["rotations"] = np.array(rotation_values)[:, [2, 0, 1]]
        # No label is specified for vector inputs
        self.metadata_input_labels["rotations"] = None