2
\$\begingroup\$

I'm working on an application that turns a raw camera image into a binary (pure black/white) image and I need this to happen as fast as possible for swift further processing. This is what my code currently looks like:

public static boolean[][] createBinaryImage( Bitmap bm )
{
    int[] pixels = new int[bm.getWidth()*bm.getHeight()];
    bm.getPixels( pixels, 0, bm.getWidth(), 0, 0, bm.getWidth(), bm.getHeight() );
    int w = bm.getWidth();

    // Calculate overall lightness of image
    long gLightness = 0;
    int lLightness;
    int c;
    for ( int x = 0; x < bm.getWidth(); x++ )
    {
        for ( int y = 0; y < bm.getHeight(); y++ )
        {
            c = pixels[x+y*w];
            lLightness = ((c&0x00FF0000 )>>16) + ((c & 0x0000FF00 )>>8) + (c&0x000000FF);
            pixels[x+y*w] = lLightness;
            gLightness += lLightness;
        }
    }
    gLightness /= bm.getWidth() * bm.getHeight();
    gLightness = gLightness * 5 / 6;

    // Extract features
    boolean[][] binaryImage = new boolean[bm.getWidth()][bm.getHeight()];

    for ( int x = 0; x < bm.getWidth(); x++ )
        for ( int y = 0; y < bm.getHeight(); y++ )
            binaryImage[x][y] = pixels[x+y*w] <= gLightness;

    return binaryImage;
}

As you can see, it uses a global threshold to tell apart the black and white pixels. I recently switched from bm.getPixel to bm.getPixels and this yielded a speed improvement of ~33%. Now my question is if there is anything else I can do to speed things up?

It currently takes <0.5 seconds to process 558x256 image and this is reasonable for my application, but I wonder if there's room for any other simple optimizations.

\$\endgroup\$
2
\$\begingroup\$

You can get rid of the nested loop, replacing this

for ( int x = 0; x < bm.getWidth(); x++ )
 {
     for ( int y = 0; y < bm.getHeight(); y++ )
     {
         c = pixels[x+y*w];
         lLightness = ((c&0x00FF0000 )>>16) + ((c & 0x0000FF00 )>>8) + (c&0x000000FF);
         pixels[x+y*w] = lLightness;
         gLightness += lLightness;
     }
 }

With this:

int size = bm.getWidth()*bm.getHeight();
for (int i = 0; i < size; i++) {
  c = pixels[i];
 // etc.
}

It will at least save one x+y*w operation per pixel.

It may be advantageous to calculate size into a separate variable as above, instead of doing it inside the loop, as in for (int i = 0; i < bm.getWidth()*bm.getHeight(); i++). The condition is checked at each round so you would end up calling the getters at each round, and who knows what's in them? The JIT compiler may be able to optimize this kind of stuff away - and probably will, if the getters are simple "return class member" statements - but at least it's not guaranteed.

More substantial improvements can be got by sacrificing perfection. Maybe you needn't take each pixel into account for calculating the threshold? Perhaps count only every 10th on each direction? Then you would probably use the nested loop again. Like this:

int width = bm.getWidth();
int height = bm.getHeight();

for ( int x = 0; x < width; x+=10; )
{
  for ( int y = 0; y < height; y+=10; )
  {
   // etc.
  }
}

gLightness /= (width / 10) * (height / 10);
| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.