Как нарисовать изображение на основе простого многоугольника? - PullRequest
12 голосов
/ 20 апреля 2011

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

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

Простое вращение исходного изображения не работает, поскольку противоположные стороны могут иметь разную длину.

До сих пор я не нашел способа сделать это вчистый C # (кроме ручного пиксельного копирования), так что я думаю, что мне придется прибегнуть к Windows API или какой-нибудь сторонней библиотеке?

Ответы [ 2 ]

8 голосов
/ 20 апреля 2011

Поскольку я не мог найти ответ, я сам написал наивную реализацию. Работает достаточно хорошо.

Примеры

Я нарисовал все примеры вручную в Paint, поэтому они не очень точны - этого было достаточно, чтобы протестировать некоторые основы.

а) Небольшое вращение.

Источник:

source image

Результат:

resulting image

б) Различные стороны

Источник:

source 2

Результат:

result 2

в) Перспектива

Источник:

source 3

Результат:

result 3

Код

(он специализирован для моего случая использования, но его легко адаптировать):

// _Corners are, well, the 4 corners in the source image
// _Px is an array of pixels extracted from the source image

public void Rescale ()
{
    RescaleImage (
        _Corners[0],
        _Corners[1],
        _Corners[3],
        _Corners[2],
        100,
        100);
}

private void RescaleImage (PointF TL, PointF TR, PointF LL, PointF LR, int sx, int sy)
{
    var bmpOut = new Bitmap (sx, sy);

    for (int x = 0; x < sx; x++) {
        for (int y = 0; y < sy; y++) {
            /*
             * relative position
             */
            double rx = (double) x / sx;
            double ry = (double) y / sy;

            /*
             * get top and bottom position
             */
            double topX = TL.X + rx * (TR.X - TL.X);
            double topY = TL.Y + rx * (TR.Y - TL.Y);
            double bottomX = LL.X + rx * (LR.X - LL.X);
            double bottomY = LL.Y + rx * (LR.Y - LL.Y);

            /*
             * select center between top and bottom point
             */
            double centerX = topX + ry * (bottomX - topX);
            double centerY = topY + ry * (bottomY - topY);

            /*
             * store result
             */
            var c = PolyColor (centerX, centerY);
            bmpOut.SetPixel (x, y, c);
        }
    }

    bmpOut.Save (_Path + "out5 rescale out.bmp");
}

private Color PolyColor (double x, double y)
{
    // get fractions
    double xf = x - (int) x;
    double yf = y - (int) y;

    // 4 colors - we're flipping sides so we can use the distance instead of inverting it later
    Color cTL = _Px[(int) y + 1, (int) x + 1];
    Color cTR = _Px[(int) y + 1, (int) x + 0];
    Color cLL = _Px[(int) y + 0, (int) x + 1];
    Color cLR = _Px[(int) y + 0, (int) x + 0];

    // 4 distances
    double dTL = Math.Sqrt (xf * xf + yf * yf);
    double dTR = Math.Sqrt ((1 - xf) * (1 - xf) + yf * yf);
    double dLL = Math.Sqrt (xf * xf + (1 - yf) * (1 - yf));
    double dLR = Math.Sqrt ((1 - xf) * (1 - xf) + (1 - yf) * (1 - yf));

    // 4 parts
    double factor = 1.0 / (dTL + dTR + dLL + dLR);
    dTL *= factor;
    dTR *= factor;
    dLL *= factor;
    dLR *= factor;

    // accumulate parts
    double r = dTL * cTL.R + dTR * cTR.R + dLL * cLL.R + dLR * cLR.R;
    double g = dTL * cTL.G + dTR * cTR.G + dLL * cLL.G + dLR * cLR.G;
    double b = dTL * cTL.B + dTR * cTR.B + dLL * cLL.B + dLR * cLR.B;

    Color c = Color.FromArgb ((int) (r + 0.5), (int) (g + 0.5), (int) (b + 0.5));

    return c;
}
3 голосов
/ 20 апреля 2011

Вообще говоря, вы хотите сопоставить координаты назначения с исходными координатами с помощью функции преобразования:

for (int y = 0; y < destHeight; y++) {
    for (x=0; x < destWidth; x++) {
        Color c = Transform(x, y, sourceImage, sourceTransform);
        SetPixel(destImage, x, y, c);
    }
}

Предположим, что sourceTransform является объектом, который инкапсулирует преобразование из координат источника в координаты дест (и наоборот).

Работа с координатами Dest позволит легче избежать этой кривой в вашем повторно преобразованном исходном изображении и позволит улучшить сглаживание, поскольку вы можете сопоставить углы пикселя dest с исходным изображением и сэмплом в нем, а также интерполировать / экстраполировать .

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

...