[英]Automatically adjusting brightness of image with 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,結果似乎沒問題:
但是,這不是自動的,也不會給出目標亮度。
我可以通過伽馬校正和/或直方圖均衡來實現,以獲得更自然的結果,但除了反復試驗之外,我沒有看到獲得目標亮度的簡單方法。
有沒有人成功地根據目標自動調整亮度?
卡納特建議:
bright_img = cv2.convertScaleAbs(img, alpha = 1, beta = 255 * (minimum_brightness - brightness))
結果更好,但仍然有面紗:
Yves Daoust 建議保持beta = 0
,所以我調整了alpha = minimum_brightness / 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)
結果很好:
您可以嘗試使用帶有直方圖剪裁的對比度優化來自動調整亮度。 您可以通過增加直方圖剪輯百分比 ( clip_hist_percent
) 來增加目標亮度。 這是 25% 剪裁的結果
Alpha 和 Beta 是自動計算的
阿爾法 3.072289156626506
測試版 -144.3975903614458
這是剪輯的可視化。 藍色(原始)、橙色(自動調整后)。
裁剪率為 35% 的結果
阿爾法 3.8059701492537314
測試版 -201.71641791044777
其他方法可以使用Histogram Equalization 或 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()
您需要修改對比度和亮度。
我不使用 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 范圍。 所以均值=0.6 相當於 60%。 我認為 66% 可能太亮了,但您可以根據需要更改這些值。
在這種情況下,由於您的圖像大部分是灰度的,因此我使用色彩空間 RGB 進行處理。 處理可以在其他幾種顏色空間中完成。
有一個類似的Python腳本在這里,剛剛匹配一個圖像到另一個,但在Lab色彩空間這樣做。 但是,將其更改為將一個圖像與一組均值和標准參數相匹配應該很容易。
(我的腳本可以在這里找到)
一種解決方案是調整圖像的伽馬。 在下面的代碼中,我首先將圖像飽和到范圍頂部和底部的某個百分位數,然后調整伽馬校正,直到達到所需的亮度。
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)
結果在這里並且不如接受的答案:
根據圖像,我在接受的答案中使用 alpha-beta 調整,或者我包括 gamma 以避免剪裁太多亮點。 每個步驟的大小,裁剪的百分位數和校正的伽瑪,決定了每次調整的權重。
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
如果你像這樣嘗試怎么辦:
bright_img = cv2.convertScaleAbs(img, alpha = 1, beta = 255 * (minimum_brightness - brightness))
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.