Get Bytes from int to avoid bit offset bias - Java (median filtering)

I am trying to execute a Median filter on an image in Java, but it is terribly slow. Firstly, if one of you knows about self-implementation, I could use it would be fantastic if you could tell me about it. I am implementing on Android trying to reproduce a small part of JAI.

In my method, I take every pixel, extract the values ​​of R, G and B using

r = pixel >> 16 & 0xFF 

Or similarly, find the median for the kernel and end with

 pixel = a | r <<16 | g << 8 | b 

Is there any way to capture bytes from int so that it is faster?

Yours faithfully,

Gavin


EDIT: complete code to help diagnose my poor performance on demand

For the actual source file, please go here where my implementation of medianFilter can be found.

width and height values ​​for size dest and are available as class member variables. Pixels are linearized into a one-dimensional array.

 private void medianFilterSquare(int[] source, int[] dest, int rWidth, int rHeight, int radius) { // Source has been reflected into a border of size radius // This makes it radius * 2 pixels wider and taller than the dest int r,g,b; int destOffset, rOffset, kOffset; // The first offset into the source to calculate a median for // This corresponds to the first pixel in dest int rFirst = radius + (rWidth*radius); // We use a square kernel with the radius passed int neighbours = (radius+radius+1)*(radius+radius+1); int index; // Arrays to accumulate the values for median calculation int[] rs = new int[neighbours]; int[] gs = new int[neighbours]; int[] bs = new int[neighbours]; // Declaring outside the loop helps speed? I'm sure this is done for me // by the compiler int pixel; // Iterate over the destination pixels for(int x = 0; x < height; x++){ for(int y = 0; y < width; y++){ // Offset into destination destOffset = x + (y * width); // Offset into source with border size radius rOffset = destOffset + rFirst + (y * (radius *2)); index = 0; // Iterate over kernel for(int xk = -radius; xk < radius ; xk ++){ for(int yk = -radius; yk < radius ; yk ++){ kOffset = rOffset + (xk + (rWidth*yk)); pixel = source[kOffset]; // Color.red is equivalent to (pixel>>16) & 0xFF rs[index] = Color.red(pixel); gs[index] = Color.green(pixel); bs[index] = Color.blue(pixel); index++; } } r = medianFilter(rs); g = medianFilter(gs); b = medianFilter(bs); dest[destOffset] = Color.rgb(r, g, b); } } } 
+4
source share
5 answers

As others have said, it is possible that a bit between them causes a problem. One thing I would say (which may be obvious, but in any case) is not just to profile the application on the desktop virtual machine and consider that the bottleneck will be in one place. I would not be surprised to see completely different bottlenecks in Dalvik.

Is it possible for you to work with values ​​that are still shifted? For example, if you simply mask different colors:

 int r = pixel & 0xff0000; int g = pixel & 0xff00; int b = pixel & 0xff; 

Could you customize your processing algorithm accordingly?

One final thought: I always find that the priority of shift operators is confusing. I highly recommend that you copy them in terms of readability:

 r = (pixel >> 16) & 0xFF; pixel = a | (r <<16) | (g << 8) | b; 

It has nothing to do with performance, but if I were an attendant, I would certainly appreciate it :)

+5
source

A quick way to get your r, g, b values ​​should be

 new byte[] { (byte)(value >>> 24), (byte)(value >>> 16), (byte)(value >>> 8), (byte)value }; 
+1
source

Focusing on how to perform bit operations is a distraction. It only matters for how you do these operations, because you unnecessarily process the same pixel over and over.

You call the average filter for each pixel three times, and you get a few pixels around the pixel per pixel. This means that you do all these bits for the same pixel several times. You have for loops nested four depths!

If your radius is 5, you are processing 121 pixels. Then you move one and process 121 pixels again, and you have already converted all but 11 of them! You do the same for each pixel, and then go to the right pixel. For a radius of five, you do two orders of magnitude as many rgb conversions as you need.

I would suggest saving the image or converting the image to first highlight the red, blue and green arrays.


If the radius is large, you can save the red, blue, and green sums when moving by subtracting the pixels from the top and adding pixels from the bottom when scanning the bitmap, but that would make the code more complicated. If you add code for optimization, it depends on your requirements.


In addition, you have a bunch of little things that can be optimized. I'm not sure if the compiler cares about them or not. For example, you can reduce the level of strength. You do not need any multiplications that you have in the lines that compute neighbors , destOffset , rOffset or kOffset . Addition - this is all you need for those who reorganize the code a little.

+1
source

In addition to my comment, I found code that executes a median filter in java.

Download Link

0
source

You can sometimes avoid doing arithmetic on the red and blue components at the same time in one int:

 int average(int rgb1, int rgb2) { int rb = (((rgb1 & 0xFF00FF) + (rgb2 & 0xFF00FF)) >> 1) & 0xFF00FF; int g = (((rgb1 & 0xFF00) + (rgb2 & 0xFF00)) >> 1) & 0xFF00; return (rb | g); } 

because the red and blue components are separated by 8 bits, they do not interfere with each other.

I have never seen significant (over 5-10%) acceleration from this, however.

0
source

Source: https://habr.com/ru/post/1286546/


All Articles