Как нарисовать сетку с правильной перспективой в 2D

У меня есть приложение, которое определяет прямоугольник реального мира поверх изображения / фотографии, конечно, в 2D это может быть не прямоугольник, потому что вы смотрите на него под углом.

Проблема в том, что, скажем, на прямоугольнике должны быть нарисованы линии сетки, например, если он имеет размер 3x5, поэтому мне нужно нарисовать 2 линии со стороны 1 на сторону 3 и 4 линии со стороны 2 на сторону 4.

На данный момент я разбиваю каждую линию на равноудаленные части, чтобы получить начальную и конечную точки всех линий сетки. Однако чем больше угол наклона прямоугольника, тем более «неправильными» становятся эти линии, поскольку горизонтальные линии дальше от вас должны быть ближе друг к другу.

Кто-нибудь знает название алгоритма, который я должен искать?

Да, я знаю, что вы можете делать это в 3D, однако я ограничен 2D для этого конкретного приложения.


person Neil N    schedule 09.02.2009    source источник
comment
Итак, в качестве примера может быть попытка нарисовать прямоугольник в окне на картинке?   -  person MSN    schedule 10.02.2009
comment
да, это был бы пример   -  person Neil N    schedule 11.02.2009
comment
тебе повезло с этим проектом? Мне нужно что-то очень похожее! Спасибо   -  person PyWebDesign    schedule 02.11.2014


Ответы (9)


Вот Решение.

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

Затем в свои подпрямоугольники вы просто применяете стандартные нескорректированные «текстурированные» треугольники, прямоугольники или что-то еще.

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

person Breton    schedule 10.02.2009
comment
Будет ли это решение работать только для сеток со степенью двойки? - person Ipsquiggle; 21.01.2010
comment
Я предполагаю, что да, но если вы достаточно приличный математик, вы, вероятно, могли бы получить матрицу нелинейного преобразования, которая отражает те же отношения между исходными и конечными пикселями. Я не такой математик, но я, по крайней мере, знаю волшебные слова, которые нужно исследовать, и теперь вы тоже. - person Breton; 22.01.2010

image descriptionИзображение: пример билинейного и перспективного преобразования (Примечание: высота верхней и нижней горизонтальных линий сетки на самом деле половина высоты остальных линий на обоих рисунках)

========================================

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

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

Я начал читать здесь: http://www.imagemagick.org/Usage/distorts/#bilinear_forward

а затем сюда (библиотека Leptonica): http://www.leptonica.com/affine.html

я нашел это:

Когда вы смотрите на объект в плоскости с произвольного направления на конечном расстоянии, вы получаете дополнительное "трапецеидальное искажение" изображения. Это проективное преобразование, которое сохраняет прямые линии прямыми, но не сохраняет углы между линиями. Это искривление не может быть описано линейным аффинным преобразованием и фактически отличается от x- и y-зависимых членов в знаменателе.

Преобразование не является линейным, как уже указали многие люди в этой ветке. Он включает в себя решение линейной системы из 8 уравнений (один раз) для вычисления 8 требуемых коэффициентов, а затем вы можете использовать их для преобразования любого количества точек.

Чтобы избежать включения всей библиотеки Leptonica в свой проект, я взял из нее некоторые фрагменты кода, удалил все специальные типы данных и макросы Leptonica, исправил некоторые утечки памяти и преобразовал его в класс C ++ (в основном по причинам инкапсуляции), который делает только одно: сопоставляет координату (Qt) QPointF float (x, y) с соответствующей координатой перспективы.

Если вы хотите адаптировать код к другой библиотеке C ++, единственное, что нужно переопределить / заменить, - это класс координат QPointF.

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

A. Пример использования класса genImageProjective C ++ для рисования сетки 2D перспективы.

Б. Файл genImageProjective.h

C. Файл genImageProjective.cpp

//============================================================
// C++ Code Example on how to use the 
//     genImageProjective class to draw a perspective 2D Grid
//============================================================

#include "genImageProjective.h"

// Input: 4 Perspective-Tranformed points:
//        perspPoints[0] = top-left
//        perspPoints[1] = top-right
//        perspPoints[2] = bottom-right
//        perspPoints[3] = bottom-left
void drawGrid(QPointF *perspPoints)
{
(...)
        // Setup a non-transformed area rectangle
        // I use a simple square rectangle here because in this case we are not interested in the source-rectangle,
        //  (we want to just draw a grid on the perspPoints[] area)
        //   but you can use any arbitrary rectangle to perform a real mapping to the perspPoints[] area
        QPointF topLeft = QPointF(0,0);
        QPointF topRight = QPointF(1000,0);
        QPointF bottomRight = QPointF(1000,1000);
        QPointF bottomLeft = QPointF(0,1000);
        float width = topRight.x() - topLeft.x();
        float height = bottomLeft.y() - topLeft.y();

        // Setup Projective trasform object
        genImageProjective imageProjective;
        imageProjective.sourceArea[0] = topLeft;
        imageProjective.sourceArea[1] = topRight;
        imageProjective.sourceArea[2] = bottomRight;
        imageProjective.sourceArea[3] = bottomLeft;
        imageProjective.destArea[0] = perspPoints[0];
        imageProjective.destArea[1] = perspPoints[1];
        imageProjective.destArea[2] = perspPoints[2];
        imageProjective.destArea[3] = perspPoints[3];
        // Compute projective transform coefficients
        if (imageProjective.computeCoeefficients() != 0)
            return; // This can actually fail if any 3 points of Source or Dest are colinear

        // Initialize Grid parameters (without transform)
        float gridFirstLine = 0.1f; // The normalized position of first Grid Line (0.0 to 1.0)
        float gridStep = 0.1f;      // The normalized Grd size (=distance between grid lines: 0.0 to 1.0)

        // Draw Horizonal Grid lines
        QPointF lineStart, lineEnd, tempPnt;
        for (float pos = gridFirstLine; pos <= 1.0f; pos += gridStep)
        {
            // Compute Grid Line Start
            tempPnt = QPointF(topLeft.x(), topLeft.y() + pos*width);
            imageProjective.mapSourceToDestPoint(tempPnt, lineStart);
            // Compute Grid Line End
            tempPnt = QPointF(topRight.x(), topLeft.y() + pos*width);
            imageProjective.mapSourceToDestPoint(tempPnt, lineEnd);

            // Draw Horizontal Line (use your prefered method to draw the line)
            (...)
        }
        // Draw Vertical Grid lines
        for (float pos = gridFirstLine; pos <= 1.0f; pos += gridStep)
        {
            // Compute Grid Line Start
            tempPnt = QPointF(topLeft.x() + pos*height, topLeft.y());
            imageProjective.mapSourceToDestPoint(tempPnt, lineStart);
            // Compute Grid Line End
            tempPnt = QPointF(topLeft.x() + pos*height, bottomLeft.y());
            imageProjective.mapSourceToDestPoint(tempPnt, lineEnd);

            // Draw Vertical Line (use your prefered method to draw the line)
            (...)
        }
(...)
}

==========================================



//========================================
//C++ Header File: genImageProjective.h
//========================================

#ifndef GENIMAGE_H
#define GENIMAGE_H

#include <QPointF>

// Class to transform an Image Point using Perspective transformation
class genImageProjective
{
public:
    genImageProjective();

    int computeCoeefficients(void);
    int mapSourceToDestPoint(QPointF& sourcePoint, QPointF& destPoint);

public:
    QPointF sourceArea[4]; // Source Image area limits (Rectangular)
    QPointF destArea[4];   // Destination Image area limits (Perspectivelly Transformed)

private:
    static int gaussjordan(float  **a, float  *b, int n);

    bool coefficientsComputed;
    float vc[8];           // Vector of Transform Coefficients
};

#endif // GENIMAGE_H
//========================================


//========================================
//C++ CPP File: genImageProjective.cpp
//========================================

#include <math.h>
#include "genImageProjective.h"

// ----------------------------------------------------
// class genImageProjective
// ----------------------------------------------------
genImageProjective::genImageProjective()
{
    sourceArea[0] = sourceArea[1] = sourceArea[2] = sourceArea[3] = QPointF(0,0);
    destArea[0] = destArea[1] = destArea[2] = destArea[3] = QPointF(0,0);
    coefficientsComputed = false;
}


// --------------------------------------------------------------
// Compute projective transform coeeeficients
// RetValue: 0: Success, !=0: Error
/*-------------------------------------------------------------*
 *                Projective coordinate transformation         *
 *-------------------------------------------------------------*/
/*!
 *  computeCoeefficients()
 *
 *      Input:  this->sourceArea[4]: (source 4 points; unprimed)
 *              this->destArea[4]:   (transformed 4 points; primed)
 *              this->vc  (computed vector of transform coefficients)
 *      Return: 0 if OK; <0 on error
 *
 *  We have a set of 8 equations, describing the projective
 *  transformation that takes 4 points (sourceArea) into 4 other
 *  points (destArea).  These equations are:
 *
 *          x1' = (c[0]*x1 + c[1]*y1 + c[2]) / (c[6]*x1 + c[7]*y1 + 1)
 *          y1' = (c[3]*x1 + c[4]*y1 + c[5]) / (c[6]*x1 + c[7]*y1 + 1)
 *          x2' = (c[0]*x2 + c[1]*y2 + c[2]) / (c[6]*x2 + c[7]*y2 + 1)
 *          y2' = (c[3]*x2 + c[4]*y2 + c[5]) / (c[6]*x2 + c[7]*y2 + 1)
 *          x3' = (c[0]*x3 + c[1]*y3 + c[2]) / (c[6]*x3 + c[7]*y3 + 1)
 *          y3' = (c[3]*x3 + c[4]*y3 + c[5]) / (c[6]*x3 + c[7]*y3 + 1)
 *          x4' = (c[0]*x4 + c[1]*y4 + c[2]) / (c[6]*x4 + c[7]*y4 + 1)
 *          y4' = (c[3]*x4 + c[4]*y4 + c[5]) / (c[6]*x4 + c[7]*y4 + 1)
 *
 *  Multiplying both sides of each eqn by the denominator, we get
 *
 *           AC = B
 *
 *  where B and C are column vectors
 *
 *         B = [ x1' y1' x2' y2' x3' y3' x4' y4' ]
 *         C = [ c[0] c[1] c[2] c[3] c[4] c[5] c[6] c[7] ]
 *
 *  and A is the 8x8 matrix
 *
 *             x1   y1     1     0   0    0   -x1*x1'  -y1*x1'
 *              0    0     0    x1   y1   1   -x1*y1'  -y1*y1'
 *             x2   y2     1     0   0    0   -x2*x2'  -y2*x2'
 *              0    0     0    x2   y2   1   -x2*y2'  -y2*y2'
 *             x3   y3     1     0   0    0   -x3*x3'  -y3*x3'
 *              0    0     0    x3   y3   1   -x3*y3'  -y3*y3'
 *             x4   y4     1     0   0    0   -x4*x4'  -y4*x4'
 *              0    0     0    x4   y4   1   -x4*y4'  -y4*y4'
 *
 *  These eight equations are solved here for the coefficients C.
 *
 *  These eight coefficients can then be used to find the mapping
 *  (x,y) --> (x',y'):
 *
 *           x' = (c[0]x + c[1]y + c[2]) / (c[6]x + c[7]y + 1)
 *           y' = (c[3]x + c[4]y + c[5]) / (c[6]x + c[7]y + 1)
 *
 */
int genImageProjective::computeCoeefficients(void)
{
    int retValue = 0;
    int     i;
    float  *a[8];  /* 8x8 matrix A  */
    float  *b = this->vc; /* rhs vector of primed coords X'; coeffs returned in vc[] */

    b[0] = destArea[0].x();
    b[1] = destArea[0].y();
    b[2] = destArea[1].x();
    b[3] = destArea[1].y();
    b[4] = destArea[2].x();
    b[5] = destArea[2].y();
    b[6] = destArea[3].x();
    b[7] = destArea[3].y();

    for (i = 0; i < 8; i++)
        a[i] = NULL;
    for (i = 0; i < 8; i++)
    {
        if ((a[i] = (float *)calloc(8, sizeof(float))) == NULL)
        {
            retValue = -100; // ERROR_INT("a[i] not made", procName, 1);
            goto Terminate;
        }
    }

    a[0][0] = sourceArea[0].x();
    a[0][1] = sourceArea[0].y();
    a[0][2] = 1.;
    a[0][6] = -sourceArea[0].x() * b[0];
    a[0][7] = -sourceArea[0].y() * b[0];
    a[1][3] = sourceArea[0].x();
    a[1][4] = sourceArea[0].y();
    a[1][5] = 1;
    a[1][6] = -sourceArea[0].x() * b[1];
    a[1][7] = -sourceArea[0].y() * b[1];
    a[2][0] = sourceArea[1].x();
    a[2][1] = sourceArea[1].y();
    a[2][2] = 1.;
    a[2][6] = -sourceArea[1].x() * b[2];
    a[2][7] = -sourceArea[1].y() * b[2];
    a[3][3] = sourceArea[1].x();
    a[3][4] = sourceArea[1].y();
    a[3][5] = 1;
    a[3][6] = -sourceArea[1].x() * b[3];
    a[3][7] = -sourceArea[1].y() * b[3];
    a[4][0] = sourceArea[2].x();
    a[4][1] = sourceArea[2].y();
    a[4][2] = 1.;
    a[4][6] = -sourceArea[2].x() * b[4];
    a[4][7] = -sourceArea[2].y() * b[4];
    a[5][3] = sourceArea[2].x();
    a[5][4] = sourceArea[2].y();
    a[5][5] = 1;
    a[5][6] = -sourceArea[2].x() * b[5];
    a[5][7] = -sourceArea[2].y() * b[5];
    a[6][0] = sourceArea[3].x();
    a[6][1] = sourceArea[3].y();
    a[6][2] = 1.;
    a[6][6] = -sourceArea[3].x() * b[6];
    a[6][7] = -sourceArea[3].y() * b[6];
    a[7][3] = sourceArea[3].x();
    a[7][4] = sourceArea[3].y();
    a[7][5] = 1;
    a[7][6] = -sourceArea[3].x() * b[7];
    a[7][7] = -sourceArea[3].y() * b[7];

    retValue = gaussjordan(a, b, 8);

Terminate:
    // Clean up
    for (i = 0; i < 8; i++)
    {
        if (a[i])
            free(a[i]);
    }

    this->coefficientsComputed = (retValue == 0);
    return retValue;
}


/*-------------------------------------------------------------*
 *               Gauss-jordan linear equation solver           *
 *-------------------------------------------------------------*/
/*
 *  gaussjordan()
 *
 *      Input:   a  (n x n matrix)
 *               b  (rhs column vector)
 *               n  (dimension)
 *      Return:  0 if ok, 1 on error
 *
 *      Note side effects:
 *            (1) the matrix a is transformed to its inverse
 *            (2) the vector b is transformed to the solution X to the
 *                linear equation AX = B
 *
 *      Adapted from "Numerical Recipes in C, Second Edition", 1992
 *      pp. 36-41 (gauss-jordan elimination)
 */
#define  SWAP(a,b)   {temp = (a); (a) = (b); (b) = temp;}
int genImageProjective::gaussjordan(float  **a, float  *b, int n)
{
    int retValue = 0;
    int i, icol=0, irow=0, j, k, l, ll;
    int *indexc = NULL, *indexr = NULL, *ipiv = NULL;
    float  big, dum, pivinv, temp;

    if (!a)
    {
        retValue = -1; // ERROR_INT("a not defined", procName, 1);
        goto Terminate;
    }
    if (!b)
    {
        retValue = -2; // ERROR_INT("b not defined", procName, 1);
        goto Terminate;
    }

    if ((indexc = (int *)calloc(n, sizeof(int))) == NULL)
    {
        retValue = -3; // ERROR_INT("indexc not made", procName, 1);
        goto Terminate;
    }
    if ((indexr = (int *)calloc(n, sizeof(int))) == NULL)
    {
        retValue = -4; // ERROR_INT("indexr not made", procName, 1);
        goto Terminate;
    }
    if ((ipiv = (int *)calloc(n, sizeof(int))) == NULL)
    {
        retValue = -5; // ERROR_INT("ipiv not made", procName, 1);
        goto Terminate;
    }

    for (i = 0; i < n; i++)
    {
        big = 0.0;
        for (j = 0; j < n; j++)
        {
            if (ipiv[j] != 1)
            {
                for (k = 0; k < n; k++)
                {
                    if (ipiv[k] == 0)
                    {
                        if (fabs(a[j][k]) >= big)
                        {
                            big = fabs(a[j][k]);
                            irow = j;
                            icol = k;
                        }
                    }
                    else if (ipiv[k] > 1)
                    {
                        retValue = -6; // ERROR_INT("singular matrix", procName, 1);
                        goto Terminate;
                    }
                }
            }
        }
        ++(ipiv[icol]);

        if (irow != icol)
        {
            for (l = 0; l < n; l++)
                SWAP(a[irow][l], a[icol][l]);
            SWAP(b[irow], b[icol]);
        }

        indexr[i] = irow;
        indexc[i] = icol;
        if (a[icol][icol] == 0.0)
        {
            retValue = -7; // ERROR_INT("singular matrix", procName, 1);
            goto Terminate;
        }
        pivinv = 1.0 / a[icol][icol];
        a[icol][icol] = 1.0;
        for (l = 0; l < n; l++)
            a[icol][l] *= pivinv;
        b[icol] *= pivinv;

        for (ll = 0; ll < n; ll++)
        {
            if (ll != icol)
            {
                dum = a[ll][icol];
                a[ll][icol] = 0.0;
                for (l = 0; l < n; l++)
                    a[ll][l] -= a[icol][l] * dum;
                b[ll] -= b[icol] * dum;
            }
        }
    }

    for (l = n - 1; l >= 0; l--)
    {
        if (indexr[l] != indexc[l])
        {
            for (k = 0; k < n; k++)
                SWAP(a[k][indexr[l]], a[k][indexc[l]]);
        }
    }

Terminate:
    if (indexr)
        free(indexr);
    if (indexc)
        free(indexc);
    if (ipiv)
        free(ipiv);
    return retValue;
}


// --------------------------------------------------------------
// Map a source point to destination using projective transform
// --------------------------------------------------------------
// Params:
//  sourcePoint: initial point
//  destPoint:   transformed point
// RetValue: 0: Success, !=0: Error
// --------------------------------------------------------------
//  Notes:
//   1. You must call once computeCoeefficients() to compute
//      the this->vc[] vector of 8 coefficients, before you call
//      mapSourceToDestPoint().
//   2. If there was an error or the 8 coefficients were not computed,
//      a -1 is returned and destPoint is just set to sourcePoint value.
// --------------------------------------------------------------
int genImageProjective::mapSourceToDestPoint(QPointF& sourcePoint, QPointF& destPoint)
{
    if (coefficientsComputed)
    {
        float factor = 1.0f / (vc[6] * sourcePoint.x() + vc[7] * sourcePoint.y() + 1.);
        destPoint.setX( factor * (vc[0] * sourcePoint.x() + vc[1] * sourcePoint.y() + vc[2]) );
        destPoint.setY( factor * (vc[3] * sourcePoint.x() + vc[4] * sourcePoint.y() + vc[5]) );
        return 0;
    }
    else // There was an error while computing coefficients
    {
        destPoint = sourcePoint; // just copy the source to destination...
        return -1;               // ...and return an error
    }
}
//========================================
person Fivos Vilanakis    schedule 22.11.2012
comment
Кажется, вы описываете проективные преобразования или омографии. Например, библиотека OpenCV (C ++, Java, Python) имеет функции для оценки этих преобразований и их применения. - person Rui Marques; 16.02.2013
comment
@Rui: Спасибо за информацию. На самом деле я думал использовать и поэкспериментировать с OpenCV когда-нибудь в будущем, так что это дополнительный мотив для этого. - person Fivos Vilanakis; 18.02.2013
comment
Поэтому, когда никакие два ребра не лежат на одной прямой, это решение работает. Когда каждое ребро коллинеарно противоположному, я могу найти аффинное преобразование. Но что, если одна пара ребер коллинеарна, а другая - нет? Как мне это сделать? - person Ivan Kuckir; 07.01.2017
comment
@IvanKuckir решение, которое я представил выше, отлично работает в случае, который вы описываете. Он работает даже тогда, когда обе пары ребер коллинеарны (т.е. в случае нормального прямоугольника), но в этом более позднем случае становится линейным преобразованием;) (PS: я предполагаю, что под словом «коллинеарность» вы подразумеваете параллельность. Если вы на самом деле имеете в виду коллинеарность, пожалуйста, укажите рисунок, потому что это выше моего воображения) - person Fivos Vilanakis; 08.01.2017
comment
@FivosVilanakis Мне очень жаль, у меня была ошибка в моей собственной реализации gaussjordan (), которая не удалась, когда два ребра были параллельны ... Я исправил это, и теперь он работает :) Кстати. Я использую его для преобразования фотографии, пользователь может перетаскивать каждый угол изображения отдельно :) i.imgur. com / tCDfhZx.png - person Ivan Kuckir; 08.01.2017
comment
@IvanKuckir Я рад, что теперь это работает! :) На самом деле я использую его таким же образом: я позволяю пользователю установить 4 произвольных угла, а затем преобразовать область изображения ниже, чтобы перевернуть и исправить искажение перспективы. - person Fivos Vilanakis; 09.01.2017

Используя метод подразделения Бретона (который связан с методом расширения Монго), вы получите точное произвольное деление степени двойки. Чтобы разделить на деления, не являющиеся степенями двойки, с использованием этих методов, вам придется разделить на субпиксельные интервалы, что может быть дорогостоящим в вычислительном отношении.

Однако я считаю, что вы можете применить вариант Haga's Теорема (которая используется в оригами для деления стороны на N-е с учетом стороны, разделенной на (N-1) th) на деление на квадрат в перспективе для получения произвольных делений от ближайшей степени двойки без необходимости дальнейшего деления .

person Sparr    schedule 10.02.2009
comment
Теория Хаги была очень интересным чтением, однако она применима только к квадратам. С переменным соотношением h / w к моим прямоугольникам я, кажется, не могу найти способ применить эту теорию. - person Neil N; 06.02.2010
comment
Вместо того, чтобы переходить к субпиксельной точности, я думаю, что собираюсь выполнить двоичный поиск для каждого подразделения. Поэтому, если мне нужно разделить свой прямоугольник на трети, я бы в основном выполнял двоичный поиск с определенной точностью до 33,33% и 66,66%, а затем выполнял взвешенное деление пополам, используя каждую часть (и под частью я имею в виду, если бы я разделил пополам) 4 раза, это приравняло бы перспективу к правильному 16-му) - person Neil N; 06.02.2010

Самым элегантным и быстрым решением было бы найти матрицу гомографии, которая сопоставляет координаты прямоугольника с координатами фотографии.

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

Ключевые слова: коллинеация, гомография, прямое линейное преобразование.

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

person Ugauga    schedule 31.01.2012

Я думаю, что выбранный ответ - не лучшее решение. Лучшее решение - применить перспективное (проективное) преобразование прямоугольника к простой сетке, как показано в сценарии Matlab и изображении. Вы также можете реализовать этот алгоритм с помощью C ++ и OpenCV.

function drawpersgrid
sz      = [ 24, 16 ]; % [x y]
srcpt   = [ 0 0; sz(1) 0; 0 sz(2); sz(1) sz(2)];
destpt  = [ 20 50; 100 60; 0 150; 200 200;];

% make rectangular grid
[X,Y]   = meshgrid(0:sz(1),0:sz(2));

% find projective transform matching corner points
tform   = maketform('projective',srcpt,destpt);

% apply the projective transform to the grid
[X1,Y1] = tformfwd(tform,X,Y);

hold on;

%% find grid

for i=1:sz(2)
    for j=1:sz(1)
        x = [ X1(i,j);X1(i,j+1);X1(i+1,j+1);X1(i+1,j);X1(i,j)];
        y = [ Y1(i,j);Y1(i,j+1);Y1(i+1,j+1);Y1(i+1,j);Y1(i,j)];
        plot(x,y,'b');
    end
end
hold off;

Проективная сетка

person Tae-Sung Shin    schedule 13.05.2017
comment
@naglas Это не дает мне достаточно информации, чтобы помочь вам. Насколько я понимаю, скрипт работает нормально. - person Tae-Sung Shin; 14.05.2020
comment
Я пробовал это как в Octave, так и в Python, и получил, по сути, ту же ошибку. - person naglas; 14.05.2020
comment
@naglas, почему бы вам не создать вопрос с вашим кодом на Python и не сообщить мне, чтобы я или кто-то другой мог вам помочь. - person Tae-Sung Shin; 14.05.2020

В особом случае, когда вы смотрите перпендикулярно сторонам 1 и 3, вы можете разделить эти стороны на равные части. Затем нарисуйте диагональ и проведите параллели стороне 1 через каждое пересечение диагонали и разделительных линий, проведенных ранее.

person stevenvh    schedule 13.02.2009

Я придумал это геометрическое решение. Я не знаю, есть ли у «алгоритма» название.

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

Цель состоит в том, чтобы разместить точки P1..Pn-1 на верхней линии, которые мы можем использовать, чтобы провести через них линии к точкам, где левая и правая линии пересекаются или параллельны им, когда такой точки не существует.

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

В противном случае разместите n точек Q1..Qn в левой строке так, чтобы они и верхний левый угол были равноудалены, а i ‹j => Qi находился ближе к верхнему левому углу, чем Qj. Чтобы сопоставить Q-точки с верхней линией, найдите пересечение S линии, идущей от Qn через верхний правый угол, и параллели левой линии через пересечение верхней и нижней линий. Теперь соедините S с Q1..Qn-1. Пересечение новых линий с верхней линией - это желаемые P-точки.

Сделайте аналог для горизонтальных линий.

person a427    schedule 14.04.2014

При вращении вокруг оси y, особенно если поверхности вращения плоские, перспектива создается вертикальными градиентами. Они становятся все ближе в перспективе. Вместо использования диагоналей для определения четырех прямоугольников, которые могут работать с заданными степенями двойки ... определите два прямоугольника, левый и правый. В конце концов, они будут больше ширины, если продолжать делить поверхность на более узкие вертикальные сегменты. Это может вместить неквадратные поверхности. Если вращение происходит вокруг оси x, то необходимы горизонтальные градиенты.

person Brian Coyle    schedule 30.09.2015

Что вам нужно сделать, так это представить его в 3D (мир), а затем спроецировать в 2D (экран).

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

Я тоже не смог найти это в Google, но в хороших книгах по компьютерной графике есть подробности.

Ключевые слова: матрица проекции, преобразование проекции, аффинное преобразование, однородный вектор, мировое пространство, пространство экрана, перспективное преобразование, 3D преобразование.

И, кстати, обычно требуется несколько лекций, чтобы все это объяснить. Удачи.

person Pyrolistical    schedule 09.02.2009
comment
Использование трехмерной математики для достижения этой цели является огромным излишеством, чрезвычайно сложным с точки зрения вычислений, чем требуется. - person Sparr; 10.02.2009
comment
Это просто набор математики, не требующий ввода-вывода. Так что это будет супер быстро. - person Pyrolistical; 10.02.2009
comment
супер быстро относительно. Если бы не использовать 3D-математику, это было бы БЫСТРО БЫСТРЕЕ. Затратить миллионы циклов процессора на задачу, которая должна занять тысячи, - это просто напрашиваться на неприятности. Что произойдет, если он решит использовать сетку 1024x1024 вместо 5x5? - person Sparr; 12.02.2009