4

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.

nathancy
  • 42,661
  • 14
  • 115
  • 137
Jerome Ariola
  • 135
  • 1
  • 11
  • Can you define what is not working? Does it visually look the same, or is the RGB for each pixel remaining the same? – scottsaenz Jan 14 '20 at 14:56
  • Did you convert your input images to float before adding the noise. – fmw42 Jan 14 '20 at 17:58
  • @scottsaenz for 'gauss' it seems to do the job. However I cannot create a PIL image that allows me to view how it looks like (via Image.fromarray() which should create me an image on the temp folder. What doesn't work is that maybe PIL image cannot be made with decimal values, but how else can noise be added? – Jerome Ariola Jan 14 '20 at 19:25
  • @fmw42 no? I didn't. I took the image and used asarray to convert it from PIL image to uint8 tensor or something. I'll look into using floats... – Jerome Ariola Jan 14 '20 at 19:26
  • See the reference you took the code from. At the top it says convert the input to float. – fmw42 Jan 14 '20 at 23:23

1 Answers1

5

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()
nathancy
  • 42,661
  • 14
  • 115
  • 137
  • 1
    This is awesome! Thank you so much, it achieves exactly what I want. Although there's a library for it I learned from my failed experience. Thank you – Jerome Ariola Jan 15 '20 at 02:34