我将首先说明我会慢慢疯狂。我试图从图像中提取轮廓并使用Java和OpenCV计算它们的质心。
对于所有内部轮廓,结果是正确的,但是对于外部(最大)轮廓,质心是远离的方式。输入图像,代码和输出结果都在下面。 OpenCV版本是3.1。
其他人遇到了这个问题,建议是:
输入图像非常大(27MB),奇怪的部分是当我将其调整为1000x800时,质心突然被正确计算,但是,我需要能够以原始分辨率处理图像。
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package com.philrovision.dxfvision.matching;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.imgproc.Moments;
import org.testng.annotations.Test;
/**
*
* @author rhobincu
*/
public class MomentsNGTest {
@Test
public void testOpenCvMoments() {
Mat image = Imgcodecs.imread("moments_fail.png");
Mat channel = new Mat();
Core.extractChannel(image, channel, 1);
Mat mask = new Mat();
Imgproc.threshold(channel, mask, 191, 255, Imgproc.THRESH_BINARY);
Mat filteredMask = new Mat();
Imgproc.medianBlur(mask, filteredMask, 5);
List<MatOfPoint> allContours = new ArrayList<>();
Mat hierarchy = new Mat();
Imgproc.findContours(filteredMask, allContours, hierarchy, Imgproc.RETR_TREE,
Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
MatOfPoint largestContour = allContours.stream().max((c1, c2) -> {
double area1 = Imgproc.contourArea(c1);
double area2 = Imgproc.contourArea(c2);
if (area1 < area2) {
return -1;
} else if (area1 > area2) {
return 1;
}
return 0;
}).get();
Mat debugCanvas = new Mat(image.size(), CvType.CV_8UC3);
Imgproc.drawContours(debugCanvas, Arrays.asList(largestContour), -1, new Scalar(255, 255, 255), 3);
Imgproc.drawMarker(debugCanvas, getCenterOfMass(largestContour),
new Scalar(255, 255, 255));
Rect boundingBox = Imgproc.boundingRect(largestContour);
Imgproc.rectangle(debugCanvas, boundingBox.br(), boundingBox.tl(), new Scalar(0, 255, 0), 3);
System.out.printf("Bounding box area is: %f and contour area is: %f", boundingBox.area(), Imgproc.contourArea(
largestContour));
Imgcodecs.imwrite("output.png", debugCanvas);
}
private static Point getCenterOfMass(MatOfPoint contour) {
Moments moments = Imgproc.moments(contour);
return new Point(moments.m10 / moments.m00, moments.m01 / moments.m00);
}
}
输入:(完整图片here) 输出:
STDOUT:
Bounding box area is: 6460729,000000 and contour area is: 5963212,000000
质心绘制在靠近左上角的位置,在轮廓之外。
答案 0 :(得分:1)
正如评论讨论中所提到的,看起来这个问题你在OpenCV的GitHub的Java实现中特别具有was reported。它最终用this simple pull request解决了。有一些不必要的int
铸件。
然后可能的解决方案:
升级OpenCV应该可以解决问题。
您可以使用修补程序编辑库文件(只需在几行上删除(int)
强制转换)。
定义自己的函数来计算质心。
如果你感到无聊并且想要弄清楚3,那实际上并不是一个困难的计算:
轮廓的质心通常根据image moments计算。如该页面所示,可以在图像上定义片刻M_ij
:
M_ij = sum_x sum_y (x^i * y^j * I(x, y))
和二进制形状的质心是
(x_c, y_c) = (M_10/M_00, M_01/M_00)
请注意M_00 = sum_x sum_y (I(x, y))
,在二进制0和1图像中,只是白色像素的数量。如果您的contourArea
按照您在评论中的说明运作,则可以将其用作M_00
。然后请注意,M_10
只是与白色像素对应的x
值和与M_01
值对应的y
的总和。这些可以很容易地计算出来,你可以用轮廓定义你自己的质心函数。