Ищем быстрые алгоритмы искажения изображения - PullRequest
5 голосов
/ 04 апреля 2011

Я пытаюсь реализовать приложение, которое использует фильтр искажений Шпере. Я использую алгоритм из здесь , который изменяет расположение пикселей методами getPixel () и setpixel (). Моя проблема в том, что это слишком медленно для устройств Android, и есть приложения, которые реализуют тот же самый (и другие) фильтр намного быстрее, чем мой подход. (например, приложение Picsay Pro) Может кто-нибудь поделиться или дать направление, чтобы найти или реализовать алгоритмы быстрого искажения.

Фактический фильтр, реализующий алгоритм:

public boolean sphereFilter(Bitmap b, boolean bSmoothing)
{   
    int nWidth = b.getWidth();
    int nHeight = b.getHeight();

    Point  [][] pt = new Point[nWidth][nHeight];
    Point mid = new Point();
    mid.x = nWidth/2;
    mid.y = nHeight/2;

    double theta, radius;
    double newX, newY;

    for (int x = 0; x < nWidth; ++x)
        for (int y = 0; y < nHeight; ++y)
        {
            pt[x][y]= new Point();
        }

    for (int x = 0; x < nWidth; ++x)
        for (int y = 0; y < nHeight; ++y)
        {
            int trueX = x - mid.x;
            int trueY = y - mid.y;
            theta = Math.atan2((trueY),(trueX));

            radius = Math.sqrt(trueX*trueX + trueY*trueY);

            double newRadius = radius * radius/(Math.max(mid.x, mid.y));

            newX = mid.x + (newRadius * Math.cos(theta));

            if (newX > 0 && newX < nWidth)
            {
                pt[x][y].x = (int) newX;
            }
            else
            {
                pt[x][y].x = 0;
                pt[x][y].y = 0;
            }

            newY = mid.y + (newRadius * Math.sin(theta));

            if (newY > 0 && newY < nHeight && newX > 0 && newX < nWidth)
            {                   
                pt[x][ y].y = (int) newY;
            }
            else
            {
                pt[x][y].x = pt[x][y].y = 0;
            }
        }
    offsetFilterAbs(b, pt);
    return true;
}

Код, который заменяет рассчитанные позиции пикселей.

public boolean offsetFilterAbs(Bitmap b, Point[][] offset )
{
        int nWidth = b.getWidth();
        int nHeight = b.getHeight();

        int xOffset, yOffset;

        for(int y=0;y < nHeight;++y)
        {
            for(int x=0; x < nWidth; ++x )
            {   
                xOffset = offset[x][y].x;
                yOffset = offset[x][y].y;

                if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth)
                {
                    b.setPixel(x, y, b.getPixel(xOffset, yOffset));
                }                   
            }               
        }

    return true;
}

1 Ответ

5 голосов
/ 05 апреля 2011

В настоящее время я использую тот же алгоритм, что и на вашей ссылке, и он все еще слишком медленный для устройств Android

Из моей ссылки в комментариях выше :

Given
r = Sqrt((x - 0.5)^2 + (y - 0.5)^2)
a = ArcTan2(y - 0.5, x - 0.5)
n = Bulge factor (default = 1)

Set
x' = r^n * Cos(a) + 0.5 
y' = r^n * Sin(a) + 0.5 

(Помните, что в этом уравнении x и y изменяются от 0 до 1. Если ваши измерения охватывают от 0 до w, замените 0.5 наw/2)

Используя немного математики , мы можем видеть, что

Cos(a) = Cos(ArcTan2(y - 0.5, x - 0.5))
       = (x - 0.5)/r
Sin(a) = Sin(ArcTan2(y - 0.5, x - 0.5))
       = (y - 0.5)/r

Это делает окончательное результирующее уравнение

r = (x - 0.5)^2 + (y - 0.5)^2
n = Bulge factor (default = 0)

Set
x' = r^n * (x - 0.5) + 0.5
y' = r^n * (y - 0.5) + 0.5

(Я удалил квадратный корень, так как мы в любом случае переносим результат в реальную силу ... поэтому, чтобы действительно сделать этот эквивалент, мы должны использовать n/2 вместо n, но так как мы определяя «коэффициент выпуклости», мы можем просто пропустить дополнительное деление)

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

...