Получите байты из int, чтобы избежать веселья смещения битов - Java (Median Filtering) - PullRequest
4 голосов
/ 22 июня 2009

Я пытаюсь выполнить медианный фильтр для изображения в Java, но это очень медленно. Во-первых, если бы кто-нибудь из вас знал об автономной реализации, я мог бы использовать ее, было бы здорово, если бы вы дали мне знать. Я реализую на Android, пытаясь воспроизвести небольшую часть JAI.

В моем методе я беру каждый пиксель, извлекаю значения R, G & B, используя

r = pixel >> 16 & 0xFF

или аналогичный, найдите медиану для ядра и закончите с

pixel = a | r <<16 | g << 8 | b 

Есть ли способ, которым я могу получить байты из int таким образом, чтобы это было быстрее?

С уважением,

Gavin


РЕДАКТИРОВАТЬ: Полный код, чтобы помочь диагностировать мою низкую производительность по запросу

Для получения исходного файла перейдите по ссылке здесь , здесь можно найти мою реализацию medianFilter .

Переменные

width и height имеют размер dest и доступны как переменные-члены класса. Пиксели линеаризуются в одномерный массив.

private void medianFilterSquare(int[] source, int[] dest, int rWidth,
        int rHeight, int radius) {
    // Source has been reflected into a border of size radius
    // This makes it radius * 2 pixels wider and taller than the dest
    int r,g,b;
    int destOffset, rOffset, kOffset;

    // The first offset into the source to calculate a median for
    // This corresponds to the first pixel in dest
    int rFirst = radius + (rWidth*radius);

    // We use a square kernel with the radius passed
    int neighbours = (radius+radius+1)*(radius+radius+1);

    int index;

    // Arrays to accumulate the values for median calculation
    int[] rs = new int[neighbours];
    int[] gs = new int[neighbours];
    int[] bs = new int[neighbours];

    // Declaring outside the loop helps speed? I'm sure this is done for me
    // by the compiler
    int pixel;

    // Iterate over the destination pixels
    for(int x = 0; x < height; x++){
        for(int y = 0; y < width; y++){
            // Offset into destination
            destOffset = x + (y * width);
            // Offset into source with border size radius
            rOffset = destOffset + rFirst + (y * (radius *2));

            index = 0;

            // Iterate over kernel
            for(int xk = -radius; xk < radius ; xk ++){
                for(int yk = -radius; yk < radius ; yk ++){
                    kOffset = rOffset + (xk + (rWidth*yk));
                    pixel = source[kOffset];
                    // Color.red is equivalent to (pixel>>16) & 0xFF
                    rs[index] = Color.red(pixel);
                    gs[index] = Color.green(pixel);
                    bs[index] = Color.blue(pixel);
                    index++;
                }   
            }
            r = medianFilter(rs);
            g = medianFilter(gs);
            b = medianFilter(bs);

            dest[destOffset] = Color.rgb(r, g, b);
        }           
    }       
}

Ответы [ 4 ]

5 голосов
/ 22 июня 2009

Как уже говорили другие, вполне возможно, что именно то, что находится между ними, вызывает проблему. Я хотел бы сказать одну вещь (которая может быть очевидной, но в любом случае) - не просто профилировать приложение на настольной виртуальной машине и предположить, что узкое место будет в том же месте. Я бы не удивился, обнаружив в Dalvik совершенно другие узкие места.

Возможно ли вам работать со смещенными значениями? Например, если бы вы просто маскировали для разных цветов:

int r = pixel & 0xff0000;
int g = pixel & 0xff00;
int b = pixel & 0xff;

Не могли бы вы соответствующим образом настроить алгоритм обработки?

Одна заключительная мысль: меня всегда смущает приоритет операторов сдвига. Я бы настоятельно рекомендовал бы с точки зрения читаемости заключить их в скобки:

r = (pixel >> 16) & 0xFF;
pixel = a | (r <<16) | (g << 8) | b;

Не имеет отношения к производительности, но если бы я был сопровождающим, я бы, безусловно, это оценил:)

1 голос
/ 22 июня 2009

Сосредоточение внимания на том, как выполнять битовые операции, отвлекает. Только то, как вы выполняете эти операции, потому что вы без необходимости обрабатываете один и тот же пиксель снова и снова.

Вы вызываете медианный фильтр для каждого пикселя три раза, и вы получаете несколько пикселей вокруг пикселя на пиксель . Это означает, что вы выполняете всю эту битовую работу для одного и того же пикселя несколько раз. У вас есть for петли, вложенные в четыре глубины!

Если ваш радиус равен 5, вы обрабатываете 121 пиксель. Затем вы перемещаетесь вниз на единицу и снова обрабатываете 121 пиксель, и вы уже преобразовали все, кроме 11 из них! Вы делаете то же самое для каждого пикселя вниз, затем перемещаетесь вправо на один пиксель. Для радиуса пять вы делаете на два порядка больше конверсий rgb, сколько вам нужно.

Я бы посоветовал сохранить ваше изображение или преобразовать его в отдельные массивы красного, синего и зеленого цветов first .


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


Кроме того, у вас есть куча мелочей, которые можно оптимизировать. Я не уверен, заботится ли о них компилятор или нет. Например, вы могли бы сделать некоторое снижение прочности. Вам не нужно умножения в строках, которые вычисляют neighbors, destOffset, rOffset или kOffset. Дополнение - это все, что вам нужно, если вы немного реорганизуете код.

1 голос
/ 22 июня 2009

Самый быстрый способ получить значения r, g, b должен быть

new byte[] {
  (byte)(value >>> 24),
  (byte)(value >>> 16),
  (byte)(value >>> 8),
  (byte)value
};
0 голосов
/ 22 июня 2009

Вы можете время от времени выполнять арифметику для красного и синего компонентов одновременно в одном целом:

int average(int rgb1, int rgb2)
{
    int rb = (((rgb1 & 0xFF00FF) + (rgb2 & 0xFF00FF)) >> 1) & 0xFF00FF;
    int g = (((rgb1 & 0xFF00) + (rgb2 & 0xFF00)) >> 1) & 0xFF00;
    return (rb | g);
}

, поскольку красный и синий компоненты разделены 8 битами, они не мешают друг другу.

Хотя я никогда не видел значительного (более 5-10%) ускорения от этого.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...