Fastest way to change the HSV value of all pixels in a bitmap I want to change all the pixels in an image to have the same value, in terms of the HSV colour space.

At the moment I'm running the following code:

private Bitmap changeVForAllPixels(Bitmap bitmap, float newV) 
{
    int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int[] rgbs = new int[width * height];

    bitmap.getPixels(rgbs, 0, width, 0, 0, width, height);

    for(int rgbi = 0; rgbi &lt; rgbs.length; rgbi++)
    {
        float[] hsv = new float[3];
        Color.colorToHSV(rgbs[rgbi], hsv);
        hsv[2] = newV;
        rgbs[rgbi] = Color.HSVToColor(hsv);
    }

    return Bitmap.createBitmap(rgbs, width, height, Bitmap.Config.ARGB_8888);
}


I would like to know if there is a faster way, as this is taking in the range of 30 seconds to process a 1920 x 1080 pixels image on an LG D802 phone. I'd like to get it down to the 1 second range, or to know if that's not possible.
Disclaimer: I am NOT familiar with Java, let alone the Android version. This may not compile or may be slower. I have no way to test this!




First of all, You deserve huge congratulations!!! Even I could read and understand what is going on! From the bottom of my heart: Thank you!

But still, there are a few points that scared me.

One of them is this:

int[] rgbs = new int[width * height];


Why rgbs? Sure, you are storing an RGB representation of a pixel in the form of an integer. So, why not just call it pixels?

Other variable that needs rewording is rgbi. Since it is an incrementer, it should be simply i. No need to complicate.



Based on http://developer.android.com/training/articles/perf-tips.html, you could use the for loop with 'cached' length, to try to speed up your code. I'm not sure if it will work.

It would look like this:

int length = pixels.length;
for(int i = 0; i &lt; length; i++)
{
    float[] hsv = new float[3];
    Color.colorToHSV(pixels[i], hsv);
    hsv[2] = newV;
    pixels[i] = Color.HSVToColor(hsv);
}


You may ask: Why aren't you using for-each?

Since you are setting the value of the pixels again, it won't work. Based on https://stackoverflow.com/questions/15844443/why-java-foreach-doesnt-change-element-value, you couldn't write back the new value since it will create a copy. You could try to get a reference instead (possible in PHP), but I'm not sure if it works in Java.

Besides that, the page also says that for performance-critical loops, this is the best choice.



Your biggest bottleneck is this block:

float[] hsv = new float[3];
Color.colorToHSV(pixels[i], hsv);
hsv[2] = newV;
pixels[i] = Color.HSVToColor(hsv);


This is where everything happens! You could try to use memoization techniques to store already-calculated values. This will speed-up for images with less colors, like a photo of the sun/clean sky or a logo or similars. Photos with areas of colors that are equal or similar.

For images with lots of colors, like a photo of a landscape, it may still produce somewhat good results in RAM. Since there are 16777216 colors that can be generated using RGB values, this would be equivalent to storing 4 * 16777216 = 67108864 Bytes = 65536 KBytes = 64MBytes in your worst case scenario. Since your bitmaps are 1920 x 1080 pixels, you have 1920 * 1080 = 2073600 pixels * 4 = 6220800 bytes = 6075 KBytes ~= 5,9326171875 MBytes of information. So, you don't have to worry about your worst case scenario. As long as the newV doesn't change, all this data is re-usable to other images, being it even faster on the next ones.