Reputation: 91
Please help me,
I have a problem for Convex Hull on Android. I use Java and OpenCV 2.3.
Before I made it on Java, I made it on C++ with Visual Studio 2008.
This code can running successfully on C++.
Now, i want to convert it from C++ to Java on Android. And I found error like "force close" when i run it on SDK Android simulator.
This is my code on C++:
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours( canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
drawing = Mat::zeros( canny_output.size(), CV_64F );
/// Find the convex hull object for each contour
vector<vector<Point> > hull ( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ convexHull( Mat(contours[i]), hull[i], false );
}
for(size_t i = 0; i < contours.size(); i++){
drawContours( drawing, hull, i, Scalar(255, 255, 255), CV_FILLED ); // FILL WHITE COLOR
}
And this is my code on Android:
Mat hierarchy = new Mat(img_canny.rows(),img_canny.cols(),CvType.CV_8UC1,new Scalar(0));
List<Mat> contours =new ArrayList<Mat>();
List<Mat> hull = new ArrayList<Mat>(contours.size());
drawing = Mat.zeros(img_canny.size(), im_gray);
Imgproc.findContours(img_dilasi, contours, hierarchy,Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
for(int i=0; i<contours.size(); i++){
Imgproc.convexHull(contours.get(i), hull.get(i), false);
}
for(int i=0; i<contours.size(); i++){
Imgproc.drawContours(drawing, hull, i, new Scalar(255.0, 255.0, 255.0), 5);
}
For your info, I did a little modification on Convex Hull at my code. I fill a color inside contour.
Anyone can help me to solve my problem?
I'm very grateful for your help.
Upvotes: 5
Views: 10452
Reputation: 1
Use this fillconvexPoly
for( int i = 0; i < contours.size(); i++ ){
Imgproc.fillConvexPoly(image_2, point,new Scalar(255, 255, 255));
}
Upvotes: -1
Reputation: 2125
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;
}
Upvotes: 2
Reputation: 21
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);
}
Upvotes: 2
Reputation: 969
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
Upvotes: 10
Reputation: 11
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;
}
Upvotes: 1
Reputation: 11359
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.
Upvotes: 1