Search code examples
pythonnumpyimage-processingdata-augmentationgaussianblur

How to create noisy images for data augmentation


I followed the most upvoted answer to a question regarding adding noise to an image. However it doesn't work for me. I just want to observe different noise effects on image while using Python How to add noise (Gaussian/salt and pepper etc) to image in Python with OpenCV

From what I know, images are something of uint8 type? I'm not certain if this type can take decimals.

The salt and pepper part don't work either

from numpy import shape, asarray
import numpy as np
import cv2
from PIL import Image
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,sigma,(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 = np.copy(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

pic = Image.open('obamaface1.jpg')
pic = pic.convert('RGB')
pixels = asarray(pic)
image = Image.fromarray(pixels)

target = noisy('speckle', pixels)
target = Image.fromarray(target)
print(target)

The last line is to just see what is output by the terminal. It's output is

  File "C:\Users\Jerome Ariola\AppData\Local\Programs\Python\Python36\lib\site-packages\PIL\Image.py", line 2647, in fromarray
    raise TypeError("Cannot handle this data type")
TypeError: Cannot handle this data type

Commenting target = Image.fromarray(target) gives me:

[[[ 10.03013352   7.53745105  11.03977542]
  [  9.38952149   7.81507808  11.53212491]
  [  9.76439692   7.88213107  11.47620008]
  ...
  [  6.76471119   5.09559321   5.9144036 ]
  [  7.34123162   4.92342273   6.31726796]
  [255.13791218 253.89755922 255.15403824]]

 [[  9.90775807   8.49642977  10.86023707]
  [  9.71078442   7.94264649  11.18820572]
  [  9.91127254   8.15716707  11.04770154]
  ...
  [  7.05173864   4.89094663   5.67662439]
  [  7.10166986   5.47480635   6.11892638]
  [255.03879603 254.07485578 254.88072098]]

 [[  9.81995678   7.55439474  11.08609859]
  [ 10.32135236   7.5301714   11.03612056]
  [ 10.17215819   8.09537629  11.30984933]
  ...
  [  7.13999574   5.12009845   7.8678079 ]
  [  7.31635614   5.1527127    8.23318054]
  [255.12283461 254.01880276 254.76894074]]

 ...

 [[ 19.72596723  22.29694693  20.95524912]
  [ 19.30898519  21.61944993  20.85653566]
  [ 20.45174165  20.55101246  21.1739277 ]
  ...
  [ 13.89796331  11.73865315  12.50874487]
  [ 14.13985843  11.97177032  12.80855176]
  [255.04963076 254.23626115 254.75904336]]

 [[ 19.17915912  21.2224852   18.37260714]
  [ 19.1068802   20.2797369   17.96846182]
  [ 20.37263348  20.23856465  18.02893703]
  ...
  [ 14.48307596  12.46348446  15.43437954]
  [ 14.11840104  12.35783324  14.64863437]
  [254.99657596 253.95241488 255.34200558]]

 [[ 20.03354477  22.02402748  18.45595882]
  [ 19.74202893  22.59472663  19.19910502]
  [ 21.96931817  22.22425014  19.59694792]
  ...
  [ 14.68256917  12.37915145  15.07832362]
  [ 14.25010143  12.45545202  14.65549651]
  [254.96105357 254.17655349 255.29863654]]]

I thought I'd also rewrite it, e.g. from image.shape to np.shape(image)

def noisify(type, target):
    if type == 'gauss':
        row,col, ch = np.shape(target)
        mean = 0
        var = 0.1
        sigma = var**0.5
        gauss = np.random.normal(mean, sigma, (row,col,ch))
        noise = target + gauss
        return noise
    elif type == 'sap':
        row, col, ch = np.shape(target)
        s_vs_p = 0.5
        amount = 0.004
        out = np.copy(target)
        #salt
        num_salt = np.ceil(amount * np.size(target) * s_vs_p)
        coords = [np.random.randint(0,i-1, int(num_salt))
                for i in np.shape(target)]
        out[coords] = 1
        #pepper
        num_pepper = np.ceil(amount* np.size(target) * (1. - s_vs_p))
        coords = [np.random.randint(0,i-1, int(num_pepper))
                for i in np.shape(target)]
        out[coords] = 0
        return out
    elif type == 'poisson':
        vals = len(np.unique(target))
        vals = 2 ** np.ceil(np.log2(vals))
        noise = np.random.poisson(target * vals) / float(vals)
        return noise
    elif type == 'speckle':
        row, col, ch = np.shape(target)
        gauss = np.random.randn(row,col,ch)
        gauss = np.reshape(gauss,(row,col,ch))
        noise = target + target * gauss
        return noise

Any help would be appreciated.


Solution

  • Here's a vectorized approach using OpenCV + skimage.util.random_noise. You can experiment with noise modes such as localvar, pepper, s&p, and speckle to obtain the desired result. You can set the proportion of noise with the amount parameter. Here's an example using s&p with amount=0.011:

    Input image

    enter image description here

    Result

    enter image description here

    With amount=0.051:

    enter image description here

    import cv2
    import numpy as np
    from skimage.util import random_noise
    
    # Load the image
    image = cv2.imread('1.png', 0)
    
    # Add salt-and-pepper noise to the image
    noise = random_noise(image, mode='s&p', amount=0.011)
    
    # The above function returns a floating-point image in the range [0, 1]
    # so need to change it to 'uint8' with range [0,255]
    noise = np.array(255 * noise, dtype=np.uint8)
    
    cv2.imshow('noise',noise)
    cv2.imwrite('noise.png',noise)
    cv2.waitKey()