OpenCV: How to normalize face (remove shadow)?

Viewed 2066

Is it possible to normalise face (remove shadow) using computer vision algorithm?

Here is example image: enter image description here

Here is result of cv2.equalizeHist: enter image description here

Here is result of cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8)): enter image description here

Here is grid of images for cv2.createCLAHE with clipLimit=2.0 and tileGridSize=[1, 2, 4, 8, 16, 32]: enter image description here

Here is grid of images for cv2.createCLAHE with clipLimit=[1, 2, 4, 8, 16, 32] and tileGridSize=(8, 8): enter image description here

Here is result of gamma correction with gamma = 0.6: enter image description here

Here is grid of images for gamma correction with gamma = [0.2, 0.4, 0.6, 0.8, 1.0]: enter image description here

Here is the code to reproduce:

def method_v1(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    res = cv2.equalizeHist(img)

    img = np.hstack([img, res])

    return img


def method_v2(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # Less 'clipLimit' value less effect
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    res = clahe.apply(img)

    img = np.hstack([img, res])

    return img


def method_v3(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    gamma = 0.6
    res = np.power((img / 255.0), gamma) * 255
    res = np.clip(res, 0, 255).astype(np.uint8)

    img = np.hstack([img, res])

    return img


def create_clahe_grid_v1(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    grid_size = [1, 2, 4, 8, 16, 32]

    res_list = []
    res_list.append(img)
    for sz in grid_size:
        clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(sz, sz))
        res = clahe.apply(img)
        res_list.append(res)

    img = np.hstack(res_list)

    return img


def create_clahe_grid_v2(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    clip_limit = [1, 2, 4, 8, 16, 32]

    res_list = []
    res_list.append(img)
    for cl in clip_limit:
        clahe = cv2.createCLAHE(clipLimit=cl, tileGridSize=(8, 8))
        res = clahe.apply(img)
        res_list.append(res)

    img = np.hstack(res_list)

    return img


def create_gamma_correction_grid_v1(img):
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    res_list = []
    res_list.append(img)
    gamma_list = [0.2, 0.4, 0.6, 0.8, 1.0] # lighter
    #gamma_list = [1.2, 1.4, 1.6, 1.8, 2.0] # darker
    for gamma in gamma_list:
        res = np.power((img / 255.0), gamma) * 255
        res = np.clip(res, 0, 255).astype(np.uint8)
        res_list.append(res)

    img = np.hstack(res_list)

    return img

Gamma correction look best so far, but it obviously can't remove shadows because it's just pixelwise non-linear filter. Is there any other computer vision algorithms worth to try?

2 Answers

You might consider division normalization in Python/OpenCV

  • Read the input
  • Convert to grayscale
  • Gaussian blur
  • Division
  • Save results

Input:

enter image description here

import cv2
import numpy as np

# read the image
img = cv2.imread('face_shaded.png')

# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# blur
smooth = cv2.GaussianBlur(gray, (95,95), 0)

# divide gray by morphology image
division = cv2.divide(gray, smooth, scale=192)

# save results
cv2.imwrite('face_shaded_division.jpg',division)

# show results
cv2.imshow('smooth', smooth)  
cv2.imshow('division', division)  
cv2.waitKey(0)
cv2.destroyAllWindows()

Result:

enter image description here

Adjust the scale value (192) as you desire for overall brightness.

Depending upon your application, you might also want to subtract the mean and divide by the standard deviation of the division normalization result.

I'm not sure whether you will accept, but 10 years ago, Tan and Triggs (creator of HoG) designed a photometric method for removing any lightning effect on the face.

Unfortunately, they designed the code in Matlab. Therefore I will show the example in the matlab.

I = imread('hZLne.png');

if size(I, 3) == 3
    ı = rgb2gray(I);
end

y = tantriggs(I);

imshow(y)

The output:

enter image description here

This is most effective on totally-dark images.

enter image description here enter image description here

enter image description here enter image description here

enter image description here enter image description here

enter image description here enter image description here

enter image description here enter image description here

enter image description here enter image description here

Related