Вопрос

Я переношу библиотеку процедур манипулирования изображениями на C из Java и получаю некоторые очень небольшие различия, когда сравниваю результаты.Разумно ли, что эти различия связаны с обработкой значений float на разных языках, или мне все еще нужно поработать!

Процедура представляет собой свертку с ядром 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; 

Clamp ограничивает значения диапазонов диапазоном [0..255], а Color.red эквивалентен (pixel & 0x00FF0000) >> 16.

Код на языке Си выглядит следующим образом;

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 для элемента ядра.

Главное, что мои результаты отличаются не более чем на один бит.Ниже приведены значения пикселей;

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

Вы считаете, что это проблема с моим кодом или, скорее, разница в языке?

С наилучшими пожеланиями,

Гэв

Это было полезно?

Решение

После беглого просмотра:

вы понимаете, что (int)r уменьшит значение r вместо обычного округления?в коде c вы, кажется, используете (int)(r + 0.5)

Другие советы

В дополнение к ответу Фортеги, попробуйте тот самый roundf() функция из математической библиотеки C.

Поведение Java с плавающей запятой довольно точное.Что я ожидаю, что здесь произойдет, так это то, что значение будет храниться в регистрах с повышенной точностью.IIRC, Java требует, чтобы точность была округлена до точности соответствующего типа.Это делается для того, чтобы убедиться, что вы всегда получаете один и тот же результат (полная информация в JLS).Компиляторы C, как правило, оставляют там любую дополнительную точность до тех пор, пока результат не будет сохранен в основной памяти.

Я бы посоветовал вам использовать double вместо float.Поплавок почти никогда не бывает лучшим выбором.

Это может быть связано с разным раундом по умолчанию на двух языках.Я не говорю, что у них есть (вам нужно прочитать, чтобы определить это), но это идея.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top