如何确定形状的旋转?

2023-11-30

我有以下形状。

enter image description here

它可能会旋转未知的角度。我想确定其相对于水平轴的旋转(因此上面的形状的旋转等于 0)。到目前为止,我提出的最好的想法是确定形状的轮廓,找到最小面积矩形,然后将其旋转作为形状本身的旋转。

Mat mask = imread("path_to_image");
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
vector<RotatedRect> rotatedRects;

findContours(mask, contours, hierarchy, RetrievalModes::RETR_TREE, ContourApproximationModes::CHAIN_APPROX_SIMPLE);

const auto& largestContour = max_element(contours.begin(), contours.end(),
    [](const auto& e1, const auto& e2) { return e1.size() < e2.size(); });
RotatedRect rotatedRect = minAreaRect(*largestContour);

问题是矩形没有以预期的方式与形状接壤。

enter image description here

我不确定我是否可以接受它并简单地计算旋转,因为形状来自其他图像处理,并且我不知道矩形是否不会放置在不同的对角线上。

有没有更可靠、更好的方法来找到这种形状的旋转?

Edit:具有形状的图像可以具有不同的比例。


我从这里改编了我的答案:https://stackoverflow.com/a/23993030/2393191它给出了相当好的结果:

inline void getCircle(cv::Point2f& p1, cv::Point2f& p2, cv::Point2f& p3, cv::Point2f& center, float& radius)
{
    float x1 = p1.x;
    float x2 = p2.x;
    float x3 = p3.x;

    float y1 = p1.y;
    float y2 = p2.y;
    float y3 = p3.y;

    // PLEASE CHECK FOR TYPOS IN THE FORMULA :)
    center.x = (x1*x1 + y1*y1)*(y2 - y3) + (x2*x2 + y2*y2)*(y3 - y1) + (x3*x3 + y3*y3)*(y1 - y2);
    center.x /= (2 * (x1*(y2 - y3) - y1*(x2 - x3) + x2*y3 - x3*y2));

    center.y = (x1*x1 + y1*y1)*(x3 - x2) + (x2*x2 + y2*y2)*(x1 - x3) + (x3*x3 + y3*y3)*(x2 - x1);
    center.y /= (2 * (x1*(y2 - y3) - y1*(x2 - x3) + x2*y3 - x3*y2));

    radius = sqrt((center.x - x1)*(center.x - x1) + (center.y - y1)*(center.y - y1));
}



std::vector<cv::Point2f> getPointPositions(cv::Mat binaryImage)
{
    std::vector<cv::Point2f> pointPositions;

    for (unsigned int y = 0; y<binaryImage.rows; ++y)
    {
        //unsigned char* rowPtr = binaryImage.ptr<unsigned char>(y);
        for (unsigned int x = 0; x<binaryImage.cols; ++x)
        {
            //if(rowPtr[x] > 0) pointPositions.push_back(cv::Point2i(x,y));
            if (binaryImage.at<unsigned char>(y, x) > 0) pointPositions.push_back(cv::Point2f(x, y));
        }
    }

    return pointPositions;
}


float verifyCircle(cv::Mat dt, cv::Point2f center, float radius, std::vector<cv::Point2f> & inlierSet)
{
    unsigned int counter = 0;
    unsigned int inlier = 0;
    float minInlierDist = 2.0f;
    float maxInlierDistMax = 100.0f;
    float maxInlierDist = radius / 25.0f;
    if (maxInlierDist<minInlierDist) maxInlierDist = minInlierDist;
    if (maxInlierDist>maxInlierDistMax) maxInlierDist = maxInlierDistMax;

    // choose samples along the circle and count inlier percentage
    for (float t = 0; t<2 * 3.14159265359f; t += 0.05f)
    {
        counter++;
        float cX = radius*cos(t) + center.x;
        float cY = radius*sin(t) + center.y;

        if (cX < dt.cols)
            if (cX >= 0)
                if (cY < dt.rows)
                    if (cY >= 0)
                        if (dt.at<float>(cY, cX) < maxInlierDist)
                        {
                            inlier++;
                            inlierSet.push_back(cv::Point2f(cX, cY));
                        }
    }

    return (float)inlier / float(counter);
}

float evaluateCircle(cv::Mat dt, cv::Point2f center, float radius)
{

    float completeDistance = 0.0f;
    int counter = 0;

    float maxDist = 1.0f;   //TODO: this might depend on the size of the circle!

    float minStep = 0.001f;
    // choose samples along the circle and count inlier percentage

    //HERE IS THE TRICK that no minimum/maximum circle is used, the number of generated points along the circle depends on the radius.
    // if this is too slow for you (e.g. too many points created for each circle), increase the step parameter, but only by factor so that it still depends on the radius

    // the parameter step depends on the circle size, otherwise small circles will create more inlier on the circle
    float step = 2 * 3.14159265359f / (6.0f * radius);
    if (step < minStep) step = minStep; // TODO: find a good value here.

    //for(float t =0; t<2*3.14159265359f; t+= 0.05f) // this one which doesnt depend on the radius, is much worse!
    for (float t = 0; t<2 * 3.14159265359f; t += step)
    {
        float cX = radius*cos(t) + center.x;
        float cY = radius*sin(t) + center.y;

        if (cX < dt.cols)
            if (cX >= 0)
                if (cY < dt.rows)
                    if (cY >= 0)
                        if (dt.at<float>(cY, cX) <= maxDist)
                        {
                            completeDistance += dt.at<float>(cY, cX);
                            counter++;
                        }

    }

    return counter;
}




int main(int argc, char* argv[])
{

    cv::Mat input = cv::imread("C:/StackOverflow/Input/rotatedShape1.png", cv::IMREAD_GRAYSCALE);
    std::string outString = "C:/StackOverflow/Output/rotatedShape1.png";

    cv::Mat output;
    cv::cvtColor(input, output, cv::COLOR_GRAY2BGR);

    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(input, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE);

    std::vector<cv::Point> biggestContour;
    double biggestArea = 0;
    for (int i = 0; i < contours.size(); ++i)
    {
        double cArea = cv::contourArea(contours[i]);
        if (cArea > biggestArea)
        {
            biggestArea = cArea;
            biggestContour = contours[i];
        }
    }

    if (biggestContour.size() == 0)
    {
        std::cout << "error: no contour found. Press enter to quit." << std::endl;
        std::cin.get();
        return 0;
    }



    cv::Mat mask = cv::Mat::zeros(input.size(), input.type());
    std::vector < std::vector<cv::Point> > tmp;
    tmp.push_back(biggestContour);
    cv::drawContours(mask, tmp, 0, cv::Scalar::all(255), 1); // contour points in the image

    std::vector<cv::Point2f> circlesList;

    unsigned int numberOfCirclesToDetect = 2;   // TODO: if unknown, you'll have to find some nice criteria to stop finding more (semi-) circles

    for (unsigned int j = 0; j<numberOfCirclesToDetect; ++j)
    {
        std::vector<cv::Point2f> edgePositions;
        //for (int i = 0; i < biggestContour.size(); ++i) edgePositions.push_back(biggestContour[i]);
        edgePositions = getPointPositions(mask);



        std::cout << "number of edge positions: " << edgePositions.size() << std::endl;

        // create distance transform to efficiently evaluate distance to nearest edge
        cv::Mat dt;
        cv::distanceTransform(255 - mask, dt, CV_DIST_L1, 3);



        unsigned int nIterations = 0;

        cv::Point2f bestCircleCenter;
        float bestCircleRadius;
        //float bestCVal = FLT_MAX;
        float bestCVal = -1;

        //float minCircleRadius = 20.0f; // TODO: if you have some knowledge about your image you might be able to adjust the minimum circle radius parameter.
        float minCircleRadius = 0.0f;

        //TODO: implement some more intelligent ransac without fixed number of iterations
        for (unsigned int i = 0; i<2000; ++i)
        {
            //RANSAC: randomly choose 3 point and create a circle:
            //TODO: choose randomly but more intelligent,
            //so that it is more likely to choose three points of a circle.
            //For example if there are many small circles, it is unlikely to randomly choose 3 points of the same circle.
            unsigned int idx1 = rand() % edgePositions.size();
            unsigned int idx2 = rand() % edgePositions.size();
            unsigned int idx3 = rand() % edgePositions.size();

            // we need 3 different samples:
            if (idx1 == idx2) continue;
            if (idx1 == idx3) continue;
            if (idx3 == idx2) continue;

            // create circle from 3 points:
            cv::Point2f center; float radius;
            getCircle(edgePositions[idx1], edgePositions[idx2], edgePositions[idx3], center, radius);

            if (radius < minCircleRadius)continue;


            //verify or falsify the circle by inlier counting:
            //float cPerc = verifyCircle(dt,center,radius, inlierSet);
            float cVal = evaluateCircle(dt, center, radius);

            if (cVal > bestCVal)
            {
                bestCVal = cVal;
                bestCircleRadius = radius;
                bestCircleCenter = center;
            }

            ++nIterations;
        }
        std::cout << "current best circle: " << bestCircleCenter << " with radius: " << bestCircleRadius << " and nInlier " << bestCVal << std::endl;
        cv::circle(output, bestCircleCenter, bestCircleRadius, cv::Scalar(0, 0, 255));

        //TODO: hold and save the detected circle.

        //TODO: instead of overwriting the mask with a drawn circle it might be better to hold and ignore detected circles and dont count new circles which are too close to the old one.
        // in this current version the chosen radius to overwrite the mask is fixed and might remove parts of other circles too!

        // update mask: remove the detected circle!
        cv::circle(mask, bestCircleCenter, bestCircleRadius, 0, 10); // here the thickness is fixed which isnt so nice.

        circlesList.push_back(bestCircleCenter);
    }



    if (circlesList.size() < 2)
    {
        std::cout << "error: not enough circles found. Press enter." << std::endl;
        std::cin.get();
        return 0;
    }

    cv::Point2f centerOfMass = circlesList[0];
    cv::Point2f cogFP = circlesList[1];
    std::cout << cogFP - centerOfMass << std::endl;
    float angle = acos((cogFP - centerOfMass).x / cv::norm(cogFP - centerOfMass)); // scalar product of [1,0] and point
    std::cout << angle * 180 / CV_PI << std::endl;

    cv::line(output, centerOfMass, cogFP, cv::Scalar(0, 255, 0), 1);
    cv::circle(output, centerOfMass, 5, cv::Scalar(0, 0, 255), 1);
    cv::circle(output, cogFP, 3, cv::Scalar(255, 0, 0), 1);


    cv::imwrite(outString, output);

    cv::imshow("input", input);
    cv::imshow("output", output);
    cv::waitKey(0);
    return 0;
}

results:

enter image description here enter image description here enter image description here enter image description here enter image description here

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何确定形状的旋转? 的相关文章

随机推荐

  • Android 改造 POST ArrayList

    正在尝试发送List
  • 从 Kafka 读取时使用 KeyBy 与 reinterpretAsKeyedStream()

    我有一个简单的 Flink 流处理应用程序 Flink 版本 1 13 Flink 应用程序从 Kakfa 读取数据 对记录进行状态处理 然后将结果写回 Kafka 阅读完 Kafka 主题后 我选择使用reinterpretAsKeyed
  • 通过分组变量折叠列(以基数为单位)

    我有一个文本变量和一个分组变量 我想按因子将文本变量折叠为每行一个字符串 合并 所以只要群栏里说m我想将文本分组在一起等等 我在之前和之后提供了一个示例数据集 我正在为一个包编写此内容 到目前为止已经避免了对其他包的所有依赖 除了wordc
  • 2.7.4 上的 Python / 和 // 运算符

    我是Python新手 我开始学习基础知识 我是一名 C 人员 所以 运算符是新的东西 根据我正在读的一本书 gt gt 4 2 2 0 gt gt 2 4 0 5 gt gt 5 4 2 gt gt 2 4 0 问题是当我写的时候5 4当我
  • 修改内存区域 - 返回 0xCC VC++

    我正在修改 dll 中编译的可执行代码的某些部分 但是我正在修改的整个段中固定地址处的单个字节无法更改 甚至无法读取 代码很简单 SEGMENT DATA segInfo getSegmentInfo mHandle segmentName
  • 如何从 Angular mat-select 获取以前的和新的值?

    你好 我使用的是 Angular 6 和 agular 材料 并且我有一个字符串数组 我将其显示在垫选择表单字段中 如果用户选择一个元素 然后选择另一个元素 我需要跟踪前一个值是什么以及新值是什么 到目前为止 我已经能够使用 event v
  • 使用 XSLT 转换在 XML 中创建 xmlns 属性

    我尝试使用 JDK Transformer Oracle XML v2 Parser 或 JAXP 在 XSLT 转换期间将 xmlns 属性添加到结果 XML 并使用参数传递的值 但它始终默认为http www w3 org 2000 x
  • 如何使文本与图标字体垂直对齐?

    我有一个非常基本的 HTML 它混合了纯文本和图标字体 问题在于图标的渲染高度与文本的高度不完全相同 div class ui menu a href t class item i class large home basic icon i
  • 创建简单的 URL(.htaccess - mod_rewrite)

    我的导航有这个结构index php v page 我希望将其转换为 www domain com page 使用 mod rewrite 对此有什么想法吗 我读了一些图和例子 但无法让它正常工作 这里有一些例子 RewriteEngine
  • 如何在 Ionic 中制作开/关按钮

    我需要在 Ionic 中放置一个按钮 该按钮在按下后保持激活状态 并且仅在再次按下后才停用 有一个类似的问题 但它仅适用于图标按钮 如何添加具有离子开 离子关功能的导航栏按钮 EDIT 我无法使用切换按钮 它需要是一个常规外观的按钮 在本例
  • 如何在 Matplotlib (Numpy) 中生成 MATLAB 图(插值)?

    我正在尝试遵循网格网格 插值的 MATLAB 示例 示例代码已找到HERE 在该网站上 我正在查看以下示例 示例 在表面上显示不均匀数据 Now I would like to produce a similar plot in Pytho
  • 带闭包的 python 计数器

    我正在尝试用 python 构建一个具有闭包属性的计数器 以下代码有效 def generate counter CNT 0 def add one CNT 0 CNT 0 1 return CNT 0 return add one 但是
  • PHP-从字符串中获取特定单词

    如果我有一个像这样的字符串 myString input name something 我怎样才能得到name得到回应 每个字符串看起来都是这样 除了名称和其他内容可能不同 所以你唯一知道的是 输入后开始 它用正斜杠分隔 gt strArr
  • std::unique_ptr 作为目标 c 中的 @property

    如何在 Objective C 类的接口部分定义 std unique ptr 的 property property std unique ptr
  • 将对象推入数组无法按预期工作

    我的目标是创建一个像这样的数组 str a number 1 str a number 2 str b number 1 str b number 2 所以我写了这个javascript abc a b num 1 2 arr a for
  • 如何使用 TestNG 框架和 Jenkins 运行 Selenium 测试

    我想使用 Jenkins 运行在 TestNG 框架中编写的 Selenium 测试 jenkins job Freestyle项目 中配置的命令 java cp J taf testng J taf workspace TestNGExa
  • 如何使用 htaccess 将下划线替换为破折号?

    好的 这是我的网址 http example com home process login 我想用破折号替换下划线 So http example com home process login会转到上面的网址 但它仍然会说process l
  • Ruby 中的常量和变量有什​​么区别?

    因此 我正在 CodeAcademy 上学习 Ruby 课程 但我一直在区分变量和类之间的差异 有人可以向我解释一下其中的区别吗 我给你饼干 无论我在网上哪里查找 都找不到任何有关此的信息 Ruby 中常量的想法是 它们只能分配一次值 而您
  • SpriteKit - 在随机位置创建而不重叠

    我想在随机位置创建一些精灵而不重叠 这是我的代码 var sprites SKSpriteNode for index in 0 spriteArray let sprite SKSpriteNode imageNamed named sp
  • 如何确定形状的旋转?

    我有以下形状 它可能会旋转未知的角度 我想确定其相对于水平轴的旋转 因此上面的形状的旋转等于 0 到目前为止 我提出的最好的想法是确定形状的轮廓 找到最小面积矩形 然后将其旋转作为形状本身的旋转 Mat mask imread path t