Detection des couleurs pour la construction Eurobot 2018

Saif
Messages : 4
Inscription : Sam Oct 21, 2017 3:40 pm

Detection des couleurs pour la construction Eurobot 2018

Messagepar Saif » Mar Nov 14, 2017 10:25 am

Salut,

Voici un lien vers le notebook préliminaire de reconnaissance de la séquence de couleurs pour la construction pour Eurobot 2018:

https://github.com/netmonster/rectangle ... %A9s.ipynb

Cordialement

Saif
Messages : 4
Inscription : Sam Oct 21, 2017 3:40 pm

Re: Detection des couleurs pour la construction Eurobot 2018

Messagepar Saif » Mar Nov 14, 2017 10:26 am

J'ai testé ce code sur une raspberry pi.

ça fonctionne, mais il y a encore des améliorations à réaliser.
J'aimerais avoir une image réaliste des carrés.

Merci

Avatar de l’utilisateur
Paul
Messages : 1119
Inscription : Mer Nov 02, 2011 6:24 pm

Re: Detection des couleurs pour la construction Eurobot 2018

Messagepar Paul » Mar Nov 14, 2017 11:05 pm

Salut Saif,

C'est très interessant ! Le sujet me rappelle un peu en 2014 où l'on avait tenté de faire de la détection de forme (des triangles), détection de couleur et calcul d'orientation. On avait aussi utilisé opencv mais avec du C++ sur une raspi. Si cela aider, voici quelques liens :
Sujet du forum:
viewtopic.php?f=9&t=379&start=10#p5314
(malheureusement les images ont jarté)

J'avais remarqué que dans des conditions d'éclairage médiocre, cela aidait considérablement d'ajouter un peu de flou gaussien. La détection de contour canny de opencv était également améliorer en utilisant des pré/post-traitements du style érosion/dilatation.

L'archive du code est trouvable sur notre ancien SVN:
https://app.assembla.com/spaces/paranoi ... D/archives

Le gros challenge avec la caméra de la raspi c'était la latence assez importante. Cela a probablement changé et n'est peut etre pas un problème dans ce cas présent.

Comment comptez-vous communiquer avec le robot ?

Sinon au passage, en général les sujets de discussions sur les détails technique de la coupe se font dans la section privée ("entre nous").

A+

Paul
Dernière édition par Paul le Mar Nov 14, 2017 11:12 pm, édité 1 fois.

Avatar de l’utilisateur
Paul
Messages : 1119
Inscription : Mer Nov 02, 2011 6:24 pm

Re: Detection des couleurs pour la construction Eurobot 2018

Messagepar Paul » Mar Nov 14, 2017 11:11 pm

J'ai retrouvé le coeur du processing (c'est plus pratique que DL le rar):

Code : Tout sélectionner

// Main processing method
void FirecamCv::process(void) {

  // Processing variables
  Mat imageContours;
  vector<vector<Point> > contours;
  vector<Vec4i> hierarchy;
  Moments contourMoments;
  double cntArea;
  double cntPerim;
  Fire foundFire;
  Scalar debugColor;

  // Declare Erode and Dilate structural elements
  Mat erodeElement = getStructuringElement(MORPH_ELLIPSE, Size(config_->procErodeSize, config_->procErodeSize), Point(1, 1));
  Mat dilateElement = getStructuringElement(MORPH_ELLIPSE, Size(config_->procDilateSize, config_->procDilateSize), Point(1, 1));

   // Grab one frame and retrieve it
   camera_.grab();
   camera_.retrieve(imageCamera_);

   // Resize it
   resize(imageCamera_, imageResize_, Size(0, 0),
         (1.0 / config_->procResizingRatio),
         (1.0 / config_->procResizingRatio), config_->procResizingMethod);

   // Create debug image from captured image (after resizing)
   imageDebug_ = imageResize_.clone();

   // BGR to HSV conversion for later use (color selection)
   cvtColor(imageResize_, imageHsv_, CV_BGR2HSV);

   // Apply gaussian blur on resized image (BGR format)
   medianBlur (imageResize_, imageBlur_, config_->procBlurSize);

   // Convert it in grayscale
  cvtColor(imageBlur_, imageGray_, CV_BGR2GRAY);

  // Apply canny detection
  Canny(imageGray_, imageCanny_, config_->procCannyThresholdMin, config_->procCannyThresholdMax, 3 );

  // Erode/Dilate threshed image
  dilate(imageCanny_, imageDilate_, dilateElement);
  erode(imageDilate_, imageErode_, erodeElement);

  // Find contour in cleaned-up image
  imageContours = imageErode_.clone();
  findContours(imageContours, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

  // Dynamically allocated vector
  vector<vector<Point> > contoursApproximated(contours.size());

  // Go through all contours
  for(unsigned int i = 0; i< contours.size(); i++ ) {

    // Compute contour moments
    cntArea = contourArea(contours[i]);
    cntPerim = arcLength(contours[i], true); // Closed

    // Apply 1st filter on AREA and PERIMETER here
    if(   (cntArea  < config_->objAreaMin)  || (cntArea  > config_->objAreaMax)
       || (cntPerim < config_->objPerimeterMin) || (cntPerim > config_->objPerimeterMax))
       continue;

    // Approximate the shape to a closed polygon, with a tolerance of objPerimeterMargin percent
    approxPolyDP(Mat(contours[i]), contoursApproximated[i], cntPerim*config_->objPerimeterMargin, true);

    // 2nd filter: to be valid, the polygon must have 3 points (= triangle !!)
    if(contoursApproximated[i].size() != 3)
     continue;

    // Calculate moment of the approximated shape
    contourMoments = moments(contoursApproximated[i]);
    foundFire.area_ = contourMoments.m00;
    foundFire.perimeter_ = arcLength(contoursApproximated[i], true); // Closed

    // Apply 3st filter on AREA and PERIMETER on the approximated triangle
    if(   (foundFire.area_  < config_->objAreaMin)  || (foundFire.area_  > config_->objAreaMax)
       || (foundFire.perimeter_ < config_->objPerimeterMin) || (foundFire.perimeter_ > config_->objPerimeterMax))
       continue;

    // From this point, the contour is considered as valid

    // Calculate centroid
    foundFire.x_ = contourMoments.m10/foundFire.area_;
    foundFire.y_ = contourMoments.m01/foundFire.area_;

    // Compute arbitrary the orientation from points 1 and 0 of the contour
    // Calculates orientation in degrees in the ]0;+120] interval
    foundFire.orientation_ = atan2(contoursApproximated[i][1].y - contoursApproximated[i][0].y,
                     contoursApproximated[i][1].x - contoursApproximated[i][0].x) * 180 / PI;

    // Get in range ]0;+360]
    foundFire.orientation_ += 180;

    if(foundFire.orientation_ >= 240) {
      foundFire.orientation_ -= 240;
    } else if(foundFire.orientation_ >= 120) {
      foundFire.orientation_ -= 120;
    } // else between ]0;+120]

    // Color filter
    Rect colorSquare(foundFire.x_ - config_->procColorSquareSize/2,
                     foundFire.y_ - config_->procColorSquareSize/2,
                     config_->procColorSquareSize, config_->procColorSquareSize);

    Mat imageColorRoi = imageHsv_(colorSquare);

    Scalar meanValueRoi = mean(imageColorRoi);
    foundFire.hueValue_ = meanValueRoi.val[0];

    if(foundFire.hueValue_ >= config_->procYellowThresholdMin && foundFire.hueValue_ <= config_->procYellowThresholdMax) {
      foundFire.color_ = MatchColor::YELLOW;
      debugColor = CV_RGB(255, 255, 0);
    } else {
      foundFire.color_ = MatchColor::RED;
      debugColor = CV_RGB(255, 0, 0);
    }

    // Add the found fire to the list
    detectedFires_.push_back(foundFire);

    // Debug: Draw triangles and  selection rectangle
    drawContours(imageDebug_, contoursApproximated, i, debugColor, 1, 8, hierarchy, 0, Point());

    // Debug: Draw cross at centroid's coordinates
    line(imageDebug_, Point(foundFire.x_ - 2, foundFire.y_),
               Point(foundFire.x_ + 2, foundFire.y_),
               debugColor, 1);
    line(imageDebug_, Point(foundFire.x_, foundFire.y_ - 2),
              Point(foundFire.x_, foundFire.y_ + 2),
               debugColor, 1);

    rectangle(imageDebug_, Point(colorSquare.x, colorSquare.y),
                            Point(colorSquare.x+colorSquare.width, colorSquare.y+colorSquare.height),
                            debugColor, 1);

  } // foreach contour


   // Debug: save resulting images
  imwrite("images/imageCamera.png", imageCamera_);
  imwrite("images/imageHsv.png", imageHsv_);
  imwrite("images/imageGray.png", imageGray_);
  imwrite("images/imageBlur.png", imageBlur_);
  imwrite("images/imageCanny.png", imageCanny_);
  imwrite("images/imageDilate.png", imageDilate_);
   imwrite("images/imageErode.png", imageErode_);
  imwrite("images/im_debug.png", imageDebug_);

   // TEMP
  imwrite("images/im_red_th.png", imageErode_);
  imwrite("images/im_yel_th.png", imageBlur_);

}

Saif
Messages : 4
Inscription : Sam Oct 21, 2017 3:40 pm

Re: Detection des couleurs pour la construction Eurobot 2018

Messagepar Saif » Dim Nov 26, 2017 9:33 pm

Salut Paul et merci pour les informations.

Le code que j'ai partagé n'était qu'une ébauche.
J'ai ajouté une saturation + une erosion / dilatation qui a permis de supprimer pas mal d'artefacts.
Dans ce cas, un filtrage gaussien n'est plus nécessaire puisqu'on a déjà complètement éliminé le bruit en saturant.


Je vais essayer de changer la position de l'érosion/dilatation. Effectivement, je crois qu'elle serait plus utile après le Canny.
J'ajoute donc, une saturation + érosion.

Je vais ajouter une petite explication de ce qu'est l'érosion et dilatation dans le document.

Si quelqu'un a une vrai photo de la scène prise de la position de la balise, ça pourrait bien aider.

J'ai aussi ajouté le tri des rectangle de gauche à droite.

Je vais ajouter une sécurité sur le fait que les trois rectangles trouvés soient séparés de la même distance et qu'ils soient sur la même ligne et qu'ils aient à peu près la même taille.

Saif
Messages : 4
Inscription : Sam Oct 21, 2017 3:40 pm

Re: Detection des couleurs pour la construction Eurobot 2018

Messagepar Saif » Dim Nov 26, 2017 9:51 pm

J'ai ajouté d'autres critères de sélection.
Il y a la taille (mais pour celle ci, il faut vérifier la position de la caméra par rapport à la scène).
Il y a le fait de voir un parallélépipède dont les lignes sont de longueurs assez proches. Ceci permet nottament de supprimer le grand rectangle qui entour les trois carrés.
Puis, il faut détecter 3 de ces derniers.

Je travaille sur la suppression des carrées voisins qui peuvent passer en exigeant de trouver deux carrées successifs restant sur la même ligne (presque) et dont la distance est la même.

Pour la raspberry pi, j'ai testé l'acquisition avec une seule photo de la camera et ça fonctionne plutot bien. En effet, le gros du travail est effectué par le code C (opencv). En plus, on travaille sur une seule photo. Les carrés ne changent ni de place ni de couleurs, donc pas besoin de faire des milliers de photos.

Une photo au début, puis on communique les données au robot soit par NRF24L01+ ou si vous voulez , on prend on utilise le bluetooth.


Revenir vers « Bavardage »

Qui est en ligne ?

Utilisateurs parcourant ce forum : Aucun utilisateur inscrit et 1 invité