"""Anomaly Map Generator for the PaDiM model implementation."""# Copyright (C) 2020 Intel Corporation## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing,# software distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions# and limitations under the License.fromtypingimportList,Tuple,Unionimporttorchimporttorch.nn.functionalasFfromkornia.filtersimportgaussian_blur2dfromomegaconfimportListConfigfromtorchimportTensor
[docs]classAnomalyMapGenerator:"""Generate Anomaly Heatmap. Args: image_size (Union[ListConfig, Tuple]): Size of the input image. The anomaly map is upsampled to this dimension. sigma (int, optional): Standard deviation for Gaussian Kernel. Defaults to 4. """def__init__(self,image_size:Union[ListConfig,Tuple],sigma:int=4):self.image_size=image_sizeifisinstance(image_size,tuple)elsetuple(image_size)self.sigma=sigma@staticmethod
[docs]defcompute_distance(embedding:Tensor,stats:List[Tensor])->Tensor:"""Compute anomaly score to the patch in position(i,j) of a test image. Ref: Equation (2), Section III-C of the paper. Args: embedding (Tensor): Embedding Vector stats (List[Tensor]): Mean and Covariance Matrix of the multivariate Gaussian distribution Returns: Anomaly score of a test image via mahalanobis distance. """batch,channel,height,width=embedding.shapeembedding=embedding.reshape(batch,channel,height*width)# calculate mahalanobis distancesmean,inv_covariance=statsdelta=(embedding-mean).permute(2,0,1)distances=(torch.matmul(delta,inv_covariance)*delta).sum(2).permute(1,0)distances=distances.reshape(batch,height,width)distances=distances.clamp(0).sqrt()returndistances
[docs]defup_sample(self,distance:Tensor)->Tensor:"""Up sample anomaly score to match the input image size. Args: distance (Tensor): Anomaly score computed via the mahalanobis distance. Returns: Resized distance matrix matching the input image size """score_map=F.interpolate(distance.unsqueeze(1),size=self.image_size,mode="bilinear",align_corners=False,)returnscore_map
[docs]defsmooth_anomaly_map(self,anomaly_map:Tensor)->Tensor:"""Apply gaussian smoothing to the anomaly map. Args: anomaly_map (Tensor): Anomaly score for the test image(s). Returns: Filtered anomaly scores """kernel_size=2*int(4.0*self.sigma+0.5)+1sigma=torch.as_tensor(self.sigma).to(anomaly_map.device)anomaly_map=gaussian_blur2d(anomaly_map,(kernel_size,kernel_size),sigma=(sigma,sigma))returnanomaly_map
[docs]defcompute_anomaly_map(self,embedding:Tensor,mean:Tensor,inv_covariance:Tensor)->Tensor:"""Compute anomaly score. Scores are calculated based on embedding vector, mean and inv_covariance of the multivariate gaussian distribution. Args: embedding (Tensor): Embedding vector extracted from the test set. mean (Tensor): Mean of the multivariate gaussian distribution inv_covariance (Tensor): Inverse Covariance matrix of the multivariate gaussian distribution. Returns: Output anomaly score. """score_map=self.compute_distance(embedding=embedding,stats=[mean.to(embedding.device),inv_covariance.to(embedding.device)],)up_sampled_score_map=self.up_sample(score_map)smoothed_anomaly_map=self.smooth_anomaly_map(up_sampled_score_map)returnsmoothed_anomaly_map
[docs]def__call__(self,**kwds):"""Returns anomaly_map. Expects `embedding`, `mean` and `covariance` keywords to be passed explicitly. Example: >>> anomaly_map_generator = AnomalyMapGenerator(image_size=input_size) >>> output = anomaly_map_generator(embedding=embedding, mean=mean, covariance=covariance) Raises: ValueError: `embedding`. `mean` or `covariance` keys are not found Returns: torch.Tensor: anomaly map """ifnot("embedding"inkwdsand"mean"inkwdsand"inv_covariance"inkwds):raiseValueError(f"Expected keys `embedding`, `mean` and `covariance`. Found {kwds.keys()}")embedding:Tensor=kwds["embedding"]mean:Tensor=kwds["mean"]inv_covariance:Tensor=kwds["inv_covariance"]returnself.compute_anomaly_map(embedding,mean,inv_covariance)