34

I'm studying Image Processing on the famous Gonzales "Digital Image Processing" and talking about image restoration a lot of examples are done with computer-generated noise (gaussian, salt and pepper, etc). In MATLAB there are some built-in functions to do it. What about OpenCV?

nkint
  • 11,513
  • 31
  • 103
  • 174

10 Answers10

35

As far as I know there are no convenient built in functions like in Matlab. But with only a few lines of code you can create those images yourself.

For example additive gaussian noise:

Mat gaussian_noise = img.clone();
randn(gaussian_noise,128,30);

Salt and pepper noise:

Mat saltpepper_noise = Mat::zeros(img.rows, img.cols,CV_8U);
randu(saltpepper_noise,0,255);

Mat black = saltpepper_noise < 30;
Mat white = saltpepper_noise > 225;

Mat saltpepper_img = img.clone();
saltpepper_img.setTo(255,white);
saltpepper_img.setTo(0,black);
sietschie
  • 7,425
  • 3
  • 33
  • 54
22

There is function random_noise() from the scikit-image package. It has several builtin noise patterns, such as gaussian, s&p (for salt and pepper noise), possion and speckle.

Below I show an example of how to use this method

from PIL import Image
import numpy as np
from skimage.util import random_noise

im = Image.open("test.jpg")
# convert PIL Image to ndarray
im_arr = np.asarray(im)

# random_noise() method will convert image in [0, 255] to [0, 1.0],
# inherently it use np.random.normal() to create normal distribution
# and adds the generated noised back to image
noise_img = random_noise(im_arr, mode='gaussian', var=0.05**2)
noise_img = (255*noise_img).astype(np.uint8)

img = Image.fromarray(noise_img)
img.show()

enter image description here

There is also a package called imgaug which are dedicated to augment images in various ways. It provides gaussian, poissan and salt&pepper noise augmenter. Here is how you can use it to add noise to image:

from PIL import Image
import numpy as np
from imgaug import augmenters as iaa


def main():
    im = Image.open("bg_img.jpg")
    im_arr = np.asarray(im)

    # gaussian noise
    # aug = iaa.AdditiveGaussianNoise(loc=0, scale=0.1*255)

    # poisson noise
    # aug = iaa.AdditivePoissonNoise(lam=10.0, per_channel=True)

    # salt and pepper noise
    aug = iaa.SaltAndPepper(p=0.05)

    im_arr = aug.augment_image(im_arr)

    im = Image.fromarray(im_arr).convert('RGB')
    im.show()


if __name__ == "__main__":
    main()
jdhao
  • 24,001
  • 18
  • 134
  • 273
13

Simple Function to add Gaussian, Salt-pepper speckle and poisson noise to an image

Parameters
----------
image : ndarray
    Input image data. Will be converted to float.
mode : str
    One of the following strings, selecting the type of noise to add:

    'gauss'     Gaussian-distributed additive noise.
    'poisson'   Poisson-distributed noise generated from the data.
    's&p'       Replaces random pixels with 0 or 1.
    'speckle'   Multiplicative noise using out = image + n*image,where
                n,is uniform noise with specified mean & variance.

import numpy as np
import os
import cv2

def noisy(noise_typ,image):

if noise_typ == "gauss":
        row,col,ch= image.shape
        mean = 0
        #var = 0.1
       #sigma = var**0.5
        gauss = np.random.normal(mean,1,(row,col,ch))
        gauss = gauss.reshape(row,col,ch)
        noisy = image + gauss
        return noisy
    elif noise_typ == "s&p":
        row,col,ch = image.shape
        s_vs_p = 0.5
        amount = 0.004
        out = image
        # Salt mode
        num_salt = np.ceil(amount * image.size * s_vs_p)
        coords = [np.random.randint(0, i - 1, int(num_salt))
                  for i in image.shape]
        out[coords] = 1

        # Pepper mode
        num_pepper = np.ceil(amount* image.size * (1. - s_vs_p))
        coords = [np.random.randint(0, i - 1, int(num_pepper))
                  for i in image.shape]
        out[coords] = 0
        return out
    elif noise_typ == "poisson":
        vals = len(np.unique(image))
        vals = 2 ** np.ceil(np.log2(vals))
        noisy = np.random.poisson(image * vals) / float(vals)
        return noisy
    elif noise_typ =="speckle":
        row,col,ch = image.shape
        gauss = np.random.randn(row,col,ch)
        gauss = gauss.reshape(row,col,ch)        
        noisy = image + image * gauss
        return noisy
Shubham Pachori
  • 1,588
  • 1
  • 11
  • 10
  • When I use the gaussian type my image becomes all white. I'm doing something like that: image = cv2.imread(fn) noise_gauss = noisy("gauss", image) cv2.imshow("gauss", noise_gauss). It seems the generated noise is not of the same type and the addition causes some weird transformation – Lxu Oct 30 '15 at 18:50
  • @Lxu can you show what this transformation looks like? – Jeru Luke Jan 03 '17 at 18:34
  • 2
    @Lxu I believe this is because the numpy array type turned to `float64`. Just write `noise_gauss = noise_gauss.astype('uint8')` before you pass it to the `cv2.imshow` – Hafiz Hilman Mohammad Sofian Nov 05 '17 at 05:20
  • @Lux Make sure you scaled the image pixel values to 0 to 1 by divide the image by 255 – Mohamed Fathallah Nov 11 '21 at 00:51
8

"Salt & Pepper" noise can be added in a quite simple fashion using NumPy matrix operations.

def add_salt_and_pepper(gb, prob):
    '''Adds "Salt & Pepper" noise to an image.
    gb: should be one-channel image with pixels in [0, 1] range
    prob: probability (threshold) that controls level of noise'''

    rnd = np.random.rand(gb.shape[0], gb.shape[1])
    noisy = gb.copy()
    noisy[rnd < prob] = 0
    noisy[rnd > 1 - prob] = 1
    return noisy
Artem S
  • 310
  • 3
  • 5
8
# Adding noise to the image    

import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

img = cv2.imread('./fruit.png',0)
im = np.zeros(img.shape, np.uint8) # do not use original image it overwrites the image
mean = 0
sigma = 10
cv2.randn(im,mean,sigma) # create the random distribution
Fruit_Noise = cv2.add(img, im) # add the noise to the original image
plt.imshow(Fruit_Noise, cmap='gray')

The values of mean and sigma can be altered to bring about a specific change in noise like gaussian or pepper-salt noise etc. You can use either randn or randu according to the need. Have a look at the documentation: https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html#cv2.randu

Gokul Krishna
  • 454
  • 1
  • 5
  • 13
  • 1
    Your code really does the job, but only in one channel. You need mean/sigma to be a tuple of values like: sigma = (10, 10, 10) – Ersain Feb 09 '20 at 07:41
3

I made some change of @Shubham Pachori 's code. When reading a image into numpy arrary, the default dtype is uint8, which can cause wrapping when adding noise onto the image.

import numpy as np
from PIL import Image

"""
image: read through PIL.Image.open('path')
sigma: variance of gaussian noise
factor: the bigger this value is, the more noisy is the poisson_noised image

##IMPORTANT: when reading a image into numpy arrary, the default dtype is uint8,
which can cause wrapping when adding noise onto the image. 
E.g,  example = np.array([128,240,255], dtype='uint8')
     example + 50 = np.array([178,44,49], dtype='uint8')
Transfer np.array to dtype='int16' can solve this problem.
"""


def gaussian_noise(image, sigma):
    img = np.array(image)
    noise = np.random.randn(img.shape[0], img.shape[1], img.shape[2])
    img = img.astype('int16')
    img_noise = img + noise * sigma
    img_noise = np.clip(img_noise, 0, 255)
    img_noise = img_noise.astype('uint8')
    return Image.fromarray(img_noise)


def poisson_noise(image, factor):
    factor = 1 / factor
    img = np.array(image)
    img = img.astype('int16')
    img_noise = np.random.poisson(img * factor) / float(factor)
    np.clip(img_noise, 0, 255, img_noise)
    img_noise = img_noise.astype('uint8')
    return Image.fromarray(img_noise)
zzhuolun
  • 31
  • 3
2

http://scikit-image.org/docs/dev/api/skimage.util.html#skimage.util.random_noise

skimage.util.random_noise(image, mode='gaussian', seed=None, clip=True, **kwargs)
Zoe
  • 27,060
  • 21
  • 118
  • 148
amsborse
  • 29
  • 1
  • Actually it is bad style to have most of the question in an URL (that might vanish), so please do not repeat that for the answer! Try to write the essential solution in the input box, and use code formatting to, well, format code. – U. Windl Mar 15 '19 at 00:49
-1
#Adding noise
[m,n]=img.shape
saltpepper_noise=zeros((m, n));
saltpepper_noise=rand(m,n); #creates a uniform random variable from 0 to 1 
for i in range(0,m):
    for j in range(0,n):
        if saltpepper_noise[i,j]<=0.5:
            saltpepper_noise[i,j]=0
        else:
            saltpepper_noise[i,j]=255
Pou
  • 9
-1

although there is no built-in functions like in matlab imnoise(image,noiseType,NoiseLevel) but we can easily add required amount random valued impulse noise or salt and pepper into an image manually.

  1. to add random valued impulse noise.

    import random as r
    def addRvinGray(image,n): # add random valued impulse noise in grayscale 
        '''parameters: 
             image: type=numpy array. input image in which you want add noise.
             n:  noise level (in percentage)'''
        k=0                  # counter variable 
        ih=image.shape[0]    
        iw=image.shape[1]
        noisypixels=(ih*iw*n)/100      # here we calculate the number of pixels to be altered.
    
        for i in range(ih*iw):
            if k<noisypixels:
                    image[r.randrange(0,ih)][r.randrange(0,iw)]=r.randrange(0,256) #access random pixel in the image gives random intensity (0-255)              
                k+=1
            else:
                break
        return image
    
  2. to add salt and pepper noise

    def addSaltGray(image,n): #add salt-&-pepper noise in grayscale image
    
        k=0
        salt=True
        ih=image.shape[0]
        iw=image.shape[1]
        noisypixels=(ih*iw*n)/100
    
        for i in range(ih*iw):
             if k<noisypixels:  #keep track of noise level
                 if salt==True:
                         image[r.randrange(0,ih)][r.randrange(0,iw)]=255
                         salt=False
                 else:
                         image[r.randrange(0,ih)][r.randrange(0,iw)]=0
                         salt=True
                 k+=1
             else:
                 break
        return image
    

Note: for color images: first split image in to three or four channels depending on the input image using opencv function:

(B, G, R) = cv2.split(image)
(B, G, R, A) = cv2.split(image)

after spliting perform the same operations on all channels. at the end merge all the channels:

merged = cv2.merge([B, G, R])
return merged
Cris Luengo
  • 55,762
  • 10
  • 62
  • 120
-1
def add_salt_noise(src, ratio: float = 0.05, noise: list = [0, 0, 0]):
    dst = src.copy()
    import random
    shuffle_dict = {}
    i = 0
    while i < (int(dst.shape[0]*dst.shape[1] * ratio)):
        x, y = random.randint(0, dst.shape[0] - 1), random.randint(0, dst.shape[1] - 1)
        if (x, y) in shuffle_dict:
            continue
        else:
            dst[x, y] = noise
            shuffle_dict[(x, y)] = 0
            i += 1
    return dst
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Feb 07 '22 at 18:08