AnomalyNCD: Towards Novel Anomaly Class Discovery in Industrial Scenarios

September 1, 2025 · View on GitHub

This is an official PyTorch implementation for "AnomalyNCD: Towards Novel Anomaly Class Discovery in Industrial Scenarios"

Authors: Ziming Huang1* | Xurui Li1* | Haotian Liu1* | Feng Xue3 | Yuzhe Wang1 | Yu Zhou1,2

Institutions: 1Huazhong University of Science and Technology | 2Wuhan JingCe Electronic Group Co.,LTD | 3University of Trento

🧐 Arxiv | OpenReview

📣Updates:

09/01/2025

We have updated the code for standalone inference. Run the scripts/anomalyncd_test.sh script to load the trained checkpoint and perform inference.

04/01/2025

The complete code of our method AnomalyNCD in paper is released.

Catalogue

Abstract: [Back to Catalogue]

Recently, multi-class anomaly classification has garnered increasing attention. Previous methods directly cluster anomalies but often struggle due to the lack of anomaly-prior knowledge. Acquiring this knowledge faces two issues: the non-prominent and weak-semantics anomalies. In this paper, we propose AnomalyNCD, a multi-class anomaly classification network compatible with different anomaly detection methods.

To address the non-prominence of anomalies, we design main element binarization (MEBin) to obtain anomaly-centered images, ensuring anomalies are learned while avoiding the impact of incorrect detections. Next, to learn anomalies with weak semantics, we design mask-guided representation learning, which focuses on isolated anomalies guided by masks and reduces confusion from erroneous inputs through re-corrected pseudo labels. Finally, to enable flexible classification at both region and image levels, we develop a region merging strategy that determines the overall image category based on the classified anomaly regions.

Our method outperforms the state-of-the-art works on the MVTec AD and MTD datasets. Compared with the current methods, AnomalyNCD combined with zero-shot anomaly detection method achieves a 10.8% F1F_1 gain, 8.8% NMI gain, and 9.5% ARI gain on MVTec AD, and 12.8% F1F_1 gain, 5.7% NMI gain, and 10.8% ARI gain on MTD.

pipline

Setup: [Back to Catalogue]

Environment:

  • Python 3.8
  • CUDA 11.8
  • PyTorch 1.9.0

Clone the repository:

git clone https://github.com/HUST-SLOW/AnomalyNCD.git

Create virtual environment:

conda create --name AnomalyNCD python=3.8
conda activate AnomalyNCD

Install the required packages:

conda install pytorch==1.9.0 torchvision==0.10.0 pytorch-cuda=11.8 -c pytorch -c nvidia
pip install -r requirements.txt

Datasets: [Back to Catalogue]

We conduct the multi-class anomaly classification on industrial datasets MVTec AD (download) and Magnetic Tile Defect (download).

For the labeled abnormal image set Dl\mathcal{D^\mathbf{l}}, we use Aero-engine Blade Anomaly Detection Dataset (AeBAD) (download).

All the datasets are placed under the ./data folder.

MVTec AD

data
|-- mvtec_anomaly_detection
|-----|-- bottle
|-----|-----|-- train
|-----|-----|-----|-- good
|-----|-----|-----|-----|-- 000.png
|-----|-----|-----|-----|-- 001.png
|-----|-----|-----|-----|-- ...
|-----|-----|-- test
|-----|-----|-----|-- broken_large
|-----|-----|-----|-----|-- 000.png
|-----|-----|-----|-----|-- 001.png
|-----|-----|-----|-----|-- ...
|-----|-----|-----|-- broken_small
|-----|-----|-----|-- ...
|-----|-----|-- ground_truth
|-----|-----|-----|-- broken_large
|-----|-----|-----|-----|-- 000_mask.png
|-----|-----|-----|-----|-- 001_mask.png
|-----|-----|-----|-----|-- ...
|-----|-----|-----|-- broken_small
|-----|-----|-----|-- ...
|-----|-- cable
|-----|-- ...

Magnetic Tile Defect (MTD)

Following PatchCore and DifferNet, we split the original MTD dataset into a training set and a test set. For details, please refer to link to obtain the file mtd_train_filenames.txt.To process the MTD dataset, you need to set train_txt in the script python datasets/mtd_preprocess.py and then run this script.

python datasets/mtd_preprocess.py

This script will transform the MTD dataset into the following structure.

data
|-- mtd_anomaly_detection
|-----|-- train
|-----|-----|-- MT_Free
|-----|-----|-----|-- exp1_num_10181.jpg
|-----|-----|-----|-- exp1_num_10334.jpg
|-----|-----|-----|-- ...
|-----|-- test
|-----|-----|-- MT_Blowhole 
|-----|-----|-----|-- exp1_num_108719.jpg
|-----|-----|-----|-- exp1_num_108889.jpg
|-----|-----|-----|-- ...
|-----|-----|-- MT_Break
|-----|-----|-----|-- ...
|-----|-- ground_truth
|-----|-----|-- MT_Blowhole
|-----|-----|-----|-- exp1_num_108719.png
|-----|-----|-----|-- exp1_num_108889.png
|-----|-----|-----|-- ...
|-----|-----|-- MT_Break
|-----|-----|-----|-- ...

Aero-engine Blade Anomaly Detection Dataset (AeBAD)

We use the sub-dataset AeBAD-S in AeBAD datasets as the labeled abnormal image set Dl\mathcal{D^\mathbf{l}}. It contains four anomaly classes: breakdown, ablation, fracture, and groove.

The AeBAD dataset needs to be processed by running the script python datasets/aebad_preprocess.py to preform cropping and obtain the sub-images.

python datasets/aebad_preprocess.py

We also provide the processed dataset AeBAD_crop (drive).

Get Anomaly Maps: [Back to Catalogue]

Our AnomalyNCD could receive anomaly maps output from any anomaly detection (AD) method as input. To accurately implement the results in our paper, we provide download links to anomaly maps output by some typical anomaly detection methods below.

AD methodMuScPatchCoreEfficientADRD++PNICPR
MVTec AD(drive)(drive)(drive)(drive)(drive)(drive)
MTD(drive)(drive)(drive)(drive)(drive)-

In order to be compatible with more custom anomaly detection methods, we provide the following code to save anomaly maps. Note that all datasets need to be pre-processed to the structure of the MVTec AD dataset.

def generate_anomaly_maps(image_paths, anomaly_maps, output_dir):
    """
    image_paths (list): List of image paths
    anomaly_maps (torch.Tensor): Anomaly maps from AD method
    output_dir (str): The directory that saves the anomaly maps
    """
    def normalization01(imgs):
        return (imgs - imgs.min()) / (imgs.max() - imgs.min())

    anomaly_maps_norm = normalization01(anomaly_maps)

    for i, path in enumerate(image_paths):
        anomaly_type = path.split('/')[-2]
        img_name = path.split('/')[-1]
        category = path.split('/')[-4]

        save_path = os.path.join(output_dir, category, anomaly_type)
        os.makedirs(save_path, exist_ok=True)
        save_path = os.path.join(save_path, img_name)
        anomaly_map = anomaly_maps_norm[i]*255
        cv2.imwrite(save_path, anomaly_map.astype(np.uint8))

We use MuSc as an example to describe how the above code can be used to save anomaly maps.

# Code from [MuSc](https://github.com/xrli-U/MuSc/blob/main/models/musc.py)

print('computing metrics...')
pr_sp = np.array(scores_cls)
gt_sp = np.array(gt_list)
gt_px = torch.cat(img_masks, dim=0).numpy().astype(np.int32)
pr_px = np.array(anomaly_maps)
image_metric, pixel_metric = compute_metrics(gt_sp, pr_sp, gt_px, pr_px)
auroc_sp, f1_sp, ap_sp = image_metric
auroc_px, f1_px, ap_px, aupro = pixel_metric

"""
Basically the code will provide the three parameters we need, 
you need to find image_paths and category in code.
"""

# our code to generate anomaly maps
save_dir = 'mvtec_ad_anomaly_map'
generate_anomaly_maps(image_path_list, pr_px, save_dir)

The anomaly maps on the MVTec AD dataset are stored according to the following structure.

data
|-- mvtec_ad_anomaly_map
|-----|-- bottle
|-----|-----|-- broken_large
|-----|-----|-----|-- 000.png
|-----|-----|-----|-- 001.png
|-----|-----|-----|-- ...
|-----|-----|-- broken_small
|-----|-----|-- ...
|-----|-- cable
|-----|-- ...

Run AnomalyNCD: [Back to Catalogue]

Run the training script:

bash scripts/anomalyncd.sh

The key arguments of the script are as follows:

  • gpu: The gpu id for training.

  • runner_name: The name of experiment.

  • dataset: The name of the novel(unlabeled) image dataset.

  • category: The product name of novel(unlabeled) images Du\mathcal{D^\mathbf{u}}.

  • dataset_path: The directory path of novel(unlabeled) images.

  • anomaly_map_path: The directory path of novel(unlabeled) anomaly maps.

  • binary_data_path: The output path of binarization results of novel(unlabeled) images.

  • crop_data_path: The output path of novel(unlabeled) sub-images and corresponding masks.

  • base_data_path: The directory path of base(labeled) abnormal images Dl\mathcal{D^\mathbf{l}}.

All checkpoints and metrics will be stored in the ./outputs/<RUN_EXP>.

Results: [Back to Catalogue]

We report the quantitative results on the MVTec AD and MTD datasets. All the results are implemented by the default settings in our paper.

Unsupervised multi-class classification

DatasetsMetricIICGATClusterSCANUNOGCDSimGCDAMENDAC (Unsup.)MuSc +AnomalyNCD
MVTec ADNMI0.0930.1360.2100.1460.4170.4520.4310.5250.613
ARI0.0200.0530.1030.0520.3020.3460.3330.4310.526
F10.2850.2640.3350.3420.5530.5690.5420.6040.712
AUPRO--------0.938
MTDNMI0.0640.0280.0410.0340.2110.1050.1380.1790.268
ARI0.0200.0090.0290.0110.1150.0480.0670.1200.228
F10.2520.2430.2820.2210.3810.2930.3240.3460.509
AUPRO--------0.769

Semi-supervised multi-class classification

DatasetsMetricAC (Semi-sup.)UniFormalyPatchCore +AnomalyNCDRD++ +AnomalyNCDEfficientAD +AnomalyNCDPNI +AnomalyNCDCPR +AnomalyNCD
MVTec ADNMI0.6080.5470.6700.6310.5160.6750.736
ARI0.4890.4330.6010.5420.3940.6090.674
F10.6520.6450.7690.7210.6410.7690.805
AUPRO-0.9530.9380.9500.9170.9420.964
MTDNMI0.3900.4210.3800.3680.2200.181-
ARI0.3140.3220.3900.3610.1880.219-
F10.4900.6090.6170.6000.4670.465-
AUPRO-0.8370.7290.7410.7310.516-

Results for each category on the MVTec dataset

MuSc+AnomalyNCDCPR+AnomalyNCD
categoryNMIARIF1NMIARIF1
bottle0.6130.5830.8190.9040.7750.757
cable0.5970.4920.6260.5610.7110.476
capsule0.4450.3350.5910.6740.5490.438
carpet0.8520.8370.9060.7950.7400.638
grid0.6220.5780.7310.7310.7660.689
hazelnut0.6620.5820.7180.8270.7230.727
leather0.8630.8380.9110.8950.8650.827
metal_nut0.6430.4670.5650.9300.8700.848
pill0.4390.2910.5130.7470.6940.592
screw0.3990.2650.4880.8000.7420.698
tile0.8850.8500.9400.9920.9800.976
toothbrush0.3680.2590.7620.8570.5080.499
transistor0.5310.4210.6200.7000.5770.523
wood0.7430.6720.8680.9120.8160.756
zipper0.5260.4170.6150.7560.7260.667
mean0.6130.5260.7120.8050.7360.674

Citation: [Back to Catalogue]

If you find this repo useful for your research, please consider citing our paper:

@inproceedings{huang2025anomalyncd,
  title={AnomalyNCD: Towards Novel Anomaly Class Discovery in Industrial Scenarios},
  author={Huang, Ziming and Li, Xurui and Liu, Haotian and Xue, Feng and Wang, Yuzhe and Zhou, Yu},
  booktitle={Proceedings of the IEEE/CVF Conference on Computer Vision and Pattern Recognition},
  year={2025}
}

Acknowledgements: [Back to Catalogue]

The codebase is built on repos: https://github.com/CVMI-Lab/SimGCD and https://github.com/sgvaze/generalized-category-discovery.

License: [Back to Catalogue]

AnomalyNCD is released under the MIT Licence, and is fully open for academic research and also allow free commercial usage. To apply for a commercial license, please contact yuzhou@hust.edu.cn.