Denoising

Good morning. Similar to yesterday’s weather here in the Twin CIties of Minneapolis and St. Paul, our high temperature for the day is forecasted to be 90F. We are still in spring but it is starting to feel like summer.

Yesterday my wife and I were planning to walk for about ten miles. Due to the temperature and humidity we decided to cut it short to just five. Today we are just planning on five miles.

Today we will experiment with denoising images. This post is based on the contents of a section in the PluralSight course Building Image Processing Applications Using scikit-image by Janani Ravi.

The subject of this post is rather interesting for me because many years ago I worked on a project to read license plates of cars moving between Texas and Mexico and vice versa. As far as I can recall all the technical aspects were met. The issue was of privacy.  If we move a few years forward, today we have police cars roaming all cities in the USA equipped with multiple cameras that are constantly scanning for license plates of interest. Our perceptions and expectations have changed.

# **** folder of interest ****
cd C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos

# **** open file of interest using VSCode ****
(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>code Denoising.py

# **** execute python script of interest ****
(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>python Denoising.py

We will start by opening an Anaconda prompt and getting to the folder of interest. The location for your folder of interest might be different. We will then open the VSCode IDE with the name for the script of interest. The script does not exist at this time. The name for the script may be different if you so decide.

At this time I would like to disclose that I am a Microsoft employee and have been using VSCode for a few years. In this post I am using the GitHub Copilot plugin. Note that the author of the course uses the Python notebook for the examples. In this post I will just be using VSCode.

Once the script is created, make sure you save it once. Now you are ready to edit, save and run the script of interest.

In this script we will use the image of a kitten. I also experimented with a modified image of a license plate from MN. I found the image on the web. It was generated by Drake317 – Own work, Public Domain.  The original image is in PNG format. I converted to a smaller JPG. You will see the line that I added to experiment with the license plate. No images for the license plate experimentation are shown in this post.

# **** imports ****
import numpy as np
import matplotlib.pyplot as plt

from skimage import data, img_as_float, color, io
from skimage.restoration import (   denoise_tv_chambolle, 
                                    denoise_bilateral,
                                    denoise_wavelet,
                                    estimate_sigma)
from skimage.util import random_noise

We start by specifying the imports we will be needing by this script.

# **** read kitten image ****
kitten = img_as_float(io.imread('./images/pexels-kitten.jpg'))
#kitten = img_as_float(io.imread('./images/mn_plate.jpg'))

# **** show kitten image ****
plt.figure(figsize=(10, 8))
plt.imshow(kitten)
plt.title('Kitten')
plt.show()

We read the image of interest which is a kitten. You can see that I have commented out the line that specifies the license plate. The image of interest is then displayed. Cats are OK with me, but I am a dog person.

# **** add noise to kitten image ****
sigma = 0.155                                           # noise standard deviation  was: 0.155
noisy_kitten = random_noise(kitten,
                            var=sigma**2)

# **** show noisy kitten image ****
plt.figure(figsize=(10, 8))
plt.imshow(noisy_kitten)
plt.title('Noisy Kitten')
plt.show()

Next we add some noise to the image of interest. You should experiment with different sigmas to see the effect the sigma value has on the quality of the image. The image with noise is then displayed.

# **** estimate the noise standard deviation from the noisy image ****
sigma_est = estimate_sigma( noisy_kitten,

                            #multichannel=True,         # deprecated
                            channel_axis=-1,            # for color image

                            average_sigmas=True)

# **** print estimated noise standard deviation 
#      close to the 0.155 standard deviation of the noise we added ****
print(f'Estimated Gaussian noise standard deviation: {sigma_est}')

Now we estimate the sigma of the noise based on the noisy image of the kitten. In this case the value is close to what we set it when introducing noise. The estimated sigma is then displayed.

(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>python Denoising.py
Estimated Gaussian noise standard deviation: 0.13888408881648898
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).

(base) C:\Documents\_Image Processing\scikit-image-building-image-processing-applications\02\demos>

We will now denoise the image using the Chambolle filter with two different weights. In addition I had to change the multichannel argument because it has been deprecated since the course materials were generated.

The denoised images are then displayed.

We will now use a bilateral filter to remove noise from the kitten image.

# **** denoise noisy image using an edge preserving bilateral filter
#      averages pixels based on their spatial closeness and 
#      radiometric similarity (how close the colors are) ****
denoise_bi_1 = denoise_bilateral(   noisy_kitten,
                                    sigma_color=0.05,
                                    sigma_spatial=15,
                                    
                                    #multichannel=True)
                                    channel_axis=-1)

# **** denoise with different sigma_color
#      accept a larger standard deviation while averaging pixels based on color ****
denoise_bi_2 = denoise_bilateral(   noisy_kitten,
                                    sigma_color=0.1,
                                    sigma_spatial=15,
                                    channel_axis=-1)

# **** show denoised images ****
fig, ax = plt.subplots( nrows=1,
                        ncols=2,
                        figsize=(18, 14),
                        sharex=True,
                        sharey=True)

ax[0].imshow(denoise_bi_1)
ax[0].axis('off')
ax[0].set_title('Bilateral Denoising (Sigma Color = 0.05)')

ax[1].imshow(denoise_bi_2)
ax[1].axis('off')
ax[1].set_title('Bilateral Denoising (Sigma Color = 0.1)')

fig.tight_layout()
plt.show()

You can see in the script that we use two different values for the sigma_color argument. In addition one of the arguments has been deprecated so I had to replace it. The images are then displayed.

Now we will try wavelet denoising.

# **** denoise noisy image using wavelet denoising 
#      soft thresholding gives a better approximation of the original image
#      denoising images is better with the YCbCr color space 
#      as compared to the RGB color space ****
denoise_wave_1 = denoise_wavelet(   noisy_kitten,
                                    mode='soft',
                                    
                                    #multichannel=True,
                                    channel_axis=-1,

                                    convert2ycbcr=True)

# **** denoise keeping image in RGB space ****
denoise_wave_2 = denoise_wavelet(   noisy_kitten,
                                    mode='soft',
                                    channel_axis=-1,
                                    convert2ycbcr=False)

# **** show denoised images ****
fig, ax = plt.subplots( nrows=1,
                        ncols=2,
                        figsize=(18, 14),
                        sharex=True,
                        sharey=True)

ax[0].imshow(denoise_wave_1)
ax[0].axis('off')
ax[0].set_title('Wavelet Denoising (YCbCr)')

ax[1].imshow(denoise_wave_2)
ax[1].axis('off')
ax[1].set_title('Wavelet Denoising (RGB)')

fig.tight_layout()
plt.show()

The approach is to make two images changing the value of one of the arguments. Note that once again one of the arguments used by the original script had been deprecated so I replaced it.

The two resulting images are then displayed.

Hope you enjoy this post as much as I did. Looking for an image of interest makes the experimentation take an additional dimension. That is why I tried it with a license plate.

Keep in mind that one of the best ways to learn is to read (in this case watch the PluralSight course), experiment, and repeat as needed.

If interested in getting access to the code in this post you can find it in my GitHub repository Denoising.

Enjoy,

John

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.