Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Normalize image exposure

I'm working on a project to measure and visualize image similarity. The images in my dataset come from photographs of images in books, some of which have very high or low exposure rates. For example, the images below come from two different books; the one on the top is an over-exposed reprint of the one on the bottom, wherein the exposure looks good:

enter image description here enter image description here

I'd like to normalize each image's exposure in Python. I thought I could do so with the following naive approach, which attempts to center each pixel value between 0 and 255:

from scipy.ndimage import imread
import sys

def normalize(img):
  '''
  Normalize the exposure of an image.
  @args:
    {numpy.ndarray} img: an array of image pixels with shape:
      (height, width)
  @returns:
    {numpy.ndarray} an image with shape of `img` wherein
      all values are normalized such that the min=0 and max=255
  '''
  _min = img.min()
  _max = img.max()
  return img - _min * 255 / (_max - _min)

img = imread(sys.argv[1])
normalized = normalize(img)

Only after running this did I realize that this normalization will only help images whose lightest value is less than 255 or whose darkest value is greater than 0.

Is there a straightforward way to normalize the exposure of an image such as the top image above? I'd be grateful for any thoughts others can offer on this question.

like image 781
duhaime Avatar asked Mar 29 '18 00:03

duhaime


People also ask

How do you normalize an image in Python?

Use the normalize() Function of OpenCV to Normalize an Image in Python. Normalization in image processing is used to change the intensity level of pixels. It is used to get better contrast in images with poor contrast due to glare. We can use the normalize() function of OpenCV to normalize an image.

How is image normalization done?

In image processing, normalization is a process that changes the range of pixel intensity values. Applications include photographs with poor contrast due to glare, for example. Normalization is sometimes called contrast stretching or histogram stretching.

Why do you normalize image?

the point from normalization comes behind calibrating the different pixels intensities into a normal distribution which makes the image looks better for the visualizer. Main purpose of normalization is to make computation efficient by reducing values between 0 to 1.


3 Answers

Histogram equalisation works surprisingly well for this kind of thing. It's usually better for photographic images, but it's helpful even on line art, as long as there are some non-black/white pixels.

It works well for colour images too: split the bands up, equalize each one separately, and recombine.

I tried on your sample image:

after hist equal

Using libvips:

$ vips hist_equal sample.jpg x.jpg

Or from Python with pyvips:

x = pyvips.Image.new_from_file("sample.jpg")
x = x.hist_equal()
x.write_to_file("x.jpg")
like image 125
jcupitt Avatar answered Oct 31 '22 01:10

jcupitt


It's very hard to say if it will work for you without seeing a larger sample of your images, but you may find an "auto-gamma" useful. There is one built into ImageMagick and the description - so that you can calculate it yourself - is:

Automagically adjust gamma level of image.

This calculates the mean values of an image, then applies a calculated -gamma adjustment so that the mean color in the image will get a value of 50%.

This means that any solid 'gray' image becomes 50% gray.

This works well for real-life images with little or no extreme dark and light areas, but tend to fail for images with large amounts of bright sky or dark shadows. It also does not work well for diagrams or cartoon like images.

You can try it out yourself on the command line very simply before you go and spend a lot of time coding something that may not work:

convert Tribunal.jpg -auto-gamma result.png

enter image description here

You can do -auto-level as per your own code beforehand, and a thousand other things too:

convert Tribunal.jpg -auto-level -auto-gamma result.png
like image 23
Mark Setchell Avatar answered Oct 31 '22 00:10

Mark Setchell


I ended up using a numpy implementation of the histogram normalization method @user894763 pointed out. Just save the below as normalize.py then you can call:

python normalize.py cats.jpg

Script:

import numpy as np
from scipy.misc import imsave
from scipy.ndimage import imread
import sys

def get_histogram(img):
  '''
  calculate the normalized histogram of an image
  '''
  height, width = img.shape
  hist = [0.0] * 256
  for i in range(height):
    for j in range(width):
      hist[img[i, j]]+=1
  return np.array(hist)/(height*width)

def get_cumulative_sums(hist):
  '''
  find the cumulative sum of a numpy array
  '''
  return [sum(hist[:i+1]) for i in range(len(hist))]

def normalize_histogram(img):
  # calculate the image histogram
  hist = get_histogram(img)
  # get the cumulative distribution function
  cdf = np.array(get_cumulative_sums(hist))
  # determine the normalization values for each unit of the cdf
  sk = np.uint8(255 * cdf)
  # normalize the normalization values
  height, width = img.shape
  Y = np.zeros_like(img)
  for i in range(0, height):
    for j in range(0, width):
      Y[i, j] = sk[img[i, j]]
  # optionally, get the new histogram for comparison
  new_hist = get_histogram(Y)
  # return the transformed image
  return Y

img = imread(sys.argv[1])
normalized = normalize_histogram(img)
imsave(sys.argv[1] + '-normalized.jpg', normalized)

Output:

enter image description here

like image 23
duhaime Avatar answered Oct 30 '22 23:10

duhaime