从两个UIViews中的每一个中的三个点计算CGAffineTransform的值?

时间:2023-02-09 10:58:27

I've got two UIViews, each of which contains a visual representation of an object. I have three known CGPoints in each UIView which correspond to the same locations on that object. I need to apply a transform to one of those views so those three points line up perfectly with the corresponding points in the other view.

我有两个UIViews,每个都包含一个对象的可视化表示。我在每个UIView中有三个已知的CGPoints,它们对应于该对象上的相同位置。我需要将变换应​​用于其中一个视图,以便这三个点与另一个视图中的对应点完美对齐。

I need to deal with scaling, rotation and translation, i.e. an affine transformation, and I know the math for calculating the parameters for such a translation (see this question). What I DON'T understand is how to actually perform those calculations in Obj-C and plug the correct numbers into CGAffineTrasnformMake. It seems like it should be obvious, but for some reason I think I'm just missing some part of the concept here.

我需要处理缩放,旋转和平移,即仿射变换,我知道计算这种翻译参数的数学(参见这个问题)。我不明白的是如何在Obj-C中实际执行这些计算并将正确的数字插入CGAffineTrasnformMake。它似乎应该是显而易见的,但出于某种原因,我认为我只是错过了这里概念的某些部分。

So in short form, given CGPoints (X1a,Y1a), (X2a,Y2a), (X3a,Y3a) in UIView A, and (X1b,Y1b), (X2b,Y2b), (X3b,Y3b) in UIView B, what do I do to get myself a CGAffineTransform I can apply to UIView B? so it lines up with UIView A?

所以在简短形式中,给出UIView A中的CGPoints(X1a,Y1a),(X2a,Y2a),(X3a,Y3a),以及UIView B中的(X1b,Y1b),(X2b,Y2b),(X3b,Y3b),我怎么办才能让自己成为一名CGAffineTransform我可以申请UIView B?它与UIView A排列在一起?

My deployment target, by the way, is iOS 5; I do NOT need to support anything earlier.

顺便说一下,我的部署目标是iOS 5;我不需要先支持任何事情。

Thanks!

1 个解决方案

#1


1  

I need the exact same thing in iOS. Here is what I finally came up with... I know the algebra calculation, but I don't know how to use LAPACK. You need to add Accelerate framework to calculate matrix inverse. Assume you have points p1, p2, p3 in the original UIView, and q1, q2, q3 in the transformed UIView:

我在iOS中需要完全相同的东西。这是我最终想出来的......我知道代数计算,但我不知道如何使用LAPACK。您需要添加Accelerate框架来计算矩阵逆。假设在原始UIView中有点p1,p2,p3,在变换后的UIView中有q1,q2,q3:

CGPoint p1, p2, p3, q1, q2, q3;

// TODO: initialize points

double A[36];

A[ 0] = p1.x; A[ 1] = p1.y; A[ 2] = 0; A[ 3] = 0; A[ 4] = 1; A[ 5] = 0;
A[ 6] = 0; A[ 7] = 0; A[ 8] = p1.x; A[ 9] = p1.y; A[10] = 0; A[11] = 1;
A[12] = p2.x; A[13] = p2.y; A[14] = 0; A[15] = 0; A[16] = 1; A[17] = 0;
A[18] = 0; A[19] = 0; A[20] = p2.x; A[21] = p2.y; A[22] = 0; A[23] = 1;
A[24] = p3.x; A[25] = p3.y; A[26] = 0; A[27] = 0; A[28] = 1; A[29] = 0;
A[30] = 0; A[31] = 0; A[32] = p3.x; A[33] = p3.y; A[34] = 0; A[35] = 1;

int err = matrix_invert(6, A);
assert(err == 0);

double B[6];

B[0] = q1.x; B[1] = q1.y; B[2] = q2.x; B[3] = q2.y; B[4] = q3.x; B[5] = q3.y;

double M[6];

M[0] = A[ 0] * B[0] + A[ 1] * B[1] + A[ 2] * B[2] + A[ 3] * B[3] + A[ 4] * B[4] + A[ 5] * B[5];
M[1] = A[ 6] * B[0] + A[ 7] * B[1] + A[ 8] * B[2] + A[ 9] * B[3] + A[10] * B[4] + A[11] * B[5];
M[2] = A[12] * B[0] + A[13] * B[1] + A[14] * B[2] + A[15] * B[3] + A[16] * B[4] + A[17] * B[5];
M[3] = A[18] * B[0] + A[19] * B[1] + A[20] * B[2] + A[21] * B[3] + A[22] * B[4] + A[23] * B[5];
M[4] = A[24] * B[0] + A[25] * B[1] + A[26] * B[2] + A[27] * B[3] + A[28] * B[4] + A[29] * B[5];
M[5] = A[30] * B[0] + A[31] * B[1] + A[32] * B[2] + A[33] * B[3] + A[34] * B[4] + A[35] * B[5];

NSLog(@"%f, %f, %f, %f, %f, %f", M[0], M[1], M[2], M[3], M[4], M[5]);

CGAffineTransform transform = CGAffineTransformMake(M[0], M[2], M[1], M[3], M[4], M[5]); // Order is correct... 

Here is the definition of matrix_inverse, it's a C function. It's modified from another SO answer:

这是matrix_inverse的定义,它是一个C函数。它是从另一个SO答案修改:

#import <Accelerate/Accelerate.h>
#include <stdlib.h>

int matrix_invert(long N, double *matrix) {

    long error=0;
    long *pivot = malloc(N*N*sizeof(long));
    double *workspace = malloc(N*sizeof(double));

    dgetrf_(&N, &N, matrix, &N, pivot, &error);

    if (error != 0) {
        // NSLog(@"Error 1");
        return error;
    }

    dgetri_(&N, matrix, &N, pivot, workspace, &N, &error);

    if (error != 0) {
        // NSLog(@"Error 2");
        return error;
    }

    free(pivot);
    free(workspace);
    return error;
}

Here is the source code on github, I might refactor it into a function taking NSArray of CGPoints later.

这是github上的源代码,我可能会将它重构为一个稍后使用NSArray的CGPoints的函数。

If you have more than 3 points, you need to solve by least square fit. Matrix inverse can do , but is not numerical stable (results can be off a lot), I might also add this solution into the gist later. The correct way is using Singular Value Decomposition, which I don't know how to do with LAPACK.

如果你有超过3分,你需要通过最小平方拟合来解决。矩阵逆可以做,但不是数值稳定的(结果可能会很多),我也可能稍后将这个解决方案添加到gist中。正确的方法是使用奇异值分解,我不知道如何处理LAPACK。

#1


1  

I need the exact same thing in iOS. Here is what I finally came up with... I know the algebra calculation, but I don't know how to use LAPACK. You need to add Accelerate framework to calculate matrix inverse. Assume you have points p1, p2, p3 in the original UIView, and q1, q2, q3 in the transformed UIView:

我在iOS中需要完全相同的东西。这是我最终想出来的......我知道代数计算,但我不知道如何使用LAPACK。您需要添加Accelerate框架来计算矩阵逆。假设在原始UIView中有点p1,p2,p3,在变换后的UIView中有q1,q2,q3:

CGPoint p1, p2, p3, q1, q2, q3;

// TODO: initialize points

double A[36];

A[ 0] = p1.x; A[ 1] = p1.y; A[ 2] = 0; A[ 3] = 0; A[ 4] = 1; A[ 5] = 0;
A[ 6] = 0; A[ 7] = 0; A[ 8] = p1.x; A[ 9] = p1.y; A[10] = 0; A[11] = 1;
A[12] = p2.x; A[13] = p2.y; A[14] = 0; A[15] = 0; A[16] = 1; A[17] = 0;
A[18] = 0; A[19] = 0; A[20] = p2.x; A[21] = p2.y; A[22] = 0; A[23] = 1;
A[24] = p3.x; A[25] = p3.y; A[26] = 0; A[27] = 0; A[28] = 1; A[29] = 0;
A[30] = 0; A[31] = 0; A[32] = p3.x; A[33] = p3.y; A[34] = 0; A[35] = 1;

int err = matrix_invert(6, A);
assert(err == 0);

double B[6];

B[0] = q1.x; B[1] = q1.y; B[2] = q2.x; B[3] = q2.y; B[4] = q3.x; B[5] = q3.y;

double M[6];

M[0] = A[ 0] * B[0] + A[ 1] * B[1] + A[ 2] * B[2] + A[ 3] * B[3] + A[ 4] * B[4] + A[ 5] * B[5];
M[1] = A[ 6] * B[0] + A[ 7] * B[1] + A[ 8] * B[2] + A[ 9] * B[3] + A[10] * B[4] + A[11] * B[5];
M[2] = A[12] * B[0] + A[13] * B[1] + A[14] * B[2] + A[15] * B[3] + A[16] * B[4] + A[17] * B[5];
M[3] = A[18] * B[0] + A[19] * B[1] + A[20] * B[2] + A[21] * B[3] + A[22] * B[4] + A[23] * B[5];
M[4] = A[24] * B[0] + A[25] * B[1] + A[26] * B[2] + A[27] * B[3] + A[28] * B[4] + A[29] * B[5];
M[5] = A[30] * B[0] + A[31] * B[1] + A[32] * B[2] + A[33] * B[3] + A[34] * B[4] + A[35] * B[5];

NSLog(@"%f, %f, %f, %f, %f, %f", M[0], M[1], M[2], M[3], M[4], M[5]);

CGAffineTransform transform = CGAffineTransformMake(M[0], M[2], M[1], M[3], M[4], M[5]); // Order is correct... 

Here is the definition of matrix_inverse, it's a C function. It's modified from another SO answer:

这是matrix_inverse的定义,它是一个C函数。它是从另一个SO答案修改:

#import <Accelerate/Accelerate.h>
#include <stdlib.h>

int matrix_invert(long N, double *matrix) {

    long error=0;
    long *pivot = malloc(N*N*sizeof(long));
    double *workspace = malloc(N*sizeof(double));

    dgetrf_(&N, &N, matrix, &N, pivot, &error);

    if (error != 0) {
        // NSLog(@"Error 1");
        return error;
    }

    dgetri_(&N, matrix, &N, pivot, workspace, &N, &error);

    if (error != 0) {
        // NSLog(@"Error 2");
        return error;
    }

    free(pivot);
    free(workspace);
    return error;
}

Here is the source code on github, I might refactor it into a function taking NSArray of CGPoints later.

这是github上的源代码,我可能会将它重构为一个稍后使用NSArray的CGPoints的函数。

If you have more than 3 points, you need to solve by least square fit. Matrix inverse can do , but is not numerical stable (results can be off a lot), I might also add this solution into the gist later. The correct way is using Singular Value Decomposition, which I don't know how to do with LAPACK.

如果你有超过3分,你需要通过最小平方拟合来解决。矩阵逆可以做,但不是数值稳定的(结果可能会很多),我也可能稍后将这个解决方案添加到gist中。正确的方法是使用奇异值分解,我不知道如何处理LAPACK。