Is it posible to know the brightness of a picture in Flutter?

1,040

There are several things wrong with your code.

First, you are getting a range error because you are attempting to access a pixel that doesn't exist. This is probably due to width and/or height being greater than the image's actual width or height. There are a lot of ways to try and get these values, but for this application it doesn't actually matter since the end result is to get an average value across all pixels in the image, and you don't need the width or height of the image for that.

Second, you are fetching the color values by serializing the color value into a hex string and then parsing the individual channel substrings. Your substring is going to result in incorrect values because:

  • foo.substring(a, b) takes the substring of foo from a to b, exclusive. That means that a and b are indices, not lengths, and the resulting string will not include the character at b. So assuming hex is "01234567", when you do hex.substring(0, 2), you get "01", and then you do hex.substring(3, 5) you get "34" while hex.substring(6, 8) gets you "67". You need to do hex.substring(0, 2) followed by hex.substring(2, 4) and hex.substring(4, 6) to get the first three channels.
  • That being said, you are fetching the wrong channels. The image package stores its pixel values in ABGR format, meaning the first two characters in the hex string are going to be the alpha channel which is unimportant when calculating image brightness. Instead, you want the second, third, and forth channels for the blue, green, and red values respectively.
  • And having said all that, this is an extremely inefficient way to do this anyway when the preferred way to retrieve channel data from an integer color value is with bitwise operations on the integer itself. (Never convert a number to a string or vice versa unless you absolutely have to.)

So in summary, what you want will likely be something akin to the following;

final pixels = image.data;
double colorSum = 0;

for (int i = 0; i < pixels.length; i++) {
  int pixel = pixels[i];
  int b = (pixel & 0x00FF0000) >> 16;
  int g = (pixel & 0x0000FF00) >> 8;
  int r = (pixel & 0x000000FF);
  avg = (r + g + b) / 3;
  colorSum += avg;
}

return colorSum / pixels.length;
Share:
1,040
Nicolas Lucero
Author by

Nicolas Lucero

Updated on December 24, 2022

Comments

  • Nicolas Lucero
    Nicolas Lucero over 1 year

    I am building an application which has a Camera inside.

    After I take a photo, I want to analyze it to know the brightness of this picture, if it is bad I have to take again the photo.

    This is my code right now, it's a javascript function that I found and writing in Dart:

    Thanks to @Abion47

    EDIT 1

    for (int i = 0; i < pixels.length; i++) {
          int pixel = pixels[i];
          int b = (pixel & 0x00FF0000) >> 16;
          int g = (pixel & 0x0000FF00) >> 8;
          int r = (pixel & 0x000000FF);
    
          avg = ((r + g + b) / 3).floor();
          colorSum += avg;
        }
    
        brightness = (colorSum / (width * height)).floor();
    }
    
    brightness = (colorSum / (width * height)).round();
    // I tried with this other code
    //brightness = (colorSum / pixels.length).round();
    
    return brightness;
    

    But I've got less brightness on white than black, the numbers are a little bit weird.

    Do you know a better way to know the brightness?

    SOLUTION:

    Under further investigation we found the solution, we had an error doing the image decoding, but we used a Image function to do it.

    Here is our final code:

    Image image = decodeImage(file.readAsBytesSync());
        var data = image.getBytes();
        var colorSum = 0;
        for(var x = 0; x < data.length; x += 4) {
          int r = data[x];
          int g = data[x + 1];
          int b = data[x + 2];
          int avg = ((r + g + b) / 3).floor();
          colorSum += avg;
        }
        var brightness = (colorSum / (image.width * image.height)).floor();
        return brightness;
    

    Hope it helps you.

    • Abion47
      Abion47 over 3 years
      What is the width and height of the image object and how are you getting them?
    • Abion47
      Abion47 over 3 years
      Also the retrieval of the color channels by converting the int to a hex string and then parsing the substrings A) probably isn't working like you think it's working due to those weird substring ranges, and B) is an excruciatingly slow way to do that.
    • Nicolas Lucero
      Nicolas Lucero over 3 years
      I'm calculating the picture with and height with image_size_getter.
    • Nicolas Lucero
      Nicolas Lucero over 3 years
      Yes, I know this is not the best way, but unfortunately I didn't find anything better :(
    • Abion47
      Abion47 over 3 years
      Why? You are using the image package. The image object already has width and height properties.
    • Nicolas Lucero
      Nicolas Lucero over 3 years
      Yes, but in my Image object, I get width and height in null.
    • Abion47
      Abion47 over 3 years
      Then something is weird in how you are loading the images.
    • Rohit
      Rohit almost 3 years
      @NicolasLucero so now do we have the same brightness level for all devices?
    • Nicolas Lucero
      Nicolas Lucero almost 3 years
      Yes, the "brightness" level will be always the same. Just FYI, we replaced this calc with computer vision and some ML models because the original calc was low accurate.
  • Nicolas Lucero
    Nicolas Lucero over 3 years
    First of all thanks for answer. Your notes are very helpful, now I'm trying your code and it works without errors, but the brightness values are differents depends on the photo resolution. I mean, from android emulator (1280x720) I see values from 0 to 3, but with galaxy s10 I see values from 4 to 6 and iPhone xr (3840x2160) from 8 to 22. I also deleted the rounds method to see the original value.
  • Abion47
    Abion47 over 3 years
    @NicolasLucero If the inaccuracies get bigger with resolution, that could indicate a rounding error. I'd say yeah, get rid of all the rounding functions and treat the average as a floating point value. (I also mistakenly left in colorSum / (width * height) which should be colorSum / pixels.length, so that might also have contributed to it.)
  • Nicolas Lucero
    Nicolas Lucero over 3 years
    Now the numbers are pretty similar in all devices. But the numbers are weird, I have a smaller number when the picture is completely white than other totally black. I'l try it to solve it and post the solution. Thank u