Android OpenCV检测围板的不同方法

Android OpenCV检测围板的不同方法,android,opencv,image-recognition,baduk,Android,Opencv,Image Recognition,Baduk,我正在开发一款Android应用程序,可以识别并创建一个新的应用程序 我制作了一个版本,能够检测到一块板并扭曲透视图使其呈方形(下面的代码和示例图像),不幸的是,添加石头时会变得有点困难。(下面的图像) 关于普通围棋板的重要事项: 圆形黑白宝石 黑板上的黑线 板的颜色范围从白色到浅棕色,有时带有木纹 石头放在两条线的交点上 如果我错了,请纠正我,但我认为我目前的方法不好。 有人对我如何将石头和线条与图片的其余部分分开有一个大致的想法吗 我的代码: Mat input = inputF

我正在开发一款Android应用程序,可以识别并创建一个新的应用程序

我制作了一个版本,能够检测到一块板并扭曲透视图使其呈方形(下面的代码和示例图像),不幸的是,添加石头时会变得有点困难。(下面的图像)

关于普通围棋板的重要事项:

  • 圆形黑白宝石
  • 黑板上的黑线
  • 板的颜色范围从白色到浅棕色,有时带有木纹
  • 石头放在两条线的交点上
如果我错了,请纠正我,但我认为我目前的方法不好。 有人对我如何将石头和线条与图片的其余部分分开有一个大致的想法吗

我的代码:

    Mat input = inputFrame.rgba(); //original image
    Mat gray = new Mat();          //grayscale image

    //convert image to grayscale
    Imgproc.cvtColor( input, gray, Imgproc.COLOR_RGB2GRAY);

    //try to improve histogram (more contrast)
    equalizeHist(gray, gray);

    //blur image
    Size s = new Size(5,5);
    GaussianBlur(gray, gray, s, 0);

    //apply adaptive treshold 
    adaptiveThreshold( gray, gray, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY,11,2);

    //adding secondary treshold, removes a lot of noise
    threshold(gray, gray, 0, 255, Imgproc.THRESH_BINARY + Imgproc.THRESH_OTSU);
一些图片:


(来源:)


(来源:)

编辑:05-03-2016

耶!设法正确检测线条和颜色。前提条件图片必须是电路板本身,没有任何其他可见背景。
我使用houghLinesP(60行)和houghricles(17圈),在手机上的持续时间(第1代Moto G)约为5秒。
检测电路板和翘曲当它必须在不同角度和闪电条件下工作时,这是一个相当大的挑战。。还在做那件事吗

对于不同方法的建议仍然欢迎


(来源:)

编辑:15-03-2016

我发现了一种很好的方法,可以通过交叉类型的形态变换来获得直线相交,当图片直接在板上方拍摄时效果非常好,不幸的是,不是以一定角度拍摄的(见下文)
(来源:)

在我上一次更新中,我用从正上方拍摄的图片展示了线条和石头检测,从那时起,我一直致力于检测电路板并将其扭曲,以使线条和石头检测变得有用

哈里斯角点检测
我努力获得正确的参数设置,我仍然不确定它们是否是最佳的,在使用harris角点之前,找不到关于如何优化图像的更多信息。现在,它检测到许多角落是有用的。虽然感觉它可以工作。(示例中带图片的上一行)

交叉型形态变换
当图片直接从上面拍摄时效果很好,从一个角度使用或与旋转的板一起使用都不起作用(示例中图片的中间线)

轮廓线和轮廓线
如果外板上没有石头,光线条件也不太恶劣,那么它的效果就相当不错。轮廓通常只是电路板的一部分(下图中的下线)

Imgproc.GaussianBlur(图像,图像,新尺寸(5,5),0);
Imgproc.adaptiveThreshold(image,image,255,Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,11,2);
Mat层次结构=新Mat();
MatOfPoint最大值=null;
int轮廓线=0;
双大面积=0;
double minSize=2000;
列表等高线=新的ArrayList();
findContours(反相器尺寸、轮廓、层次、Imgproc.RETR\u外部、Imgproc.CHAIN\u近似\u简单);
//找到最大的
对于(int x=0;x最小尺寸和面积>大面积){
大面积=面积;
最大=轮廓。获取(x);
轮廓线=x;
}
}
这三种方法都能提供正确的图像,但不够可靠。欢迎对参数、图像预处理、不同方法或任何可能改进检测的想法=)

编辑:2016年3月31日

探测线和石头已经基本解决了,所以我将结束这个问题。用于准确检测和翘曲

任何对我的进步感兴趣的人:现在不要期望太多

编辑:2016年10月16日

更新:我看到一些人仍在关注这个问题。 我测试了更多的东西,开始使用Tensorflow,我的神经网络看起来很有前途, 还有很多工作要做,我当前的图像数据集很糟糕,现在我正在准备一个大数据集


这款应用程序最好使用一块有粗线条和漂亮闪电的方形板

假设您不想“强迫”最终用户拍摄最干净的照片(例如使用二维码扫描仪之类的覆盖层)

也许您可以使用一些具有不同内核的形态变换:

  • 使用直线的矩形内核打开和关闭
  • 用椭圆内核打开和关闭以获得石头(应该可以在某个点反转图像以恢复白色或黑色)

看看(对不起,这个是C++的,但我觉得java中的这个几乎相同)

我尝试了这些操作,从数独游戏中移除网格,以避免细胞提取过程中的噪音,效果非常好


让我知道这些信息对您很有用(这肯定是一个非常有趣的案例)

我正在使用同一个程序。我一点也不喜欢找线。
首先使用透视变换,使电路板成为一个正方形,就像你所做的那样。找到19x19网格的边。然后假设电路板是19x19,您可以只计算线的位置。这对我很有效。然后计算石头中心的最近交点,以确定石头所在的行和列。对我来说很有效。可能只是针对不同的照明条件和不同颜色的石头和木板的校准程序。

谢谢!我理解这个想法。我必须测试它,并将返回给你的结果。我真的很高兴这个建议,不幸的是更为不同的项目
    Mat corners = new Mat();
    Imgproc.cornerHarris(image, corners, 5, 3, 0.03);

    Mat mask = new Mat(corners.size(), CvType.CV_8U, new Scalar(1));
    Core.MinMaxLocResult maxVal = Core.minMaxLoc(corners);

    Core.inRange(corners, new Scalar(maxVal.maxVal * 0.01), new Scalar(maxVal.maxVal), mask);
    Imgproc.GaussianBlur(image, image, new Size(5, 5), 0);
    Imgproc.adaptiveThreshold(image, image, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 11, 2);

    int morph_elem = 1;     //0: Rect - 1: Cross - 2: Ellipse
    int morph_size = 5;

    int morph_operator = 0; //0: Opening - 1: Closing \n 2: Gradient - 3: Top Hat \n 4: Black Hat
    Mat element = getStructuringElement( morph_elem, new Size(2 * morph_size + 1, 2 * morph_size + 1), new Point( morph_size, morph_size ));
    morphologyEx(image, image, morph_operator + 2, element);
    Imgproc.GaussianBlur(image, image, new Size(5, 5), 0);
    Imgproc.adaptiveThreshold(image, image, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY_INV, 11, 2);

    Mat hierarchy = new Mat();
    MatOfPoint biggest     = null;
    int contourId          = 0;
    double biggestArea     = 0;

    double minSize = 2000;
    List<MatOfPoint> contours = new ArrayList<>();

    findContours(InvertedImage, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

    //find biggest
    for( int x = 0; x < contours.size() ; x++ ){

        double area = Imgproc.contourArea(contours.get(x));

        if( area > minSize && area > biggestArea ){

            biggestArea = area;
            biggest     = contours.get(x);
            contourId   = x;
        }
    }