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
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
.
Select lower threshold for higher percentage.
Create random normal (Gaussian) distribution image with mean=0 and sigma=1:
sigma = 1
gauss = np.random.normal(0, sigma, im.shape) # Create random normal (Gaussian) distribution image with mean=0 and sigma=1.
Convert to values to -1
, 0
, 1
- assume pixels value above 2 sigma are "1", below -2 sigma are -1
and other are "0" (2 sigma is an example, we may select other value):
binary_gauss = (gauss > 2*sigma).astype(np.int8)
binary_gauss[gauss < -2*sigma] = -1
After adding binary_gauss
to im
, clip the result to range [0, 1]
:
noisey_im = (im + binary_gauss).clip(0, 1)
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):
binary_gauss
(noise image after threshold - values are -1
, 0
, 1
):