Basic Colour Adjustments

Preparing

First of all we should prepare Python infrastructure:

  • numpy for array computing.
  • cv2 for reading images.
  • matplotlib.pyplot for plotting results.
  • histograms it's my own code, you can find it here.
import numpy as np
import cv2
import math

from IPython.display import IFrame

%matplotlib inline
import matplotlib.pyplot as plt

from histograms import *

plt.style.use('seaborn-dark')
                

Then we read foreground and background images. As cv2.IMREAD_UNCHANGED mode is used for reading PNG as is with its transparency. Also read image is converted here to float within a range of values [0; 1].

Then channels R and B must be swaped because OpenCV storage images in BGR mode:

img = cv2.imread("photo1.jpg", cv2.IMREAD_UNCHANGED).astype(np.float) / 255.0
img = img[...,[2, 1, 0]]
                

Channel value offset

Increses or decreses each channel on a specified value. The result must be clamped for the correct working of the adjustment. You should understand, that this adjustment isn't reversible because part of information will be lost after clamping.

img_offset = img.copy()
img_offset[...,:] += [0.3, 0.0, -0.3]
img_offset = np.clip(img_offset, 0.0, 1.0)
                

Result image:

Result image's histogram:

Gamma correction

Moves point of mid-tones of a channel preserving white and black points. Note, that it works only with values in the range [0; 1]

img_gamma = img.copy()
img_gamma[...,:] = np.power(img_gamma[...,:], np.exp([1.0, 0.0, -1.0]))
img_gamma = np.clip(img_gamma, 0.0, 1.0)
                

Result image:

Result image's histogram:

Channel value multiply

Works like offset but instead of sum here is used multiplication.

img_mul = img.copy()
img_mul[...,:] *= [1.5, 1.0, 0.5]
img_mul = np.clip(img_mul, 0.0, 1.0)
                

Result image:

Result image's histogram:

Normalization

Moves point of maximal and/or minimum value of a channel. These points can be moved both inside and outside source box therefore here can be cases when adjustments will be not reversible. The adjustments implement linear operations with the histogram.

This adjustment can be used for increasing or decreasing the contrast of the image, offset (with an identical offset for black and white points) or inversing (with swapping black and white points).

min_val = np.array([0.3, 0.0, 0.2])
max_val = np.array([0.7, 0.9, 0.8])
img_norm = img.copy()
img_norm[...,:] = (img_norm[...,:] - min_val) / (max_val - min_val)
img_norm = np.clip(img_norm, 0.0, 1.0)
                

Result image:

Result image's histogram:

Contrast

Of course, you can implement contrast adjustment using the previous operation. But here is some trick:

coeffs = np.tan(np.pi * 0.25 * (1.0 + np.array([-0.5, 0.0, 0.5])))
img_contrast = img.copy()
img_contrast[...,:] = (img_contrast[...,:] - 0.5) * coeffs + 0.5
img_contrast = np.clip(img_contrast, 0.0, 1.0)
                

Result image:

Result image's histogram: