From 348fd22d670edc9ae9b639b2e856d4c0a40d1f57 Mon Sep 17 00:00:00 2001 From: Shreeyash Date: Thu, 2 Sep 2021 14:43:33 +0530 Subject: [PATCH 1/2] Added SRE SAM Perturbation Metrics Added SRE SAM Visual Perturbation Metrics to resolve 2/5 tasks from Issue#61 --- code_soup/common/vision/perturbations.py | 84 ++++++++++++++++++++---- 1 file changed, 72 insertions(+), 12 deletions(-) diff --git a/code_soup/common/vision/perturbations.py b/code_soup/common/vision/perturbations.py index c68bc3c..290838d 100644 --- a/code_soup/common/vision/perturbations.py +++ b/code_soup/common/vision/perturbations.py @@ -9,7 +9,10 @@ class VisualPerturbation(Perturbation): """ - Docstring for VisualPerturbations + An abstract method for various Visual Perturbation Metrics + Methods + __init__(self, original : Union[np.ndarray, torch.Tensor], perturbed: Union[np.ndarray, torch.Tensor]) + - init method """ def __init__( @@ -21,19 +24,76 @@ def __init__( Docstring #Automatically cast to Tensor using the torch.from_numpy() in the __init__ using if """ - raise NotImplementedError + self.original = self.__totensor(original) + self.perturbed = self.__totensor(perturbed) + self.original = self.original.type(dtype = torch.float64) + self.perturbed = self.perturbed.type(dtype = torch.float64) - def calculate_LPNorm(self, p: Union[int, str]): - raise NotImplementedError - def calculate_PSNR(self): - raise NotImplementedError + def __flatten(self, atensor: torch.Tensor) -> torch.Tensor: + """ + A method which will flatten out the inputted tensor + + """ + return torch.flatten(atensor) + + + def __totensor(self, anarray: Union[np.ndarray, torch.Tensor]) -> torch.Tensor: + """ + A method which will convert anything inputted into a Torch tensor. + If it is already a torch tensor it will return itself + + """ + if type(anarray) == torch.Tensor: + return anarray + else: + return torch.from_numpy(anarray) - def calculate_RMSE(self): - raise NotImplementedError - def calculate_SAM(self): - raise NotImplementedError + def sam(self, convert_to_degree=True): + """ + Spectral Angle Mapper defines the spectral similarity by the angle between the image pixel spectrum + Expects the Images to be in (C,H,W) format + + Parameters + ---------- + convert_to_degree + - will return the spectral angle in degrees(default true) + + """ + original_img = self.original + new_img = self.perturbed + + assert original_img.size() == new_img.size(), 'Size of the inputs not same please give correct values to SAM metric' + + # assuming the image is in (C,H,W) method + numerator = torch.sum(torch.mul(new_img, original_img),axis=0) + denominator = torch.linalg.norm(original_img, axis=0) * torch.linalg.norm(new_img, axis=0) + val = torch.clip(numerator / denominator, -1, 1) + sam_angles = torch.arccos(val) + if convert_to_degree: + sam_angles = sam_angles * 180.0 / np.pi + + # The original paper states that SAM values are expressed as radians, while e.g. Lanares + # et al. (2018) use degrees. We therefore made this configurable, with degree the default + return torch.mean(torch.nan_to_num(sam_angles)).item() + + + def sre(self): + """ + signal to reconstruction error ratio + Expects the Images to be in (C,H,W) format + """ + original_img = self.original + new_img = self.perturbed + + assert original_img.size()==new_img.size(), 'Size of the inputs not same please give correct values to SRE' - def calculate_SRE(self): - raise NotImplementedError + sre_final = [] + for i in range(original_img.shape[0]): + numerator = torch.square(torch.mean(original_img[:, :, ][i])) + denominator = (torch.linalg.norm(original_img[:, :, ][i] - new_img[:, :, ][i])) /\ + (original_img.shape[2] * original_img.shape[1]) + sre_final.append(numerator/denominator) + sre_final = torch.as_tensor(sre_final) + return (10 * torch.log10(torch.mean(sre_final))).item() \ No newline at end of file From 3e4771977a30fdea5a11ea30bed005c5d37e39ba Mon Sep 17 00:00:00 2001 From: Shreeyash Date: Thu, 2 Sep 2021 15:12:51 +0530 Subject: [PATCH 2/2] Fixed Styling for SRE, SAM Fixed styling for perturbation Metrics as per the guidelines --- code_soup/common/vision/perturbations.py | 65 +++++++++++++++--------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/code_soup/common/vision/perturbations.py b/code_soup/common/vision/perturbations.py index 290838d..7297818 100644 --- a/code_soup/common/vision/perturbations.py +++ b/code_soup/common/vision/perturbations.py @@ -26,9 +26,8 @@ def __init__( """ self.original = self.__totensor(original) self.perturbed = self.__totensor(perturbed) - self.original = self.original.type(dtype = torch.float64) - self.perturbed = self.perturbed.type(dtype = torch.float64) - + self.original = self.original.type(dtype=torch.float64) + self.perturbed = self.perturbed.type(dtype=torch.float64) def __flatten(self, atensor: torch.Tensor) -> torch.Tensor: """ @@ -37,38 +36,40 @@ def __flatten(self, atensor: torch.Tensor) -> torch.Tensor: """ return torch.flatten(atensor) - def __totensor(self, anarray: Union[np.ndarray, torch.Tensor]) -> torch.Tensor: """ - A method which will convert anything inputted into a Torch tensor. + A method which will convert anything inputted into a Torch tensor. If it is already a torch tensor it will return itself - + """ if type(anarray) == torch.Tensor: return anarray else: return torch.from_numpy(anarray) - def sam(self, convert_to_degree=True): """ Spectral Angle Mapper defines the spectral similarity by the angle between the image pixel spectrum Expects the Images to be in (C,H,W) format - + Parameters ---------- convert_to_degree - will return the spectral angle in degrees(default true) - + """ original_img = self.original new_img = self.perturbed - - assert original_img.size() == new_img.size(), 'Size of the inputs not same please give correct values to SAM metric' - + + assert ( + original_img.size() == new_img.size() + ), "Size of the inputs not same please give correct values to SAM metric" + # assuming the image is in (C,H,W) method - numerator = torch.sum(torch.mul(new_img, original_img),axis=0) - denominator = torch.linalg.norm(original_img, axis=0) * torch.linalg.norm(new_img, axis=0) + numerator = torch.sum(torch.mul(new_img, original_img), axis=0) + denominator = torch.linalg.norm(original_img, axis=0) * torch.linalg.norm( + new_img, axis=0 + ) val = torch.clip(numerator / denominator, -1, 1) sam_angles = torch.arccos(val) if convert_to_degree: @@ -78,7 +79,6 @@ def sam(self, convert_to_degree=True): # et al. (2018) use degrees. We therefore made this configurable, with degree the default return torch.mean(torch.nan_to_num(sam_angles)).item() - def sre(self): """ signal to reconstruction error ratio @@ -86,14 +86,33 @@ def sre(self): """ original_img = self.original new_img = self.perturbed - - assert original_img.size()==new_img.size(), 'Size of the inputs not same please give correct values to SRE' + + assert ( + original_img.size() == new_img.size() + ), "Size of the inputs not same please give correct values to SRE" sre_final = [] for i in range(original_img.shape[0]): - numerator = torch.square(torch.mean(original_img[:, :, ][i])) - denominator = (torch.linalg.norm(original_img[:, :, ][i] - new_img[:, :, ][i])) /\ - (original_img.shape[2] * original_img.shape[1]) - sre_final.append(numerator/denominator) - sre_final = torch.as_tensor(sre_final) - return (10 * torch.log10(torch.mean(sre_final))).item() \ No newline at end of file + numerator = torch.square( + torch.mean( + original_img[ + :, + :, + ][i] + ) + ) + denominator = ( + torch.linalg.norm( + original_img[ + :, + :, + ][i] + - new_img[ + :, + :, + ][i] + ) + ) / (original_img.shape[2] * original_img.shape[1]) + sre_final.append(numerator / denominator) + sre_final = torch.as_tensor(sre_final) + return (10 * torch.log10(torch.mean(sre_final))).item()