我正在研究著名的冈萨雷斯“数字图像处理”的图像处理,并谈论图像恢复,很多示例都是使用计算机生成的噪声(高斯、盐和胡椒等)完成的。在 MATLAB 中有一些内置函数可以做到这一点。那么 OpenCV 呢?
10 回答
据我所知,没有像 Matlab 这样方便的内置函数。但只需几行代码,您就可以自己创建这些图像。
例如加性高斯噪声:
Mat gaussian_noise = img.clone();
randn(gaussian_noise,128,30);
椒盐噪声:
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);
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()
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()
向图像添加高斯、椒盐散斑和泊松噪声的简单函数
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
可以使用 NumPy 矩阵运算以非常简单的方式添加“Salt & Pepper”噪声。
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
# 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
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)
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)
#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
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.
- 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
- 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
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
