2009-05-29 18 views
4

私は画像操作ルーチンのライブラリをJavaからCに移植しています。結果を比較すると、少しの違いがあります。これらの違いが異なる言語の浮動小数点値の扱いにあるのは合理的ですか、それとも私はまだやるべきことがありますか?畳み込みフィルタ - Float Precision C Vs Java

このルーチンは、3 x 3カーネルの畳み込みで、ピクセルの線形配列で表されるビットマップ、幅と深さで動作します。私の質問に答えるためにこのコードを正確に理解する必要はありません。参考のためだけです。

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; 

クランプ[0..255]の範囲にバンド値を制限しColor.redは(ピクセル& 0x00FF0000)と等価である>> 16

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

xOffsetsは、たとえばカーネル要素のxOffsetを提供します。

主なポイントは、私の結果はせいぜい1ビット外です。以下はピクセル値です。

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 

これは私のコードに問題があるのではなく、その言語の違いと思われますか?

種類のよろしく、

Gavさん

+0

「クランプ」の適切な単語は、実際には「飽和」です。あなたが誰かと話している場合、彼らは「飽和している」ということをすぐに知るでしょうが、必ずしも「クランプ」する必要はありません。 –

+0

@トレヴァー:それは本当に人の背景に依存します。 –

答えて

7

は簡単に見た後:

あなたは(int型)R意志階という代わりに、通常はそれを丸めるのr値を実現していますか? をCコードで使用しているようです(r + 0.5)

+0

+1:私は同意する - 私は同じことを投稿しようとしていた:-) –

+1

こんにちはFortega 私は(int)(rAcc + 0.5)は、あなたが肯定的であることを知っているintsに浮動小数点を丸めるの安い方法だと思った? しかし、私はそれらを丸めている間(Trying to)、レガシーコードは浮動小数点を切り捨てます。あなたの助けてくれてありがとう:) – gav

+0

(int)(rAcc + 0.5)は本当に(安い?)丸めの方法は、javaとCの両方でintsに浮動小数点数を返します。 – Fortega

0

これは、二つの言語の異なるデフォルトのラウンドが原因である可能性があります。私は彼らが持っていると言っているわけではありません(あなたがそれを判断するために読む必要があります)が、それはアイデアです。

1

Javaの浮動小数点の動作はかなり正確です。私がここで起こると予想されるのは、その値が拡張精度でレジスタに保持されているということです。 IIRC、Javaでは、精度は適切な型の精度に丸められる必要があります。これは、常に同じ結果を得るようにするためです(JLSの詳細)。 Cコンパイラは、その結果がメインメモリに格納されるまで、そこに余分な精度を残す傾向があります。

+0

それは私の初期の考えでもありましたが、彼が示す違いは大きすぎて「長い倍精度」の精度では説明できません。私はFortegaがそれを得たと思う。 –

2

フォルテガの回答に加えて、the roundf() function from the C math libraryを試してください。

+0

Sidenote:これを使用している場合は、1ピクセルあたり少なくとも3回呼び出されることを覚えておいてください。 –

+3

@ジャスパー:心配していると、しばらく時間がかかるかもしれませんが、まずそれを試してみて、それが許容できないほど遅くなったら、それを心配してください。時期尚早に光線をあてることはありません。 –

1

floatの代わりにdoubleを使用することをお勧めします。フロートは決して最善の選択ではありません。

関連する問題