OpenCV #004 Common Types of Noise

OpenCV #004 Common Types of Noise

Digital Image Processing using OpenCV (Python & C++)

Highlights: We will give an overview of the most common types of noise that is present in images. We will show how we can generate these types of noise and add them to clean images. Then, we will show how we can filter these images using a simple median filter.

In this post, we will assume that we “know” how the noise looks like in our experiments and then it will be easier for us to find an optimal way how to remove that noise. #YesFilter 🙂

Tutorial Overview:

  1. Noise generation in Python and C++
  2. Adding noise to images
  3. Explore how we can remove noise and filter our image

1. Noise generation in Python and C++

Different kind of imaging systems might give us different noise. Here, we give an overview of three basic types of noise that are common in image processing applications:

  1. Gaussian noise.
  2. Random noise
  3. Salt and Pepper noise (Impulse noise – only white pixels)

Before we start with the generation of noise in images, we will give a brief method of how we can generate random numbers from a Gaussian distribution or from a uniform distribution.

Python

C++

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main() {
    
    // Let's start with a basic method to generate a random number using OpenCV.
    // For this we will use a cv::RNG method
    // the code below illustrates how we can obtain 2 random numbers from uniform distribution
    
    // this returns the random number generator
    cv::RNG rng = theRNG();
    // two random numbers will be generated
    // from the uniform distribution [0,1].
    float a = rng.uniform(0.f, 1.f); float b = rng.uniform(0.f, 1.f);
    // printing two random numbers
    std::cout << "a:" << a << std::endl << "b:" << b <<  std::endl;
    
    // in the similar manner we can get two random numbers from a normal distribution
    
    // Here we can just specify the sigma value (we have use a value 1)
    // the distribution will be zero centered.
    
    float a_g = rng.gaussian(1);
    
    std::cout << a_g << std::endl ;

For this code we get the following output:

// The outputs are here below
a=0.302828, b=0.699259
  1. Gaussian noise. We may say that a Gaussian noise will be an independent identically distributed intensity level drawn from a Gaussian distribution. Note that here we use 1D Gaussian distribution. Commonly, it is determined with parameters \(\mu\) and \(\sigma\).

The following code will generate a Gaussian noise.

Python

C++

  // Another way to generate the random values form the same distribution is to use
    // functions randu and randn
    
    cv::Mat image = cv::imread("a11.jpg", IMREAD_GRAYSCALE);
    
    // Let's first create a zero image with the same dimensions of the loaded image
    
    cv::Mat gaussian_noise = cv::Mat::zeros (image.rows, image.cols, CV_8UC1);
    
    cv::imshow("All zero values", gaussian_noise);
    cv::waitKey();
    
    // now, we can set the pixel values as a Gaussian noise
    // we have set a mean value to 128 and a standard deviation to 20
    cv::randn(gaussian_noise, 128, 20);
    
    // Let's plot this image and see how it looks like
    cv::imshow("Gaussian noise", gaussian_noise);
    cv::waitKey();

    cv::imwrite("Gaussian random noise.jpg", gaussian_noise);
Common Types of Noise guassian.
Random Gaussian Noise

This image is generated to have the same dimension as our test image.

Common Types of Noise, image of a dragon.
Our test image

In a similar way, we can create a random uniform noise. Both in Python and C++ the difference will actually be in just one letter within a command (so easy to figure that out!).

Python

C++

    // In a similar manner we can create an image whose pixel values have
    // random values drawn from an uniform distribution
    
    cv::Mat uniform_noise = cv::Mat::zeros (image.rows, image.cols, CV_8UC1);
    
    cv::randu(uniform_noise, 0, 255);
    cv::imshow("Uniform random noise", uniform_noise );
    cv::waitKey();
    cv::imwrite("Uniform random noise.jpg", uniform_noise);
Common Types of Noise, uniform random noise.
Uniform Random Noise

As we can see the uniform random noise is very similar to a Gaussian noise. Here, the pixel values were set from 0 to 255.

Next, we have to see how we can generate an impulse noise. This will be a black image with random white pixels. There are many ways to implement such an image. For instance, we can actually post-process a “uniform_noise” image.  We can simply set a threshold value (binary thresholding) and convert an image into a set of black and white pixels. All pixels below a threshold (in our case 250 ) will become black (0), and those above this value will become white (255). By varying the values of a threshold we will get more or less white pixels (more or less noise).

Python

C++


    cv::Mat impulse_noise = uniform_noise.clone();
    
    
    // here a number 250 is defined as a threshold value
    // Obviously, if we want to increase a number of white pixels
    // we will need to decrease it.
    // Otherwise, we can increase it and in that way we will suppress the
    // number of white pixles.
    
    cv::threshold(uniform_noise, impulse_noise, 250, 255, CV_8UC1);
    
    cv::imshow("Impulse_noise", impulse_noise);
    cv::waitKey();
    cv::imwrite("Impulse_noise.jpg", impulse_noise);
    
Common Types of Noise, salt and pepper noise.
Impulse Noise

2. Adding Noise to Images

If images are just functions, then we can add two images similarly like we can add two functions. Simply, every pixel value will be summed with the corresponding pixel value that has the same coordinates. Of course, the images need to have the same dimensions. In case that you are a science geek and you miss some formulas in this post, we can write the following one just for you 🙂

\(\vec{I}'(x, y) = \vec{I}(x, y) + \vec{\eta}(x, y)\)

Python

C++


    cv::Mat noisy_image=image.clone();
    // note that we can simply sum two Mat objects, that is, two images.
    // in order not to degrade the image quality too much
    // we will multipliy the gaussian_noise with 0.5.
    // in this way the effect of noise will be reduced
    noisy_image = image + gaussian_noise*0.5;

    cv::imshow("Noisy_image - Gaussian noise", noisy_image);
    cv::waitKey();

    cv::Mat noisy_image1=image.clone();
    noisy_image1 = image + uniform_noise*0.2;

    cv::imshow("Noisy_image - Uniform noise", noisy_image1);
    cv::waitKey();

    cv::Mat noisy_image2=image.clone();
    // similarly for a uniform noise, we will use even a lower factor of 0.2

    noisy_image2 = image + impulse_noise*0.5;

    cv::imshow("Noisy_image - Impulse noise", noisy_image2);
    cv::waitKey();
    
gaussian noise, salt and pepper noise, uniform noise.
Gaussian noise, Uniform noise, Impulse noise

3. Median Filter

Next, we are going to present a median filter and basic image processing. Do you know what a median operator/function does? Yes, you are right! It is that simple. A median filter just scrolls across the image, and for all the elements that are overlapping with the filter, position outputs the median element.

Let’s have a look at the illustration of a 2D median filter. Imagine that those are the pixel values in the image as shown in the Figure below. This means that the filter is centered at the value of 90. In this case, we use a 3 x 3 filter size, so all nine values we will sort in the ascending order. The median value is 27 and, that is the output value for this location of the filter. In this case, a value of 90 (an extreme value in this example), will be replaced with a number 27.

median filter.
Median Filter Diagram

This type of filter can be relatively successful for both uniform and Gaussian random noise. However, it can be very effective for impulse (salt and pepper) noise! The reason is that for pixels values that are much lower or higher than the mean values, the median operator will work just fine to suppress them.

Python

C++


    cv::medianBlur(noisy_image, noisy_image, 3);
    cv::imshow("Gaussian random noise removed", noisy_image);
    cv::waitKey();

    cv::medianBlur(noisy_image1, noisy_image1, 3);
    cv::imshow("Uniform random noise removed", noisy_image1);
    cv::waitKey();

    cv::medianBlur(noisy_image2, noisy_image2, 3);
    cv::imshow("Impulse random noise removed", noisy_image2);
    cv::waitKey();
    cv::destroyAllWindows();
    return 0;
}
median filter on images.
Applying a Median Filter to Gaussian, uniform and impulse noise. The best results are achieved in suppression of an impulse noise.

It is good to note, that the median filter is actually an example of a non-linear filter. This is in contrast with the majority of other filters that can be applied using a convolution operation. In addition, a median filter is also sometimes referred to as an edge-preserving filter.

Summary

In this blog post, we gave an overview of the most common types of noise that are present in images. How to generate these types of noise, add them to the image and clean the image using a simple median filter. In the next post, we will talk more about blur/mean/box and Gaussian 2D image filters.

More resources on the topic:

For more resources about common types of noise and filter, check these other sites.

 

Leave a Reply

Your email address will not be published. Required fields are marked *