I wrote some code to detect convexity defects from a picture. I use opencv320 (tried 240 and got the exact same issue) and the code is Java.
And the result is always the same, it stops at a point and I don't understand why:
As you see, I drew the bounding rectangle, the contour, the convex hull and when I want to calculate the convexity defects they are calculated only up until the ring finger starts.
public class Recognition {
protected String whichHand = "left";
public ArrayList<MatOfPoint> contours = new ArrayList<>();
public MatOfPoint largestContour;
public MatOfInt convexHull = new MatOfInt();
protected Mat originalImage;
public int largestContourIndex = -1;
public MatOfInt4 convexityDefects = new MatOfInt4();
public ArrayList<Defect> relevantDefects = new ArrayList<Defect>();
public Recognition(Mat originalImage) {
this.originalImage = originalImage;
}
public Recognition flip() {
if (whichHand != "left")
Core.flip(originalImage, originalImage, 1);
return this;
}
public Recognition getContours() {
Imgproc.findContours(originalImage, contours, new Mat(), Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
return this;
}
public Recognition getLargestContour() {
double largestArea = 0;
int contourNumber = contours.size();
for (int i = 0; i < contourNumber; i++) {
//double a=contourArea( hull[i],false); // surface
double contourArea = Imgproc.contourArea(contours.get(i));
if (contourArea > largestArea) {
largestArea = contourArea;
largestContourIndex = i;
}
}
largestContour = contours.get(largestContourIndex);
return this;
}
public Recognition getConvexHull() {
Imgproc.convexHull(largestContour, convexHull);
return this;
}
public Recognition getConvexityDefects() {
Imgproc.convexityDefects(largestContour, convexHull, convexityDefects);
int convexityDefectsList[] = convexityDefects.toArray();
int convexityDefectsSize = convexityDefectsList.length;
Point contourArray[] = largestContour.toArray();
for (int d = 0; d < convexityDefectsSize; d += 4) {
Point startPoint = contourArray[d];
Point endPoint = contourArray[d + 1];
Point depthPoint = contourArray[d + 2];
double depth = convexityDefectsList[d + 3] / 256;
Defect defect = new Defect();
defect.startPoint = startPoint;
defect.endPoint = endPoint;
defect.depthPoint = depthPoint;
defect.depth = depth;
relevantDefects.add(defect);
}
return this;
}
}
I think the problem is that I am getting way too few defects, around 20-30, but the Java function has really strange output when compared with the c++ one, it puts all the defects info in groups of 4 in the same array, really strange, so I've made an object to store it better.
I think I calculate the contour and hull properly, because you can see in the image that it's displayed fully.
This is the code I use, drawer is just another class that draws the specific points on the image, if you want I can provide it, too. I load the image, detect skin from the binary version, get the largest contour, get the hull and try to get the convexity defects.
Mat imagemat = Imgcodecs.imread("src/hsr/util/D1.png");
Mat skinMat = SkinDetector.detectSkin(imagemat);
Recognition recognition = new Recognition(skinMat);
Drawer drawer = new Drawer();
recognition.flip().getContours().getLargestContour();
Mat contourMat = Mat.zeros(skinMat.size(), CvType.CV_8UC3);
recognition.getConvexHull().getConvexityDefects();
drawer.showBiggestContour(contourMat,recognition.contours,recognition.largestContourIndex);
drawer.showHull(contourMat,recognition.convexHull,recognition.largestContour);
drawer.showRotatedBoundingRectangle(recognition.largestContour,contourMat);
drawer.drawDefects(contourMat,recognition.relevantDefects);
Imgcodecs.imwrite("asd.png",contourMat);