def encode_image(self, batch: torch.Tensor) -> tuple[torch.Tensor, list[torch.Tensor], torch.Tensor]:
        """Encode batch of images to get image, window and patch embeddings.

        The image and patch embeddings are obtained by passing images through the model.
        Window embeddings are obtained by masking feature map and passing through
        transformer. A forward hook retrieves intermediate feature map to share
        computation.

        Args:
            batch (torch.Tensor): Input images of shape ``(N, C, H, W)``.

        Returns:
            tuple[torch.Tensor, list[torch.Tensor], torch.Tensor]: Tuple containing:
                - Image embeddings of shape ``(N, D)``
                - Window embeddings list, each of shape ``(N, W, D)``
                - Patch embeddings of shape ``(N, P, D)``
                where ``D`` is embedding dimension, ``W`` is number of windows,
                and ``P`` is number of patches.

        Examples:
            >>> model = WinClipModel()  # doctest: +SKIP
            >>> model.prepare_masks()  # doctest: +SKIP
            >>> batch = torch.rand((1, 3, 240, 240))  # doctest: +SKIP
            >>> outputs = model.encode_image(batch)  # doctest: +SKIP
            >>> image_embeddings, window_embeddings, patch_embeddings = outputs
            >>> image_embeddings.shape  # doctest: +SKIP
            torch.Size([1, 640])
            >>> [emb.shape for emb in window_embeddings]  # doctest: +SKIP
            [torch.Size([1, 196, 640]), torch.Size([1, 169, 640])]
            >>> patch_embeddings.shape  # doctest: +SKIP
            torch.Size([1, 225, 896])
        """
        # apply transform if needed
        if self.apply_transform:
            batch = torch.stack([self.transform(image) for image in batch])

        # register hook to retrieve intermediate feature map
        outputs = {}

        def get_feature_map(name: str) -> Callable:
            def hook(_model: Identity, inputs: tuple[torch.Tensor,], _outputs: torch.Tensor) -> None:
                del _model, _outputs
                outputs[name] = inputs[0].detach()

            return hook

        # register hook to get the intermediate tokens of the transformer
        self.clip.visual.patch_dropout.register_forward_hook(get_feature_map("feature_map"))

        # get image and patch embeddings
        image_embeddings, patch_embeddings = self.clip.encode_image(batch)

        # get window embeddings
        feature_map = outputs["feature_map"]
        window_embeddings = [self._get_window_embeddings(feature_map, masks) for masks in self.masks]

        return (
            image_embeddings,
            window_embeddings,
            patch_embeddings,
        )
Encode batch of images to get image, window and patch embeddings.

        The image and patch embeddings are obtained by passing images through the model.
        Window embeddings are obtained by masking feature map and passing through
        transformer. A forward hook retrieves intermediate feature map to share
        computation.

        Args:
            batch (torch.Tensor): Input images of shape ``(N, C, H, W)``.

        Returns:
            tuple[torch.Tensor, list[torch.Tensor], torch.Tensor]: Tuple containing:
                - Image embeddings of shape ``(N, D)``
                - Window embeddings list, each of shape ``(N, W, D)``
                - Patch embeddings of shape ``(N, P, D)``
                where ``D`` is embedding dimension, ``W`` is number of windows,
                and ``P`` is number of patches.

        Examples:
            >>> model = WinClipModel()  # doctest: +SKIP
            >>> model.prepare_masks()  # doctest: +SKIP
            >>> batch = torch.rand((1, 3, 240, 240))  # doctest: +SKIP
            >>> outputs = model.encode_image(batch)  # doctest: +SKIP
            >>> image_embeddings, window_embeddings, patch_embeddings = outputs
            >>> image_embeddings.shape  # doctest: +SKIP
            torch.Size([1, 640])
            >>> [emb.shape for emb in window_embeddings]  # doctest: +SKIP
            [torch.Size([1, 196, 640]), torch.Size([1, 169, 640])]
            >>> patch_embeddings.shape  # doctest: +SKIP
            torch.Size([1, 225, 896])
        
src/anomalib/models/image/winclip/torch_model.py