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

Я хотел бы дать несколько советов по выполнению простого анализа изображений в Python. Мне нужно рассчитать значение для "яркости" изображения. Я знаю, что PIL - это библиотека goto, которая делает что-то подобное. Есть встроенная функция гистограммы.

Что мне нужно, так это значения "воспринимаемой яркости", которые я могу решить, если необходимы дальнейшие корректировки изображения. Итак, каковы основные методы, которые будут работать в этой ситуации? Должен ли я просто работать со значениями RGB, или гистограмма даст мне что-то достаточно близкое?

Одним из возможных решений может быть объединение этих двух значений и генерирование средних значений R,G и B с использованием гистограммы, а затем применение формулы "воспринимаемой яркости".

6 ответов

Решение

Используя методы, упомянутые в вопросе, я придумал несколько разных версий.

Каждый метод возвращает значение close, но не точно так же, как другие. Кроме того, все методы работают с одинаковой скоростью, кроме последней, которая значительно медленнее в зависимости от размера изображения.

  1. Преобразовать изображение в оттенки серого, вернуть среднюю яркость пикселей.

    def brightness( im_file ):
       im = Image.open(im_file).convert('L')
       stat = ImageStat.Stat(im)
       return stat.mean[0]
    
  2. Преобразовать изображение в оттенки серого, вернуть RMS яркость пикселей.

    def brightness( im_file ):
       im = Image.open(im_file).convert('L')
       stat = ImageStat.Stat(im)
       return stat.rms[0]
    
  3. Среднее количество пикселей, а затем преобразовать в "воспринимаемую яркость".

    def brightness( im_file ):
       im = Image.open(im_file)
       stat = ImageStat.Stat(im)
       r,g,b = stat.mean
       return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))
    
  4. Среднеквадратичное значение пикселей, а затем преобразование в "воспринимаемую яркость".

    def brightness( im_file ):
       im = Image.open(im_file)
       stat = ImageStat.Stat(im)
       r,g,b = stat.rms
       return math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2))
    
  5. Рассчитайте "воспринимаемую яркость" пикселей, затем верните среднее значение.

    def brightness( im_file ):
       im = Image.open(im_file)
       stat = ImageStat.Stat(im)
       gs = (math.sqrt(0.241*(r**2) + 0.691*(g**2) + 0.068*(b**2)) 
             for r,g,b in im.getdata())
       return sum(gs)/stat.count[0]
    

Обновить результаты теста Я запустил симуляцию на 200 изображениях. Я обнаружил, что методы № 2, № 4 дали почти идентичные результаты. Также методы № 3, № 5 были также почти идентичны. Метод № 1 внимательно следовал № 3, № 5 (за некоторыми исключениями).

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

При использовании ImageMagick (с привязками PythonMagick) я бы предложил использовать команду identifier с установленным параметром "verbose". Это даст вам среднее значение для каждого канала, избавляя вас от необходимости суммировать и усреднять гистограмму - вы можете просто умножить каждый канал напрямую.

Приведенный ниже код даст вам уровень яркости изображения от 0 до 10.

1 вычислить среднюю яркость изображения после преобразования изображения в формат HSV с помощью opencv.

2 найдите, где находится это значение в списке диапазонов яркости.

 import numpy as np
 import cv2
 import sys
 from collections import namedtuple

#brange brightness range
#bval brightness value
BLevel = namedtuple("BLevel", ['brange', 'bval'])

#all possible levels
_blevels = [
    BLevel(brange=range(0, 24), bval=0),
    BLevel(brange=range(23, 47), bval=1),
    BLevel(brange=range(46, 70), bval=2),
    BLevel(brange=range(69, 93), bval=3),
    BLevel(brange=range(92, 116), bval=4),
    BLevel(brange=range(115, 140), bval=5),
    BLevel(brange=range(139, 163), bval=6),
    BLevel(brange=range(162, 186), bval=7),
    BLevel(brange=range(185, 209), bval=8),
    BLevel(brange=range(208, 232), bval=9),
    BLevel(brange=range(231, 256), bval=10),
]


def detect_level(h_val):
     h_val = int(h_val)
     for blevel in _blevels:
        if h_val in blevel.brange:
            return blevel.bval
    raise ValueError("Brightness Level Out of Range")


 def get_img_avg_brightness():
     if len(sys.argv) < 2:
        print("USAGE: python3.7 brightness.py <image_path>")
        sys.exit(1)
     img = cv2.imread(sys.argv[1])
     hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
     _, _, v = cv2.split(hsv)

     return int(np.average(v.flatten()))

 if __name__ == '__main__':

     print("the image brightness level is: 
            {0}".format(detect_level(get_img_avg_brightness())))

Я думаю, что ваш лучший результат будет от преобразования RGB в оттенки серого, используя вашу любимую формулу, а затем взяв гистограмму этого результата. Я не уверен, будет ли более подходящим среднее значение или медиана гистограммы, но на большинстве изображений они, вероятно, похожи.

Я не уверен, как сделать преобразование в градациях серого в PIL, используя произвольную формулу, но я предполагаю, что это возможно.

Это можно сделать, преобразовав изображение BGR из cv2 в оттенки серого, а затем определив интенсивность - x и y - координаты пикселей. Это хорошо объяснено в этом https://docs.opencv.org/3.4/d5/d98/tutorial_mat_operations.html документе.

      Scalar intensity = img.at<uchar>(y, x);
      def calculate_brightness(image):

    greyscale_image = image.convert('L')
    histogram = greyscale_image.histogram()
    pixels = sum(histogram)
    brightness = scale = len(histogram)

    for index in range(0, scale):

        ratio = histogram[index] / pixels
        brightness += ratio * (-scale + index)
    return 1 if brightness == 255 else brightness / scale
Другие вопросы по тегам