OpenCV线/标尺检测

时间:2017-02-10 14:50:08

标签: ios opencv opencv3.0 objective-c++ opencv3.1

我正在尝试检测图像上的标尺,我将按照下一个过程进行操作:

1)准备图像(模糊,Canny等)

2)检测线

3)准备一组平行线

所以,我有一张图片: enter image description here

该应用转换为: enter image description here

接下来我尝试了HoughLinesP方法,看起来我不能在我的情况下应用它,因为我不知道线的角度,所以找不到标尺垂直线,但发现水平(对于例子)并且每个标尺线由许多细线组成,这将是一个需要处理的问题: enter image description here

代码:

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,并且得到了更接近我期望的结果: enter image description here

代码:

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);
}

但结果看起来也很奇怪(计算需要花费很多时间)enter image description here

1 个答案:

答案 0 :(得分:4)

猜猜我找到了应该遵循的方式:

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);

它看起来像这样:

enter image description here

2)使用LineSigmentDetector检测行:

vector<Vec4f> lines_std;
Ptr<LineSegmentDetector> ls = createLineSegmentDetector(LSD_REFINE_NONE);
ls->detect(skel, lines_std);

3)按角度计算线角和组ID:

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);
}

结果我得到了:

enter image description here

更新

但如果我们将这张图片放大,仍然会看到重复的线条 为了消除重复我已经制定了简单的逻辑,通过为每个点建立平均值来合并线,例如在3行(绿色)的情况下,我们在最后有3个点:

enter image description here