从图像中识别井字游戏板的状态 [英] Identifying state of tic-tac-toe board from image

查看:64
本文介绍了从图像中识别井字游戏板的状态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在一个项目中,我必须在Java中使用openCV来识别井字游戏板的状态.请在下面查看示例程序执行.

输入

输出

  X,-,--,O,-X,-,- 

我正在尝试通过在图像中找到轮廓来解决此问题,但是问题是,空的未标记框也被捕获了,我无法使用轮廓属性(例如多边形大小和轮廓)来区分形状区域.下面是我到目前为止的代码.

  package finalproject;导入java.awt.Color;导入java.util.ArrayList;导入java.util.List;导入org.opencv.core.Core;导入org.opencv.core.Mat;导入org.opencv.core.MatOfInt;导入org.opencv.core.MatOfPoint;导入org.opencv.core.MatOfPoint2f;导入org.opencv.core.Rect;导入org.opencv.core.Scalar;导入org.opencv.core.Size;导入org.opencv.imgcodecs.Imgcodecs;导入org.opencv.imgproc.Imgproc;公共课程FinalProject {公共静态void main(String [] args){System.loadLibrary(Core.NATIVE_LIBRARY_NAME);素材图片= Imgcodecs.imread("C://Users//BadarJahan//Desktop//board-perfect.jpg");Mat binaryImage =预处理(图像);列表< MatOfPoint>轮廓=新的ArrayList<>();Imgproc.findContours(binaryImage,轮廓,新Mat(),Imgproc.RETR_CCOMP,Imgproc.CHAIN_APPROX_SIMPLE);列表< MatOfPoint2f>ContoursConvert = new ArrayList<>();for(MatOfPoint轮廓:轮廓){SilhouettesConvert.add(new MatOfPoint2f(contour.toArray()));}identificationTicTacToeConfiguration(binaryImage,contoursConvert);}私人静态Mat预处理(Mat colorImage){//Imgproc.resize(colorImage,colorImage,new Size(489,0));Mat grayImage =新Mat(),binaryImage =新Mat();Imgproc.cvtColor(colorImage,grayImage,Imgproc.COLOR_BGR2GRAY);binaryImage = grayImage;Imgproc.threshold(grayImage,binaryImage,0、255,Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);最终Mat内核= Imgproc.getStructuringElement(Imgproc.MORPH_RECT,新的Size(5,5));Imgproc.morphologyEx(binaryImage,binaryImage,Imgproc.MORPH_CLOSE,内核);返回binaryImage;}私有静态MatOfPoint2f getApproxPoly(最终MatOfPoint2f轮廓){MatOfPoint2f polyContour =新的MatOfPoint2f();最终双epsillon = Imgproc.arcLength(contour,true)* 0.02;final boolean close = true;Imgproc.approxPolyDP(轮廓,polyContour,epsillon,关闭);返回polyContour;}私有静态无效printContourProperties(最终MatOfPoint等高线){最终双轮廓区域= Imgproc.contourArea(contour);MatOfInt凸包=新的MatOfInt();Imgproc.convexHull(contour,凸面壳);//最终的double doubleHullArea = Imgproc.contourArea(convexHull);矩形boundingRect = Imgproc.boundingRect(contour);MatOfPoint2f poly = getApproxPoly(newMatOfPoint2f(contour.toArray()));System.out.println(轮廓区域:" + outlineArea);System.out.println(纵横比:" +boundingRect.width/boundingRect.height);System.out.println("Extend:" + outlineArea/boundingRect.area());//System.out.println("Solidity:" + outlineArea/convexHullArea);System.out.println("Poly Size:" + poly.size().area()+是凸"+ Imgproc.isContourConvex(new MatOfPoint(poly.toArray())));System.out.println();}私有静态void showContourProperties(最终Mat输入,最终List< MatOfPoint>等高线){Mat inputCopy =新的Mat();for(int i = 0; i< contours.size(); i ++){input.copyTo(inputCopy);标量颜色=新标量(255);最终的int厚度= 3;Imgproc.drawContours(inputCopy,contours,i,颜色,厚度);printContourProperties(contours.get(i));Imgcodecs.imwrite("C://Users//BadarJahan//Desktop//Test-1-check-"+ i +".jpg,inputCopy);}}私人静态ContourType识别ContourType(最终MatOfPoint2f轮廓){最终双轮廓区域= Imgproc.contourArea(contour);最终MatOfPoint2f poly = getApproxPoly(contour);ContourType类型= ContourType.Unknown;if((poly.elemSize()> 7&&poly.elemSize()< 10)&& contourArea< 1000){类型= ContourType.OType;}否则if(contourArea> 10000){类型= ContourType.XType;}返回类型;}私有静态无效voididentificationTicTacToeConfiguration(最终Mat输入,最终List< MatOfPoint2f>等高线){for(MatOfPoint2f轮廓:轮廓){ContourType类型=识别ContourType(轮廓);if(type == ContourType.XType){System.out.print("X");}否则if(type == ContourType.OType){System.out.print("O");}}}} 

任何帮助将不胜感激.谢谢

解决方案

当我看着这个过程时,我很开心,所以我有点生气.结果和代码如下.我使用了python,但是我敢肯定您会发现的;)

要区分X en OI,请使用

 将numpy导入为np导入cv2#创建一个2D数组来保存游戏状态gamestate = [[-",-",-"],[-",-",-"],[-",-",-"]]]#内核用于去除噪音内核= np.ones((7,7),np.uint8)#加载彩色图像img = cv2.imread('X_O.jpg')#获取图像的宽度和高度img_width = img.shape [0]img_height = img.shape [1]#变成灰度img_g = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#变成阈值二进制ret,thresh1 = cv2.threshold(img_g,127,255,cv2.THRESH_BINARY)#从二进制文件中删除噪声thresh1 = cv2.morphologyEx(thresh1,cv2.MORPH_OPEN,内核)#找到并绘制轮廓.RETR_EXTERNAL仅检索极端的外部轮廓im2,轮廓,层次结构= cv2.findContours(thresh1,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)cv2.drawContours(img,轮廓,-1,(0,255,0),15)tileCount = 0对于轮廓中的cnt:#忽略不是平铺的小轮廓如果cv2.contourArea(cnt)>200000:tileCount = tileCount + 1#使用boundingrect获取图块的坐标x,y,w,h = cv2.boundingRect(cnt)#从二进制文件创建新映像,以供进一步分析.修剪掉有线条的边缘瓦= thresh1 [x + 40:x + w-80,y + 40:y + h-80]#从主图像创建新图像,以便我们轻松绘制轮廓imgTile = img [x + 40:x + w-80,y + 40:y + h-80]#确定图块的数组索引tileX =圆((x/img_width)* 3)tileY =圆((y/img_height)* 3)#在图块图像中查找轮廓.RETR_TREE检索所有轮廓,并重建嵌套轮廓的完整层次.im2,c,层次结构= cv2.findContours(tile,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)对于c中的ct:#防止图块发现自己为轮廓如果cv2.contourArea(ct)<180000:cv2.drawContours(imgTile,[ct],-1,(255,0,0),15)#计算单数面积= cv2.contourArea(ct)船体= cv2.convexHull(ct)hull_area = cv2.contourArea(船体)坚固度=浮点(面积)/船体面积#用正确的符号填充游戏状态如果(实心> 0.5):gamestate [tileX] [tileY] ="O"别的:gamestate [tileX] [tileY] ="X"#在图块中放置一个数字cv2.putText(img,str(tileCount),(x + 200,y + 300),cv2.FONT_HERSHEY_SIMPLEX,10,(0,0,255),20)#打印游戏状态print("Gamestate:")对于游戏状态中的行:linetxt ="对于cel而言:linetxt = linetxt +"|"+ cel打印(linetxt)#调整最终图像的大小res = cv2.resize(img,None,fx = 0.2,fy = 0.2,插值= cv2.INTER_CUBIC)#显示图像并在按下键时释放资源cv2.imshow('image1',res)cv2.waitKey(0)cv2.destroyAllWindows() 

I'm working on a project where I have to use openCV in java to identify the state of a tic tac toe board. Please see the sample program execution below.

input

Output

X,-,-

-,O,-

X,-,-

I'm trying to solve this by finding contours in the image, but the problem is that the empty unmarked boxes are also being captured and I'm not being able to distinguish between the shapes using contour properties like polygon size and contour area. Below is the code that I have so far.

package finalproject;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfInt;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;


public class FinalProject {
    public static void main(String[] args) {

    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    Mat image = Imgcodecs.imread("C://Users//BadarJahan//Desktop//board- 
    perfect.jpg");

    Mat binaryImage = preprocess(image);

    List<MatOfPoint> contours = new ArrayList<>();
    Imgproc.findContours(binaryImage, contours,new Mat() ,Imgproc.RETR_CCOMP, Imgproc.CHAIN_APPROX_SIMPLE);
    List<MatOfPoint2f> contoursConvert = new ArrayList<>();

    for(MatOfPoint contour : contours) {
        contoursConvert.add(new MatOfPoint2f(contour.toArray()));
    }

    identifyTicTacToeConfiguration(binaryImage,contoursConvert);

}

    private static Mat preprocess(Mat colorImage) {
//      Imgproc.resize(colorImage, colorImage, new Size(489,0));
        Mat grayImage = new Mat() , binaryImage = new Mat();
        Imgproc.cvtColor(colorImage, grayImage,Imgproc.COLOR_BGR2GRAY);
        binaryImage = grayImage;
        Imgproc.threshold(grayImage, binaryImage, 0, 255, Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
        final Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(5,5));
        Imgproc.morphologyEx(binaryImage, binaryImage, Imgproc.MORPH_CLOSE, kernel);
        return binaryImage;
    }

    private static MatOfPoint2f getApproxPoly(final MatOfPoint2f contour) {
        MatOfPoint2f polyContour = new MatOfPoint2f();
        final double epsillon = Imgproc.arcLength(contour, true) * 0.02;
        final boolean close = true;
        Imgproc.approxPolyDP(contour, polyContour, epsillon, close);
        return polyContour;
    }

    private static void printContourProperties(final MatOfPoint contour) {
        final double contourArea = Imgproc.contourArea(contour);
        MatOfInt convexHull = new MatOfInt();
        Imgproc.convexHull(contour, convexHull);
 //     final double convexHullArea = Imgproc.contourArea(convexHull);
        Rect boundingRect = Imgproc.boundingRect(contour);
        MatOfPoint2f poly =  getApproxPoly(new 
        MatOfPoint2f(contour.toArray()));
        System.out.println("Contour area : " + contourArea);
        System.out.println("Aespect Ratio : " + 
        boundingRect.width/boundingRect.height);
        System.out.println("Extend: " + contourArea/boundingRect.area());
 //     System.out.println("Solidity : " + contourArea/convexHullArea);
        System.out.println("Poly Size : " + poly.size().area() + ", is 
        convex " + Imgproc.isContourConvex(new MatOfPoint(poly.toArray())));
        System.out.println();
    }

    private static void showContourProperties(final Mat input, final List<MatOfPoint> contours) {
        Mat inputCopy = new Mat();
        for(int i = 0; i < contours.size(); i++) {
            input.copyTo(inputCopy);
            Scalar color = new Scalar(255);
            final int thickness = 3;
            Imgproc.drawContours(inputCopy, contours, i, color,thickness);
            printContourProperties(contours.get(i));
            Imgcodecs.imwrite("C://Users//BadarJahan//Desktop//Test-1-check- 
            "+i+".jpg", inputCopy);
        }
    }

    private static ContourType recognizeContourType(final MatOfPoint2f contour) {
        final double contourArea = Imgproc.contourArea(contour);
        final MatOfPoint2f poly = getApproxPoly(contour);
        ContourType type = ContourType.Unknown;
        if((poly.elemSize() > 7 && poly.elemSize() < 10) && contourArea < 1000) {
             type = ContourType.OType;
        }else if(contourArea > 10000) {
            type = ContourType.XType;
        }
        return type;
    }


    private static void identifyTicTacToeConfiguration(final Mat input, final List<MatOfPoint2f> contours) {

        for(MatOfPoint2f contour: contours) {
            ContourType type = recognizeContourType(contour);

            if(type == ContourType.XType) {
                System.out.print("X");
            }else if(type == ContourType.OType) {
                System.out.print("O");
            }
        }
    }
}

Any help would be greatly appreciated. Thanks

解决方案

As I looked into this I was having fun, so I got carried away a bit. The result and code is below. I used python, but I'm sure you'll figure it out ;)

To diffenciate between X en O I used solidity. Solidity is the ratio of contour area to its convex hull area. For an O that is close to 1, for an X less than half.

Note: the tiles are randomly numbered, the actual location needs to be determined based on the x/y location. Second, an O will result in 2 circles, both with solidity near 1.

import numpy as np
import cv2

#create a 2d array to hold the gamestate
gamestate = [["-","-","-"],["-","-","-"],["-","-","-"]]

#kernel used for noise removal
kernel =  np.ones((7,7),np.uint8)
# Load a color image 
img = cv2.imread('X_O.jpg')
# get the image width and height
img_width = img.shape[0]
img_height = img.shape[1]

# turn into grayscale
img_g =  cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# turn into thresholded binary
ret,thresh1 = cv2.threshold(img_g,127,255,cv2.THRESH_BINARY)
#remove noise from binary
thresh1 = cv2.morphologyEx(thresh1, cv2.MORPH_OPEN, kernel)

#find and draw contours. RETR_EXTERNAL retrieves only the extreme outer contours
im2, contours, hierarchy = cv2.findContours(thresh1, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(img, contours, -1, (0,255,0), 15)

tileCount = 0
for cnt in contours:
        # ignore small contours that are not tiles
        if cv2.contourArea(cnt) > 200000: 
                tileCount = tileCount+1
                # use boundingrect to get coordinates of tile
                x,y,w,h = cv2.boundingRect(cnt)
                # create new image from binary, for further analysis. Trim off the edge that has a line
                tile = thresh1[x+40:x+w-80,y+40:y+h-80]
                # create new image from main image, so we can draw the contours easily
                imgTile = img[x+40:x+w-80,y+40:y+h-80]

                #determine the array indexes of the tile
                tileX = round((x/img_width)*3)
                tileY = round((y/img_height)*3)     

                # find contours in the tile image. RETR_TREE retrieves all of the contours and reconstructs a full hierarchy of nested contours.
                im2, c, hierarchy = cv2.findContours(tile, cv2.RETR_TREE , cv2.CHAIN_APPROX_SIMPLE)
                for ct in c:
                        # to prevent the tile finding itself as contour
                        if cv2.contourArea(ct) < 180000:
                                cv2.drawContours(imgTile, [ct], -1, (255,0,0), 15)
                                #calculate the solitity
                                area = cv2.contourArea(ct)
                                hull = cv2.convexHull(ct)
                                hull_area = cv2.contourArea(hull)
                                solidity = float(area)/hull_area

                                # fill the gamestate with the right sign
                                if(solidity > 0.5):
                                        gamestate[tileX][tileY] = "O"
                                else: 
                                        gamestate[tileX][tileY] = "X"
                # put a number in the tile
                cv2.putText(img, str(tileCount), (x+200,y+300), cv2.FONT_HERSHEY_SIMPLEX, 10, (0,0,255), 20)

#print the gamestate
print("Gamestate:")
for line in gamestate:
        linetxt = ""
        for cel in line:
                linetxt = linetxt + "|" + cel
        print(linetxt)

# resize final image
res = cv2.resize(img,None,fx=0.2, fy=0.2, interpolation = cv2.INTER_CUBIC)

# display image and release resources when key is pressed
cv2.imshow('image1',res)
cv2.waitKey(0)
cv2.destroyAllWindows()

这篇关于从图像中识别井字游戏板的状态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆