使用OpenCV自动调整图像的亮度

新手上路,请多包涵

我想在 OpenCV 中将图像的亮度调整到某个值。例如,考虑这张图片:

原图

我用以下方法计算亮度:

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

我得到的平均亮度为 35%。例如,要将其提高到 66%,我会这样做:

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

我得到一张似乎有 50% 透明度面纱的图像:

具有偏差和对比度的图像

我可以通过仅使用偏差来避免这种影响:

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

而且图像似乎也有面纱:

仅使用偏差调整图像

如果我手动完成,例如在 Photoshop 中将亮度调整为 150,结果似乎还不错:

使用 Photoshop 调整的图像

但是,这不是自动的,也不会给出目标亮度。

我可以通过伽玛校正和/或直方图均衡来实现,以获得更自然的结果,但除了反复试验之外,我没有看到获得目标亮度的简单方法。

有没有人成功地将亮度自动调整到目标?

更新

卡纳特建议:

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

结果更好但仍然有面纱:

由 Kanat 建议调整的图片

Yves Daoust 建议保留 beta = 0 ,所以我调整了 alpha = minimum_brightness / brightness 以获得目标亮度:

 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)

结果很好:

由 Yves Daoust 建议调整的图像

原文由 miguelmorin 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 2.3k
2 个回答

您可以尝试使用带有直方图裁剪的对比度优化来自动调整亮度。您可以通过增加直方图剪辑百分比 ( clip_hist_percent ) 来增加目标亮度。这是 25% 裁剪的结果

在此处输入图像描述在此处输入图像描述

自动计算 Alpha 和 Beta

阿尔法 3.072289156626506

测试版 -144.3975903614458

这是剪辑的可视化。蓝色(原始),橙色(自动调整后)。

剪裁为 35% 的结果

在此处输入图像描述在此处输入图像描述

阿尔法 3.8059701492537314

测试版 -201.71641791044777

其他方法可以使用 直方图均衡或 CLAHE

 import cv2
import numpy as np
# from matplotlib import pyplot as plt

# Automatic brightness and contrast optimization with optional histogram clipping
def automatic_brightness_and_contrast(image, clip_hist_percent=25):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Calculate grayscale histogram
    hist = cv2.calcHist([gray],[0],None,[256],[0,256])
    hist_size = len(hist)

    # Calculate cumulative distribution from the histogram
    accumulator = []
    accumulator.append(float(hist[0]))
    for index in range(1, hist_size):
        accumulator.append(accumulator[index -1] + float(hist[index]))

    # Locate points to clip
    maximum = accumulator[-1]
    clip_hist_percent *= (maximum/100.0)
    clip_hist_percent /= 2.0

    # Locate left cut
    minimum_gray = 0
    while accumulator[minimum_gray] < clip_hist_percent:
        minimum_gray += 1

    # Locate right cut
    maximum_gray = hist_size -1
    while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
        maximum_gray -= 1

    # Calculate alpha and beta values
    alpha = 255 / (maximum_gray - minimum_gray)
    beta = -minimum_gray * alpha

    '''
    # Calculate new histogram with desired range and show histogram
    new_hist = cv2.calcHist([gray],[0],None,[256],[minimum_gray,maximum_gray])
    plt.plot(hist)
    plt.plot(new_hist)
    plt.xlim([0,256])
    plt.show()
    '''

    auto_result = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
    return (auto_result, alpha, beta)

image = cv2.imread('1.png')
auto_result, alpha, beta = automatic_brightness_and_contrast(image)
print('alpha', alpha)
print('beta', beta)
cv2.imshow('auto_result', auto_result)
cv2.imwrite('auto_result.png', auto_result)
cv2.imshow('image', image)
cv2.waitKey()

另一种版本是使用饱和度算法而不是使用 OpenCV 的 cv2.convertScaleAbs 向图像添加偏差和增益。内置方法不采用绝对值,这会导致无意义的结果(例如,使用 alpha = 3 和 beta = -210 的 44 像素在 OpenCV 中变为 78,而实际上它应该变为 0)。

 import cv2
import numpy as np
# from matplotlib import pyplot as plt

def convertScale(img, alpha, beta):
    """Add bias and gain to an image with saturation arithmetics. Unlike
    cv2.convertScaleAbs, it does not take an absolute value, which would lead to
    nonsensical results (e.g., a pixel at 44 with alpha = 3 and beta = -210
    becomes 78 with OpenCV, when in fact it should become 0).
    """

    new_img = img * alpha + beta
    new_img[new_img < 0] = 0
    new_img[new_img > 255] = 255
    return new_img.astype(np.uint8)

# Automatic brightness and contrast optimization with optional histogram clipping
def automatic_brightness_and_contrast(image, clip_hist_percent=25):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Calculate grayscale histogram
    hist = cv2.calcHist([gray],[0],None,[256],[0,256])
    hist_size = len(hist)

    # Calculate cumulative distribution from the histogram
    accumulator = []
    accumulator.append(float(hist[0]))
    for index in range(1, hist_size):
        accumulator.append(accumulator[index -1] + float(hist[index]))

    # Locate points to clip
    maximum = accumulator[-1]
    clip_hist_percent *= (maximum/100.0)
    clip_hist_percent /= 2.0

    # Locate left cut
    minimum_gray = 0
    while accumulator[minimum_gray] < clip_hist_percent:
        minimum_gray += 1

    # Locate right cut
    maximum_gray = hist_size -1
    while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
        maximum_gray -= 1

    # Calculate alpha and beta values
    alpha = 255 / (maximum_gray - minimum_gray)
    beta = -minimum_gray * alpha

    '''
    # Calculate new histogram with desired range and show histogram
    new_hist = cv2.calcHist([gray],[0],None,[256],[minimum_gray,maximum_gray])
    plt.plot(hist)
    plt.plot(new_hist)
    plt.xlim([0,256])
    plt.show()
    '''

    auto_result = convertScale(image, alpha=alpha, beta=beta)
    return (auto_result, alpha, beta)

image = cv2.imread('1.jpg')
auto_result, alpha, beta = automatic_brightness_and_contrast(image)
print('alpha', alpha)
print('beta', beta)
cv2.imshow('auto_result', auto_result)
cv2.imwrite('auto_result.png', auto_result)
cv2.imshow('image', image)
cv2.waitKey()

原文由 nathancy 发布,翻译遵循 CC BY-SA 4.0 许可协议

您需要修改对比度和亮度。

我不使用 OpenCV,但这是我为 Imagemagick 构建的 (Unix) bash 脚本的解决方案。 请注意,mean 控制亮度,std 控制对比度。

该脚本最初旨在调整一幅图像以匹配另一幅图像的颜色/亮度/对比度。匹配根据等式使用每个图像的平均值和标准偏差:(I2-Mean2)/Std2 = (I1-Mean1)/Std1。该等式表示归一化强度,因此由于除以标准差,它具有零均值和大致相同的值范围。我们根据 I2 = A x I1 + B 求解此方程,形成 I1 和 I2 之间的线性变换,其中 A=(Std2/Std1) 是斜率或增益,B=(Mean2 - A x Mean1) 是截距偏见。如果没有提供第二张图像,但提供了(一组)均值和标准差,则第一个文件将与提供的均值和标准差匹配。 斜率或增益与对比度相关,截距或偏差与亮度相关。

输入:

在此处输入图像描述

 matchimage -c rgb -m 0.6 -s 0.25 bunny.png result1.png

在此处输入图像描述

或者稍微对比一下:

 matchimage -c rgb -m 0.6 -s 0.35 bunny.png result2.png

在此处输入图像描述

参数标准化为 0 到 1 范围。所以 mean=0.6 相当于 60%。我认为 66% 可能太亮了,但您可以根据需要更改值。

在这种情况下,由于您的图像主要是灰度图像,因此我使用色彩空间 RGB 进行处理。可以在其他几个色彩空间中进行处理。

这里 有一个类似的 Python 脚本,它只是将一个图像与另一个图像匹配,但在 LAB 色彩空间中进行。但是,更改它以将一个图像与一组均值和标准参数相匹配应该很容易。

(我的脚本在 这里 可用)

原文由 fmw42 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题