Найти контуры на изображениях со сложным фоном и богатой текстурой в OpenCV 3.3

Я работаю над проектом по обработке изображений мраморной плиты, как показано ниже, с использованием OpenCV 3.3.

Изображение мраморной плиты

Еще несколько образцов с различными мраморными текстурами и размерами, над которыми я работаю, можно найти по https://1drv.ms/f/s!AjoScZ1lKToFheM6wmamv45R7zHwaQ

Требования следующие:

  1. Отделите мраморную плиту от фона и удалите фон (закрасьте белым цветом), чтобы отображалась только плита.
  2. Рассчитать площадь плиты (расстояние от камеры до мраморной плиты и параметры линзы известны)

Стратегия, которую я использую: 1) найти контур мраморной плиты, 2) удалить части, не входящие в контур, 3) получить размер области контура, 4) вычислить его физическую площадь.

Контур плиты показан на рисунке ниже красным (это было сделано вручную).

Контур плиты

Я попробовал несколько способов найти контуры плиты на изображении, но не смог достичь удовлетворительного результата из-за сложного фона и богатой текстуры мрамора.

Логика обработки, которую я использую: преобразовать изображение в серое и размыть его и использовать 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, чтобы найти край плиты с несколькими группами различных параметров, но также не смог это сделать.

Я новичок в компьютерном зрении, у меня есть следующие вопросы:

  1. Я иду к неправильному пути или стратегии, чтобы найти контур плиты? Могут ли быть лучшие алгоритмы или комбинации? Или мне нужно сосредоточиться на настройке параметров алгоритмов Canny/findContours/HoughLinesP?
  2. Действительно ли этот тип изображения трудно обрабатывать из-за сложного фона?

Я открыт для любых предложений, которые могут помочь мне достичь цели. Заранее спасибо.

1 ответ

Решение

Методы, которые вы можете рассмотреть

  1. Соответствие шаблонам, вам может понадобиться подготовить много шаблонов для разных мраморов (освещение, вращение и т. Д.)
  2. Изучите предложения классификатор + регион, примите это решение только в случае сбоя других решений (возможно, это решение самое надежное, но и наиболее подробное для реализации)

Поскольку у вас есть только 10~20 видов мраморной плиты, я думаю, что решение 1 - хорошее начало.

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

    }

  1. Найти матрицу гомографии между изображением запроса (мраморная плита) и изображением поезда (изображение может содержать мраморную плиту)

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

    }

  1. Сопоставьте 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);
    

Вам нужно будет подготовить 10~20 изображений для этого решения, предпочитайте одно существующее большинство точек соответствия, чтобы найти вашу мраморную плиту. Если производительность является проблемой, уменьшите разрешение изображения, вам не нужны большие изображения для получения результатов.

Полные коды размещены на github.

ps: я не знаю деталей вашего проекта, если есть только 10~20 видов мраморной плиты + все они имеют хорошие функции для отслеживания, вам не нужно 3 месяца, чтобы решить ее (но вы можете сказать своим боссам / клиентам нужно 3 месяца:), иногда лучшая производительность приводит только к большему количеству хлопот, но не к большим деньгам).

Другие вопросы по тегам