Python | Intensity Conversion Operations on Images

The following are commonly used intensity transformations:

  1. Image negatives (linear)
  2. Log transforms
  3. Power transformations (gamma)
  4. Piecewise linear transform functions

Spatial domain processes —
Spatial domain processes can be described using the equation: where is an input image, T is an operator on f defined around the point (x, y) , and this is the way out.

Image negatives —

Negative image is discussed in this article . Mathematically, let`s say the image goes from intensity levels from 0 to (L-1). Usually L = 256. Then negative conversion can be described by the expression s = L-1-r, where r — initial intensity level, and s — the final level of pixel intensity. This produces a photographic negative.

Log transforms —

Mathematically, log transforms can be expressed as s = clog (1 + r) . Here s — output intensity, r & gt; = 0 — input pixel intensity while c — constant scaling. c is given as 255 / (log (1 + m)) , where m — the maximum pixel value in the image. This is done so that the final pixel value does not exceed (L-1) or 255. In practice, the log transform maps a narrow range of low intensity inputs to a wide range of outputs.

Consider the following input image.

Below is the code to apply a log transform to an image.

import cv2

import numpy as np

 
# Open the image.

img = cv2.imread ( ` sample.jpg` )

  
# Apply log transform.

c = 255 / (np. log ( 1 + np. max (img)))

log_transformed = c * np.log ( 1 + img)

 
# Specify the data type.

log_transformed = np.array (log_transformed, dtype = np.uint8)

 
# Save output.

cv2.imwrite ( `log_transformed.jpg` , log_transformed)

Below is the log-transformed output. 

Power Law Conversion (Gamma) —

Power Law transforms (gamma) can be mathematically expressed as , Gamma correction is important for the correct display of images on the screen, to prevent discoloration or darkening of images when viewed from different types of monitors with different display settings. This is because our eyes perceive images as a gamma curve, whereas cameras capture images in a linear fashion. Below is the Python code to apply gamma correction.

import cv2

import numpy as np

  
# Open the image.

img = cv2.imread ( `sample.jpg` )

 
# Let`s try 4 gamma values.

for gamma in [ 0.1 , 0.5 , 1.2 , 2.2 ]:

  

  # Apply gamma correction.

gamma_corrected = np.array ( 255 * (img / 255 ) * * gamma, dtype = ` uint8` )

 

# Save edited images.

cv2. imwrite ( `gamma_transformed` + str (gamma) + `.jpg` , gamma_corrected)

Below are the gamma corrected outputs for different gamma values .

Gamma = 0,1:

Gamma = 0.5:

Gamma = 1,2:

Gamma = 2, 2:

As you can see from the output, as well as the graphics, gamma & gt; 1 (indicated by the curve corresponding to the mark "n-th degree" on the graph), the intensity of the pixels decreases, i. E. the image becomes darker. On the other hand, gamma & lt; 1 (indicated by the curve corresponding to the "nth root" label on the graph), the intensity increases, i. E. the image becomes lighter.

Piecewise linear transformation functions —

These functions, as the name suggests, are not completely linear in nature. However, they are linear between certain x-intervals. One of the most commonly used piecewise linear transform functions is contrast stretch.

Contrast can be defined as:

  Contrast = (I_max - I_min) / (I_max + I_min) 

This process expands the range of intensity levels in the image to cover the full intensity of the camera / display. The figure below shows a graph corresponding to contrast stretching. 

Using (r1, s1), (r2, s2) as parameters, the function stretches intensity levels, significantly reducing the intensity of dark pixels and increasing the intensity of light pixels. If r1 = s1 = 0 and r2 = s2 = L-1 , the function becomes a straight dashed line on the graph (which has no effect). The function is incremented monotonically so that the order of the intensity levels between pixels is preserved.

Below is the Python code for performing contrast stretching.

import cv2

import numpy as np

 
# Function to map each intensity level to the output intensity level.

def pixelVal (pix, r1, s1, r2, s2):

if ( 0 & lt; = pix and pix & lt; = r1):

return (s1 / r1) * pix

elif (r1 & lt; pix and pix & lt; = r2):

return ((s2 - s1) / (r2 - r1)) * (pix - r1) + s1

  else :

return (( 255 - s2) / ( 255 - r2)) * (pix - r2) + s2

  
# Open the image.

img = cv2.imread ( `sample.jpg` )

 
# Define parameters.

r1 = 70

s1 = 0

r2 = 140

s2 = 255

 
# Vectorize the function to apply it to every value in the Numpy array.

pixelVal_vec = np.vectorize (pixelVal)

  
# Apply contrast stretch.

contrast_stretched = pixelVal_vec (img, r1, s1, r2, s2)

 
# Save edited image.

cv2.imwrite ( `contrast_stretch.jpg` , contrast_stretched)

Output: