How to implement global contrast normalization in python?

I am trying to implement global contrast normalization in python from Yoshua Bengio's Deep Learning book (section 12.2.1.1 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))

X_prime=X
r,c,u=X.shape
contrast =0
su=0
sum_x=0

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

sum_x=sum_x+X[i][j][k]
X_average=float(sum_x)/(r*c*u)

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

su=su+((X[i][j][k])-X_average)**2
contrast=np.sqrt(lmda+(float(su)/(r*c*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)
Image.fromarray(X_prime).save("result.jpg")
global_contrast_normalization("cat.jpg", 1, 10, 0.000000001)


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.