使用透视法找到矩形物体的质量

4
我从摄像头获取图像(已校准且没有镜头畸变),需要检测矩形对象,标记是一个很好的例子。对于标记,我检查角点数量、最小尺寸、板对比度和凸性。我有一个想法,可以在存在大量虚假矩形的情况下改进此方法。以下是示例图像: image 通常情况下,所有这些都是有效的,因为在不知道摄像机任何信息的情况下,我们无法确定透视是否允许这些形状。我知道实际生活中矩形的大小(或至少是比例)。因此,我认为我应该能够通过重新投影并检查误差来忽略其中许多形状。例如,如果我使用solvePnPRansac,它将无法收敛,如果形状不可能。如果它无法收敛,我就会忽略它。遗憾的是,OpenCV的解决函数都不允许检查错误或收敛。实际上,我需要一些比率或质量,因为某些矩形可能重叠。例如,我的对象查找器识别这些矩形: overlap image 其中三个中的一个实际上是正确的,或者至少是“最好的”。但我需要一些方法来知道它是哪一个。由于摄像机透视关系,我不能使用线长之类的东西。因此,我认为我可以求解并查看哪个误差最小。图像中没有镜头畸变,但即使有,solvePnP通常也允许将D传递给它。这是否可能,还是我遗漏了什么?我想我可以尝试绕过solvePnPRansac来返回收敛,但也许有更简单的方法?

这是一个非常有趣的问题,但我怀疑它是否适合 Stack Exchange 的格式,因为它有点太理论化并且涉及到讨论。你应该尝试一些计算机视觉论坛,与其他人讨论这个问题。 - Tomáš Zato
您可以计算一个投影矩形的宽高比(请参见此处:https://dev59.com/kVoT5IYBdhLWcg3wrRGE#38402378)。如果您已知宽高比,则可以根据角点估算宽高比,然后检查是否与您所知道的宽高比匹配。 - yhenon
Tomáš Zato - 我本想在answers.opencv.org上发布它,但由于我之前没有在那里发布过太多内容,他们不允许我发布图片(或链接到图片)。我必须提高我的声望值。 - user1214513
y300,我试试看。尽管由于校准的缘故,我确实知道相机焦距。因此,我猜这应该能给我更高的精确度。 - user1214513
1个回答

0

我想我可以做一些类似于校准网格的事情。我可以计算再投影误差。首先,我解决获取变换矩阵的问题。然后,我使用变换矩阵将3D中的点进行变换,然后使用projectPoints将它们重新投影到2D中。然后,我检查原始2D点和投影2D点之间的距离。这可以用于质量控制。在我的图像中,不可能的对象通常具有100个或更多像素的再投影误差,但是可能的对象的再投影误差小于20px。因此,我只是做了一个25像素的截止,并且似乎工作得很好。

请注意,比我想象的变换更多。在我的原始图像中,也许有两个变换对于我的当前相机来说是不可能的,但它仍然拒绝了很多虚假数据。

如果没有其他人有更好的想法,我会接受这个答案。

以下是我使用的方法的一些代码:

  //This is the object in 3D
  double width = 50.0; //Object is 50mm wide
  double height = 30.0; //Object is 30mm tall
  cv::Mat object_points(4,3,CV_64FC1);
  object_points.at<double>(0,0)=0;
  object_points.at<double>(0,1)=0;
  object_points.at<double>(0,2)=0;
  object_points.at<double>(1,0)=width;
  object_points.at<double>(1,1)=0;
  object_points.at<double>(1,2)=0;
  object_points.at<double>(2,0)=width;
  object_points.at<double>(2,1)=height;
  object_points.at<double>(2,2)=0;
  object_points.at<double>(3,0)=0;
  object_points.at<double>(3,1)=height;
  object_points.at<double>(3,2)=0;

  //Check all rectangles for error
  cv::Mat image_points(4,2,CV_64FC1);
  for (size_t i = 0; i < rectangles_to_test.size(); i++) {
    // Get rectangle points
    for (size_t c = 0; c < 4; ++c) {
      image_points.at<double>(c,0) = (rectangles_to_test[i].points[c].x);
      image_points.at<double>(c,1) = (rectangles_to_test[i].points[c].y);
    }

    // Calculate transformation matrix
    cv::Mat rvec, tvec;
    cv::solvePnP(object_points, image_points, M1, D1, rvec, tvec);

    cv::Mat rotation; 
    Matrix4<double> transform;
    transform.init_identity();
    cv::Rodrigues(rvec, rotation);

    for(size_t row = 0; row < 3; ++row) {
      for(size_t col = 0; col < 3; ++col) {
        transform.set(row, col, rotation.at<double>(row, col));
      }
      transform.set(row, 3, tvec.at<double>(row, 0));
    }

    // Calculate projection
    std::vector<cv::Point3f> p3(4);
    std::vector<cv::Point2f> p2;
    Vector4<double> p = transform * Vector4<double>(0, 0, 0, 1);
    p3[0] = cv::Point3f((float)p.x, (float)p.y, (float)p.z);
    p = transform * Vector4<double>(width, 0, 0, 1);
    p3[1] = cv::Point3f((float)p.x, (float)p.y, (float)p.z); 
    p = transform * Vector4<double>(width, height, 0, 1);
    p3[2] = cv::Point3f((float)p.x, (float)p.y, (float)p.z); 
    p = transform * Vector4<double>(0, height, 0, 1);
    p3[3] = cv::Point3f((float)p.x, (float)p.y, (float)p.z); 

    cv::projectPoints(p3, cv::Mat::zeros(1, 3, CV_64FC1), cv::Mat::zeros(1, 3, CV_64FC1), M1, D1, p2);

    // Calculate reprojection error
    rectangles_to_test[i].reprojection_error = 0.0;
    for (size_t c = 0; c < 4; ++c) {
      double dx = p2[c].x - rectangles_to_test[i].points[c].x;
      double dy = p2[c].y - rectangles_to_test[i].points[c].y;
      rectangles_to_test[i].reprojection_error += std::sqrt(dx*dx + dy*dy);
    }
    if (rectangles_to_test[i].reprojection_error > reprojection_error_threshold) {
      //rectangle is no good
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接