Определение состояния крестики-нолики по изображению - PullRequest
0 голосов
/ 08 декабря 2018

Я работаю над проектом, в котором мне нужно использовать openCV в java для определения состояния платы Tic Tac Toe.Смотрите пример выполнения программы ниже.

вход

example image of a tic tac toe board

Выход

X,-,-

-,O,-

X,-,-

Я пытаюсь решить эту проблему путем нахождения контуров на изображении, но проблема в том, что захватываются также пустые неотмеченные поля, и я не могу различить формы, используя свойства контуракак размер многоугольника и площадь контура.Ниже приведен код, который у меня есть.

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");
            }
        }
    }
}

Любая помощь будет принята с благодарностью.Спасибо

1 Ответ

0 голосов
/ 08 декабря 2018

Пока я смотрел на это, мне было весело, поэтому я немного увлекся.Результат и код ниже.Я использовал python, но я уверен, что вы поймете это;)

Для дифференциации между X en OI используется solidity .Твердость - это отношение площади контура к его площади выпуклой оболочки.Для О, близкого к 1, для Х меньше половины.

Примечание: плитки нумеруются случайным образом, фактическое местоположение должно быть определено на основе местоположения x / y.Во-вторых, O приведет к 2 кругам, оба с твердостью около 1.

final result

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()
...