Convolutional Autoencoder for Image Denoising in Keras

I remember working on a project for a photography firm in Chicago where we had to restore thousands of grainy vintage photos.

I used a convolutional autoencoder in Keras to automatically remove digital noise while preserving sharp details.

Load the Dataset using Keras

I usually start by loading a standard dataset like MNIST because it is perfect for testing how well an autoencoder reconstructs shapes.

This method downloads the handwritten digits and splits them into training and testing sets for our Keras model.

from tensorflow.keras.datasets import mnist
import numpy as np

def load_keras_dataset():
    # Load the MNIST dataset and ignore the labels as we only need images
    (x_train, _), (x_test, _) = mnist.load_data()
    
    # Normalize pixel values to be between 0 and 1
    x_train = x_train.astype('float32') / 255.
    x_test = x_test.astype('float32') / 255.
    
    # Reshape to include the channel dimension (28, 28, 1)
    x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
    x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))
    
    return x_train, x_test

x_train, x_test = load_keras_dataset()

Add Synthetic Noise using Python

In a real-world scenario, you don’t always have “clean” versions of noisy images, so I often simulate noise to train the network.

I apply a Gaussian noise factor to the clean images and then clip the values to ensure they remain within the valid range of 0 to 1.

def add_noise_to_images(x_train, x_test):
    noise_factor = 0.5
    
    # Generate random noise and add it to the original images
    x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) 
    x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape) 

    # Clip values to stay in [0, 1]
    x_train_noisy = np.clip(x_train_noisy, 0., 1.)
    x_test_noisy = np.clip(x_test_noisy, 0., 1.)
    
    return x_train_noisy, x_test_noisy

x_train_noisy, x_test_noisy = add_noise_to_images(x_train, x_test)

Build the Convolutional Autoencoder in Keras

Building the architecture is the most exciting part, where I define the encoder to compress the image and the decoder to reconstruct it.

I use Conv2D layers for feature extraction and UpSampling2D to bring the compressed representation back to its original size.

from tensorflow.keras import layers, Model

def build_keras_autoencoder():
    input_img = layers.Input(shape=(28, 28, 1))

    # Encoder: Downsample the image
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(input_img)
    x = layers.MaxPooling2D((2, 2), padding='same')(x)
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    encoded = layers.MaxPooling2D((2, 2), padding='same')(x)

    # Decoder: Upsample to original size
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(encoded)
    x = layers.UpSampling2D((2, 2))(x)
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    x = layers.UpSampling2D((2, 2))(x)
    decoded = layers.Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)

    # Create the Model
    autoencoder = Model(input_img, decoded)
    autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
    
    return autoencoder

autoencoder = build_keras_autoencoder()

Train the Keras Model

When I train this model, I pass the noisy images as the input and the original clean images as the target.

This teaches the Keras model to ignore the noise and focus only on the structural features of the digits.

def train_denoising_model(model, x_noisy, x_clean, x_val_noisy, x_val_clean):
    # Map noisy images to clean images during training
    model.fit(x_noisy, x_clean,
                epochs=50,
                batch_size=128,
                shuffle=True,
                validation_data=(x_val_noisy, x_val_clean))

train_denoising_model(autoencoder, x_train_noisy, x_train, x_test_noisy, x_test)

Visualize Denoised Results in Python

I always finish by visualizing the results because seeing the “before” and “after” images is the best way to verify the model’s performance.

import matplotlib.pyplot as plt

def visualize_keras_predictions(model, x_test_noisy):
    # Predict clean images from noisy test data
    denoised_images = model.predict(x_test_noisy)

    n = 10
    plt.figure(figsize=(20, 4))
    for i in range(1, n + 1):
        # Display Noisy
        ax = plt.subplot(2, n, i)
        plt.imshow(x_test_noisy[i].reshape(28, 28))
        plt.gray()
        
        # Display Denoised
        ax = plt.subplot(2, n, i + n)
        plt.imshow(denoised_images[i].reshape(28, 28))
        plt.gray()
    plt.show()

visualize_keras_predictions(autoencoder, x_test_noisy)

I use Matplotlib to display a row of noisy images alongside the cleaned versions generated by our Keras autoencoder.

Full Code

Here is the complete code.

import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from tensorflow.keras import layers, Model
from tensorflow.keras.datasets import mnist

# 1. Load the Dataset
def load_keras_dataset():
    (x_train, _), (x_test, _) = mnist.load_data()
    x_train = x_train.astype('float32') / 255.
    x_test = x_test.astype('float32') / 255.
    x_train = np.reshape(x_train, (len(x_train), 28, 28, 1))
    x_test = np.reshape(x_test, (len(x_test), 28, 28, 1))
    return x_train, x_test

# 2. Add Synthetic Noise
def add_noise_to_images(x_train, x_test):
    noise_factor = 0.5
    x_train_noisy = x_train + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_train.shape) 
    x_test_noisy = x_test + noise_factor * np.random.normal(loc=0.0, scale=1.0, size=x_test.shape) 
    x_train_noisy = np.clip(x_train_noisy, 0., 1.)
    x_test_noisy = np.clip(x_test_noisy, 0., 1.)
    return x_train_noisy, x_test_noisy

# 3. Build the Convolutional Autoencoder
def build_keras_autoencoder():
    input_img = layers.Input(shape=(28, 28, 1))
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(input_img)
    x = layers.MaxPooling2D((2, 2), padding='same')(x)
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    encoded = layers.MaxPooling2D((2, 2), padding='same')(x)
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(encoded)
    x = layers.UpSampling2D((2, 2))(x)
    x = layers.Conv2D(32, (3, 3), activation='relu', padding='same')(x)
    x = layers.UpSampling2D((2, 2))(x)
    decoded = layers.Conv2D(1, (3, 3), activation='sigmoid', padding='same')(x)
    autoencoder = Model(input_img, decoded)
    autoencoder.compile(optimizer='adam', loss='binary_crossentropy')
    return autoencoder

# 4. Train the Model
def train_denoising_model(model, x_noisy, x_clean, x_val_noisy, x_val_clean):
    model.fit(x_noisy, x_clean,
                epochs=50,
                batch_size=128,
                shuffle=True,
                validation_data=(x_val_noisy, x_val_clean))

# 5. Visualize Results
def visualize_keras_predictions(model, x_test_noisy):
    denoised_images = model.predict(x_test_noisy)
    n = 10
    plt.figure(figsize=(20, 4))
    for i in range(1, n + 1):
        ax = plt.subplot(2, n, i)
        plt.imshow(x_test_noisy[i].reshape(28, 28))
        plt.gray()
        ax = plt.subplot(2, n, i + n)
        plt.imshow(denoised_images[i].reshape(28, 28))
        plt.gray()
    plt.show()

# Execute the complete pipeline
x_train, x_test = load_keras_dataset()
x_train_noisy, x_test_noisy = add_noise_to_images(x_train, x_test)
autoencoder = build_keras_autoencoder()
train_denoising_model(autoencoder, x_train_noisy, x_train, x_test_noisy, x_test)
visualize_keras_predictions(autoencoder, x_test_noisy)

You can see the output in the screenshot below.

Convolutional Autoencoder for Image Denoising in Keras

In this article, I showed you how to build a convolutional autoencoder in Keras to remove noise from images.

I have personally used this technique to clean up medical scans and satellite imagery with great success.

You may also like to read other Keras tutorials:

51 Python Programs

51 PYTHON PROGRAMS PDF FREE

Download a FREE PDF (112 Pages) Containing 51 Useful Python Programs.

pyython developer roadmap

Aspiring to be a Python developer?

Download a FREE PDF on how to become a Python developer.

Let’s be friends

Be the first to know about sales and special discounts.