确定提供的坐标表示多边形还是椭圆

huangapple go评论73阅读模式
英文:

Determine if provided coordinates represent a polygon or an ellipse

问题

我已翻译了您提供的内容中的代码部分:

#include <iostream>
#include <vector>
#include <sstream>
#include <cmath>

// 结构体用于保存坐标数据
struct Coordinate {
    double x;
    double y;
};

// 函数用于检查坐标是否满足椭圆方程
bool isEllipse(const std::vector<Coordinate>& coordinates) {
    // 坐标点数量
    int numPoints = coordinates.size();

    // 计算质心
    double sumX = 0.0, sumY = 0.0;
    for (const auto& coord : coordinates) {
        sumX += coord.x;
        sumY += coord.y;
    }
    double centerX = sumX / numPoints;
    double centerY = sumY / numPoints;

    // 计算主轴和副轴
    double maxDistSqr = 0.0;
    for (const auto& coord : coordinates) {
        double dx = coord.x - centerX;
        double dy = coord.y - centerY;
        double distSqr = dx * dx + dy * dy;
        if (distSqr > maxDistSqr) {
            maxDistSqr = distSqr;
        }
    }
    double a = std::sqrt(maxDistSqr);
    double b = std::sqrt(maxDistSqr);

    // 检查坐标是否满足椭圆方程
    for (const auto& coord : coordinates) {
        double dx = coord.x - centerX;
        double dy = coord.y - centerY;
        double ellipseResult = (dx * dx) / (a * a) + (dy * dy) / (b * b);
        if (ellipseResult > 1.0) {
            return false;
        }
    }

    return true;
}

int main() {
    std::string coordinatesStr = "M/-0.247283/512.418;L/166.935/505.209;L/175.478/415.698;L/141.124/384.968;L/207.244/354.6;L/211.621/292.758;L/269.472/283.712;L/259.446/10.8997;L/773.612/-0.156929;L/773.644/277.548;L/850.632/280.289;L/850.638/335.28;L/927.629/368.266;L/886.392/423.262;L/894.646/470.004;L/1007.98/479.435;L/1015.16/565.507;L/20.5376/572.793;L/-0.0663959/512.529;L/0.189167/513.04";

    std::vector<Coordinate> coordinates;

    // 解析坐标字符串
    std::istringstream iss(coordinatesStr);
    std::string segment;
    while (std::getline(iss, segment, ';')) {
        // 解析 M 或 L 坐标
        if (segment[0] == 'M' || segment[0] == 'L') {
            std::istringstream coordIss(segment.substr(2));
            std::string xStr, yStr;
            if (std::getline(coordIss, xStr, '/') && std::getline(coordIss, yStr, '/')) {
                try {
                    double x = std::stod(xStr);
                    double y = std::stod(yStr);
                    coordinates.push_back({ x, y });
                } catch (const std::invalid_argument& e) {
                    std::cerr << "Failed to parse coordinate: " << segment << std::endl;
                }
            }
        }
    }

    // 检查坐标是否满足椭圆方程
    bool isAnEllipse = isEllipse(coordinates);

    if (isAnEllipse) {
        std::cout << "The coordinates form an ellipse." << std::endl;
    } else {
        std::cout << "The coordinates do not form an ellipse." << std::endl;
    }

    return 0;
}

请注意,我只翻译了代码部分,其他内容保持原样。如果您需要任何进一步的帮助,请随时提出。

英文:

I have a string of coordinates, read from a JSON file, which belongs to a certain ROI, drawn by the user of an application. I am supposed to eventually determine if the coordinates represent a polygon or an ellipse (AKA did the user paint a polygon or an ellipse). I have a serious problem of figuring out the correct algorithm, since it is also a math-related problem. The code have written so far, recognizes an ellipse, but then considers a polygon also an ellipse. My initial idea was to put all the coords inside the ellipse equation and check if it matches the characteristics of an ellipse. But somehow I fail to differentiate it from the polygon.

Example for the format of the coordinates that I retrieve from the JSON file:

Ellipse coordinates:

M/322.504/80.6014;L/322.3/84.7773;L/321.684/88.899;L/319.253/96.9595;L/315.292/104.742;L/309.881/112.205;L/303.102/119.309;L/295.036/126.012;L/285.763/132.273;L/275.364/138.052;L/263.921/143.307;L/251.515/147.997;L/238.226/152.082;L/224.136/155.521;L/209.325/158.273;L/193.875/160.297;L/177.866/161.551;L/161.38/161.996;L/144.892/161.603;L/128.88/160.399;L/113.423/158.423;L/98.6038/155.718;L/84.5029/152.323;L/71.2013/148.28;L/58.7804/143.628;L/47.3212/138.409;L/36.9047/132.663;L/27.6122/126.431;L/19.5247/119.753;L/12.7234/112.671;L/7.28933/105.224;L/3.30368/97.4543;L/0.847538/89.4014;L/0.218384/85.2816;L/0.00202717/81.1064;L/0.205307/76.9305;L/0.821556/72.8088;L/3.25246/64.7483;L/7.21376/56.9658;L/12.6245/49.5023;L/19.4036/42.3987;L/27.4701/35.6959;L/36.7431/29.4347;L/47.1415/23.6562;L/58.5843/18.4012;L/70.9906/13.7107;L/84.2794/9.62544;L/98.3697/6.1865;L/113.18/3.43473;L/128.631/1.41106;L/144.639/0.156398;L/161.126/-0.288348;L/177.613/0.104763;L/193.626/1.30929;L/209.083/3.28456;L/223.902/5.98993;L/238.003/9.38472;L/251.304/13.4283;L/263.725/18.08;L/275.185/23.2991;L/285.601/29.045;L/294.894/35.2771;L/302.981/41.9547;L/309.782/49.037;L/315.216/56.4835;L/319.202/64.2535;L/321.658/72.3064;L/322.287/76.4262;L/322.504/80.6014

Polygon coordinates:

M/0.0102565/69.1651;L/19.019/51.4713;L/19.6427/5.25438;L/111.389/0.385824;L/112.778/24.1719;L/288.807/24.6385;L/288.255/129.985;L/242.72/131.399;L/221.009/162.01;L/138.096/166.188;L/116.982/128.833;L/113.55/100.971;L/65.9781/103.747;L/48.9573/79.3007;L/1.3638/64.406

So, the program is supposed to determine that the first set of coordinates belong to an ellipse, and the second one to a polygon.

Note: the number of coordinates are never the same, since it is fully up to the user, how the polygon should look like, therefore it is not safe to say that the length of the coordinates from a polygon will also be fewer than an ellipse.

I thank you guys in advance and hope to be able to find a solution for this problem.

My current code:


#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;sstream&gt;
#include &lt;cmath&gt;
// Structure to hold coordinate data
struct Coordinate {
double x;
double y;
};
// Function to check if coordinates satisfy the ellipse equation
bool isEllipse(const std::vector&lt;Coordinate&gt;&amp; coordinates) {
// Number of coordinates
int numPoints = coordinates.size();
// Calculate the centroid
double sumX = 0.0, sumY = 0.0;
for (const auto&amp; coord : coordinates) {
sumX += coord.x;
sumY += coord.y;
}
double centerX = sumX / numPoints;
double centerY = sumY / numPoints;
// Calculate the major and minor axes
double maxDistSqr = 0.0;
for (const auto&amp; coord : coordinates) {
double dx = coord.x - centerX;
double dy = coord.y - centerY;
double distSqr = dx * dx + dy * dy;
if (distSqr &gt; maxDistSqr) {
maxDistSqr = distSqr;
}
}
double a = std::sqrt(maxDistSqr);
double b = std::sqrt(maxDistSqr);
// Check if the coordinates satisfy the ellipse equation
for (const auto&amp; coord : coordinates) {
double dx = coord.x - centerX;
double dy = coord.y - centerY;
double ellipseResult = (dx * dx) / (a * a) + (dy * dy) / (b * b);
if (ellipseResult &gt; 1.0) {
return false;
}
}
return true;
}
int main() {
std::string coordinatesStr = &quot;M/-0.247283/512.418;L/166.935/505.209;L/175.478/415.698;L/141.124/384.968;L/207.244/354.6;L/211.621/292.758;L/269.472/283.712;L/259.446/10.8997;L/773.612/-0.156929;L/773.644/277.548;L/850.632/280.289;L/850.638/335.28;L/927.629/368.266;L/886.392/423.262;L/894.646/470.004;L/1007.98/479.435;L/1015.16/565.507;L/20.5376/572.793;L/-0.0663959/512.529;L/0.189167/513.04&quot;;
std::vector&lt;Coordinate&gt; coordinates;
// Parse the coordinate string
std::istringstream iss(coordinatesStr);
std::string segment;
while (std::getline(iss, segment, &#39;;&#39;)) {
// Parse M or L coordinates
if (segment[0] == &#39;M&#39; || segment[0] == &#39;L&#39;) {
std::istringstream coordIss(segment.substr(2));
std::string xStr, yStr;
if (std::getline(coordIss, xStr, &#39;/&#39;) &amp;&amp; std::getline(coordIss, yStr, &#39;/&#39;)) {
try {
double x = std::stod(xStr);
double y = std::stod(yStr);
coordinates.push_back({ x, y });
} catch (const std::invalid_argument&amp; e) {
std::cerr &lt;&lt; &quot;Failed to parse coordinate: &quot; &lt;&lt; segment &lt;&lt; std::endl;
}
}
}
}
// Check if the coordinates satisfy the ellipse equation
bool isAnEllipse = isEllipse(coordinates);
if (isAnEllipse) {
std::cout &lt;&lt; &quot;The coordinates form an ellipse.&quot; &lt;&lt; std::endl;
} else {
std::cout &lt;&lt; &quot;The coordinates do not form an ellipse.&quot; &lt;&lt; std::endl;
}
return 0;
}

答案1

得分: 4

以下是翻译好的部分:

这个问题归结为拟合最佳椭圆(例如,通过最小二乘法),然后决定(归一化的)误差是否小于某个预设的容差。

一般的椭圆(不一定与坐标轴对齐)为ax^2+bxy+cy^2+dx+ey+f=0,其中b^2-4ac<0。

由于椭圆的x^2系数a不能为零,你可以除以它,或者等效地令a=1。然后,你需要拟合自由参数b、c、d、e、f,可以通过最小化平方误差来实现

sum(对所有点求和) (ax^2+bxy+cy^2+dx+ey+f)^2

依次对每个参数进行微分,你会得到五个线性方程来解决五个参数。(如果你想要坐标轴平行的椭圆,可以令b=0,从而将参数数量减少到4个。)

这给出了最佳拟合椭圆,但要决定你的点集是否足够拟合它,你需要查看平方误差和(归一化的,否则它将取决于x和y的单位)。

使用容差为0.1%,你的第一个示例符合椭圆的条件,而第二个示例不符合。

#include <iostream>
#include <vector>
#include <utility>
#include <cmath>
using namespace std;

// 结构体定义和函数定义...

输出:

a, b, c, d, e, f = 1  0.00923995  3.94893  -323.252  -640.062  25930.5
Normalised squared error = 6.12962e-09
true

a, b, c, d, e, f = 1  -0.67009  1.80408  -264.498  -137.44  9896.96
Normalised squared error = 0.0197783
false
英文:

Well, I guess it comes down to fitting the best ellipse (e.g. by least squares) and then deciding whether the (normalised) error is less than some pre-set tolerance.

The general ellipse (which is NOT necessarily aligned with the coordinate axes) is ax<sup>2</sup>+bxy+cy<sup>2</sup>+dx+ey+f=0, with b<sup>2</sup>-4ac<0.

Since the coefficient of x<sup>2</sup>, a, can't be zero for an ellipse you can divide through by this, or, equivalently, take a=1. You then need to fit the free parameters b, c, d, e, f which you can do by minimising the squared error

sum(over all points) (ax<sup>2</sup>+bxy+cy<sup>2</sup>+dx+ey+f)<sup>2</sup>

Differentiating w.r.t. each parameter in turn you get five linear equations for the five parameters. (If you want an ellipse with axes parallel to the coordinate axes then take b=0 and reduce the number of parameters to 4.)

This gives the best-fit ellipse, but to decide whether your set of points fits it adequately you need to look at the sum-of-squared-errors (or, rather, that normalised, since it would otherwise depend on the units for x and y).

With a tolerance of 0.1%, your first example fits an ellipse and your second doesn't.

#include &lt;iostream&gt;
#include &lt;vector&gt;
#include &lt;utility&gt;
#include &lt;cmath&gt;
using namespace std;
struct Pt{ double x, y; };
bool isEllipse( const vector&lt;Pt&gt; &amp;S );
bool solve( vector&lt;vector&lt;double&gt;&gt; A, vector&lt;double&gt; B, vector&lt;double&gt; &amp;X );
//======================================================================
int main()
{
bool result;
vector&lt;Pt&gt; case1 = {
{322.504,80.6014}, {322.3,84.7773}, {321.684,88.899}, {319.253,96.9595}, {315.292,104.742},
{309.881,112.205}, {303.102,119.309}, {295.036,126.012}, {285.763,132.273}, {275.364,138.052}, 
{263.921,143.307}, {251.515,147.997}, {238.226,152.082}, {224.136,155.521}, 
{209.325,158.273}, {193.875,160.297}, {177.866,161.551}, {161.38,161.996}, 
{144.892,161.603}, {128.88,160.399}, {113.423,158.423}, {98.6038,155.718}, 
{84.5029,152.323}, {71.2013,148.28}, {58.7804,143.628}, {47.3212,138.409}, 
{36.9047,132.663}, {27.6122,126.431}, {19.5247,119.753}, {12.7234,112.671}, 
{7.28933,105.224}, {3.30368,97.4543}, {0.847538,89.4014}, {0.218384,85.2816}, 
{0.00202717,81.1064}, {0.205307,76.9305}, {0.821556,72.8088}, {3.25246,64.7483}, 
{7.21376,56.9658}, {12.6245,49.5023}, {19.4036,42.3987}, {27.4701,35.6959}, 
{36.7431,29.4347}, {47.1415,23.6562}, {58.5843,18.4012}, {70.9906,13.7107}, 
{84.2794,9.62544}, {98.3697,6.1865}, {113.18,3.43473}, {128.631,1.41106}, 
{144.639,0.156398}, {161.126,-0.288348}, {177.613,0.104763}, {193.626,1.30929}, 
{209.083,3.28456}, {223.902,5.98993}, {238.003,9.38472}, {251.304,13.4283}, 
{263.725,18.08}, {275.185,23.2991}, {285.601,29.045}, {294.894,35.2771}, 
{302.981,41.9547}, {309.782,49.037}, {315.216,56.4835}, {319.202,64.2535}, 
{321.658,72.3064}, {322.287,76.4262}, {322.504,80.6014} };
result = isEllipse( case1 );
cout &lt;&lt; boolalpha &lt;&lt; result &lt;&lt; &quot;\n\n\n&quot;;
vector&lt;Pt&gt; case2 = {
{0.0102565,69.1651}, {19.019,51.4713}, {19.6427,5.25438}, {111.389,0.385824}, {112.778,24.1719}, {288.807,24.6385}, 
{288.255,129.985}, {242.72,131.399}, {221.009,162.01}, {138.096,166.188}, {116.982,128.833}, {113.55,100.971}, {65.9781,103.747}, 
{48.9573,79.3007}, {1.3638,64.406 } };
result = isEllipse( case2 );
cout &lt;&lt; boolalpha &lt;&lt; result &lt;&lt; &quot;\n\n\n&quot;;
}
//=====================================================================
// Fit best ellipse of form    x^2 + bxy + cy^2 + dx + ey + f = 0
bool isEllipse( const vector&lt;Pt&gt; &amp;S )
{
const double EPS = 1.0e-3;                    // tolerance on normalised squared error
int N = 5;                                    // number of parameters (b, c, d, e, f )
if ( S.size() &lt; N ) return false;             // too small; any ellipse wouldn&#39;t be unique
vector&lt;vector&lt;double&gt;&gt; M( N, vector&lt;double&gt;( N ) );     // N equations for N parameters
vector&lt;double&gt; rhs( N ), coeff( N );
for ( Pt p : S )
{
double x = p.x, y = p.y;
vector&lt;double&gt; w = { x * y, y * y, x, y, 1 };        // weights on the parameters
for ( int i = 0; i &lt; N; i++ )
{
for ( int j = 0; j &lt; N; j++ ) M[i][j] += w[i] * w[j];
rhs[i] -= w[i] * x * x;
}
}
if ( !solve( M, rhs, coeff ) ) return false;            // solve for parameters
double a = 1.0, b = coeff[0], c = coeff[1], d = coeff[2], e = coeff[3], f = coeff[4];
cout &lt;&lt; &quot;a, b, c, d, e, f = &quot; &lt;&lt; a &lt;&lt; &quot;  &quot; &lt;&lt; b &lt;&lt; &quot;  &quot; &lt;&lt; c &lt;&lt; &quot;  &quot; &lt;&lt; d &lt;&lt; &quot;  &quot; &lt;&lt; e &lt;&lt; &quot;  &quot; &lt;&lt; f &lt;&lt; &#39;\n&#39;;
if ( b * b - 4.0 * a * c &gt;= 0.0 ) return false;         // Wrong conic section
double sumEsq = 0.0, sumRsq = 0.0;
for ( Pt p : S )
{
double x = p.x, y = p.y;
vector&lt;double&gt; w = { x * y, y * y, x, y, 1 };
double error = x * x, rsq = x * x + y * y;
for ( int j = 0; j &lt; N; j++ ) error += w[j] * coeff[j];
sumEsq += error * error;
sumRsq += rsq   * rsq  ;
}
sumEsq /= sumRsq;
cout &lt;&lt; &quot;Normalised squared error = &quot; &lt;&lt; sumEsq &lt;&lt; &#39;\n&#39;;
return sumEsq &lt; EPS;
}
//======================================================================
bool solve( vector&lt;vector&lt;double&gt;&gt; A, vector&lt;double&gt; B, vector&lt;double&gt; &amp;X )
// Solve AX = B by Gaussian elimination
// Note: copies deliberately made of A and B through passing by value
{
const double SMALL = 1.0e-10;
int n = A.size();
// Row operations for i = 0, ,,,, n - 2 (n-1 not needed)
for ( int i = 0; i &lt; n - 1; i++ )
{
// Pivot: find row r below with largest element in column i
int r = i;
double maxA = abs( A[i][i] );
for ( int k = i + 1; k &lt; n; k++ )
{
double val = abs( A[k][i] );
if ( val &gt; maxA )
{
r = k;
maxA = val;
}
}
if ( r != i )
{
for ( int j = i; j &lt; n; j++ ) swap( A[i][j], A[r][j] );
swap( B[i], B[r] );
}
// Row operations to make upper-triangular
double pivot = A[i][i];
if ( abs( pivot ) &lt; SMALL ) return false;            // Singular matrix
for ( int r = i + 1; r &lt; n; r++ )                    // On lower rows
{
double multiple = A[r][i] / pivot;                // Multiple of row i to clear element in ith column
for ( int j = i; j &lt; n; j++ ) A[r][j] -= multiple * A[i][j];
B[r] -= multiple * B[i];
}
}
// Check last row
if ( abs( A[n-1][n-1] ) &lt; SMALL ) return false;         // Singular matrix
// Back-substitute
X = B;
for ( int i = n - 1; i &gt;= 0; i-- )
{
for ( int j = i + 1; j &lt; n; j++ )  X[i] -= A[i][j] * X[j];
X[i] /= A[i][i];
}
return true;
}

Output:

a, b, c, d, e, f = 1  0.00923995  3.94893  -323.252  -640.062  25930.5
Normalised squared error = 6.12962e-09
true
a, b, c, d, e, f = 1  -0.67009  1.80408  -264.498  -137.44  9896.96
Normalised squared error = 0.0197783
false

huangapple
  • 本文由 发表于 2023年7月10日 21:59:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76654506.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定