2017-09-30 3 views
2

Je travaille sur un projet de traitement d'images de dalles de marbre comme celui ci-dessous en utilisant OpenCV 3.3.Trouver des contours dans des images avec un arrière-plan complexe et une texture riche dans OpenCV 3.3

Marble Slab Image

Plusieurs autres échantillons avec différentes textures de marbre et de tailles sur lesquels je travaille sont disponibles sur https://1drv.ms/f/s!AjoScZ1lKToFheM6wmamv45R7zHwaQ

Les exigences sont les suivantes:

  1. séparé la dalle de marbre de l'arrière-plan et Enlevez l'arrière-plan (remplissez avec la couleur blanche) ainsi seulement la dalle est montrée.
  2. Calculer la surface de la dalle (la distance de la caméra à la dalle de marbre et les paramètres de la lentille sont connus)

La stratégie que je utilise est: 1) trouver le contour de la dalle de marbre, 2) enlever les parties qui ne sont pas dans le contour, 3) obtenir la taille de la zone du contour, 4) calculer sa surface physique.

Le contour de la dalle est représenté en rouge sur l'image ci-dessous (celle-ci a été réalisée à la main).

Slab Contour

J'ai essayé plusieurs façons de trouver les contours de la dalle dans l'image, mais n'a pas réussi à obtenir un résultat satisfaisant en raison de l'arrière-plan complexe et la riche texture du marbre.

logique de traitement J'utilise est: convertir l'image en gris et flou et utiliser Canny pour trouver les bords, puis utilisez findContours pour trouver les contours, ce qui suit est le code:

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

J'ai essayé de ajuster le flou et les paramètres Canny pour des dizaines de combinaisons et toujours échoué. J'ai également essayé d'utiliser HoughLinesP pour trouver le bord de la dalle avec plusieurs groupes de paramètres différents, mais aussi pour le faire.

Je suis un débutant à la vision informatique, les questions que j'ai maintenant sont:

  1. Vais-je vers une mauvaise façon ou d'une stratégie pour trouver le contour de la dalle? Pourrait-il y avoir de meilleurs algorithmes ou combinaisons? Ou dois-je me concentrer sur les paramètres de réglage des algorithmes Canny/findContours/HoughLinesP?
  2. Ce type d'image est-il vraiment difficile à traiter en raison de l'arrière-plan complexe?

Je suis ouvert à toutes les suggestions qui peuvent m'aider à terminer mon objectif. Merci d'avance.

+0

Quelle partie est la plaque de marbre que vous voulez détecter? – StereoMatching

Répondre

2

techniques que vous pouvez envisager

  1. correspondant de modèle, vous devrez peut-être préparer un grand nombre de modèles pour différents marbre (état lumière, rotation, etc.)
  2. train a Propositions de classificateur + région, seulement adopter cette solution si d'autres solutions échouent (cette solution est peut-être la plus robuste, mais aussi la plus bavarde à implémenter)

Puisque vous avez seulement 10 ~ 20 types de dalles de marbre, je pense que la solution 1 est un bon début .

  1. trouver manuellement 4 coins points de marbre, ne perspective transformer

    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

  1. Découvrez la matrice d'homographie entre l'image de requête (plaque de marbre) et tr ain images (l'image peut contenir plaque de marbre)

    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

  1. sur la carte les 4 lignes de l'image d'interrogation à l'image cible

    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

Vous devez préparer 10 ~ 20 images pour cette solution, préfèrent une existe la plupart des points de matchs pour localiser votre dalle de marbre. Si performances est un problème, diminuer la résolution de l'image, vous n'avez pas besoin de grandes images pour obtenir des résultats.

Les codes complets sont mis à github. Ps: Je ne connais pas les détails de votre projet, s'il y a seulement 10 ~ 20 types de dalles de marbre + tous ont de bonnes caractéristiques à suivre, vous n'avez pas besoin de 3 mois pour le résoudre (Mais vous pouvez dites à vos patrons/clients que vous avez besoin de 3 mois :), parfois de meilleures performances ne mènent qu'à plus de corvées mais pas plus d'argent).

+0

Merci pour vos suggestions, j'ai édité le poste pour montrer le contour que j'ai besoin de savoir, c'est toute la dalle de marbre, pourriez-vous s'il vous plaît m'aider avec la nouvelle information? –

+0

Combien de types de dalles de marbre voulez-vous trouver? Occupent-ils toujours la plupart de l'image comme celle-ci? – StereoMatching

+0

Je travaille sur plus de 10 différents types de dalles de marbre de couleur et de texture différentes. Et nous avons déjà demandé que la dalle de marbre occupe la plus grande partie de l'image sur l'image pour chaque photo prise par l'appareil photo, ce qui ne posera aucun problème. –