Search code examples
pythonimage-processingnoise

Denoising a photo with Python


I have the following image which is a scanned copy of an old book. I want to remove the noise in the background (which is a bit reddish) that is coming due to the scanning of the old photo.

enter image description here

Update:

After applying opencv, following the parameter settings in opencv doc, I am getting the following output. enter image description here

Please help fixing this.

The code that I am using:

import numpy as np
import cv2
from matplotlib import pyplot as plt

def display_image_in_actual_size(im_data):

    dpi = 80
    height, width, depth = im_data.shape

    # What size does the figure need to be in inches to fit the image?
    figsize = width / float(dpi), height / float(dpi)

    # Create a figure of the right size with one axes that takes up the full figure
    fig = plt.figure(figsize=figsize)
    ax = fig.add_axes([0, 0, 1, 1])

    # Hide spines, ticks, etc.
    ax.axis('off')

    # Display the image.
    ax.imshow(im_data, cmap='gray')

    plt.show()

img = cv2.imread('scan03.jpg')

dst = cv2.fastNlMeansDenoisingColored(img,None,10,10,7,21)

display_image_in_actual_size(img)
display_image_in_actual_size(dst)

Solution

  • The color of some pixels which has near threshold pixel values will be affected, but that depends on the task, here is one solution that you might adjust the threshold to a value that suits your task, also you might remove the median filter, or reduce the sigma value(5) if it affects the text badly, you might have some undesired noise, but the text will be readable.

    result

    import numpy as np
    import matplotlib.pyplot as plt
    import cv2
    # Read Image
    img = cv2.imread('input.jpg')
    # BGR --> RGB
    RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    # BGR --> Gray
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # Set thresholds
    th_white = 210
    th_black = 85
    # copy original gray
    mask_white = gray.copy()
    mask_black = gray.copy()
    # Thresholding
    mask_white[mask_white<th_white] = 0
    mask_black[mask_black<th_black] = 0
    mask_white[mask_white>=th_white] = 255
    mask_black[mask_black>=th_black] = 255
    # Median Filtering (you can remove if the text is not readable)
    median_white = cv2.medianBlur(mask_white,5)
    median_black = cv2.medianBlur(mask_black,5)
    # Mask 3 channels
    mask_white_3 = np.stack([median_white, median_white, median_white], axis=2)
    mask_black_3 = np.stack([median_black, median_black, median_black], axis=2)
    # Masking the image(in RGB)
    result1 = np.maximum(mask_white_3, RGB)
    result2 = np.minimum(mask_black_3, result1)
    # Visualize the results
    plt.imshow(result2)
    plt.axis('off')
    plt.show()