用透视法寻找矩形物体质量
Find rectangular object quality with perspective
我从相机获取图像(经过校准且没有镜头失真),我需要检测一个矩形物体。标记就是一个很好的例子。对于标记,我检查角数、最小尺寸、板对比度和凸度。在存在大量错误矩形的情况下,我想到了如何改进这一点。
这是一个示例图片:
通常所有这些都是有效的,因为在不了解相机的情况下我们无法确定透视是否允许这些形状。我知道现实生活中矩形的大小(或至少是比例)。所以我有一个想法,我应该能够忽略其中的许多形状,只需重新投影它们并检查错误即可。
就像我使用 solvePnPRansac 一样,如果形状不可能,它就无法收敛。如果它不收敛,我就忽略它。遗憾的是,none 的 OpenCV 求解函数允许检查我的错误或收敛。我实际上需要一些比例或质量,因为一些矩形可能会重叠。例如我的对象查找器识别这些矩形:
实际上三者中有一个是正确的,或者至少 "the best"。但我需要一些方法来知道它是哪一个。由于相机视角的原因,我不能使用诸如线长之类的东西。所以我只是想我可以解决并看看哪个错误最小。
图像中没有镜头失真,但即使有 solvePnP 通常也允许将 D 传递给它。
这甚至可能还是我错过了什么?
我想我可以尝试破解 solvePnPRansac 只是为了 return 收敛,但也许有更简单的方法?
我想我可以做一些类似于在使用网格校准期间所做的事情。我可以计算重投影误差。所以首先我解决得到变换矩阵。然后我使用转换矩阵转换 3D 中的点,然后使用 projectPoints 将它们投影回 2D。然后我检查原始二维点和投影二维点之间的距离。然后可以将其用于质量。不可能的对象在我的图像中通常有 100 像素或更多的重投影误差,但可能的对象小于 20 像素。所以我只是做了一个 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
}
}
我从相机获取图像(经过校准且没有镜头失真),我需要检测一个矩形物体。标记就是一个很好的例子。对于标记,我检查角数、最小尺寸、板对比度和凸度。在存在大量错误矩形的情况下,我想到了如何改进这一点。 这是一个示例图片:
通常所有这些都是有效的,因为在不了解相机的情况下我们无法确定透视是否允许这些形状。我知道现实生活中矩形的大小(或至少是比例)。所以我有一个想法,我应该能够忽略其中的许多形状,只需重新投影它们并检查错误即可。 就像我使用 solvePnPRansac 一样,如果形状不可能,它就无法收敛。如果它不收敛,我就忽略它。遗憾的是,none 的 OpenCV 求解函数允许检查我的错误或收敛。我实际上需要一些比例或质量,因为一些矩形可能会重叠。例如我的对象查找器识别这些矩形:
实际上三者中有一个是正确的,或者至少 "the best"。但我需要一些方法来知道它是哪一个。由于相机视角的原因,我不能使用诸如线长之类的东西。所以我只是想我可以解决并看看哪个错误最小。
图像中没有镜头失真,但即使有 solvePnP 通常也允许将 D 传递给它。 这甚至可能还是我错过了什么? 我想我可以尝试破解 solvePnPRansac 只是为了 return 收敛,但也许有更简单的方法?
我想我可以做一些类似于在使用网格校准期间所做的事情。我可以计算重投影误差。所以首先我解决得到变换矩阵。然后我使用转换矩阵转换 3D 中的点,然后使用 projectPoints 将它们投影回 2D。然后我检查原始二维点和投影二维点之间的距离。然后可以将其用于质量。不可能的对象在我的图像中通常有 100 像素或更多的重投影误差,但可能的对象小于 20 像素。所以我只是做了一个 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
}
}