Как быстро изменить яркость изображения с помощью Python + OpenCV?

У меня есть последовательность изображений. Мне нужно усреднить яркость этих изображений.

Первый пример (очень медленный):

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 ответов

Решение

Slice выбрать только третий канал, а затем изменить эти элементы -

hsv[:,:,2] += value

Я знаю, что этот вопрос немного устарел, но я подумал, что мог бы опубликовать полное решение, которое сработало для меня (заботится о ситуации переполнения путем насыщения на 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)

В других ответах предлагается выполнять насыщение "вручную", используя всевозможную магию, но вы также можете использовать 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)

Это было моим решением как для увеличения, так и для уменьшения яркости. Возникли проблемы с ошибками с парой других ответов. Функция принимает положительное или отрицательное значение и изменяет яркость.

пример в коде

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

Итерации по всему изображению для внесения изменений - не очень масштабируемая опция в 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)
def change_brightness(img, alpha, beta):
   return cv2.addWeighted(img, alpha, np.zeros(img.shape, img.dtype),0, beta)

Здесь альфа и бета - входные параметры. Каждый пиксель входного изображения будет меняться в соответствии с этой формулой.

 alpha(pixel_value) + beta.

Хорошее значение альфа, например, 2 или 3

import cv2
import numpy as np

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

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()

Самый быстрый?

Для чистой скорости просто добавьте положительное или отрицательное целое число к исходному изображению 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)

Как фотошоп

Для функции яркости, больше похожей на 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

Сроки

Я рассчитал время для 1000 итераций запуска каждой функции. И что удивительно, времена почти идентичны

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

Надеюсь, это кому-нибудь пригодится

@Divakar answer 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()

Может быть, слишком стар, но я использую cv.covertTo, который работает для меня

Mat resultBrightImage;    
origImage.convertTo(resultBrightImage, -1, 1, percent); // Where percent = (int)(percent_val/100)*255, e.g., percent = 50 to increase brightness by 50%

convertTo использует saturate_cast в конце, чтобы избежать переполнения. Я не использую Python, а вышеприведенное написано в C++, но я надеюсь, что он легко конвертируется в Python, и надеюсь, что это поможет

Вы можете использовать эту функцию, чтобы изменить желаемую яркость или контрастность, используя C++, точно так же, как вы делаете это в фотошопе или другом подобном программном обеспечении для редактирования фотографий.

def apply_brightness_contrast(input_img, brightness = 255, contrast = 127):
    brightness = map(brightness, 0, 510, -255, 255)
    contrast = map(contrast, 0, 254, -127, 127)

    if brightness != 0:
        if brightness > 0:
            shadow = brightness
            highlight = 255
        else:
            shadow = 0
            highlight = 255 + brightness
        alpha_b = (highlight - shadow)/255
        gamma_b = shadow

        buf = cv2.addWeighted(input_img, alpha_b, input_img, 0, gamma_b)
    else:
        buf = input_img.copy()

    if contrast != 0:
        f = float(131 * (contrast + 127)) / (127 * (131 - contrast))
        alpha_c = f
        gamma_c = 127*(1-f)

        buf = cv2.addWeighted(buf, alpha_c, buf, 0, gamma_c)

    cv2.putText(buf,'B:{},C:{}'.format(brightness,contrast),(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
    return buf

def map(x, in_min, in_max, out_min, out_max):
    return int((x-in_min) * (out_max-out_min) / (in_max-in_min) + out_min)

После этого вам нужно вызвать функции, создав трекбар с помощью cv2.createTrackbar() и вызовите эти функции с соответствующими параметрами. Чтобы отобразить значения яркости в диапазоне от -255 до +255 и значения контрастности от -127 до +127, вы можете использовать это map() функция. Вы можете проверить полную информацию о реализации Python здесь.

Изображение 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)

Я знаю, что это не должно быть так сложно и там настроить яркость изображения. Кроме того, уже есть много хороших ответов. Я хотел бы улучшить ответ @BillGrates, чтобы он работал на изображениях в оттенках серого и с уменьшением яркости: value = -255 создает черное изображение, тогда как value = 255 белый.

def adjust_brightness(img, value):
    num_channels = 1 if len(img.shape) < 3 else 1 if img.shape[-1] == 1 else 3
    img = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) if num_channels == 1 else img
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)

    if value >= 0:
        lim = 255 - value
        v[v > lim] = 255
        v[v <= lim] += value
    else:
        value = int(-value)
        lim = 0 + value
        v[v < lim] = 0
        v[v >= lim] -= value

    final_hsv = cv2.merge((h, s, v))

    img = cv2.cvtColor(final_hsv, cv2.COLOR_HSV2BGR)
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) if num_channels == 1 else img
    return img

Каналы HSV имеют тип uint8, диапазон значений оттенка [0, 179]. Поэтому при добавлении с большим числом или отрицательным числом Python возвращает мусор. Итак, в канале оттенка нам нужно изменить тип на int16, а затем обратно на тип uint8. На каналах насыщения (S) и значения (V) возникает та же проблема, поэтому нам нужно проверить значение перед добавлением или вычитанием.

Вот мое решение для случайного изменения оттенка, насыщенности и значения. Он основан на примерах кода @alkasm и @bill- grates.

def shift_channel(c, amount):
   if amount > 0:
        lim = 255 - amount
        c[c >= lim] = 255
        c[c < lim] += amount
    elif amount < 0:
        amount = -amount
        lim = amount
        c[c <= lim] = 0
        c[c > lim] -= amount
    return c

rand_h, rand_s, rand_v = 50, 50, 50
img_hsv = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(img_hsv)
# Random shift hue
shift_h = random.randint(-rand_h, rand_h)
h = ((h.astype('int16') + shift_h) % 180).astype('uint8')
# Random shift saturation
shift_s = random.randint(-rand_s, rand_s)
s = shift_channel(s, shift_s)
# Random shift value
shift_v = random.randint(-rand_v, rand_v)
v = shift_channel(v, shift_v)
shift_hsv = cv2.merge([h, s, v])
print(shift_h, shift_s, shift_v)
img_rgb = cv2.cvtColor(shift_hsv, cv2.COLOR_HSV2RGB)
Другие вопросы по тегам