java - OpenCV 寻找轮廓的角点

标签 java android opencv contour

我对 OpenCV 完全陌生。为了练习,我决定做一个“数独求解器”。到目前为止我能够做到这一点:

public Mat processImage(final Mat originalImage, final CvCameraViewFrame frame) {
    image = originalImage.clone();
    image = frame.gray();

    /*
      We load the image in grayscale mode. We don't want to bother with the colour information,
      so just skip it. Next, we create a blank image of the same size. This image will hold
      the actual outer box of puzzle.
     */
    Mat outerBox = new Mat(image.size(), CV_8UC1);


    /*
      Blur the image a little. This smooths out the noise a bit and makes extracting the grid
      lines easier.
     */
    GaussianBlur(image, image, new Size(11, 11), 0);

    /*
      With the noise smoothed out, we can now threshold the image. The image can have varying
      illumination levels, so a good choice for a thresholding algorithm would be an adaptive
      threshold. It calculates a threshold level several small windows in the image.
      This threshold level is calculated using the mean level in the window. So it keeps things
      illumination independent.

      It calculates a mean over a 5x5 window and subtracts 2 from the mean.
      This is the threshold level for every pixel.
     */
    adaptiveThreshold(image, outerBox, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 5, 2);

    /*
      Since we're interested in the borders, and they are black, we invert the image outerBox.
      Then, the borders of the puzzles are white (along with other noise).
     */
    bitwise_not(outerBox, outerBox);

    /*
      This thresholding operation can disconnect certain connected parts (like lines).
      So dilating the image once will fill up any small "cracks" that might have crept in.
     */
    Mat kernel = new Mat(3,3, outerBox.type()) {
        {
            put(0,0,0);
            put(0,1,1);
            put(0,2,0);

            put(1,0,1);
            put(1,1,1);
            put(1,2,1);

            put(2,0,0);
            put(2,1,1);
            put(2,2,0);
        }
    };
    dilate(outerBox, outerBox, kernel);

    final List<MatOfPoint> contours = new ArrayList<>();
    findContours(outerBox, contours, new Mat(outerBox.size(), outerBox.type()), CV_SHAPE_RECT, CHAIN_APPROX_SIMPLE);

    final Integer biggestPolygonIndex = getBiggestPolygonIndex(contours);

    if (biggestPolygonIndex != null) {
        setGreenFrame(contours, biggestPolygonIndex, originalImage);
        return originalImage;
    }

     return outerBox;
}

最终看起来像这样

sudokuArea

所以绿色区域内的所有东西都是我的谜题。我的问题是如何提取它并对其进行一些数字识别。

所以在我看来,第一个合乎逻辑的步骤是削减这个区域。但我不知道如何才能得到它。那么如何获得绿色轮廓的角点呢?

欢迎任何帮助/提示。

最佳答案

经过一番尝试,我终于解决了

    final List<MatOfPoint> contours = new ArrayList<>();
    findContours(outerBox, contours, new Mat(outerBox.size(), outerBox.type()), CV_SHAPE_RECT, CHAIN_APPROX_SIMPLE);

    final Integer biggestPolygonIndex = getBiggestPolygonIndex(contours);
    if (biggestPolygonIndex != null) {
        final MatOfPoint biggest = contours.get(biggestPolygonIndex);
        List<Point> corners = getCornersFromPoints(biggest.toList());
        System.out.println("corner size " + corners.size());
        for (Point corner : corners) {
            drawMarker(originalImage, corner, new Scalar(0,191,255), 0, 20, 3);
        }

        setGreenFrame(contours, biggestPolygonIndex, originalImage);
    }

private List<Point> getCornersFromPoints(final List<Point> points) {
    double minX = 0;
    double minY = 0;
    double maxX = 0;
    double maxY = 0;


    for (Point point : points) {
        double x = point.x;
        double y = point.y;

        if (minX == 0 || x < minX) {
            minX = x;
        }
        if (minY == 0 || y < minY) {
            minY = y;
        }
        if (maxX == 0 || x > maxX) {
            maxX = x;
        }
        if (maxY == 0 || y > maxY) {
            maxY = y;
        }
    }

    List<Point> corners = new ArrayList<>(4);
    corners.add(new Point(minX, minY));
    corners.add(new Point(minX, maxY));
    corners.add(new Point(maxX, minY));
    corners.add(new Point(maxX, maxY));

    return corners;
}

private Integer getBiggestPolygonIndex(final List<MatOfPoint> contours) {
    double maxVal = 0;
    Integer maxValIdx = null;
    for (int contourIdx = 0; contourIdx < contours.size(); contourIdx++) {
        double contourArea = contourArea(contours.get(contourIdx));
        if (maxVal < contourArea) {
            maxVal = contourArea;
            maxValIdx = contourIdx;
        }
    }

    return maxValIdx;
}

private void setGreenFrame(final List<MatOfPoint> contours,
                           final int biggestPolygonIndex,
                           Mat originalImage) {
    drawContours(originalImage, contours, biggestPolygonIndex, new Scalar(124,252,0), 3);
}

关于java - OpenCV 寻找轮廓的角点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55311775/

相关文章:

java - Spring 3.1 WebApplicationInitializer & Embedded Jetty 8 AnnotationConfiguration

Java - 契约测试

java - keyPressed 方法不工作 Java

android - 如何删除actionbar的底线

c++ - 保持 OpenCV 2 和 OpenCV 3 之间的代码兼容性

c++ - 将 Mat 转换为 <vector<vector>> C++

image - 使用opencv将变量放在图像上

java - 如何使用下拉菜单的结果创建字符串?

java - 未找到资源异常将可绘制动画加载到 ImageView 中

android - Android 中所有屏幕中的页脚