我正在一个项目中,我必须在Java中使用openCV来识别井字游戏板的状态。请在下面查看示例程序执行。
输入
输出
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");
}
}
}
}
任何帮助将不胜感激。谢谢
最佳答案
当我看着这个过程时,我很开心,所以我有点生气。结果和代码如下。我使用了python,但是我敢肯定您会发现的;)
为了区分X en O,我使用了solidity。坚固度是轮廓面积与其凸包面积的比率。对于接近1的O,X小于一半。
注意:图块是随机编号的,实际位置需要根据x / y位置确定。其次,O将导致2个圆圈,两个圆圈的实心度都接近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()