I am trying to build functionality to check if an image taken by the camera is too dark. So I added the following 2 functions to a utility class:
public static boolean checkIfImageIsTooDark(BitmapRegionDecoder bitmapRegionDecoder)
{
if(bitmapRegionDecoder == null) {
Timber.e("Expected bitmapRegionDecoder was null");
return false;
}
int loadingBitmapHeight = bitmapRegionDecoder.getHeight();
int loadingBitmapWidth = bitmapRegionDecoder.getWidth();
boolean isImageTooDark = true;
int checkBitmapWidth = loadingBitmapWidth / 10;
int checkBitmapHeight = loadingBitmapHeight / 10;
int topBitmapPosition = 0;
int leftBitmapPosition = 0;
while((checkBitmapWidth <= loadingBitmapWidth) && (leftBitmapPosition < checkBitmapWidth)) {
while((checkBitmapHeight <= loadingBitmapHeight) && (topBitmapPosition < checkBitmapHeight)) {
Rect bitmapRect = new Rect(leftBitmapPosition,topBitmapPosition,checkBitmapWidth,checkBitmapHeight);
Timber.d("left: " + leftBitmapPosition + " right: " + checkBitmapWidth + " top: " + topBitmapPosition + " bottom: " + checkBitmapHeight);
Bitmap processBitmap = bitmapRegionDecoder.decodeRegion(bitmapRect,null);
if(!checkIfImageIsTooDark(processBitmap)) {
isImageTooDark = false;
break;
}
topBitmapPosition = checkBitmapHeight;
checkBitmapHeight += (checkBitmapHeight < (loadingBitmapHeight - checkBitmapHeight)) ? checkBitmapHeight : (loadingBitmapHeight - checkBitmapHeight);
}
if(!isImageTooDark) {
break;
}
topBitmapPosition = 0;
checkBitmapHeight = loadingBitmapHeight / 10;
leftBitmapPosition = checkBitmapWidth;
checkBitmapWidth += (checkBitmapWidth < (loadingBitmapWidth - checkBitmapWidth)) ? checkBitmapWidth : (loadingBitmapWidth - checkBitmapWidth);
}
return isImageTooDark;
}
public static boolean checkIfImageIsTooDark(Bitmap bitmap) {
if(bitmap == null)
{
Timber.e("Expected bitmap was null");
return false;
}
int[] brightnessHistogram = new int[256];
for(int i = 0; i < bitmap.getHeight(); i++) {
for(int j = 0; j < bitmap.getWidth(); j++) {
int pixel = bitmap.getPixel(j,i);
int r = Color.red(pixel);
int g = Color.green(pixel);
int b = Color.blue(pixel);
int brightness = (int)(0.2126 * r + 0.7152 * g + 0.0722 * b);
brightnessHistogram[brightness]++;
}
}
int allPixelsCount = bitmap.getWidth() * bitmap.getHeight();
//counting pixels with brightness less than 10
int darkPixelCount = 0;
for(int i = 0; i < 10; i++)
{
darkPixelCount += brightnessHistogram[i];
}
//if more than 70% pixels are too dark then image is too dark
return darkPixelCount > allPixelsCount * 0.70;
}
The brightness histogram approach I found over here -> https://stackoverflow.com/a/35914745/3287853. And it works quite well.
The only problem however is that processing an image takes way too much time. I tried it on my phone which produces 4160x3120 and it takes close to a minute to get a result off it. Also, the 4 loops don't look great and I think there should be a better way to make this work, personally.
I am using an AsyncTask to do this in the background so one improvement that I could think of is to break up the task across multiple threads.
I could also compress the image initially bringing it down to a certain limit of width and height while attempting to maintain its aspect ratio and then process the image. However I wonder if that would actually help in reducing the amount of time taken at all.
brightness < 10
directly without allocating full histogram and break once the threshold is exceeded (or clearly will not be). \$\endgroup\$