简体   繁体   English

卷积滤波器 - Float Precision C Vs Java

[英]Convolution Filter - Float Precision C Vs Java

I'm porting a library of image manipulation routines into C from Java and I'm getting some very small differences when I compare the results. 我正在将一个图像处理例程库从Java移植到C中,当我比较结果时,我得到了一些非常小的差异。 Is it reasonable that these differences are in the different languages' handling of float values or do I still have work to do! 这些差异在于不同语言对浮动值的处理是否合理,还是我还有工作要做!

The routine is Convolution with a 3 x 3 kernel, it's operated on a bitmap represented by a linear array of pixels, a width and a depth. 例程是使用3 x 3内核的卷积,它在由线性像素阵列,宽度和深度表示的位图上操作。 You need not understand this code exactly to answer my question, it's just here for reference. 您无需完全理解这段代码来回答我的问题,它只是供参考。

Java code; Java代码;

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 restricts the bands' values to the range [0..255] and Color.red is equivalent to (pixel & 0x00FF0000) >> 16. Clamp将波段的值限制在[0..255]范围内,Color.red相当于(pixel&0x00FF0000)>> 16。

The C code goes like this; C代码是这样的;

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) ;
        }
    }

It's a little different xOffsets provides the xOffset for the kernel element for example. 它有点不同xOffsets为内核元素提供了xOffset。

The main point is that my results are out by at most one bit. 重点是我的结果最多只有一点。 The following are 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 believe this is a problem with my code or rather a difference in the language? 你认为这是我的代码的问题,还是相反的语言差异?

Kind regards, 亲切的问候,

Gav GAV

After a quick look: 快速浏览一下:

do you realize that (int)r will floor the r value instead of rounding it normally? 你是否意识到(int)r会将r值置于底层而不是正常舍入? in the c code, you seem to use (int)(r + 0.5) 在c代码中,你似乎使用(int)(r + 0.5)

继Fortega的回答之后,尝试C数学库中的roundf()函数

Java's floating point behaviour is quite precise. Java的浮点行为非常精确。 What I expect to be happening here is that the value as being kept in registers with extended precision. 我期望在这里发生的是将值保存在寄存器中并具有更高的精度。 IIRC, Java requires that the precision is rounded to that of the appropriate type. IIRC,Java要求精度四舍五入到相应类型的精度。 This is to try to make sure you always get the same result (full details in the JLS). 这是为了确保您始终获得相同的结果(JLS中的完整详细信息)。 C compilers will tend to leave any extra precision there, until the result in stored into main memory. C编译器会在那里留下任何额外的精度,直到结果存储到主存储器中。

I would suggest you use double instead of float. 我建议你使用double而不是float。 Float is almost never the best choice. Float几乎不是最好的选择。

This might be due to different default round in the two languages. 这可能是由于两种语言中的默认轮次不同。 I'm not saying they have (you need to read up to determine that), but it's an idea. 我不是说他们有(你需要阅读以确定),但这是一个想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM