【问题标题】:Detect centre and angle of rectangles in an image using Opencv使用 Opencv 检测图像中矩形的中心和角度
【发布时间】:2016-03-18 04:13:18
【问题描述】:

我有一张如下图:

我需要找出矩形的数量,每个矩形的中心,并测量平行于通过中心的矩形长边的轴之间的角度,并测量从水平方向逆时针方向的角度。我发现图像中矩形的数量。我很惊讶地发现了反射的中心和角度。通过瞬间找到中心并没有给我正确的答案。

我的代码:

import cv2
import numpy as np 
import sys

img = cv2.imread(str(sys.argv[1]),0)
ret,thresh = cv2.threshold(img,127,255,0)
contours,hierarchy = cv2.findContours(thresh,1,2)



for contour in contours:
    area = cv2.contourArea(contour)
    if area>100000:
        contours.remove(contour)




cnt = contours[0]

epsilon = 0.02*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)

print 'No of rectangles',len(approx)


#finding the centre of the contour
M = cv2.moments(cnt)

cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])

print cx,cy

【问题讨论】:

  • 用 findContours 提取轮廓并使用函数 minAreaRect 计算 RotatedRect
  • 检查我的答案here。您将找到@Micka 指出的方式的示例 C++ 实现。
  • 对于矩形 Micka 的解决方案是最佳的,作为更一般的情况,您还可以查看基于 PCA 的方法:docs.opencv.org/master/d1/dee/…
  • @Micka 你能详细说明一下 minAreaRect 吗,这对从水平方向找到逆时针方向的角度有什么帮助?如果我使用矩,我也没有得到轮廓的中心。为什么会这样?

标签: python opencv image-processing computer-vision


【解决方案1】:

这就是使用 openCV 的 minAreaRect 函数的方法。它是用 C++ 编写的,但您可能很容易适应它,因为几乎只使用了 OpenCV 函数。

    cv::Mat input = cv::imread("../inputData/rectangles.png");

    cv::Mat gray;
    cv::cvtColor(input,gray,CV_BGR2GRAY);

    // since your image has compression artifacts, we have to threshold the image
    int threshold = 200;
    cv::Mat mask = gray > threshold;

    cv::imshow("mask", mask);

    // extract contours
    std::vector<std::vector<cv::Point> > contours;
    cv::findContours(mask, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    for(int i=0; i<contours.size(); ++i)
    {
        // fit bounding rectangle around contour
        cv::RotatedRect rotatedRect = cv::minAreaRect(contours[i]);

        // read points and angle
        cv::Point2f rect_points[4]; 
        rotatedRect.points( rect_points );

        float  angle = rotatedRect.angle; // angle

        // read center of rotated rect
        cv::Point2f center = rotatedRect.center; // center

        // draw rotated rect
        for(unsigned int j=0; j<4; ++j)
            cv::line(input, rect_points[j], rect_points[(j+1)%4], cv::Scalar(0,255,0));

        // draw center and print text
        std::stringstream ss;   ss << angle; // convert float to string
        cv::circle(input, center, 5, cv::Scalar(0,255,0)); // draw center
        cv::putText(input, ss.str(), center + cv::Point2f(-25,25), cv::FONT_HERSHEY_COMPLEX_SMALL, 1, cv::Scalar(255,0,255)); // print angle
    }

生成此图像:

如您所见,角度可能不是您想要的(因为它们随机使用较长或较小的线作为参考)。 您可以改为提取矩形的长边并手动计算角度。

如果您选择旋转矩形的较长边缘并从中计算角度,它看起来像这样:

// choose the longer edge of the rotated rect to compute the angle
        cv::Point2f edge1 = cv::Vec2f(rect_points[1].x, rect_points[1].y) - cv::Vec2f(rect_points[0].x, rect_points[0].y);
        cv::Point2f edge2 = cv::Vec2f(rect_points[2].x, rect_points[2].y) - cv::Vec2f(rect_points[1].x, rect_points[1].y);

        cv::Point2f usedEdge = edge1;
        if(cv::norm(edge2) > cv::norm(edge1))
            usedEdge = edge2;

        cv::Point2f reference = cv::Vec2f(1,0); // horizontal edge


        angle = 180.0f/CV_PI * acos((reference.x*usedEdge.x + reference.y*usedEdge.y) / (cv::norm(reference) *cv::norm(usedEdge)));

给出这个结果,这应该是你要找的!

编辑:看起来操作没有使用他发布的输入图像,因为参考矩形中心将位于图像之外。

使用此输入(手动重新调整但可能仍不是最佳):

我得到了这些结果(蓝点是操作提供的参考矩形中心):

比较参考与检测:

reference (x,y,angle)    detection (x,y,angle)
(320,240,0)              (320, 240, 180) // angle 180 is equal to angle 0 for lines
(75,175,90)              (73.5, 174.5, 90)
(279,401,170)            (279.002, 401.824, 169.992)
(507,379,61)             (507.842, 379.75, 61.1443)
(545,95,135)             (545.75, 94.25, 135)
(307,79,37)              (306.756, 77.8384, 37.1042)

我很想看到真实的输入图像,也许结果会更好。

【讨论】:

  • 你能告诉我如何找到平行于长边的轴与水平线之间的角度吗?这就是我印象深刻的部分。我应该退化边缘以找到更长的边缘并手动计算角度吗?
  • 更新了代码和结果。这很简单。如果对您有帮助,请随意投票。
  • 非常感谢..它确实有很大帮助..如果我使用矩,我无法正确获取矩形的中心。这些是矩形的实际中心和角度 (x,y,angle) (320,240,0) (75,175,90) (279,401,170) (507,379,61) (545,95,135) (307,79,37) 当我用瞬间找到轮廓的中心,它给了我一个不同的答案。为什么会这样?
  • 我对时刻没有太多经验。对于轮廓提取,您应该尝试使用标志来密集地表示轮廓。
  • 非常感谢米卡。这个解决方案拯救了我的一天。这是一个正确方向的转折点。我曾经计算文档的平均旋转,计算所有(重要)角度的加权平均值。这是核心。我只需要消除进一步的噪音(在 C# 中)
【解决方案2】:

你可以这样做:

  1. 连接组件标签以检测每个模式(在您的情况下为矩形)
  2. 分离不同图像中的图案
  3. (可选)如果图案不都是矩形,则使用形状索引来区分它们
  4. 使用主成分分析 (PCA) 计算主轴,它会为您提供所需的角度。

【讨论】:

  • 您可以使用代码 sn-p 改进答案。另外,由于这里只有矩形,minAreaRect 就足够了。
【解决方案3】:

approx = cv2.approxPolyDP(cnt,epsilon,True) 创建给定闭合轮廓的近似多边形。多边形中的线段长度可变,导致矩计算不正确,因为它期望从规则网格中采样点以提供正确的中心。

您的问题有三种解决方案:

  1. 在调用多边形逼近方法之前使用原始轮廓的矩。
  2. 使用 drawContours 生成每个闭合轮廓内区域的遮罩,然后使用生成的遮罩的矩来计算中心。
  3. 沿闭合多边形的每条线段在单位距离处采样点,并使用生成的点集合自行计算矩。这应该给你同样的中心。

【讨论】:

    猜你喜欢
    • 2013-03-23
    • 2021-07-03
    • 2019-11-29
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-24
    相关资源
    最近更新 更多