Search code examples
pythonopencvcomputer-visionchess

how to correctly select the outline of the board in opencv?


I'm trying to determine the boundaries of the chessboard (they are bounded by a black bar) enter image description here

I do the following

    img = cv2.imread(filename)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
    cv2.imwrite("gray.jpg", gray)

enter image description here

    thresh = cv2.inRange(gray, hsv_min, hsv_max)

    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
    thresh = cv2.dilate(thresh, kernel, iterations = 1)
    thresh = cv2.erode(thresh, None, iterations = 4)

enter image description here

and there's no way I can get a better result, after

contours0, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    for cnt in contours0:
        rect = cv2.minAreaRect(cnt)
        box = cv2.boxPoints(rect)
        box = np.int0(box) 
        cv2.drawContours(img, [box], -1, (255, 0, 0), 0)

    cv2.imshow('contours', img)

enter image description here can someone help me?


Solution

  • Here is one way to do that in Python OpenCV. Basically, threshold the image. Apply some morphology. Get the external contours. Then draw the largest ones above some threshold in perimeter on a black background. The important aspect is that you get the 4 corners included. Then get the convex hull and draw that as the boundary.

    Input:

    enter image description here

    import cv2
    import numpy as np
    
    # read image
    img = cv2.imread('chess.jpg')
    
    # convert to grayscale
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    # threshold to binary
    thresh = cv2.threshold(gray, 25, 255, cv2.THRESH_BINARY)[1]
    
    # apply morphology
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
    morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,1))
    morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
    
    # invert morph
    morph = 255 - morph
    
    # get external contours
    cnts = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    cnts = cnts[0] if len(cnts) == 2 else cnts[1]
    
    # draw white contour on black background
    cntr_img = np.zeros_like(morph)
    for c in cnts:
        perimeter = cv2.arcLength(c, True)
        if perimeter > 200: 
            cv2.drawContours(cntr_img, [c], 0, 255, 1)
    
    # get all non-zero points
    points = np.column_stack(np.where(cntr_img.transpose() > 0))
    hull = cv2.convexHull(points)
    
    # draw convex hull vertices on input image
    result = img.copy()
    cv2.polylines(result, [hull], True, (0,0,255), 2)
    
    # save results
    cv2.imwrite('chess_threshold.jpg', thresh)
    cv2.imwrite('chess_morph.jpg', morph)
    cv2.imwrite('chess_contour.jpg', cntr_img)
    cv2.imwrite('chess_convexhull.jpg', result)
    
    # show results
    cv2.imshow('thresh', thresh)
    cv2.imshow('morph', morph)
    cv2.imshow('cntr_img', cntr_img)
    cv2.imshow('result', result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

    Threshold Image:

    enter image description here

    Morphology Image:

    enter image description here

    Contour Image:

    enter image description here

    Resulting Convex Hull on copy of Input:

    enter image description here