OpenCV lines/ruler 检测
OpenCV lines/ruler detection
我正在尝试检测图像上的标尺,我将遵循下一个过程:
1) 准备图像(模糊、Canny 等)
2) 检测线
3) 准备一组平行线
所以,我有一张图片:
该应用程序转换为:
接下来我尝试了 HoughLinesP
方法,但看起来我无法在我的情况下应用它,因为我不知道线条的角度,所以找不到标尺垂直线,但找到水平线(例如)并且每条标尺线都包含许多细线,这将是一个处理问题:
代码:
std::vector<cv::Vec4i> lines_std;
cv::HoughLinesP( grayMat, lines_std, 1, CV_PI/90, 50, 10, 0 );
// drawing lines (with random color)
for( size_t i = 0; i < lines_std.size(); i++ )
{
cv::line( originalMat, cv::Point(lines_std[i][0], lines_std[i][1]),
cv::Point(lines_std[i][2], lines_std[i][3]), cv::Scalar(arc4random_uniform(155)+100,
arc4random_uniform(155)+100,
arc4random_uniform(155)+100), 1);
}
我也试过 LineSegmentDetector
,得到了我预期的更接近的结果:
代码:
vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(grayMat, lines_std);
但在这里我遇到了一些问题 (看起来没有办法自定义 createLineSegmentDetector
) :
并不是所有的线都被检测到;线检测不是在中心,而是在两侧,有时只在左侧或右侧,但我需要得到粗线的中心,因为这将在接下来的计算中使用。
那么,找到所有行(并且每行只在粗线中心出现一次)的正确方法是什么?
更新
尝试了 HoughLines
还:
矢量线;
cv::HoughLines(grayMat, lines, 1, CV_PI/90, 100 , 100, 0 );
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
cv::Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
cv::line( originalMat, pt1, pt2, cv::Scalar(0,255,0), 3, CV_AA);
}
但结果看起来也很奇怪(而且计算很花时间):
猜猜我找到了我应该遵循的方式:
1) 线条尽量细(经过Canny变换):
cv::Mat skel(grayMat.size(), CV_8UC1, cv::Scalar(0));
cv::Mat temp(grayMat.size(), CV_8UC1);
cv::Mat elementSkel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));
bool done;
do
{
cv::morphologyEx(grayMat, temp, cv::MORPH_OPEN, elementSkel);
cv::bitwise_not(temp, temp);
cv::bitwise_and(grayMat, temp, temp);
cv::bitwise_or(skel, temp, skel);
cv::erode(grayMat, grayMat, elementSkel);
double max;
cv::minMaxLoc(grayMat, 0, &max);
done = (max == 0);
} while (!done);
看起来像这样:
2) 检测线 LineSigmentDetector
:
vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(skel, lines_std);
3)按角度计算线角度和组ids:
NSMutableDictionary *testHashMap = [[NSMutableDictionary alloc]init];
for( size_t i = 0; i < lines_std.size(); i++ )
{
cv::Point p1 = cv::Point(lines_std[i][0], lines_std[i][1]);
cv::Point p2 = cv::Point(lines_std[i][2], lines_std[i][3]);
int angle = abs(atan2(p1.y - p2.y, p1.x - p2.x)); // int for rounding (for test only)
NSMutableArray *idArray=testHashMap[[NSString stringWithFormat:@"%i", angle]];
if(idArray == nil) {
idArray = [[NSMutableArray alloc] init];
}
[idArray addObject:[NSNumber numberWithInt:i]];
[testHashMap setObject:idArray forKey:[NSString stringWithFormat:@"%i", angle] ];
}
4) 找到标尺线组并画出来:
for( NSInteger i = 0; i < [rulerIds count]; i++ )
{
int itemId = [[rulerIds objectAtIndex:i] integerValue];
cv::Point p1 = cv::Point(lines_std[itemId][0], lines_std[itemId][1]);
cv::Point p2 = cv::Point(lines_std[itemId][2], lines_std[itemId][3]);
cv::line( originalMat, p1 , p2, cv::Scalar(0,255,0), 1);
}
我得到的结果:
更新
但如果我们很好地缩放此图像,仍然会看到重复的线条
为了消除重复,我做了简单的逻辑,通过为每个点建立平均值来合并线,例如在 3 条线(绿色)的情况下,我们在末尾有 3 个点:
我正在尝试检测图像上的标尺,我将遵循下一个过程:
1) 准备图像(模糊、Canny 等)
2) 检测线
3) 准备一组平行线
所以,我有一张图片:
该应用程序转换为:
接下来我尝试了 HoughLinesP
方法,但看起来我无法在我的情况下应用它,因为我不知道线条的角度,所以找不到标尺垂直线,但找到水平线(例如)并且每条标尺线都包含许多细线,这将是一个处理问题:
代码:
std::vector<cv::Vec4i> lines_std;
cv::HoughLinesP( grayMat, lines_std, 1, CV_PI/90, 50, 10, 0 );
// drawing lines (with random color)
for( size_t i = 0; i < lines_std.size(); i++ )
{
cv::line( originalMat, cv::Point(lines_std[i][0], lines_std[i][1]),
cv::Point(lines_std[i][2], lines_std[i][3]), cv::Scalar(arc4random_uniform(155)+100,
arc4random_uniform(155)+100,
arc4random_uniform(155)+100), 1);
}
我也试过 LineSegmentDetector
,得到了我预期的更接近的结果:
代码:
vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(grayMat, lines_std);
但在这里我遇到了一些问题 (看起来没有办法自定义 createLineSegmentDetector
) :
并不是所有的线都被检测到;线检测不是在中心,而是在两侧,有时只在左侧或右侧,但我需要得到粗线的中心,因为这将在接下来的计算中使用。
那么,找到所有行(并且每行只在粗线中心出现一次)的正确方法是什么?
更新
尝试了 HoughLines
还:
矢量线;
cv::HoughLines(grayMat, lines, 1, CV_PI/90, 100 , 100, 0 );
for( size_t i = 0; i < lines.size(); i++ )
{
float rho = lines[i][0], theta = lines[i][1];
cv::Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a*rho, y0 = b*rho;
pt1.x = cvRound(x0 + 1000*(-b));
pt1.y = cvRound(y0 + 1000*(a));
pt2.x = cvRound(x0 - 1000*(-b));
pt2.y = cvRound(y0 - 1000*(a));
cv::line( originalMat, pt1, pt2, cv::Scalar(0,255,0), 3, CV_AA);
}
但结果看起来也很奇怪(而且计算很花时间):
猜猜我找到了我应该遵循的方式:
1) 线条尽量细(经过Canny变换):
cv::Mat skel(grayMat.size(), CV_8UC1, cv::Scalar(0));
cv::Mat temp(grayMat.size(), CV_8UC1);
cv::Mat elementSkel = cv::getStructuringElement(cv::MORPH_CROSS, cv::Size(3, 3));
bool done;
do
{
cv::morphologyEx(grayMat, temp, cv::MORPH_OPEN, elementSkel);
cv::bitwise_not(temp, temp);
cv::bitwise_and(grayMat, temp, temp);
cv::bitwise_or(skel, temp, skel);
cv::erode(grayMat, grayMat, elementSkel);
double max;
cv::minMaxLoc(grayMat, 0, &max);
done = (max == 0);
} while (!done);
看起来像这样:
2) 检测线 LineSigmentDetector
:
vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(skel, lines_std);
3)按角度计算线角度和组ids:
NSMutableDictionary *testHashMap = [[NSMutableDictionary alloc]init];
for( size_t i = 0; i < lines_std.size(); i++ )
{
cv::Point p1 = cv::Point(lines_std[i][0], lines_std[i][1]);
cv::Point p2 = cv::Point(lines_std[i][2], lines_std[i][3]);
int angle = abs(atan2(p1.y - p2.y, p1.x - p2.x)); // int for rounding (for test only)
NSMutableArray *idArray=testHashMap[[NSString stringWithFormat:@"%i", angle]];
if(idArray == nil) {
idArray = [[NSMutableArray alloc] init];
}
[idArray addObject:[NSNumber numberWithInt:i]];
[testHashMap setObject:idArray forKey:[NSString stringWithFormat:@"%i", angle] ];
}
4) 找到标尺线组并画出来:
for( NSInteger i = 0; i < [rulerIds count]; i++ )
{
int itemId = [[rulerIds objectAtIndex:i] integerValue];
cv::Point p1 = cv::Point(lines_std[itemId][0], lines_std[itemId][1]);
cv::Point p2 = cv::Point(lines_std[itemId][2], lines_std[itemId][3]);
cv::line( originalMat, p1 , p2, cv::Scalar(0,255,0), 1);
}
我得到的结果:
更新
但如果我们很好地缩放此图像,仍然会看到重复的线条 为了消除重复,我做了简单的逻辑,通过为每个点建立平均值来合并线,例如在 3 条线(绿色)的情况下,我们在末尾有 3 个点: