另一个关于检测图片中的卡片的问题。 我已经设法将图片中的卡片隔离开了,我有一个靠近的凸包,从这里我就卡住了。
对于上下文/约束,目标:
我使用的方法:
步骤1,3和6主要是去除噪音和小文物。
所以我几乎停留在第9步。 我试过了样本图片:
在调试图片上:
(结果图像从minAreaRect中提取)
所以轮廓是可以接受的,我可以通过调整canny或第一次模糊的参数来做得更好。 但是现在这是可以接受的,现在的问题是,我怎样才能得到将形成" minarea quadria"的4分。 正如你所看到的,minAreaRect给出了一个不完美的矩形,而且aboutPolyDp丢失了太多的卡片。
我有什么方法可以接近这个?
我在使用approxPolyDp(我使用arcLength*0.1
)时尝试使用epsilon值,但没有。但是没有。
这种方法的另一个问题是,在canny(参见示例)中丢失了一个角落它将无法工作(除非使用minAreaRect时)。但这可能在之前(通过更好的预处理)或之后(因为我们知道宽度/高度比)得到解决。
这里没有要求代码,只是想法如何处理这个问题,
谢谢!
修改:Yves Daoust的解决方案:
结果:
编辑2:使用Hough变换(而不是8个极值点)可以为找到4个边的所有情况提供更好的结果。如果找到超过4行,可能我们有重复,所以使用一些数学来尝试过滤并保留4行。我使用行列式(如果平行接近0)和点线距离公式编码草稿工作
答案 0 :(得分:3)
以下是我在输入图片上尝试的管道:
HoughLinesP
查找卡片的 4面 findHomography
查找卡片的仿射转换(在步骤2 处找到4个交叉点)请注意,您必须找到一种方法对4个交叉点进行排序,以便总是以相同的顺序(否则findHomography
将无效)。
我知道你没有要求代码,但我必须测试我的管道所以这里是...... :)
Vec3f calcParams(Point2f p1, Point2f p2) // line's equation Params computation
{
float a, b, c;
if (p2.y - p1.y == 0)
{
a = 0.0f;
b = -1.0f;
}
else if (p2.x - p1.x == 0)
{
a = -1.0f;
b = 0.0f;
}
else
{
a = (p2.y - p1.y) / (p2.x - p1.x);
b = -1.0f;
}
c = (-a * p1.x) - b * p1.y;
return(Vec3f(a, b, c));
}
Point findIntersection(Vec3f params1, Vec3f params2)
{
float x = -1, y = -1;
float det = params1[0] * params2[1] - params2[0] * params1[1];
if (det < 0.5f && det > -0.5f) // lines are approximately parallel
{
return(Point(-1, -1));
}
else
{
x = (params2[1] * -params1[2] - params1[1] * -params2[2]) / det;
y = (params1[0] * -params2[2] - params2[0] * -params1[2]) / det;
}
return(Point(x, y));
}
vector<Point> getQuadrilateral(Mat & grayscale, Mat& output) // returns that 4 intersection points of the card
{
Mat convexHull_mask(grayscale.rows, grayscale.cols, CV_8UC1);
convexHull_mask = Scalar(0);
vector<vector<Point>> contours;
findContours(grayscale, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
vector<int> indices(contours.size());
iota(indices.begin(), indices.end(), 0);
sort(indices.begin(), indices.end(), [&contours](int lhs, int rhs) {
return contours[lhs].size() > contours[rhs].size();
});
/// Find the convex hull object
vector<vector<Point> >hull(1);
convexHull(Mat(contours[indices[0]]), hull[0], false);
vector<Vec4i> lines;
drawContours(convexHull_mask, hull, 0, Scalar(255));
imshow("convexHull_mask", convexHull_mask);
HoughLinesP(convexHull_mask, lines, 1, CV_PI / 200, 50, 50, 10);
cout << "lines size:" << lines.size() << endl;
if (lines.size() == 4) // we found the 4 sides
{
vector<Vec3f> params(4);
for (int l = 0; l < 4; l++)
{
params.push_back(calcParams(Point(lines[l][0], lines[l][1]), Point(lines[l][2], lines[l][3])));
}
vector<Point> corners;
for (int i = 0; i < params.size(); i++)
{
for (int j = i; j < params.size(); j++) // j starts at i so we don't have duplicated points
{
Point intersec = findIntersection(params[i], params[j]);
if ((intersec.x > 0) && (intersec.y > 0) && (intersec.x < grayscale.cols) && (intersec.y < grayscale.rows))
{
cout << "corner: " << intersec << endl;
corners.push_back(intersec);
}
}
}
for (int i = 0; i < corners.size(); i++)
{
circle(output, corners[i], 3, Scalar(0, 0, 255));
}
if (corners.size() == 4) // we have the 4 final corners
{
return(corners);
}
}
return(vector<Point>());
}
int main(int argc, char** argv)
{
Mat input = imread("playingcard_input.png");
Mat input_grey;
cvtColor(input, input_grey, CV_BGR2GRAY);
Mat threshold1;
Mat edges;
blur(input_grey, input_grey, Size(3, 3));
Canny(input_grey, edges, 30, 100);
vector<Point> card_corners = getQuadrilateral(edges, input);
Mat warpedCard(400, 300, CV_8UC3);
if (card_corners.size() == 4)
{
Mat homography = findHomography(card_corners, vector<Point>{Point(warpedCard.cols, 0), Point(warpedCard.cols, warpedCard.rows), Point(0,0) , Point(0, warpedCard.rows)});
warpPerspective(input, warpedCard, homography, Size(warpedCard.cols, warpedCard.rows));
}
imshow("warped card", warpedCard);
imshow("edges", edges);
imshow("input", input);
waitKey(0);
return 0;
}
编辑:我已经调整了一些Canny
和HoughLinesP
函数的参数,以便更好地检测卡片(程序现在适用于两个输入样本)。
答案 1 :(得分:2)