今天有点棘手,但绝对可行,我认为我走在正确的轨道上。我在试着检测图像中的虚线。我通过寻找一对最适合的轮廓来做到这一点,它们的斜率也与彼此产生的角度一致。参见下面的代码块示例:
//Get canny mat
Mat frame, framegray, detectededges, dummyimg, cannyedges;
cvtColor(frame, framegray, CV_BGR2GRAY);
blur(framegray, detectededges, Size(3,3));
double otsu_thresh_val = cv::threshold(detectededges, dummyimg,0, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::Canny( detectededges, cannyedges, otsu_thresh_val*0.5, otsu_thresh_val );
//
Scalar color(255,255,0,255);
//Find contours
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(cannyedges,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
double contourlength = arcLength(contours[idx], false);
vector<Vec4f> vectorofbestfits;
//Find dotted lines
for (vector<Point> contour : contours){
//Some filtering
if ((contourlength > 70.0) && (contours[idx].size() > 5)){
Vec4f bestfitline;
fitLine(contour, bestfitline,CV_DIST_L2,0,0.01,0.01);
vectorofbestfits.push_back(bestfitline);
}
}
float mtol = 0.05;
for (Vec4f veci : vectorofbestfits){
float mi = veci[1]/veci[0];
for (Vec4f vecj : vectorofbestfits){
float mj = vecj[1]/vecj[0];
double length = cv::norm(Mat(Point(veci[2],veci[3])),Mat(Point(vecj[2],vecj[3])));
if (length < 30){
continue;
}
float mk = (veci[3]-vecj[3])/(veci[2]-vecj[2]);
mi = abs(mi);
mj = abs(mj);
mk = abs(mk);
float mij = abs(mi - mj);
float mjk = abs(mj - mk);
float mki = abs(mk - mi);
if ((mij < mtol) && (mjk < mtol) && (mki < mtol)){
line(frame,Point(veci[2],veci[3]),Point(vecj[2],vecj[3]),color,2);
}
}
}
的方法是:1)创建最适合轮廓线的数组2)遍历数组,得到直线斜率3)重新遍历数组,每隔一条线求斜率4)计算两条线中心形成的直线的斜率5)将所有3个斜率相互比较,并使用容差值
进行过滤下面的代码从我的图像中生成了大量的行,但是它们没有一个碰到明显的虚线。我觉得斜率的计算有问题。现在它是如此无效,我需要创建一些图像和测试,而不是与现实世界的图形工作。有什么跳出来的吗?
同样,下面的代码计算量相当大,应用程序用于视频解释和帧率是至关重要的,所以任何提高性能的建议都是值得赞赏的。
查找匹配如下:图片
编辑:
下面是一些更清晰的代码。我也意识到在找到差异之前取斜率的绝对值没有任何意义,只是创建了一堆匹配的偶然轮廓。我需要再玩一些,看看是否有帮助:
//Get canny mat
Mat frame, framegray, detectededges, dummyimg, cannyedges;
cvtColor(frame, framegray, CV_BGR2GRAY);
blur(framegray, detectededges, Size(3,3));
double otsu_thresh_val = cv::threshold(detectededges, dummyimg,0, 255,
CV_THRESH_BINARY | CV_THRESH_OTSU);
cv::Canny( detectededges, cannyedges, otsu_thresh_val*0.5, otsu_thresh_val );
//Set color for Lines
Scalar color(255,255,0,255);
//Find contours
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
findContours(cannyedges,contours,hierarchy,CV_RETR_CCOMP,CV_CHAIN_APPROX_SIMPLE);
double contourlength = arcLength(contours[idx], false);
vector<Vec4f> arrayofbestfits;
//Create an array of best fit Lines
for (vector<Point> contour : contours){
//Filter out contours that are too small
if ((contourlength > 70.0) && (contours[idx].size() > 5)){
Vec4f bestfitLine;
fitLine(contour, bestfitLine,CV_DIST_L2,0,0.01,0.01);
arrayofbestfits.push_back(bestfitLine);
}
}
float SlopeTolerance = 0.05;
for (Vec4f firstIterationLine : arrayofbestfits){
float firstIterationSlope = firstIterationLine[1]/firstIterationLine[0];
for (Vec4f secondIterationLine : arrayofbestfits){
//Filter out Lines too close in proxmity
double length = cv::norm(Mat(Point(firstIterationLine[2],firstIterationLine[3])),
Mat(Point(secondIterationLine[2],secondIterationLine[3])));
if (length < 30){
continue;
}
//Find slope between two points
float commonSlope = (firstIterationLine[3]-secondIterationLine[3])
/(firstIterationLine[2]-secondIterationLine[2]);
//Find absolute value of differences (makes comparison simpler)
float commonSlopediff = abs(firstIterationSlope - secondIterationSlope);
float secondtocommonSlopediff = abs(secondIterationSlope - commonSlope);
float commontofirstSlopediff = abs(commonSlope - firstIterationSlope);
//If within tolerances draw the line bridging the two best fit lines together
if ((commonSlopediff < SlopeTolerance) && (secondtocommonSlopediff < SlopeTolerance) && (commontofirstSlopediff < SlopeTolerance)){
Line(frame,Point(firstIterationLine[2],firstIterationLine[3]),
Point(secondIterationLine[2],secondIterationLine[3]),color,2);
}
}
}
根据您的示例图像,看起来您正在尝试检测具有以下约束的线段:
- 段具有相似的斜率(或方向)
- 段沿着类似的线。
这个问题似乎非常适合RANSAC解决方案。
使用简单的2D RANSAC,您可以找到大多数数据点对齐的线。
- 通过随机抽样子集(在本例中为2的子集)并连接子集中的点来求解这条线。
- 然后,你设置一个窗口的大小,并包括所有其他点的线内连接你的子集+/-窗口的大小。这些点称为内线。
- 你想要找到最大化内线数量的线段。
有关更多信息,请参阅有关RANSAC的幻灯片
在您的例子中,您希望稍微修改问题以反映您正在寻找线段而不是点的事实。有许多方法可以做到这一点。
一种解决方案是基于坡度对分段进行聚类,然后对每个聚类应用简单的2D RANSAC,并将分段视为点。