I am trying to implement global contrast normalization in python from Yoshua Bengio's Deep Learning book (section pg. 442). From the book, to get a normalized image using global contrast normalization we use this equation: $$\mathsf{X}^{\prime}_{i,j,k}=s\frac{\mathsf{X}_{i,j,k}-\overline{\mathsf{X}}}{max\left\lbrace \epsilon, \sqrt{\lambda+\frac{1}{3rc}\sum_{i=1}^{r}\sum_{j=1}^{c}\sum_{k=1}^{3}(\mathsf{X}_{i,j,k}-\overline{\mathsf{X}})^2}\right\rbrace }$$ where $\mathsf{X}_{i,j,k}$ is a tensor of the image and $\mathsf{X}^{\prime}_{i,j,k}$ is a tensor of the normalized image, and $\overline{\mathsf{X}} = \frac{1}{3rc}\sum_{i=1}^{r}\sum_{j=1}^{c}\sum_{k=1}^{3} \mathsf{X}_{i,j,k}$ is the average value of the pixels of the original image. $\epsilon$ and $\lambda$ are some constants, usually with $\lambda=10$ and $\epsilon$ set to be a very small number, and here is my implementation:

import Image
import numpy as np
import math
def global_contrast_normalization(filename, s, lmda, epsilon):
    X = np.array(Image.open(filename))

    contrast =0

    for i in range(r):
        for j in range(c):
            for k in range(u):


    for i in range(r):
        for j in range(c):
            for k in range(u):


    for i in range(r):
        for j in range(c):
            for k in range(u):

                X_prime[i][j][k] = s * (X[i][j][k] - X_average) / max(epsilon, contrast)
global_contrast_normalization("cat.jpg", 1, 10, 0.000000001)

original image:

original image

result image:


I got an unexpected result. What is wrong with my implementation?


there are multiple issues with the code:

  1. You force the values in the image to be uint8 (8-bit integer). Since the values are floats they will be casted/rounded to either 0 or 1. This will later be interpreted as image in black and the darkest form of gray (1 out of 255).

  2. Once you have proper floats as values PIL or pillow can't handle the array (they only do images with values in [0, 255])

The first problem happened because you/numpy wants the array to be a uint8. The normalize version will have floats.

You should have used:

X_prime = X.astype(float)

Here is a working version of the code:

import numpy
import scipy
import scipy.misc
from PIL import Image

def global_contrast_normalization(filename, s, lmda, epsilon):
    X = numpy.array(Image.open(filename))

    # replacement for the loop
    X_average = numpy.mean(X)
    print('Mean: ', X_average)
    X = X - X_average

    # `su` is here the mean, instead of the sum
    contrast = numpy.sqrt(lmda + numpy.mean(X**2))

    X = s * X / max(contrast, epsilon)

    # scipy can handle it
    scipy.misc.imsave('result.jpg', X)

global_contrast_normalization("cat.jpg", 1, 10, 0.000000001)

PS: X_prime = X will make X_prime reference X. So changing X_prime will also change X.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.