卷积滤波器-浮点精度C与Java
我正在将一个图像处理例程库从Java移植到C中,在比较结果时,我得到了一些非常小的差异。这些差异存在于不同语言对浮点值的处理中是合理的还是我还有工作要做 该例程是一个3 x 3内核的卷积,它在一个由像素、宽度和深度的线性阵列表示的位图上运行。你不需要完全理解这个代码来回答我的问题,这里只是供参考 Java代码卷积滤波器-浮点精度C与Java,java,c,image,floating-point,convolution,Java,C,Image,Floating Point,Convolution,我正在将一个图像处理例程库从Java移植到C中,在比较结果时,我得到了一些非常小的差异。这些差异存在于不同语言对浮点值的处理中是合理的还是我还有工作要做 该例程是一个3 x 3内核的卷积,它在一个由像素、宽度和深度的线性阵列表示的位图上运行。你不需要完全理解这个代码来回答我的问题,这里只是供参考 Java代码 for (int x = 0; x < width; x++){ for (int y = 0; y < height; y++){
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;
您认为这是我的代码的问题还是语言的差异
亲切问候,
Gav这可能是由于两种语言的默认回合不同。我不是说他们有(你需要仔细阅读才能确定),但这是一个想法。快速浏览后: 您是否意识到(int)r会将r值降到最低,而不是正常地四舍五入?
在c代码中,您似乎使用了(int)(r+0.5)Java的浮点行为非常精确。我期望在这里发生的是,值以扩展精度保存在寄存器中。IIRC,Java要求精度四舍五入到适当类型的精度。这是为了确保您始终获得相同的结果(JLS中的完整详细信息)。C编译器将倾向于保留任何额外的精度,直到结果存储到主内存中。关于Fortega的答案,请尝试。我建议您使用double而不是float。浮动几乎从来都不是最好的选择。这也是我最初的想法——但他显示的差异太大,无法用“长双精度”来解释。我想Fortega已经做到了。旁注:如果你在使用它,请记住每像素至少会被调用3次。@Jasper:如果你担心它可能需要一段时间,我会先尝试一下,如果它看起来慢得让人无法接受,那么就开始担心它。提前验光没有意义。嗨,福特加,我认为(int)(rAcc+0.5)是一种将浮点值舍入为整数的便宜方法,你知道整数是正的吗?但是,您完全正确,遗留代码截断了浮动,而我正在舍入它们(尝试舍入)。感谢您的帮助:)(int)(rAcc+0.5)确实是一种(便宜?脏?快?)将浮点值舍入为int的方法,在java和c中都是如此。正确的“钳制”一词实际上是“饱和”。如果你和某人谈话,他们会马上知道“饱和”是什么意思,但不一定是“钳制”。@Trevor:这取决于此人的背景。
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) ;
}
}
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