What are some methods to analyze image brightness using Python?
Asked Answered
R

6

40

I'd like some advice on performing a simple image analysis in python. I need to calculate a value for the "brightness" of an image. I know PIL is the goto library for doing something like this. There is a built-in histogram function.

What I need is a "perceived brightness" values I can decide if further adjustments to the image are necessary. So what are something of the basic techniques that will work in this situation? Should I just work with the RGB values, or will histogram give me something close enough?

One possible solution might be to combine the two, and generate average R,G,and B values using the histogram, then apply the "perceived brightness" formula.

Reconnoitre answered 16/8, 2010 at 5:9 Comment(4)
What prevents you from implementing the algorithm you linked to in Python with PIL? Are you asking about an alternative algorithm, about how to use PIL or what?Honewort
Just want to make sure I'm not missing some obvious feature/module that already does this. It seems like this type of thing should already exist.Reconnoitre
Figuring out the perceived brightness on an image is radically different than finding that of a simple color. The viewer may care more about the subject's brightness, but there are typically more background pixels than subject pixels.Amaze
Another thing is, you may want to measure the range of brightnesses in the image. Sharper differences and higher range will lead to higher perceived lightKeldon
R
84

Using the techniques mentioned in the question, I came up with a few different versions.

Each method returns a value close, but not exactly the same as the others. Also, all methods run about the same speed except for the last one, which is much slower depending on the image size.

  1. Convert image to greyscale, return average pixel brightness.

    def brightness( im_file ):
       im = Image.open(im_file).convert('L')
       stat = ImageStat.Stat(im)
       return stat.mean[0]
    
  2. Convert image to greyscale, return RMS pixel brightness.

    def brightness( im_file ):
       im = Image.open(im_file).convert('L')
       stat = ImageStat.Stat(im)
       return stat.rms[0]
    
  3. Average pixels, then transform to "perceived brightness".

    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. RMS of pixels, then transform to "perceived brightness".

    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. Calculate "perceived brightness" of pixels, then return average.

    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]
    

Update Test Results I ran a simulation against 200 images. I found that methods #2, #4 gave almost identical results. Also methods #3, #5 were also nearly identical. Method #1 closely followed #3, #5 (with a few exceptions).

Reconnoitre answered 16/8, 2010 at 23:35 Comment(5)
Just to comment on speed: In my tests, taking them just as they are, Method #2, #3, and #4 were about evenly fast (taking ~140ms to process 20 images of varying sizes), Method #1 was only slightly slower (~180ms) and Method #5 is really slow (~3505ms). To be expected, but should probably rather stick with #3 if you're choosing between #1, #3 and #5.Wolenik
For me method #3 worked super fast and accurate (didn't experiment with the others though). For return values > 120.0 I consider the image to be bright, otherwise - not. And ~ 200.0 would be super-bright, ~ 20.0 - super dark.Banka
What I needed to do additionally is to get the image band separately, since some .png images have r,g,b and others - r,g,b,a channels: band = stat.mean, r = band[0], g = band[1], b = band[2]. And don't forget the dependencies: import math, from PIL import Image, ImageStat.Banka
The author, who came up with these magical numbers actually states, that one should use (0.299, 0.587, 0.114) instead of (0.241, 0.691, 0.068), because the first ones are more accurate. Check here for reference: alienryderflex.com/hsp.htmlBanka
Regarding method 2, is it sufficient to convert to greyscale as a normalisaton process before calculating RMS? The wiki states the following "the input image is assumed to have its pixel intensities normalized in the range [0, 1]".Vallation
G
3

Given that you're just looking for an average across the whole image, and not per-pixel brightness values, averaging PIL's histogram and applying the brightness function to the output seems like the best approach for that library.

If using ImageMagick (with the PythonMagick bindings), I would suggest using the identify command with the "verbose" option set. This will provide you with a mean value for each channel, saving you the need to sum and average a histogram — you can just multiply each channel directly.

Gissing answered 16/8, 2010 at 5:42 Comment(1)
As one who tried to maintain PythonMagick and provided 0.9.1 version, I would not recommend using it in its current state. Recent changes in ImageMagick broken a lot of PythonMagick code. Package's original author came up with another ImageMagick python bindings, public.procoders.net/PythonMagickWand/docs/html/index.html, it is preferrable over current PythonMagickInflate
D
2

I think your best result would come from converting the RGB to grayscale using your favorite formula, then taking the histogram of that result. I'm not sure if the mean or the median of the histogram would be more appropriate, but on most images they are probably similar.

I'm not sure how to do the conversion to grayscale in PIL using an arbitrary formula, but I'm guessing it's possible.

Dr answered 17/8, 2010 at 2:12 Comment(0)
C
2

the code below will give you the brightness level of an image from 0-10

1- calculate the average brightness of the image after converting the image to HSV format using opencv.

2- find where this value lies in the list of brightness range.

 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())))
Categorize answered 29/9, 2019 at 12:55 Comment(0)
M
0

This can be done by converting the BGR image from cv2 to grayscale and then finding the intensity - x and y are pixel coordinates. It's been explained well in this https://docs.opencv.org/3.4/d5/d98/tutorial_mat_operations.html document.

Scalar intensity = img.at<uchar>(y, x);
Mundford answered 12/5, 2021 at 14:16 Comment(0)
L
0
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
Lathe answered 12/1, 2022 at 15:9 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Kinsman

© 2022 - 2024 — McMap. All rights reserved.