Как вычислить моменты, чтобы найти центр тяжести из крупнейшего сгустка с использованием contourArea OpenCV - PullRequest
0 голосов
/ 22 сентября 2019

Я хочу обнаружить объекты желтого цвета и нанести на карту положение центроида на самом большом обнаруженном объекте желтого цвета.

Я выполняю свои шаги в следующем порядке:

  1. преобразовал входной rgbaframe в hsv с помощью метода cvtColor ()
  2. выполните сегментацию цвета в HSV с помощью метода inRange (), привязывает его только к диапазонам желтого цвета и возвращает двоичную пороговую маску.
  3. Я выполняю морфологическую операцию (в частности, MORPH_CLOSE), чтобы выполнить расширение, затем эрозию маски, чтобы удалить любой шум.размытие по Гауссу для сглаживания маски.
  4. Я применяю хитрый алгоритм, чтобы выполнить обнаружение краев, чтобы сделать края более очевидными для подготовки к обнаружению контуров на следующем шаге.(Я начинаю задаваться вопросом, полезен ли этот шаг вообще?)
  5. Я применяю алгоритм findContour (), чтобы найти контуры на изображении, а также найти иерархию.
  6. ЗДЕСЬ Я намерен использовать feature2d.FeatureDetection (SIMPLEBLOB) и передать в область большого двоичного объекта для обнаружения в качестве Params, однако, похоже, реализации для Android не поддерживается, поэтому мне пришлось обойти ограничение и найти самый большой BLOB-объект с помощью Imgproc.contourArea ().

    есть ли способ сделать это?

  7. Я передаю контуры, полученные ранее из метода findContours (), в качестве параметра для Imgproc.momentsдля вычисления положения центроида обнаруженных объектов.

    ОДНАКО я хотел бы обратить внимание каждого, что эта текущая реализация вычислит все центроиды в КАЖДОМ обнаруженном контуре (желтые объекты).* PLS SEE / REFER на рис. 1, 2, чтобы увидеть, что выводится на фрейм обратно пользователю.

    Чего я хотел бы добиться, так это найти способ использовать контур самого большого шарика (через mostContourArea) и передать эту информацию в качестве параметра в ImgprocMoments (), чтобы я ТОЛЬКО ВЫЧИСЛ СРЕДНИК этого самого большого обнаруженного Контура (Объекта), чтобы в любой конкретный момент времени на экране отображался только 1 Pos-центр тяжести.

    Я пробовал несколько методов, таких как передача контура самого большого объекта в качестве параметра в Imgproc.moments (), но он также не работал из-за разницы в типе данных / если он работал, выводне так, как хотелось бы, с несколькими точками центроида, нанесенными в пределах или вдоль периметра объекта, а не одной единственной точкой в ​​центре самого большого контурного объекта.

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
    
         InputFrame = inputFrame.rgba();
    
         Core.transpose(InputFrame,mat1); //transpose mat1(src) to mat2(dst), sorta like a Clone!
         Imgproc.resize(mat1,mat2,InputFrame.size(),0,0,0);    // params:(Mat src, Mat dst, Size dsize, fx, fy, interpolation)   Extract the dimensions of the new Screen Orientation, obtain the new orientation's surface width & height.  Try to resize to fit to screen.
         Core.flip(mat2,InputFrame,-1);   // mat3 now get updated, no longer is the Origi inputFrame.rgba BUT RATHER the transposed, resized, flipped version of inputFrame.rgba().
    
         int rowWidth = InputFrame.rows();
         int colWidth = InputFrame.cols();
    
         Imgproc.cvtColor(InputFrame,InputFrame,Imgproc.COLOR_RGBA2RGB);
         Imgproc.cvtColor(InputFrame,InputFrame,Imgproc.COLOR_RGB2HSV);
    
    
         Lower_Yellow = new Scalar(21,150,150);    //HSV color scale  H to adjust color, S to control color variation, V is indicator of amt of light required to be shine on object to be seen.
         Upper_Yellow = new Scalar(31,255,360);    //HSV color scale
    
    
         Core.inRange(InputFrame,Lower_Yellow, Upper_Yellow, maskForYellow);
    
    
         final Size kernelSize = new Size(5, 5);  //must be odd num size & greater than 1.
         final Point anchor = new Point(-1, -1);   //default (-1,-1) means that the anchor is at the center of the structuring element.
         final int iterations = 1;   //number of times dilation is applied.  https://docs.opencv.org/3.4/d4/d76/tutorial_js_morphological_ops.html
    
         Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, kernelSize);
    
         Imgproc.morphologyEx(maskForYellow, yellowMaskMorphed, Imgproc.MORPH_CLOSE, kernel, anchor, iterations);   //dilate first to remove then erode.  White regions becomes more pronounced, erode away black regions
    
    
    
         Mat mIntermediateMat = new Mat();
         Imgproc.GaussianBlur(yellowMaskMorphed,mIntermediateMat,new Size(9,9),0,0);   //better result than kernel size (3,3, maybe cos reference area wider, bigger, can decide better whether inrange / out of range.
         Imgproc.Canny(mIntermediateMat, mIntermediateMat, 5, 120);   //try adjust threshold   //https://stackoverflow.com/questions/25125670/best-value-for-threshold-in-canny
    
         List<MatOfPoint> contours = new ArrayList<>();
         Mat hierarchy = new Mat();
         Imgproc.findContours(mIntermediateMat, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));   
    
         byte[] arr = new byte[100];
         //List<double>hierarchyHolder = new ArrayList<>();
         int cols = hierarchy.cols();
         int rows = hierarchy.rows();
         for (int i=0; i < rows; i++) {
             for (int j = 0; j < cols; j++) {
                //hierarchyHolder.add(hierarchy.get(i,j));
                //hierarchy.get(i,j) is a double[] type, not byte.
                Log.d("hierarchy"," " + hierarchy.get(i,j).toString());   
    
            }
        }
    
    
         double maxArea1 = 0;
         int maxAreaIndex1 = 0;
         //MatOfPoint max_contours = new MatOfPoint();
         Rect r = null;
         ArrayList<Rect> rect_array = new ArrayList<Rect>();
    
         for(int i=0; i < contours.size(); i++) {
             //if(Imgproc.contourArea(contours.get(i)) > 300) {   //Size of Mat contour @ that particular point in ArrayList of Points.
             double contourArea1 = Imgproc.contourArea(contours.get(i));    
            //Size of Mat contour @ that particular point in ArrayList of Points.
                 if (maxArea1 < contourArea1){
                     maxArea1 = contourArea1;
                     maxAreaIndex1 = i;
                 }
                 //maxArea1 = Imgproc.contourArea(contours.get(i));  //assigned but nvr used
                 //max_contours = contours.get(i);
                 r = Imgproc.boundingRect(contours.get(maxAreaIndex1));    
                 rect_array.add(r);  //will only have 1 r in the array eventually, cos we will only take the one w largestContourArea.
         }
    
    
         Imgproc.cvtColor(InputFrame, InputFrame, Imgproc.COLOR_HSV2RGB);
    
    
         if (rect_array.size() > 0) {   //if got more than 1 rect found in rect_array, draw them out!
    
             Iterator<Rect> it2 = rect_array.iterator();    //only got 1 though, this method much faster than drawContour, wont lag. =D
             while (it2.hasNext()) {
                 Rect obj = it2.next();
                 //if
                 Imgproc.rectangle(InputFrame, obj.br(), obj.tl(),
                         new Scalar(0, 255, 0), 1);
             }
    
         }
    
    
     //========= Compute CENTROID POS! WHAT WE WANT TO SHOW ON SCREEN EVENTUALLY!====================== 
    
         List<Moments> mu = new ArrayList<>(contours.size());    //HUMoments
         for (int i = 0; i < contours.size(); i++) {
             mu.add(Imgproc.moments(contours.get(i)));
         }
    
         List<Point> mc = new ArrayList<>(contours.size());   //the Circle centre Point!
         for (int i = 0; i < contours.size(); i++) {
             //add 1e-5 to avoid division by zero
             mc.add(new Point(mu.get(i).m10 / (mu.get(i).m00 + 1e-5), mu.get(i).m01 / (mu.get(i).m00 + 1e-5)));
         }
    
    
         for (int i = 0; i < contours.size(); i++) {
             Scalar color = new Scalar(150, 150, 150);
    
             Imgproc.circle(InputFrame, mc.get(i), 20, color, -1);   //just to plot the small central point as a dot on the detected ImgObject.
         }
    

Изображениевывод при просмотре на CameraFrame:

1:

enter image description here

2:

enter image description here

1 Ответ

0 голосов
/ 23 сентября 2019

Управляемый, чтобы решить эту проблему, вместо того, чтобы цикл через весь массив Контуров для желтых объектов, обнаруженных и пройти каждый контур в качестве параметра Imgproc.moments, я теперь только присвоить контур в частности, индекс, который детектируется LargestContour,теперь Imgproc.moments обрабатывает только один отдельный контур для вычисления Centroid!Коррекции кода, как показано ниже

1002 *
...