2014-06-13 1 views
2

Je suis nouveau à OpenCV et j'essaie de compter le nombre d'objets dans une image. Je l'ai fait avant d'utiliser MATLAB Image Processing Toolbox et adapté la même approche dans OpenCV (Android) aussi.compter les objets et une meilleure façon de remplir les trous

La première étape consistait à convertir une image en échelle de gris. Ensuite, pour le seuil et ensuite en comptant le nombre de blobs. Dans Matlab il y a une commande - "bwlabel", qui donne le nombre de blobs. Je ne pouvais pas trouver une telle chose dans OpenCV (encore une fois, je suis un Noob dans OpenCV ainsi que Android).

Voici mon code,

//JPG to Bitmap to MAT 
Bitmap i = BitmapFactory.decodeFile(imgPath + "mms.jpg"); 
Bitmap bmpImg = i.copy(Bitmap.Config.ARGB_8888, false); 
Mat srcMat = new Mat (bmpImg.getHeight(), bmpImg.getWidth(), CvType.CV_8UC3); 
Utils.bitmapToMat(bmpImg, srcMat); 

enter image description here

//convert to gray scale and save image 
Mat gray = new Mat(srcMat.size(), CvType.CV_8UC1); 
Imgproc.cvtColor(srcMat, gray, Imgproc.COLOR_RGB2GRAY,4); 
//write bitmap 
Boolean bool = Highgui.imwrite(imgPath + "gray.jpg", gray); 

enter image description here

//thresholding 
Mat threshed = new Mat(bmpImg.getWidth(),bmpImg.getHeight(), CvType.CV_8UC1); 
Imgproc.adaptiveThreshold(gray, threshed, 255, Imgproc.ADAPTIVE_THRESH_MEAN_C, Imgproc.THRESH_BINARY, 75, 5);//15, 8 were original tests. Casey was 75,10 
Core.bitwise_not(threshed, threshed); 
Utils.matToBitmap(threshed, bmpImg); 
//write bitmap 
bool = Highgui.imwrite(imgPath + "threshed.jpg", threshed); 
Toast.makeText(this, "Thresholded image saved!", Toast.LENGTH_SHORT).show(); 

enter image description here

Dans la prochaine ste p, j'ai essayé de remplir les trous et les lettres en utilisant la dilatation suivie d'une érosion mais les gouttes se sont attachées les unes aux autres ce qui donnera finalement un mauvais compte. Il y a un compromis entre remplir les trous et obtenir les blobs attachés les uns aux autres sur l'accord des paramètres pour la dilatation et l'érosion.

Voici le code,

//morphological operations 
//dilation 
Mat dilated = new Mat(bmpImg.getWidth(),bmpImg.getHeight(), CvType.CV_8UC1); 
Imgproc.dilate(threshed, dilated, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new org.opencv.core.Size (16, 16))); 
Utils.matToBitmap(dilated, bmpImg); 
//write bitmap 
bool = Highgui.imwrite(imgPath + "dilated.jpg", dilated); 
Toast.makeText(this, "Dilated image saved!", Toast.LENGTH_SHORT).show(); 

enter image description here

//erosion 
Mat eroded = new Mat(bmpImg.getWidth(),bmpImg.getHeight(), CvType.CV_8UC1); 
Imgproc.erode(dilated, eroded, Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new org.opencv.core.Size(15, 15))); 
Utils.matToBitmap(eroded, bmpImg); 
//write bitmap 
bool = Highgui.imwrite(imgPath + "eroded.jpg", eroded); 
Toast.makeText(this, "Eroded image saved!", Toast.LENGTH_SHORT).show(); 

Parce que parfois ma M & Mme pourrait être juste à côté de l'autre! ;)

enter image description here

J'ai aussi essayé d'utiliser des cercles Hough, mais le résultat est très peu fiable (testé avec des images de pièces de monnaie, ainsi que des pièces réelles)

Voici le code,

//hough circles 
Mat circles = new Mat(); 

// parameters 
int iCannyUpperThreshold = 100; 
int iMinRadius = 20; 
int iMaxRadius = 400; 
int iAccumulator = 100; 

Imgproc.HoughCircles(gray, circles, Imgproc.CV_HOUGH_GRADIENT, 
     1.0, gray.rows()/8, iCannyUpperThreshold, iAccumulator, 
     iMinRadius, iMaxRadius); 

// draw 
if (circles.cols() > 0) 
{ 
    Toast.makeText(this, "Coins : " +circles.cols() , Toast.LENGTH_LONG).show(); 
} 
else 
{ 
    Toast.makeText(this, "No coins found", Toast.LENGTH_LONG).show(); 
} 

Le problème avec cette approche est que l'algorithme est limité aux cercles parfaits seulement (AFAIK). Donc, ça ne marche pas bien quand j'essaie de numériser et de compter M & Ms ou des pièces sur mon bureau (car l'angle de l'appareil change). Avec cette approche, parfois je reçois moins de non. de pièces de monnaie détectées et parfois plus (je ne comprends pas pourquoi plus ??). En scannant cette image, l'application affiche parfois 19 pièces et parfois 38 pièces comptées ... Je sais qu'il y a d'autres caractéristiques qui peuvent être détectées comme des cercles mais je ne comprends absolument pas pourquoi 38 ..?

enter image description here

Alors mes questions ...

  1. Y at-il une meilleure façon de remplir les trous sans se joindre blobs adjacents?
  2. Comment compter le nombre d'objets avec précision? Je ne veux pas limiter mon application à ne compter que les cercles avec l'approche HoughCircles.

FYI: OpenCV-2.4.9-android-sdk. Veuillez garder à l'esprit que je suis un débutant dans OpenCV et Android aussi.

Toute aide est très appréciée.

Merci & À la votre!

Jainam

+0

Avez-vous regardé ici http://stackoverflow.com/questions/1716274/fill-the-holes-in-opencv?rq= 1 ? –

+0

Un simple seuillage Otsu peut fonctionner car les arrière-plans des exemples d'images ne sont pas complexes. – dhanushka

Répondre

0

suffit de vérifier dehors,

Vous pouvez prendre la taille des contours en tant que nombre d'objets.

enter image description here

enter image description here

code:

Mat tmp,thr; 
    Mat src=imread("img.jpg",1); 
    blur(src,src,Size(3,3)); 
    cvtColor(src,tmp,CV_BGR2GRAY); 
    threshold(tmp,thr,220,255,THRESH_BINARY_INV); 
    imshow("thr",thr); 

    vector< vector <Point> > contours; // Vector for storing contour 
    vector<Vec4i> hierarchy; 

    findContours(thr, contours, hierarchy,CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); // Find the contours in the image 
    for(int i = 0; i< contours.size(); i=hierarchy[i][0]) // iterate through each contour. 
    { 
     Rect r= boundingRect(contours[i]); 
     rectangle(src,r, Scalar(0,0,255),2,8,0); 
    } 
cout<<"Numeber of contour = "<<contours.size()<<endl; 
imshow("src",src); 
waitKey(); 
+0

merci pour votre réponse rapide. Je reçois des résultats erronés pour l'image M & M ci-dessus. List contours = new ArrayList(); \t \t Imgproc.findContours(smoothed, contours, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE); Imgproc.drawContours(smoothed, contours, -1, new Scalar(255,255,0)); \t \t Toast.makeText(this, "No of objects : "+contours.size(), Toast.LENGTH_LONG).show(); Je comprends le nombre 14..une idée pourquoi? – jainam

+0

désolé pour la mauvaise présentation dans le commentaire ci-dessus. J'ai essayé de le coder dans un commentaire mais je ne pouvais pas ... de toute façon. ce serait génial si vous pouviez m'aider. Merci! – jainam

1

Donc, pour procéder, nous prenons votre image de seuil que vous avez généré en entrée et modifier davantage. Le présent code est en C++, mais je suppose que vous pouvez facilement le convertir en plate-forme Android Input Image

Maintenant, au lieu de la dilatation ou le flou, vous pouvez essayer inondation remplir

qui se traduit par

Flood-filled image

Enfin maintenant en appliquant l'algorithme algorithme de détection de contour nous obtenons Final output

Le code pour ce qui précède est

Mat dst = imread($path to the threshold image); // image should be single channel black and white image 
    imshow("dst",dst); 

    cv::Mat mask = cv::Mat::zeros(dst.rows + 2, dst.cols + 2, CV_8U); 

      // A image with size greater than the present object is created 

    cv::floodFill(dst, mask, cv::Point(0,0), 255, 0, cv::Scalar(), cv::Scalar(), 4 + (255 << 8) + cv::FLOODFILL_MASK_ONLY); 
    erode(mask,mask,Mat()); 
    // Now to remove the outer boundary 
    rectangle(mask,Rect(0,0,mask.cols,mask.rows), Scalar(255,255,255),2,8,0); 
    imshow("Mask",mask); 


    Mat copy; 
    mask.copyTo(copy); 

    vector<vector<Point> > contours; 
    vector<Vec4i> hierarchy; 
    findContours(copy, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); 

    vector<vector<Point> > contours_poly(contours.size()); 
    vector<Rect> boundRect(contours.size()); 
    vector<Point2f>center(contours.size()); 
    vector<float>Distance(contours.size()); 
    vector<float>radius(contours.size()); 

    Mat drawing = cv::Mat::zeros(mask.rows, mask.cols, CV_8U); 
    int num_object = 0; 
    for(int i = 0; i < contours.size(); i++){ 
     approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true); 

      // To get rid of the smaller object and the outer rectangle created 
      //because of the additional mask image we enforce a lower limit on area 
      //to remove noise and an upper limit to remove the outer border.  

     if (contourArea(contours_poly[i])>(mask.rows*mask.cols/10000) && contourArea(contours_poly[i])<mask.rows*mask.cols*0.9){ 
      boundRect[i] = boundingRect(Mat(contours_poly[i])); 
      minEnclosingCircle((Mat)contours_poly[i], center[i], radius[i]); 
      circle(drawing,center[i], (int)radius[i], Scalar(255,255,255), 2, 8, 0); 
      rectangle(drawing,boundRect[i], Scalar(255,255,255),2,8,0); 
      num_object++; 
     } 
    } 

    cout <<"No. of object detected =" <<num_object<<endl; 


    imshow("drawing",drawing); 

    waitKey(2); 
    char key = (char) waitKey(20); 
    if(key == 32){ 
    // You can save your images here using a space 

      } 

J'espère que cela vous aide à résoudre votre problème

+0

Je suis coincé avec la syntaxe floodFill dans Android. tous les exemples que je reçois sont en C++. Voici mon code, 'Masque de mat = nouveau Mat (threshed.rows() + 2, threshed.cols() + 2, CvType.CV_8U, nouveau scalaire (0)); Point point = new Point(); point.x = 0; point.y = 0; Imgproc.floodFill (battu, masque, point, nouveau scalaire (255, 255, 255)); ' En survolant sur floodFill, j'ai lu cette suggestion de solution rapide," Change point to Point ". Aucune idée sur la façon de résoudre ça? Merci! – jainam

+0

Au lieu de Point point = new Point(); point.x = 0; point.y = 0; as-tu essayé le nouveau Point (0,0)? –

+0

Vérifiez également le texte cité dans la question ici http://grokbase.com/t/gg/android-opencv/123en5esw9/how-to-use-imgproc-floodfill-function –

Questions connexes