findHomography, getPerspectiveTransform и getAffineTransform

Этот вопрос о функциях OpenCV findHomography, getPerspectiveTransform & getAffineTransform

  1. В чем разница между findHomography а также getPerspectiveTransform?. Насколько я понимаю из документации, getPerspectiveTransform вычисляет преобразование с использованием 4 соответствий (что является минимумом, необходимым для вычисления преобразования гомографии / перспективы), где как findHomography вычисляет преобразование, даже если вы предоставляете более 4 соответствий (предположительно, используя метод наименьших квадратов?). Это правильно? (В этом случае единственная причина, по которой OpenCV все еще продолжает поддерживать getPerspectiveTransform, должна быть устаревшей?)

  2. Моя следующая проблема заключается в том, что я хочу знать, есть ли эквивалент findHomography для вычисления аффинного преобразования? т.е. функция, которая использует метод наименьших квадратов или эквивалентный надежный метод для вычисления и аффинного преобразования. Согласно документации getAffineTransform занимает всего 3 соответствия (это минимум, необходимый для вычисления аффинного преобразования).

Лучший,

4 ответа

Q # 1: Да, findHomography пытается найти лучшее преобразование между двумя наборами точек. Он использует нечто более умное, чем наименьшие квадраты, называемое RANSAC, которое способно отклонять выбросы - если хотя бы 50% + 1 ваших точек данных в порядке, RANSAC приложит все усилия, чтобы найти их и построить надежное преобразование.

У getPerspectiveTransform есть много полезных причин остаться - это основа для findHomography, и она полезна во многих ситуациях, когда у вас есть только 4 балла, и вы знаете, что они правильные. FindHomography обычно используется с автоматическими наборами точек - вы можете найти множество из них, но с низкой достоверностью. Функция getPerspectiveTransform хороша, если вы точно знаете 4 угла - например, ручная маркировка или автоматическое определение прямоугольника.

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

Я согласен со всем, что написал @vasile. Я просто хочу добавить несколько замечаний:

getPerspectiveTransform() а также getAffineTransform() предназначены для работы на 4 или 3 балла (соответственно), которые, как известно, являются правильными соответствиями. На реальных изображениях, снятых реальной камерой, вы никогда не сможете получить такие точные соответствия, как с помощью автоматической, так и ручной маркировки соответствующих точек.

Всегда есть выбросы. Достаточно взглянуть на простой случай, когда вы хотите подогнать кривую через точки y1 = f(x) = 3.12x + gauss_noise или же y2 = g(x) = 0.1x^2 + 3.1x + gauss_noise): гораздо проще найти хорошую квадратичную функцию для оценки точек в обоих случаях, чем хорошую линейную. Квадратик может быть излишним, но в большинстве случаев его не будет (после удаления выбросов), и если вы хотите разместить там прямую линию, лучше быть уверенным, что это правильная модель, иначе вы получите непригодные результаты.

Тем не менее, если вы абсолютно уверены, что аффинное преобразование является правильным, вот предложение:

  • использование findHomography, который имеет RANSAC, включенный в функциональность, чтобы избавиться от выбросов и получить первоначальную оценку преобразования изображения
  • выберите 3 правильных соответствия-соответствия (которые соответствуют найденной гомографии), или перепроектируйте 3 точки от 1-го изображения до 2-го (используя гомографию)
  • используйте эти 3 совпадения (которые настолько близки к правильному, насколько это возможно) в getAffineTransform()
  • оберните все это в свой собственный findAffine() если хочешь - и вуаля!

Существует простое решение для нахождения аффинного преобразования для системы переопределенных уравнений.

  1. Обратите внимание, что в общем случае аффинное преобразование находит решение переопределенной системы линейных уравнений Ax=B, используя псевдообратную или подобную технику, поэтому

x = (AAt)-1 At B

Более того, это обрабатывается в основной функциональности openCV простым вызовом решения (A, B, X).

  1. Ознакомьтесь с кодом преобразования Affine в opencv/modules/imgproc/src/imgwarp.cpp: он действительно выполняет только две вещи:

    а. переставляет входы для создания системы Ax=B;

    б. тогда вызовы решают (A, B, X);

ПРИМЕЧАНИЕ: игнорируйте комментарии функции в коде openCV - они сбивают с толку и не отражают фактическое упорядочение элементов в матрицах. Если вы решаете [u, v] '= Affine * [x, y, 1], перестановка:

         x1 y1 1 0  0  1
         0  0  0 x1 y1 1
         x2 y2 1 0  0  1
    A =  0  0  0 x2 y2 1
         x3 y3 1 0  0  1
         0  0  0 x3 y3 1

    X = [Affine11, Affine12, Affine13, Affine21, Affine22, Affine23]’

         u1 v1
    B =  u2 v2
         u3 v3 

Все, что вам нужно сделать, это добавить больше очков. Чтобы заставить Solve(A, B, X) работать в переопределенной системе, добавьте параметр DECOMP_SVD. Чтобы увидеть слайды PowerPoint по теме, используйте эту ссылку. Если вы хотите узнать больше о псевдообращении в контексте компьютерного зрения, лучшим источником будет: ComputerVision, см. Главу 15 и приложение C.

Если вы все еще не знаете, как добавить больше очков, посмотрите мой код ниже:

// extension for n points;
cv::Mat getAffineTransformOverdetermined( const Point2f src[], const Point2f dst[], int n )
{
    Mat M(2, 3, CV_64F), X(6, 1, CV_64F, M.data); // output
    double* a = (double*)malloc(12*n*sizeof(double));
    double* b = (double*)malloc(2*n*sizeof(double));
    Mat A(2*n, 6, CV_64F, a), B(2*n, 1, CV_64F, b); // input

    for( int i = 0; i < n; i++ )
    {
        int j = i*12;   // 2 equations (in x, y) with 6 members: skip 12 elements
        int k = i*12+6; // second equation: skip extra 6 elements
        a[j] = a[k+3] = src[i].x;
        a[j+1] = a[k+4] = src[i].y;
        a[j+2] = a[k+5] = 1;
        a[j+3] = a[j+4] = a[j+5] = 0;
        a[k] = a[k+1] = a[k+2] = 0;
        b[i*2] = dst[i].x;
        b[i*2+1] = dst[i].y;
    }

    solve( A, B, X, DECOMP_SVD );
    delete a;
    delete b;
    return M;
}

// call original transform
vector<Point2f> src(3);
vector<Point2f> dst(3);
src[0] = Point2f(0.0, 0.0);src[1] = Point2f(1.0, 0.0);src[2] = Point2f(0.0, 1.0);
dst[0] = Point2f(0.0, 0.0);dst[1] = Point2f(1.0, 0.0);dst[2] = Point2f(0.0, 1.0);
Mat M = getAffineTransform(Mat(src), Mat(dst));
cout<<M<<endl;
// call new transform
src.resize(4); src[3] = Point2f(22, 2);
dst.resize(4); dst[3] = Point2f(22, 2);
Mat M2 = getAffineTransformOverdetermined(src.data(), dst.data(), src.size());
cout<<M2<<endl;

В отношении Q#2, measureRigidTransform является эквивалентом getAffineTransform с избыточной дискретизацией. Я не знаю, было ли это в OCV, когда это было впервые опубликовано, но это доступно в 2.4.

Get AffineTransform: аффинное преобразование - это сочетание сдвига, масштабирования, сдвига и вращения https://www.mathworks.com/discovery/affine-transformation.html https://www.tutorialspoint.com/computer_graphics/2d_transformation.htm

getPerspectiveTransform: перспективное преобразование - отображение проекта, введите описание изображения здесь

Другие вопросы по тегам