java - 质心计算在 OpenCV 中产生错误的结果

标签 java opencv centroid

首先我要声明我正在慢慢发疯。我正在尝试从图像中提取轮廓并使用 Java 和 OpenCV 计算它们的质心。

对于所有内部轮廓,结果都是正确的,但是对于外部(最大)轮廓,质心偏离得很远。输入图像、代码和输出结果都在下面。 OpenCV 版本为 3.1。

其他人遇到过这个问题,建议是:

  1. 检查轮廓是否闭合。是的,我查过了。
  2. 在提取轮廓之前使用 Canny 检测边缘。我不明白为什么这是必要的,但我试过了,结果是它弄乱了树的层次结构,因为它为每条边生成了两个轮廓,这不是我想要的。

输入图像非常大 (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 ) enter image description here 输出: enter image description here

标准输出:

Bounding box area is: 6460729,000000 and contour area is: 5963212,000000

质心靠近左上角绘制,在轮廓之外。

最佳答案

正如评论讨论中提到的,您遇到的这个问题似乎是 was reported特别是在 OpenCV 的 GitHub 上的 Java 实现中。最终用 this simple pull request 解决了。 .有一些不必要的 int 转换。

可能的解决方案:

  1. 升级 OpenCV 应该可以解决问题。

  2. 您可以使用修复程序编辑您的库文件(它只是删除几行上的 (int) 强制转换)。

  3. 定义您自己的函数来计算质心。


如果你无聊想算出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_01y 的总和值。这些可以很容易地计算出来,您可以使用轮廓定义自己的质心函数。

关于java - 质心计算在 OpenCV 中产生错误的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46333547/

相关文章:

java - 使用 UnetSocket 在 unetstack 中创建客户端和服务器节点之间的通信

java - 为什么在输入字段中输入内容时,我的 ActionBar 消失了?

java - 在java中获取实例化类型变量

OPENCV如何加速imshow?

c - 如何围绕质心旋转/缩放/平移

python - 在python中计算矩形的质心

java - 带有 gradle 和 groovy 的 eclipselink 元模型

android - Opencv4Android getPerspectiveTransform错误

c++ - 在 OpenCV C++ 中将图像区域设置为零的最佳方法?

c - 如何在 C 中计算多边形的质心