Java OpenCV查找等高线的角点

Java OpenCV查找等高线的角点,java,android,opencv,opencv4android,opencv-contour,Java,Android,Opencv,Opencv4android,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

我对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;
}
public Mat processImage(最终Mat原始图像、最终cvcameravewframe帧){
image=originalImage.clone();
image=frame.gray();
/*
我们以灰度模式加载图像。我们不想为颜色信息而烦恼,
所以跳过它。下一步,我们创建一个相同大小的空白图像。这个图像将保持不变
拼图的实际外部框。
*/
Mat outerBox=新Mat(image.size(),CV_8UC1);
/*
稍微模糊一下图像。这会稍微平滑一些噪声,并使提取网格变得简单
线路更简单。
*/
高斯模糊(图像,图像,新尺寸(11,11),0);
/*
在平滑噪声后,我们现在可以对图像进行阈值设置。图像可以具有不同的阈值
照明水平,因此阈值算法的一个好选择是自适应的
阈值。它计算图像中几个小窗口的阈值级别。
这个阈值水平是使用窗口中的平均水平来计算的,所以它保留了一些东西
与照明无关。
它计算5x5窗口的平均值,并从平均值中减去2。
这是每个像素的阈值级别。
*/
自适应阈值(图像,外框,255,自适应阈值平均值,阈值二进制,5,2);
/*
因为我们对边框感兴趣,而且它们是黑色的,所以我们将图像反转为外部框。
然后,谜题的边界是白色的(以及其他噪声)。
*/
按位_not(outerBox,outerBox);
/*
此阈值操作可以断开某些连接部件(如线路)。
因此,将图像放大一次将填满任何可能出现的小“裂缝”。
*/
Mat内核=新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);
}
};
扩张(外盒,外盒,内核);
最终列表轮廓=新的ArrayList();
findContours(外盒、轮廓、新垫(外盒尺寸()、外盒类型())、CV形状、链近似、简单);
最终整数biggestPolygonIndex=getBiggestPolygonIndex(等高线);
if(biggestPolygonIndex!=null){
设置绿框(等高线、最大多边形索引、原始图像);
返回原始图像;
}
返回外盒;
}
结果是这样的

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

因此,在我看来,第一个合乎逻辑的步骤是削减这一领域。但我不知道怎么才能得到它。那么我怎样才能得到绿色轮廓的角呢


欢迎提供任何帮助/提示。

经过一些尝试,我终于解决了这个问题

    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);
}
final List contours=new ArrayList();
findContours(外盒、轮廓、新垫(外盒尺寸()、外盒类型())、CV形状、链近似、简单);
最终整数biggestPolygonIndex=getBiggestPolygonIndex(等高线);
if(biggestPolygonIndex!=null){
最终MatOfPoint最大=等高线.get(最大多边形索引);
List corners=getCornersFromPoints(maxist.toList());
System.out.println(“拐角尺寸”+拐角尺寸());
用于(点角点:角点){
drawMarker(原始图像、角点、新标量(0191255)、0、20、3);
}
设置绿框(等高线、最大多边形索引、原始图像);
}
私有列表getCornersFromPoints(最终列表点){
双最小值=0;
双minY=0;
双maxX=0;
双最大值=0;
用于(点:点){
双x=点x;
双y=点y;
if(minX==0 | | xmaxX){
maxX=x;
}
如果(maxY==0 | | y>maxY){
maxY=y;
}
}
列表角点=新的ArrayList(4);
添加(新点(minX,minY));
添加(新点(minX,maxY));
添加(新点(maxX,minY));
添加(新点(maxX,maxY));
返回角;
}
私有整数getBiggestPolygonIndex(最终列表){
双maxVal=0;
整数maxValIdx=null;
用于(int-contourIdx=0;contourIdx
经过一些尝试,我终于解决了这个问题

    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);
}
final List contours=new ArrayList();
findContours(外盒、轮廓、新垫(外盒尺寸()、外盒类型())、CV形状、链近似、简单);
最终整数biggestPolygonIndex=getBiggestPolygonIndex(等高线);
if(biggestPolygonIndex!=null){
最终MatOfPoint最大=等高线.get(最大多边形索引);
List corners=getCornersFromPoints(maxist.toList());
System.out.println(“拐角尺寸”+拐角尺寸());
用于(点角点:角点){
drawMarker(原始图像、角点、新标量(0191255)、0、20、3);
}
设置绿框(等高线、最大多边形索引、原始图像);
}
私有列表getCornersFromPoints(最终列表点){
双最小值=0;
双minY=0;
双maxX=0;
双最大值=0;
用于(点:点){
双x=点x;
双y=点y;
如果