Filtre Convolution - Flotteur Precision C Vs Java
-
06-09-2019 - |
Question
Je suis le portage d'une bibliothèque de routines de manipulation d'images en C de Java et je reçois des différences très petites quand je compare les résultats. Est-il raisonnable que ces différences sont dans les différentes langues de manipulation des valeurs de flotteur ou dois-je encore du travail à faire!
La routine est convolution avec un noyau de 3 x 3, il est utilisé sur un bitmap représenté par un réseau linéaire de pixels, une largeur et une profondeur. Vous devez comprendre pas ce code exactement à répondre à ma question, il est juste ici pour référence.
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 limite les valeurs de bandes à l'intervalle [0..255] et Color.red est équivalente à (pixel & 0x00FF0000) >> 16.
Le code C va comme ceci;
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) ;
}
}
Il est un peu différent xOffsets fournit les xOffset pour l'élément du noyau par exemple.
Le point principal est que mes résultats sont par au plus un peu. Ci-dessous sont des valeurs de pixel;
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
Croyez-vous que cela est un problème avec mon code ou plutôt une différence dans la langue?
Cordialement,
Gav
La solution
Après un rapide coup d'oeil:
vous vous rendez compte que (int) r étage volonté de la valeur r au lieu de l'arrondissement normalement? dans le code c, vous semblez utiliser (int) (r + 0,5)
Autres conseils
Suite à la réponse de Fortega, essayez la fonction roundf()
du calcul C bibliothèque .
Le comportement à virgule flottante Java est tout à fait précis. Ce que je pense à se produire ici est que la valeur comme étant conservée dans les registres avec précision étendue. IIRC, Java exige que la précision est arrondie à celle du type approprié. Ceci est d'essayer de vous assurer que vous obtenez toujours le même résultat (les détails du JLS). compilateurs C auront tendance à laisser toute précision supplémentaire là-bas, jusqu'à ce que le résultat dans stocké dans la mémoire principale.
Je vous suggère d'utiliser double au lieu de flotter. Flotteur est presque jamais le meilleur choix.
Cela peut être dû à différents tour par défaut dans les deux langues. Je ne dis pas qu'ils ont (vous avez besoin de lire pour déterminer cela), mais il est une idée.