Per channel Normalization

Hi,
For the medical images, I have to normalize each image’s channel one by one. So for that, I created this code :

def normalizeImage(paths):
    img = cv2.imread(paths)
    normImg = np.zeros(img.shape)
    k = 0

    for i in range(img.shape[2]):
        if(img[:, :, i].std() != 0):
            normImg[:, :, i] = (img[:, :, i] - img[:, :, i].mean()) / (img[:, :, i].std())
            k +=1
            # print(k)
            if k ==3:
                array_list.append(normImg)
    return normImg

then converting them to tensors:

tensor_x = torch.Tensor(np.array(array_list))
tensor_y = ....
my_dataset = TensorDataset(tensor_x,tensor_y) 
my_dataloader = DataLoader(my_dataset,batch_size=16)

However, I couldn’t continue after that. I have three directories:
train: wound/ no_wound
test
val

I am open to applying new methods. How can I do that for the whole dataset?

        if(img[:, :, i].std() != 0):
            normImg[:, :, i] = (img[:, :, i] - img[:, :, i].mean()) / (img[:, :, i].std())

For a single image you can use this which is more efficient.

def normalizeImage(path: str) -> torch.Tensor:
        img = cv2.imread(paths)
        img_tensor = torch.tensor(img, dtype= torch.float)
        return (img_tensor - img_tensor.mean([0, 1])) / img_tensor.std([0, 1])

To apply it to the whole dataset, just add it to the __getitem__ method of your dataset. :slight_smile:

1 Like

But the formula that you shared is not the same thing. It doesn’t update each channel, does it? :frowning:

It does, here you can see that the returned tensor has the mean for each channel subtracted and then it’s divided channel-wise by the std (broadcasting takes care of the dimensionality). You can store the returned tensor in a variable and check that the mean and std for each channel are 0 (approximately, it will be really small) and 1 respectively.

Maybe you can add an epsilon to the std just to avoid divisions by zero.

1 Like
    def __getitem__(self, idx):
        img_path = os.path.join(self.img_dir, self.img_labels.iloc[idx, 0])
        image = read_image(img_path) // or cv2.imread()
        label = self.img_labels.iloc[idx, 1]
       
        img_tensor = torch.tensor(image , dtype= torch.float)
        image=  (img_tensor - img_tensor.mean([0, 1])) / img_tensor.std([0, 1]
        return image, label

Is it true implementation?

I cannot guarantee about the first 3 lines of code, but yes. It looks correct :slight_smile:

Just make sure that the layout is the same, openCV returns (H,W,C). If you use read_image, make sure that the layout is also like that, otherwise you won’t normalise with respect to the channels but to another dimension.

1 Like