Convolution Filter - Float Precision C Vs Java

I am transferring a library of image processing routines in C with Java, and when comparing the results I get very slight differences. Is it reasonable that these differences are related to the handling of float values โ€‹โ€‹by different languages โ€‹โ€‹or do I still have work!

A subprogram is a convolution with a 3 x 3 kernel; it works on a raster image represented by a linear array of pixels, width and depth. You do not have to understand this code exactly to answer my question, it is here for reference only.

Java code

for (int x = 0; x < width; x++){ for (int y = 0; y < height; y++){ int offset = (y*width)+x; if(x % (width-1) == 0 || y % (height-1) == 0){ input.setPixel(x, y, 0xFF000000); // Alpha channel only for border } else { float r = 0; float g = 0; float b = 0; for(int kx = -1 ; kx <= 1; kx++ ){ for(int ky = -1 ; ky <= 1; ky++ ){ int pixel = pix[offset+(width*ky)+kx]; int t1 = Color.red(pixel); int t2 = Color.green(pixel); int t3 = Color.blue(pixel); float m = kernel[((ky+1)*3)+kx+1]; r += Color.red(pixel) * m; g += Color.green(pixel) * m; b += Color.blue(pixel) * m; } } input.setPixel(x, y, Color.rgb(clamp((int)r), clamp((int)g), clamp((int)b))); } } } return input; 

Clamp limits the range values โ€‹โ€‹to the range [0..255], and Color.red is equivalent (pixel and 0x00FF0000) โ†’ 16.

C code is as follows:

 for(x=1;x<width-1;x++){ for(y=1; y<height-1; y++){ offset = x + (y*width); rAcc=0; gAcc=0; bAcc=0; for(z=0;z<kernelLength;z++){ xk = x + xOffsets[z]; yk = y + yOffsets[z]; kOffset = xk + (yk * width); rAcc += kernel[z] * ((b1[kOffset] & rMask)>>16); gAcc += kernel[z] * ((b1[kOffset] & gMask)>>8); bAcc += kernel[z] * (b1[kOffset] & bMask); } // Clamp values rAcc = rAcc > 255 ? 255 : rAcc < 0 ? 0 : rAcc; gAcc = gAcc > 255 ? 255 : gAcc < 0 ? 0 : gAcc; bAcc = bAcc > 255 ? 255 : bAcc < 0 ? 0 : bAcc; // Round the floats r = (int)(rAcc + 0.5); g = (int)(gAcc + 0.5); b = (int)(bAcc + 0.5); output[offset] = (a|r<<16|g<<8|b) ; } } 

A bit different xOffsets provides xOffset for a kernel element, for example.

The main thing is that my results go no more than one bit. Below are the pixel values:

 FF205448 expected FF215449 returned 44 wrong FF56977E expected FF56977F returned 45 wrong FF4A9A7D expected FF4B9B7E returned 54 wrong FF3F9478 expected FF3F9578 returned 74 wrong FF004A12 expected FF004A13 returned 

Do you think this is a problem with my code, or rather, a difference in language?

Yours faithfully,

Woof

+4
source share
5 answers

After a quick look:

Do you realize that (int) r will set the value of r instead of the usual rounding? in c code you seem to use (int) (r + 0.5)

+7
source

In response to the Forteg answer, try the roundf() function from the C math library .

+2
source

Java floating point behavior is pretty accurate. What I expect here is that value is stored in registers with extended precision. IIRC, Java requires precision to be rounded to precision of the appropriate type. This is done so that you always get the same result (full information in JLS). C compilers will tend to leave extra precision there until the result is stored in main memory.

+1
source

I suggest you use double instead of float. A float is almost never the best choice.

+1
source

This may be due to different default default bilingual rounds. I am not saying that they (you need to read to determine this), but it is an idea.

0
source

All Articles