imageimage-processingnoiseimage-preprocessingnoise-generator

Add Gaussian noise to a binary image knowing noise variance or SNR in python


I am using python open cv for image restoration and in the first step, I have to add gaussian noise to my "binary" image. My image pixel values are integers that can take values 0 or 1. How can I add gaussian noise to my image knowing SNR or noise variance?

I came up with this piece of code which adds 15 percent noise to my image but I don't know if this noise is normal Gaussian and how I can find its variance and SNR.

def add_noise(im):
  im_noisy = im.copy()
  for i in range(len(im_noisy)):
    for j in range(len(im_noisy[0])):
      r = np.random.rand()
      if r < 0.15:
        im_noisy[i][j] = -im_noisy[i][j]+1    #(converts 0 to 1 and vice versa)
  return im_noisy

Solution

  • Since the image is binary, the solution is not well defined - we have to select a threshold that above the threshold the noise is 1.
    The Gaussian noise supposes to be symmetric, so pixels below the minus threshold are going to be -1.

    We may define the threshold by number of sigma's (for example: above 2 sigma, the noise is 1, and below -2 sigma the noise is -1).
    When selecting threshold of 2 sigma, statistically we expect that about 2.5% of total pixels are going to be 1 and 2.5% are going to be -1.

    enter image description here
    Select lower threshold for higher percentage.



    Code sample (first part reads a sample image, and convert to binary):

    import numpy as np
    import cv2  # Used only for testing
    
    im = cv2.imread('chelsea.png', cv2.IMREAD_GRAYSCALE)  # Read input image (for testing).
    im = cv2.threshold(im, 0, 1, cv2.THRESH_OTSU)[1]  # Convert image to binary (for testing).
    im = im.astype(np.int8)  # Convert to type int8 (8 bits singed)
    
    sigma = 1
    gauss = np.random.normal(0, sigma, im.shape)  # Create random normal (Gaussian) distribution image with mean=0 and sigma=1.
    binary_gauss = (gauss > 2*sigma).astype(np.int8)  # Convert to binary - assume pixels with value above 2 sigmas are "1".
    binary_gauss[gauss < -2*sigma] = -1 # Set all pixels below 2 sigma to "-1".
    
    noisey_im = (im + binary_gauss).clip(0, 1)  # Add noise image, and clip the result ot [0, 1].
    noisey_im = noisey_im.astype(np.uint8)  # Convert to type uint8
    
    # Show images (for testing).
    cv2.imshow('im', im*255)
    cv2.imshow('binary_gauss', (binary_gauss+1).astype(np.uint8)*127)
    cv2.imshow('noisey_im', noisey_im*255)
    cv2.waitKey()
    cv2.destroyAllWindows()
    

    im (input image after converting to binary):
    enter image description here

    binary_gauss (noise image after threshold - values are -1, 0, 1):
    enter image description here

    noisey_im (im + binary_gauss after threshold):
    enter image description here