如何使用Python + OpenCV快速更改图像亮度?

36
我有一系列图像。我需要计算这些图像的平均亮度。 第一个例子(非常慢):
img = cv2.imread('test.jpg') #load rgb image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #convert it to hsv

for x in range(0, len(hsv)):
    for y in range(0, len(hsv[0])):
        hsv[x, y][2] += value

img = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
cv2.imwrite("image_processed.jpg", img)

第二个例子(快速地)

hsv += value

这个例子非常快,但它改变了所有HSV值(我只需要更改V(亮度))

14个回答

78

我知道这个问题有些旧了,但我想发表一下对我有效的完整解决方案(通过在255处饱和处理来解决溢出情况):

def increase_brightness(img, value=30):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)

    lim = 255 - value
    v[v > lim] = 255
    v[v <= lim] += value

    final_hsv = cv2.merge((h, s, v))
    img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
    return img

可以按如下方式使用:

frame = increase_brightness(frame, value=20)

8
清晰易懂、有效的。这应该是被接受的答案。 - Eduardo Pignatelli
我遇到了这个错误:error: (-215:Assertion failed) mv[i].size == mv[0].size && mv[i].depth() == depth in function 'merge',有人能帮忙吗? - Lorenzo Sciuto
谢谢,这对我今天的需要完美无缺! - curiouscode
你为什么在下标中使用 v > lim?那不是返回一个布尔值吗? - palapapa

29

其他答案建议使用各种numpy魔法手动执行饱和度,但您也可以使用cv2.add(),让OpenCV为您处理:

import cv2
import numpy as np

image = cv2.read('image.png')
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
value = 42 #whatever value you want to add
cv2.add(hsv[:,:,2], value, hsv[:,:,2])
image = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
cv2.imwrite('out.png', image)

5
迄今为止最好的答案。我读了其他答案,想知道为什么没人采纳你的建议。可悲的是,这个答案在底部。被接受的答案容易受到8位溢出的影响,而有关cv2.add的有用评论不会根据颜色均匀地使图像变亮。这个答案做得很好。 - Jesse Pangburn
6
我尝试了这段代码,但出现了TypeError: Expected Ptrcv::UMat for argument 'dst' at cv2.add()的错误提示。 - Monk247uk

23

Slice选择第三个通道,然后修改这些元素-

hsv[:,:,2] += value

2
不起作用。在增加亮度后输出一些奇怪的图像。 - Gaurav Raj
@GauravRaj 你可能会遇到溢出的问题。如果是这样,你可能需要使用剪辑:numpy.clip。另外,提醒一下,这是在HSV方面工作。因此,你需要进行RGB到HSV和反向转换。 - Divakar
1
@GauravRaj 这个链接应该会有所帮助。如果要设置限制值,您可能需要阅读一下HSV Wiki。 - Divakar
8
您好,我只是使用OpenCV方法处理了BGR图像 - cv2.add(inputImage, np.array([50.0])) 将图像亮度增加了50.0。 - Gaurav Raj
@Santhosh hsv[:,:,2] -= value - Divakar
显示剩余6条评论

12

这是我解决增加和减少亮度的方案。其他答案中有一些错误问题。该函数接受正数或负数,并调整亮度。

代码示例:

img = cv2.imread(path_to_image)
img = change_brightness(img, value=30) #increases
img = change_brightness(img, value=-30) #decreases

函数被调用

def change_brightness(img, value=30):
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)
    v = cv2.add(v,value)
    v[v > 255] = 255
    v[v < 0] = 0
    final_hsv = cv2.merge((h, s, v))
    img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
return img

9

在opencv中,遍历整张图像进行更改并不是一个非常可扩展的选择。Opencv提供了许多方法和函数来对给定的图像执行算术操作。

您可以简单地将转换后的HSV图像拆分为各个通道,然后相应地处理V通道:

img = cv2.imread('test.jpg') #load rgb image
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #convert it to hsv

h, s, v = cv2.split(hsv)
v += 255
final_hsv = cv2.merge((h, s, v))

img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
cv2.imwrite("image_processed.jpg", img)

4
def change_brightness(img, alpha, beta):
   return cv2.addWeighted(img, alpha, np.zeros(img.shape, img.dtype),0, beta)

这里的 alpha 和 beta 是输入参数。每个输入图像像素都将根据此公式进行更改。

 alpha(pixel_value) + beta.

较小的alpha值比如2或3是不错的选择。


1
我尝试更改beta,但结果没有改变,那么它应该确切地做什么。 - Zaher88abd

2
import cv2
import numpy as np

image = cv2.imread('image.jpg')
image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)

increase = 100

v = image[:, :, 2]
v = np.where(v <= 255 - increase, v + increase, 255)
image[:, :, 2] = v

image = cv2.cvtColor(image, cv2.COLOR_HSV2BGR)

cv2.imshow('Brightness', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

什么是 increase - mLstudent33
1
@mLstudent33 这是期望亮度增加的数量。我已经编辑了我的答案以使其更清晰。 - Rendicahya

2

希望这对某人有用

@Divakar的回答Python, OpenCV: 不会溢出UINT8数组的增加图像亮度

mImage = cv2.imread('image1.jpg')

hsvImg = cv2.cvtColor(mImage,cv2.COLOR_BGR2HSV)

value = 0

vValue = hsvImg[...,2]
hsvImg[...,2] = np.where((255-vValue)<value,255,vValue+value)

plt.subplot(111), plt.imshow(cv2.cvtColor(hsvImg,cv2.COLOR_HSV2RGB))
plt.title('brightened image'), plt.xticks([]), plt.yticks([])
plt.show()

降低亮度
mImage = cv2.imread('image1.jpg')

hsvImg = cv2.cvtColor(mImage,cv2.COLOR_BGR2HSV)

# decreasing the V channel by a factor from the original
hsvImg[...,2] = hsvImg[...,2]*0.6

plt.subplot(111), plt.imshow(cv2.cvtColor(hsvImg,cv2.COLOR_HSV2RGB))
plt.title('brightened image'), plt.xticks([]), plt.yticks([])
plt.show()

2

最快的方法?

为了获得更高的处理速度,可以在原始BGR图像上添加正或负整数。但是要使用OpenCV函数来避免溢出。一个好的选择是convertScaleAbs函数。我们使用USC SIPI的参考“狒狒”图像:

import cv2

def fast_brightness(input_image, brightness):
    ''' input_image:  color or grayscale image
        brightness:  -255 (all black) to +255 (all white)

        returns image of same type as input_image but with
        brightness adjusted'''
    img = input_image.copy()
    cv2.convertScaleAbs(img, img, 1, brightness)
    return img

img = cv2.imread('mandrill.tiff',cv2.IMREAD_COLOR)

cv2.imwrite('output.jpg', fast_brightness(img, 100))

针对亮度值为100的情况,以下是相关it技术内容的翻译:

像Photoshop一样

如果需要更像Photoshop、The Gimp或其他图像处理程序的亮度函数,可以使用类似@md-hanif-ali-sohag此答案中的函数:

def photoshop_brightness(input_img, brightness = 0):
    ''' input_image:  color or grayscale image
        brightness:  -127 (all black) to +127 (all white)

            returns image of same type as input_image but with
            brightness adjusted

    '''
    img = input_img.copy()
    if brightness != 0:
        if brightness > 0:
            shadow = brightness
            highlight = 255
        else:
            shadow = 0
            highlight = 255 + brightness
        alpha_b = (highlight - shadow)/255
        gamma_b = shadow

        cv2.convertScaleAbs(input_img, img, alpha_b, gamma_b)

    return img

时间

我对每个函数运行1,000次进行了计时。令人惊讶的是,两者的时间几乎相同。

elapsed fast_brightness [sec]:       0.8595983982086182
elapsed photoshop_brightness [sec]:  0.8565976619720459

而且令人惊讶的是,时间几乎相同。我认为这并不令人惊讶。代码的“花哨”版本调用相同的图像复制,执行O(1)操作来计算适当的alpha和gamma值,然后(假设brightness!= 0)进行相同的convertScaleAbs调用以迭代像素。 - Karl Knechtel
@KarlKnechtel。观点很好。在“高级”版本中的一些额外操作在计算上非常便宜。 “快速”版本乘以1,而“高级”版本乘以其他因子。如果convertScaleAbs在极端情况下更加优化,也许“快速”版本会更快。 - bfris

1

OpenCV图像是一个numpy.uint8数据类型的数组。将任意值添加到任何通道中的问题在于容易发生溢出。例如,numpy.uint8(255) + numpy.uint8(1) = 0。为了避免这个问题,我们首先将BGR图像转换为HLS。然后我们将HLS图像(一个numpy.uint8数组)转换为numpy.int16,将亮度值添加到第二个通道中,将超过255的所有条目下推到255的亮度通道中,并将低于0的所有条目上推到0。现在,亮度通道中的所有值v都满足0 <= v <=255。此时,我们可以将其转换回numpy.uint8,然后再转换为BGR。

import cv2 as cv
import numpy as np

# Negative values for the percentage parameter are allowed
def increase_brightness(bgr_img, percentage):
    hls_img = cv.cvtColor(bgr_img, cv.COLOR_BGR2HLS)
    value = np.int16(255*percentage/100)
    hls_arr_16bit = np.int16(hls_img)
    hls_arr_16bit[:,:,1] += value

    if percentage > 0:
        hls_arr_16bit[:,:,1] = np.where(hls_arr_16bit[:,:,1] <= 255, hls_arr_16bit[:,:,1], np.int16(255))
    elif percentage < 0:
        hls_arr_16bit[:,:,1] = np.where(hls_arr_16bit[:,:,1] >= 0, hls_arr_16bit[:,:,1], np.int16(0))

    hls_img = np.uint8(hls_arr_16bit)
    brightened_bgr_img = cv.cvtColor(hls_img, cv.COLOR_HLS2BGR)
    return brightened_bgr_img

img = cv.imread('path\\to\\image.jpg')
mod_img = increase_brightness(img)
cv.imwrite('path\\to\\modified_image.jpg', mod_img)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接