Convex Hull on Java Android Opencv 2.3

落爺英雄遲暮 提交于 2019-11-28 12:33:02

Don't have the rep to add comment, just wanted to say the two answers above helped me get Imgproc.convexHull() working for my use case with something like this (2.4.8):

MatOfPoint mopIn = ...
MatOfInt hull = new MatOfInt();
Imgproc.convexHull(mopIn, hull, false);

MatOfPoint mopOut = new MatOfPoint();
mopOut.create((int)hull.size().height,1,CvType.CV_32SC2);

for(int i = 0; i < hull.size().height ; i++)
{
    int index = (int)hull.get(i, 0)[0];
    double[] point = new double[] {
        mopIn.get(index, 0)[0], mopIn.get(index, 0)[1]
    };
    mopOut.put(i, 0, point);
}           
// do something interesting with mopOut
user4473007

This code works well in my application. In my case, I had multiple contours to work with, so you will notice a lot of Lists, but if you only have one contour, just adjust it to work without the .get(i) iterations.

This thread explains the process more simply.

android java opencv 2.4 convexhull convexdefect

   // Find the convex hull
            List<MatOfInt> hull = new ArrayList<MatOfInt>();
            for(int i=0; i < contours.size(); i++){
                hull.add(new MatOfInt());
            }
            for(int i=0; i < contours.size(); i++){
                Imgproc.convexHull(contours.get(i), hull.get(i));
            }

            // Convert MatOfInt to MatOfPoint for drawing convex hull

            // Loop over all contours
            List<Point[]> hullpoints = new ArrayList<Point[]>();
            for(int i=0; i < hull.size(); i++){
                Point[] points = new Point[hull.get(i).rows()];

                // Loop over all points that need to be hulled in current contour
                for(int j=0; j < hull.get(i).rows(); j++){
                    int index = (int)hull.get(i).get(j, 0)[0];
                    points[j] = new Point(contours.get(i).get(index, 0)[0], contours.get(i).get(index, 0)[1]);
                }

                hullpoints.add(points);
            }

            // Convert Point arrays into MatOfPoint
            List<MatOfPoint> hullmop = new ArrayList<MatOfPoint>();
            for(int i=0; i < hullpoints.size(); i++){
                MatOfPoint mop = new MatOfPoint();
                mop.fromArray(hullpoints.get(i));
                hullmop.add(mop);
            }


            // Draw contours + hull results
            Mat overlay = new Mat(binaryImage.size(), CvType.CV_8UC3);
            Scalar color = new Scalar(0, 255, 0);   // Green
            for(int i=0; i < contours.size(); i++){
                Imgproc.drawContours(overlay, contours, i, color);
                Imgproc.drawContours(overlay, hullmop, i, color);
            }

Example in Java (OpenCV 2.4.11)

hullMat contains the sub mat of gray, as identified by the convexHull method.
You may want to filter the contours you really need, for example based on their area.

List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
MatOfInt4 hierarchy = new MatOfInt4();
MatOfInt hull = new MatOfInt();

void foo(Mat gray) {
    Imgproc.findContours(gray, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);        
    for (int i = 0; i < contours.size(); i++) {
        Imgproc.convexHull(contours.get(i), hull);
        MatOfPoint hullContour = hull2Points(hull, contours.get(i));
        Rect box = Imgproc.boundingRect(hullContour);
        Mat hullMat = new Mat(gray, box);
        ...
    }
}

MatOfPoint hull2Points(MatOfInt hull, MatOfPoint contour) {
    List<Integer> indexes = hull.toList();
    List<Point> points = new ArrayList<>();
    MatOfPoint point= new MatOfPoint();
    for(Integer index:indexes) {
        points.add(contour.toList().get(index));
    }
    point.fromList(points);
    return point;
}

Looking at the documentation of findContours() and convexHull(), it appears that you have declared the variables contours and hull incorrectly.

Try changing the declarations to:

List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
List<MatOfInt> hull = new ArrayList<MatOfInt>();

Then, after you call convexHull(), hull contains the indices of the points in contours which comprise the convex hull. In order to draw the points with drawContours(), you will need to populate a new MatOfPoint containing only the points on the convex hull, and pass that to drawContours(). I leave this as an exercise for you.

To add on to what Aurelius said, in your C++ implementation you used a vector of points, therefore the hull matrix contains the actual convex Points:

"In the first case [integer vector of indices], the hull elements are 0-based indices of the convex hull points in the original array (since the set of convex hull points is a subset of the original point set). In the second case [vector of points], hull elements are the convex hull points themselves." - convexHull

This is why you were able to call

drawContours( drawing, hull, i, Scalar(255, 255, 255), CV_FILLED );

In your android version, the hull output is simply an array of indices which correspond to the points in the original contours.get(i) Matrix. Therefore you need to look up the convex points in the original matrix. Here is a very rough idea:

MatOfInt hull = new MatOfInt();
MatOfPoint tempContour = contours.get(i);
Imgproc.convexHull(tempContour, hull, false); // O(N*Log(N))
//System.out.println("hull size: " + hull.size() + " x" + hull.get(0,0).length);
//System.out.println("Contour matrix size: " + tempContour.size() + " x" + tempContour.get(0,0).length);

int index = (int) hull.get(((int) hull.size().height)-1, 0)[0];
Point pt, pt0 = new Point(tempContour.get(index, 0)[0], tempContour.get(index, 0)[1]);
for(int j = 0; j < hull.size().height -1 ; j++){
    index = (int) hull.get(j, 0)[0];
    pt = new Point(tempContour.get(index, 0)[0], tempContour.get(index, 0)[1]);
    Core.line(frame, pt0, pt, new Scalar(255, 0, 100), 8);
    pt0 = pt;
}

Use this fillconvexPoly

 for(;;){
          Imgproc.fillConvexPoly(image_2, point,new Scalar(255, 255, 255));
    }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!