web-dev-qa-db-fra.com

Ajuster automatiquement la luminosité de l'image avec OpenCV

Je veux régler la luminosité d'une image à une certaine valeur dans OpenCV. Par exemple, considérez cette image:

original image

Je calcule la luminosité avec:

import cv2
img = cv2.imread(filepath)
cols, rows = img.shape
brightness = numpy.sum(img) / (255 * cols * rows)

et j'obtiens une luminosité moyenne de 35%. Pour le porter à 66%, par exemple, je fais:

minimum_brightness = 0.66
alpha = brightness / minimum_brightness
bright_img = cv2.convertScaleAbs(img, alpha = alpha, beta = 255 * (1 - alpha))

et j'obtiens une image qui semble avoir un voile de transparence à 50%:

Image with bias and contrast

Je peux éviter cet effet en utilisant uniquement le biais:

bright_img = cv2.convertScaleAbs(img, alpha = 1, beta = 128)

et l'image semble aussi avoir un voile:

Image adjusted with bias only

Si je le fais à la main, par exemple dans Photoshop avec un réglage de la luminosité à 150, le résultat semble correct:

Image adjusted with Photoshop

Mais ce n'est pas automatique et ne donne pas la luminosité cible.

Je pourrais le faire avec une correction gamma et/ou une égalisation d'histogramme pour un résultat peut-être plus naturel, mais je ne vois pas de moyen facile d'obtenir la luminosité cible autre que des essais et erreurs.

Quelqu'un a-t-il réussi à régler automatiquement la luminosité sur une cible?

Mettre à jour

Kanat a suggéré:

bright_img = cv2.convertScaleAbs(img, alpha = 1, beta = 255 * (minimum_brightness - brightness))

et le résultat est meilleur mais a toujours un voile:

Image with adjustment suggested by Kanat

Yves Daoust a suggéré de garder beta = 0, j'ai donc ajusté alpha = minimum_brightness / brightness pour obtenir la luminosité cible:

ratio = brightness / minimum_brightness
if ratio >= 1:
    print("Image already bright enough")
    return img

# Otherwise, adjust brightness to get the target brightness
return cv2.convertScaleAbs(img, alpha = 1 / ratio, beta = 0)

et le résultat est bon:

Image with adjustment suggested by Yves Daoust

6
miguelmorin

Une solution consiste à ajuster le gamma de l'image. Dans le code ci-dessous, je sature d'abord l'image à un certain centile en haut et en bas de la plage, puis ajuste la correction gamma jusqu'à atteindre la luminosité requise.

import cv2
import numpy as np

def saturate(img, percentile):
    """Changes the scale of the image so that half of percentile at the low range
    becomes 0, half of percentile at the top range becomes 255.
    """

    if 2 != len(img.shape):
        raise ValueError("Expected an image with only one channel")

    # copy values
    channel = img[:, :].copy()
    flat = channel.ravel()

    # copy values and sort them
    sorted_values = np.sort(flat)

    # find points to clip
    max_index = len(sorted_values) - 1
    half_percent = percentile / 200
    low_value = sorted_values[math.floor(max_index * half_percent)]
    high_value = sorted_values[math.ceil(max_index * (1 - half_percent))]

    # saturate
    channel[channel < low_value] = low_value
    channel[channel > high_value] = high_value

    # scale the channel
    channel_norm = channel.copy()
    cv2.normalize(channel, channel_norm, 0, 255, cv2.NORM_MINMAX)

    return channel_norm

def adjust_gamma(img, gamma):
    """Build a lookup table mapping the pixel values [0, 255] to
    their adjusted gamma values.
    """

    # code from
    # https://www.pyimagesearch.com/2015/10/05/opencv-gamma-correction/

    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255 for i in np.arange(0, 256)]).astype("uint8")

    # apply gamma correction using the lookup table
    return cv2.LUT(img, table)


def adjust_brightness_with_gamma(gray_img, minimum_brightness, gamma_step = GAMMA_STEP):

    """Adjusts the brightness of an image by saturating the bottom and top
    percentiles, and changing the gamma until reaching the required brightness.
    """
    if 3 <= len(gray_img.shape):
        raise ValueError("Expected a grayscale image, color channels found")

    cols, rows = gray_img.shape
    changed = False
    old_brightness = np.sum(gray_img) / (255 * cols * rows)
    new_img = gray_img
    gamma = 1

    while True:
        brightness = np.sum(new_img) / (255 * cols * rows)
        if brightness >= minimum_brightness:
            break

        gamma += gamma_step
        new_img = adjust_gamma(gray_img, gamma = gamma)
        changed = True

    if changed:
        print("Old brightness: %3.3f, new brightness: %3.3f " %(old_brightness, brightness))
    else:
        print("Maintaining brightness at %3.3f" % old_brightness)

    return new_img

def main(filepath):

    img = cv2.imread(filepath)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    saturated = saturate(gray, 1)
    bright = adjust_brightness_with_gamma(saturated, minimum_brightness = 0.66)

Le résultat est ici et inférieur à la réponse acceptée:

Image brightened with gamma

Selon l'image, j'utilise soit l'ajustement alpha-bêta dans la réponse acceptée, soit j'inclus le gamma pour éviter d'écraser trop de hautes lumières. La taille de chaque pas, le centile pour l'écrêtage et le gamma pour la correction, détermine le poids de chaque ajustement.

PERCENTILE_STEP = 1
GAMMA_STEP = 0.01

def adjust_brightness_alpha_beta_gamma(gray_img, minimum_brightness, percentile_step = PERCENTILE_STEP, gamma_step = GAMMA_STEP):
    """Adjusts brightness with histogram clipping by trial and error.
    """

    if 3 <= len(gray_img.shape):
        raise ValueError("Expected a grayscale image, color channels found")

    new_img = gray_img
    percentile = percentile_step
    gamma = 1
    brightness_changed = False

    while True:
        cols, rows = new_img.shape
        brightness = np.sum(new_img) / (255 * cols * rows)

        if not brightness_changed:
            old_brightness = brightness

        if brightness >= minimum_brightness:
            break

        # adjust alpha and beta
        percentile += percentile_step
        alpha, beta = percentile_to_bias_and_gain(new_img, percentile)
        new_img = convertScale(gray_img, alpha = alpha, beta = beta)
        brightness_changed = True

        # adjust gamma
        gamma += gamma_step
        new_img = adjust_gamma(new_img, gamma = gamma)

    if brightness_changed:
        print("Old brightness: %3.3f, new brightness: %3.3f " %(old_brightness, brightness))
    else:
        print("Maintaining brightness at %3.3f" % old_brightness)

    return new_img
0
miguelmorin