Noise & Blurring

What is Image Noise?

Noise: Random variation in pixel intensity (unwanted signal)

Sources:

  • Sensor imperfections
  • Low light conditions
  • Electrical interference
  • Transmission errors
  • Compression artifacts

Impact: Degrades image quality, affects processing algorithms

Types of Noise

Gaussian noise: Random values from normal distribution

  • Most common type
  • Additive: noisy = clean + noise
  • Looks like grainy texture

Salt-and-pepper noise: Random black/white pixels

  • Also called impulse noise
  • Pixels are either 0 or 255
  • Looks like random dots

Poisson noise: Varies with signal intensity

  • Common in low-light imaging
  • Variance proportional to intensity

Adding Gaussian Noise

  1. import cv2
  2. import numpy as np
  3. # generate gaussian noise
  4. mean = 0
  5. sigma = 25
  6. gaussian_noise = np.random.normal(mean, sigma, img.shape)
  7. #ans: mean=0, sigma=25 controls noise strength
  8. # add noise to image
  9. #ans: noisy = img + gaussian_noise
  10. #ans: noisy = np.clip(noisy, 0, 255).astype(np.uint8)
  11. #ans: clip to valid range [0, 255]

Adding Salt-and-Pepper Noise

  1. # add salt-and-pepper noise
  2. def add_salt_pepper(img, prob=0.01):
  3. noisy = img.copy()
  4. # salt (white pixels)
  5. num_salt = int(prob * img.size * 0.5)
  6. coords = [np.random.randint(0, i, num_salt) for i in img.shape[:2]]
  7. noisy[coords[0], coords[1]] = 255
  8. # pepper (black pixels)
  9. num_pepper = int(prob * img.size * 0.5)
  10. coords = [np.random.randint(0, i, num_pepper) for i in img.shape[:2]]
  11. noisy[coords[0], coords[1]] = 0
  12. return noisy
  13. #ans: prob controls percentage of noisy pixels

Gaussian Blur for Noise Reduction

Why Gaussian blur?

  • Effective for Gaussian noise
  • Weighted averaging (preserves features better)
  • Smooth, natural-looking results

Trade-off: Reduces noise but also blurs edges

Gaussian Blur Parameters

  1. # gaussian blur for noise reduction
  2. #ans: denoised = cv2.GaussianBlur(noisy, (5, 5), 0)
  3. #ans: 5x5 kernel, sigma auto-calculated
  4. # larger kernel for more smoothing
  5. denoised = cv2.GaussianBlur(noisy, (9, 9), 0)
  6. #ans: 9x9 kernel removes more noise, more blur
  7. # specific sigma value
  8. denoised = cv2.GaussianBlur(noisy, (0, 0), sigmaX=2.0)
  9. #ans: kernel auto-calculated from sigma=2.0
  10. # different sigma for x and y
  11. denoised = cv2.GaussianBlur(noisy, (0, 0), sigmaX=2.0, sigmaY=1.0)
  12. #ans: anisotropic blur (different in x and y)

Median Filter for Salt-Pepper

Why median for salt-pepper?

  • Not affected by outliers
  • Picks middle value, ignores extreme noise
  • Preserves edges better than averaging

Algorithm: Sort neighborhood, pick median

Median Blur in OpenCV

  1. # median blur (best for salt-pepper)
  2. #ans: denoised = cv2.medianBlur(noisy, 5)
  3. #ans: 5x5 kernel, takes median of 25 pixels
  4. # larger kernel for heavy noise
  5. denoised = cv2.medianBlur(noisy, 9)
  6. #ans: 9x9 kernel for strong salt-pepper noise
  7. # why median works for salt-pepper?
  8. #ans: median ignores extreme values (0 or 255)
  9. #ans: preserves original pixel values better

Bilateral Filter (Edge-Preserving)

Bilateral filter: Smooths noise while keeping edges sharp

How it works:

  • Two weights: spatial + intensity
  • Close pixels + similar intensity → high weight
  • Far pixels OR different intensity → low weight

Best for: Natural images, portrait smoothing

Bilateral Filter in OpenCV

  1. # bilateral filter (edge-preserving denoising)
  2. #ans: denoised = cv2.bilateralFilter(noisy, d=9, sigmaColor=75, sigmaSpace=75)
  3. # d: diameter of pixel neighborhood
  4. #ans: larger d = considers more neighbors, slower
  5. # sigmaColor: intensity difference threshold
  6. #ans: larger value = more colors averaged (more blur)
  7. # sigmaSpace: spatial distance threshold
  8. #ans: larger value = farther pixels considered
  9. # when to use bilateral?
  10. #ans: preserve edges while removing noise
  11. #ans: better than Gaussian for natural images

Non-Local Means Denoising

NLM denoising: Compares patches (not just neighborhoods)

Concept: Similar patches anywhere in image are averaged

Advantage: Better noise reduction, preserves texture

Disadvantage: Computationally expensive

NLM Denoising in OpenCV

  1. # non-local means denoising (grayscale)
  2. gray_noisy = cv2.cvtColor(noisy, cv2.COLOR_BGR2GRAY)
  3. #ans: denoised = cv2.fastNlMeansDenoising(gray_noisy, None, h=10, templateWindowSize=7, searchWindowSize=21)
  4. # for color images
  5. #ans: denoised = cv2.fastNlMeansDenoisingColored(noisy, None, h=10, hColor=10, templateWindowSize=7, searchWindowSize=21)
  6. # h: filter strength
  7. #ans: higher h = more denoising, more blur
  8. # templateWindowSize: patch size
  9. #ans: usually 7x7
  10. # searchWindowSize: search area
  11. #ans: usually 21x21

Comparing Denoising Methods

Gaussian blur:

  • Fast, simple
  • Blurs edges
  • Good for light Gaussian noise

Median filter:

  • Excellent for salt-pepper
  • Preserves edges
  • Slower than Gaussian

Bilateral filter:

  • Edge-preserving
  • Good for natural images
  • Moderate speed

NLM denoising:

  • Best quality
  • Preserves texture
  • Slowest

Exercises - Part 1 (Concepts)

  1. # what causes image noise?
  2. #ans: sensor issues, low light, interference, compression
  3. # most common noise type?
  4. #ans: Gaussian noise
  5. # what noise looks like random dots?
  6. #ans: salt-and-pepper (impulse) noise
  7. # best filter for salt-pepper?
  8. #ans: median filter
  9. # what does bilateral preserve?
  10. #ans: edges while smoothing noise

Exercises - Part 2 (Concepts)

  1. # trade-off of denoising?
  2. #ans: removes noise but also blurs details/edges
  3. # why median ignores salt-pepper?
  4. #ans: median not affected by outliers (extreme values)
  5. # what is NLM?
  6. #ans: non-local means, compares similar patches
  7. # fastest denoising method?
  8. #ans: Gaussian blur
  9. # what is sigmaColor in bilateral?
  10. #ans: intensity difference threshold for averaging

Exercises - Part 3 (Coding)

  1. # add gaussian noise (sigma=20)
  2. mean = 0
  3. sigma = 20
  4. noise = np.random.normal(mean, sigma, img.shape)
  5. #ans: noisy = np.clip(img + noise, 0, 255).astype(np.uint8)
  6. # denoise with gaussian blur
  7. #ans: denoised = cv2.GaussianBlur(noisy, (5, 5), 0)
  8. # denoise with median filter
  9. #ans: denoised = cv2.medianBlur(noisy, 5)

Exercises - Part 4 (Coding)

  1. # bilateral denoising
  2. #ans: denoised = cv2.bilateralFilter(noisy, 9, 75, 75)
  3. # NLM denoising on grayscale
  4. #ans: denoised = cv2.fastNlMeansDenoising(gray_noisy, None, h=10, templateWindowSize=7, searchWindowSize=21)
  5. # NLM denoising on color image
  6. #ans: denoised = cv2.fastNlMeansDenoisingColored(noisy, None, h=10, hColor=10, templateWindowSize=7, searchWindowSize=21)

Exercises - Part 5 (Coding)

  1. # strong gaussian blur (11x11)
  2. #ans: denoised = cv2.GaussianBlur(noisy, (11, 11), 0)
  3. # median with large kernel
  4. #ans: denoised = cv2.medianBlur(noisy, 11)
  5. # bilateral with strong smoothing
  6. #ans: denoised = cv2.bilateralFilter(noisy, 15, 150, 150)
  7. # compare sizes before and after
  8. print(noisy.shape, denoised.shape)
  9. #ans: same shape, only pixel values change

Exercises - Part 6 (Mixed)

  1. # apply gaussian blur twice
  2. blur1 = cv2.GaussianBlur(noisy, (5, 5), 0)
  3. #ans: blur2 = cv2.GaussianBlur(blur1, (5, 5), 0)
  4. #ans: double blur = stronger smoothing
  5. # denoise each channel separately
  6. b, g, r = cv2.split(noisy)
  7. #ans: b_denoised = cv2.GaussianBlur(b, (5, 5), 0)
  8. #ans: g_denoised = cv2.GaussianBlur(g, (5, 5), 0)
  9. #ans: r_denoised = cv2.GaussianBlur(r, (5, 5), 0)
  10. #ans: denoised = cv2.merge([b_denoised, g_denoised, r_denoised])

Exercises - Part 7 (Mixed)

  1. # NLM with higher h value
  2. #ans: denoised = cv2.fastNlMeansDenoising(gray_noisy, None, h=20, templateWindowSize=7, searchWindowSize=21)
  3. #ans: h=20 removes more noise, more blur
  4. # bilateral with different sigmas
  5. denoised = cv2.bilateralFilter(noisy, 9, 50, 100)
  6. #ans: sigmaColor=50 (less color smoothing)
  7. #ans: sigmaSpace=100 (larger spatial range)
  8. # combine median and gaussian
  9. median = cv2.medianBlur(noisy, 5)
  10. #ans: final = cv2.GaussianBlur(median, (3, 3), 0)
  11. #ans: median removes salt-pepper, gaussian smooths

Exercises - Part 8 (Advanced)

  1. # create noisy image with both types
  2. gaussian_noise = np.random.normal(0, 15, img.shape)
  3. noisy = np.clip(img + gaussian_noise, 0, 255).astype(np.uint8)
  4. # add salt-pepper on top
  5. prob = 0.02
  6. num_salt = int(prob * noisy.size * 0.5)
  7. coords = [np.random.randint(0, i, num_salt) for i in noisy.shape[:2]]
  8. noisy[coords[0], coords[1]] = 255
  9. num_pepper = int(prob * noisy.size * 0.5)
  10. coords = [np.random.randint(0, i, num_pepper) for i in noisy.shape[:2]]
  11. noisy[coords[0], coords[1]] = 0
  12. #ans: combined gaussian and salt-pepper noise
  13. # denoise strategy
  14. median = cv2.medianBlur(noisy, 5)
  15. #ans: denoised = cv2.bilateralFilter(median, 9, 75, 75)
  16. #ans: median first removes salt-pepper, then bilateral smooths

Google tag (gtag.js)