首页 文章

在OpenCV 3.3中查找具有复杂背景和丰富纹理的图像中的轮廓

提问于
浏览
2

我正在开发一个项目,通过使用OpenCV 3.3来处理下面的大理石平板图像 .

Marble Slab Image

我正在研究的几个具有不同大理石纹理和尺寸的样品可以在https://1drv.ms/f/s!AjoScZ1lKToFheM6wmamv45R7zHwaQ找到

要求是:

  • 将大理石板与背景分开并移除背景(填充白色),这样只显示平板 .

  • 计算板坯的面积(从相机到大理石板的距离以及镜头的参数已知)

我使用的策略是:1)找到大理石板的轮廓,2)去除不在轮廓内的部分,3)获得轮廓的面积大小,4)计算其物理面积 .

板坯的轮廓如下图所示为红色(这是手工完成的) .

Slab Contour

我尝试了几种方法来找到图像中的板坯轮廓,但由于复杂的背景和大理石的丰富质感,未能获得令人满意的结果 .

我正在使用的处理逻辑是:将图像转换为灰色并模糊它并使用Canny查找边缘,然后使用findContours查找轮廓,以下是代码:

Mat img = imread('test2.jpg', 1);
Mat gray, edge;
cvtColor(img, gray, COLOR_BGR2GRAY);
blur(gray, gray, Size(3, 3));

Canny(gray, edge, 50, 200, 3);

vector<vector<Point> > contours;
vector<Vec4i> lines;
findContours(edge, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);

cout << "Number of contours detected: " << contours.size() << endl;

vector<Vec4i> hierarchy;

for (int i = 0; i < contours.size(); i++)
{
    drawContours(img, contours, i, Scalar(0, 0, 255), 1, 8, hierarchy, 0, Point());
}

imshow("Output", img);

我试图调整模糊和Canny参数的几十种组合,但仍然失败 . 我还尝试使用HoughLinesP来找到具有几组不同参数的板坯边缘,但也可以这样做 .

我是计算机视觉的新手,我现在的问题是:

  • 我是否采用错误的方式或策略来寻找板坯轮廓?可以有更好的算法或组合吗?或者我是否需要专注于调整Canny / findContours / HoughLinesP算法的参数?

  • 由于背景复杂,这种图像真的难以处理吗?

我愿意接受任何有助于我完成目标的建议 . 先感谢您 .

1 回答

  • 2

    你可以考虑的技巧

    • 模板匹配,您可能需要为不同的大理石准备大量模板(光照条件,旋转等)

    • 训练分类器区域提议,只有在其他解决方案失败时才采用此解决方案(此解决方案可能是最强大的解决方案,但也是最冗长的解决方案)

    由于你只有10~20种大理石板,我认为解决方案1是一个良好的开端 .

    • 手动找出大理石的4个角点,做透视变换
    pair<Mat, vector<Point2f>> get_target_marble(Mat const &input, vector<Point2f> const &src_pts)
    {
    using namespace cv;
    using namespace std;
    
    Point2f const tl = src_pts[0];
    Point2f const tr = src_pts[1];
    Point2f const br = src_pts[2];
    Point2f const bl = src_pts[3];
    
    auto const euclidean_dist = [](Point const &a, Point const &b)
    {
        return std::sqrt(std::pow(a.x-b.x, 2) + std::pow(a.y - b.y, 2));
    };
    int const max_width = static_cast<int>(std::max(euclidean_dist(br, bl), euclidean_dist(tr, tl)));
    int const max_height = static_cast<int>(std::max(euclidean_dist(tr, br), euclidean_dist(tl, bl)));
    
    vector<Point2f> const src{tl, tr, br, bl};
    vector<Point2f> dst{Point(0,0), Point(max_width -1,0), Point(max_width-1,max_height-1), Point(0,max_height-1)};
    Mat const hmat = getPerspectiveTransform(src, dst);
    Mat target;
    warpPerspective(input, target, hmat, {max_width, max_height});
    
    return std::make_pair(std::move(target), std::move(dst));
    

    }

    enter image description here

    • 找出查询图像(大理石板)和火车图像之间的单应矩阵(图像可能包含大理石板)
    Mat find_homography(Mat const &train, Mat const &query)
    {
    Ptr<AKAZE> akaze = AKAZE::create();
    vector<KeyPoint> query_kpts, train_kpts;
    cv::Mat query_desc, train_desc;
    akaze->detectAndCompute(train, cv::noArray(), query_kpts, query_desc);
    akaze->detectAndCompute(query, cv::noArray(), train_kpts, train_desc);
    
    BFMatcher matcher(NORM_HAMMING);
    vector<vector<DMatch>> nn_matches;
    //top 2 matches because we need to apply David Lowe's ratio test
    matcher.knnMatch(train_desc, query_desc, nn_matches, 2);
    
    vector<KeyPoint> matches1, matches2;
    for(auto const &m : nn_matches){
        float const dist1 = m[0].distance;
        float const dist2 = m[1].distance;
        if(dist1 < 0.7 * dist2){
            matches1.emplace_back(train_kpts[m[0].queryIdx]);
            matches2.emplace_back(query_kpts[m[0].trainIdx]);
        }
    }
    
    if(matches1.size() > 4){
        std::vector<cv::Point2f> points1, points2;
        for(size_t i = 0; i != matches1.size(); ++i){
            points1.emplace_back(matches1[i].pt);
            points2.emplace_back(matches2[i].pt);
        }
        return cv::findHomography(points1, points2, cv::RANSAC, 5.0);
    }
    
    return {};
    

    }

    enter image description here

    • 将4行查询图像映射到目标图像
    vector<Point2f> query_points;
    vector<Point> qpts;
    perspectiveTransform(dst, query_points, hmat);
    for(auto const &pt : query_points){
        cout<<pt<<endl;
        qpts.emplace_back(pt);
    }
    
    polylines(input, qpts, true, {255,0,0}, 2);
    

    enter image description here

    您将需要为此解决方案准备10~20张图像,更喜欢存在大多数匹配点的图像来定位您的大理石板 . 如果性能是一个问题,降低图像的分辨率,您不需要大图像来获得结果 .

    完整代码放在github .

    ps:我不知道你项目的细节,如果只有10~20种大理石板,它们都有很好的追踪功能,你不需要3个月就可以解决它(但你可以告诉你的老板/客户你需要3个月:),有时更好的表现只会导致更多的家务,但不是更多的钱) .

相关问题