Custom Detector

December 29, 2025 · View on GitHub

The hiad.detectors.base.BaseDetector class serves as a wrapper for detectors in HiAD. You can create a custom detector by inheriting from this class.

Initialize the detector

from hiad.detectors.base import BaseDetector
from typing import List, Union
import torch
import logging

class CustomizedDetector(BaseDetector):

    def __init__(self,
                 other_params,
                 patch_size: Union[int, List],  
                 device: torch.device,  
                 fusion_weights = None,
                 logger: logging.Logger = None, 
                 seed: int = 0,  
                 early_stop_epochs = -1,
                 **kwargs):
        r"""
           Args:
               other_params: Define the parameters required by the anomaly detection algorithm; they should match those provided in the config.
               patch_size (int or list): Image patch resolution.
               device (torch.Device): Computing device in PyTorch.
               fusion_weights (list): Fusion weights used for multi-resolution feature fusion. This functionality is implemented in `BaseDetector`, so you only need to pass the weights to `BaseDetector`.
               logger (logging.Logger): Logger object
               seed (int): random seed.
               early_stop_epochs (int): Controls early stopping during training: 
                        if the detector shows no performance improvement over N epochs, 
                        training will stop early. If set to -1, early stopping is disabled. 
                        This functionality is implemented in `BaseDetector`, so you only need to pass the weights to `BaseDetector`.
        """
        super().__init__(patch_size, device, fusion_weights, logger, seed, early_stop_epochs)
        pass

Define the data preprocessing pipeline by implementing the create_dataset method.

    from hiad.datasets.patch_dataset import PatchDataset
    
    def create_dataset(self, patches: List[LRPatch], training: bool, task_name: str):
        r"""
           This method defines the data preprocessing pipeline for the model by constructing a `Dataset` object.
           Args
               patches (List[LRPatch]): A list of `LRPatch` objects. HiAD encapsulates image patches using the `hiad.utils.split_and_gather.LRPatch` class.
                        Some key attributes included in the `LRPatch` class are:
                        image: np.ndarray               # image patch
                        mask: np.ndarray = None         # Anomaly mask, not `None` only when the patch comes from an anomaly sample used for testing.
                        foreground: np.ndarray = None   # Foreground segmentation mask, typically not used.
                        label: int = None               # 0: normal sample; 1:anomaly sample.
                        label_name: str = None          # The specific anomaly category.
                        clsname: str = None             # Image category.
               training (boolean): True: Trainint mode; False: Inference mode.
               task_name (str):    Task Name
           return:
               return a torch.utils.data.Dataset object
        """
        # The default data loading method provided by HiAD, when overriding this method, refer to the `PatchDataset` class and ensure it returns data in the required format.
        dataset = PatchDataset(patches = patches, training = training, task_name = task_name)
        return dataset

Define the feature extraction process by implementing the embedding method.

    @abstractmethod
    def embedding(self, input_tensor: torch.Tensor ) -> List[torch.Tensor]:
        r"""
           This method encodes image patches into features (feature extraction).
           Args
               input_tensor (torch.Tensor): image patch tensor. Shape: (B,3,Hp,Wp)
           return:
                returns the extracted multi-scale features; Shape: ([B,C1,H1,W1], [B,C2,H2,W2], ..., [B,Cn,Hn,Wn])
        """
        input_tensor = input_tensor.to(self.device)
        # feature extraction process
        raise NotImplementedError

Define the model's training process

    @abstractmethod
    def train_step(self,
                   train_dataloader: DataLoader,
                   task_name: str,
                   checkpoint_path: str,
                   val_dataloader: DataLoader = None,
                   evaluators=None,
                   ) -> bool:
        r"""
           This method defines the model's training process.
           Args
               train_dataloader (torch.utils.data.DataLoader): Dataloader used for training; the format of the returned data is defined by the `create_dataset` method.
               task_name (str): Task Name
               checkpoint_path: Path of checkpoint
               val_dataloader (torch.utils.data.DataLoader): Dataloader used for validation; the format of the returned data is defined by the `create_dataset` method.
                    If no val_config is provided, this value will be `None`.
               evaluators: Evaluation methods.
               The `checkpoint_path`, `val_dataloader`, and `evaluators` are used during the model validation and checkpoint saving. 
               The `val_step` method in `BaseDetector` already provides a basic implementation of the validation and checkpoint-saving process, 
               which can be directly invoked using the following code:
                    best_metrics = {}
                    best_metrics = self.val_step(val_dataloader, evaluators, checkpoint_path, best_metrics)
               `best_metrics` Contains the best validation metrics recorded so far.
               Once the `embedding` method is implemented, you can directly obtain multi-resolution fusion features using the `get_multi_resolution_fusion_embeddings` method provided in `BaseDetector`.
               Use multi-resolution fused features instead of the original pre-trained features for anomaly detection:
               for data in train_dataloader:
                   features = self.get_multi_resolution_fusion_embeddings(data)
                   # The shape of `features` is the same as that of the original features. ([B,C1,H1,W1], [B,C2,H2,W2], ..., [B,Cn,Hn,Wn])
           return:
                Returns a boolean: `True` if the checkpoint has already been saved; `False` if not, which will trigger the `trainer` to perform the save.  

        """
        raise NotImplementedError

Define the model's inference process

    def inference_step(self,
                       test_dataloader: DataLoader,
                       task_name: str):
        r"""
           This method defines the model's inference process.
           Args
               test_dataloader (torch.utils.data.DataLoader): Dataloader used for inference; the format of the returned data is defined by the `create_dataset` method.
               task_name (str): Task Name
    
           return:
               return a list of numpy.ndarray. Each `ndarray` corresponds to the pixel-level detection result of a single patch.
               Shape: ([Hp,Wp],..., [Hp,Wp]).
        """
        raise NotImplementedError

Saving and loading of the checkpoint.

    @abstractmethod
    def save_checkpoint(self,
                        checkpoint_path: str
                        ):
        r"""
            save checkpoint
            Args
                checkpoint_path (str): Path of checkpoint
        """
        raise NotImplementedError


    @abstractmethod
    def load_checkpoint(self, checkpoint_path: str):
        r"""
            load checkpoint
            Args
                checkpoint_path (str): Path of checkpoint
        """
        raise NotImplementedError

Note: All steps described above operate on low-resolution image patches.

The to_device function is used to define the detector's behavior when switching computing devices.

    @abstractmethod
    def to_device(self, device: torch.device):
       r"""
            for example: self.model = self.model.to(device)
        """
        raise NotImplementedError

You can redefine the computation of image-level anomaly scores by overriding the get_image_score method:

    @staticmethod
    def get_image_score(segmentations):
        r"""
            This method defines the process for obtaining image-level anomaly detection scores.
            Args
                 segmentations (torch.Tensor): Pixel-level detection results of the high-resolution image, with all patch-level results already concatenate together.
                 Shape: [B,H,W], Device: cpu
            return:
                return image-level anomaly detection scores. Shape: [B]
        """
        pass

If you would like HiAD to support additional detectors, feel free to create a new issue. We’ll get back to you as soon as possible!