def join_scene()

in pytorch3d/renderer/mesh/textures.py [0:0]


    def join_scene(self) -> "TexturesUV":
        """
        Return a new TexturesUV amalgamating the batch.

        We calculate a large single map which contains the original maps,
        and find verts_uvs to point into it. This will not replicate
        behavior of padding for verts_uvs values outside [0,1].

        If align_corners=False, we need to add an artificial border around
        every map.

        We use the function `pack_unique_rectangles` to provide a layout for
        the single map. This means that if self was created with a list of maps,
        and to() has not been called, and there were two maps which were exactly
        the same tensor object, then they will become the same data in the unified map.
        _place_map_into_single_map is used to copy the maps into the single map.
        The merging of verts_uvs and faces_uvs is handled locally in this function.
        """
        maps = self.maps_list()
        heights_and_widths = []
        extra_border = 0 if self.align_corners else 2
        for map_ in maps:
            heights_and_widths.append(
                Rectangle(
                    map_.shape[0] + extra_border, map_.shape[1] + extra_border, id(map_)
                )
            )
        merging_plan = pack_unique_rectangles(heights_and_widths)
        C = maps[0].shape[-1]
        single_map = maps[0].new_zeros((*merging_plan.total_size, C))
        verts_uvs = self.verts_uvs_list()
        verts_uvs_merged = []

        for map_, loc, uvs in zip(maps, merging_plan.locations, verts_uvs):
            new_uvs = uvs.clone()
            if loc.is_first:
                self._place_map_into_single_map(single_map, map_, loc)
            do_flip = loc.flipped
            x_shape = map_.shape[1] if do_flip else map_.shape[0]
            y_shape = map_.shape[0] if do_flip else map_.shape[1]

            if do_flip:
                # Here we have flipped / transposed the map.
                # In uvs, the y values are decreasing from 1 to 0 and the x
                # values increase from 0 to 1. We subtract all values from 1
                # as the x's become y's and the y's become x's.
                new_uvs = 1.0 - new_uvs[:, [1, 0]]
                if TYPE_CHECKING:
                    new_uvs = torch.Tensor(new_uvs)

            # If align_corners is True, then an index of x (where x is in
            # the range 0 .. map_.shape[1]-1) in one of the input maps
            # was hit by a u of x/(map_.shape[1]-1).
            # That x is located at the index loc[1] + x in the single_map, and
            # to hit that we need u to equal (loc[1] + x) / (total_size[1]-1)
            # so the old u should be mapped to
            #   { u*(map_.shape[1]-1) + loc[1] } / (total_size[1]-1)

            # Also, an index of y (where y is in
            # the range 0 .. map_.shape[0]-1) in one of the input maps
            # was hit by a v of 1 - y/(map_.shape[0]-1).
            # That y is located at the index loc[0] + y in the single_map, and
            # to hit that we need v to equal 1 - (loc[0] + y) / (total_size[0]-1)
            # so the old v should be mapped to
            #   1 - { (1-v)*(map_.shape[0]-1) + loc[0] } / (total_size[0]-1)
            # =
            # { v*(map_.shape[0]-1) + total_size[0] - map.shape[0] - loc[0] }
            #        / (total_size[0]-1)

            # If align_corners is False, then an index of x (where x is in
            # the range 1 .. map_.shape[1]-2) in one of the input maps
            # was hit by a u of (x+0.5)/(map_.shape[1]).
            # That x is located at the index loc[1] + 1 + x in the single_map,
            # (where the 1 is for the border)
            # and to hit that we need u to equal (loc[1] + 1 + x + 0.5) / (total_size[1])
            # so the old u should be mapped to
            #   { loc[1] + 1 + u*map_.shape[1]-0.5 + 0.5 } / (total_size[1])
            #  = { loc[1] + 1 + u*map_.shape[1] } / (total_size[1])

            # Also, an index of y (where y is in
            # the range 1 .. map_.shape[0]-2) in one of the input maps
            # was hit by a v of 1 - (y+0.5)/(map_.shape[0]).
            # That y is located at the index loc[0] + 1 + y in the single_map,
            # (where the 1 is for the border)
            # and to hit that we need v to equal 1 - (loc[0] + 1 + y + 0.5) / (total_size[0])
            # so the old v should be mapped to
            #   1 - { loc[0] + 1 + (1-v)*map_.shape[0]-0.5 + 0.5 } / (total_size[0])
            #  = { total_size[0] - loc[0] -1 - (1-v)*map_.shape[0]  }
            #         / (total_size[0])
            #  = { total_size[0] - loc[0] - map.shape[0] - 1 + v*map_.shape[0] }
            #         / (total_size[0])

            # We change the y's in new_uvs for the scaling of height,
            # and the x's for the scaling of width.
            # That is why the 1's and 0's are mismatched in these lines.
            one_if_align = 1 if self.align_corners else 0
            one_if_not_align = 1 - one_if_align
            denom_x = merging_plan.total_size[0] - one_if_align
            scale_x = x_shape - one_if_align
            denom_y = merging_plan.total_size[1] - one_if_align
            scale_y = y_shape - one_if_align
            new_uvs[:, 1] *= scale_x / denom_x
            new_uvs[:, 1] += (
                merging_plan.total_size[0] - x_shape - loc.x - one_if_not_align
            ) / denom_x
            new_uvs[:, 0] *= scale_y / denom_y
            new_uvs[:, 0] += (loc.y + one_if_not_align) / denom_y

            verts_uvs_merged.append(new_uvs)

        faces_uvs_merged = []
        offset = 0
        for faces_uvs_, verts_uvs_ in zip(self.faces_uvs_list(), verts_uvs):
            faces_uvs_merged.append(offset + faces_uvs_)
            offset += verts_uvs_.shape[0]

        return self.__class__(
            maps=[single_map],
            verts_uvs=[torch.cat(verts_uvs_merged)],
            faces_uvs=[torch.cat(faces_uvs_merged)],
            align_corners=self.align_corners,
            padding_mode=self.padding_mode,
            sampling_mode=self.sampling_mode,
        )