Christopher Haywood
Christopher Haywood

Reputation: 23

Detecting tennis court lines intercepts

I am trying to improve my code to find the tennis court line intercepts so that I can find the boundaries of the different quadrants of the court.

Input image This is the tennis court image I am trying to analyze

Output Image This is the final result

I achieved this by first finding the white pixels in the image, then applying canny edge detection with some preprocessing such as Gaussian blur. Then the canny edge output is dilated to help prepare it for hough lines detection.

Then taking the hough lines output I used the python implementation of the Bentley–Ottmann algorithm by github user ideasman42 to find the hough line intercepts.

This seems to work pretty well, but I'm struggling to tune my system to find the last 4 intercept points. If anyone could give me advice to improve or tune this implementation or even offer up some ideas for a better way to solve the problem of finding the court boundaries I would appreciate it.

# import the necessary packages
import numpy as np
import argparse
import cv2
import scipy.ndimage as ndi
import  poly_point_isect as bot

# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", help = "path to the image")
args = vars(ap.parse_args())

# load the image
image = cv2.imread(args["image"])

# define the list of boundaries
boundaries = [
    ([180, 180, 100], [255, 255, 255])
]

# loop over the boundaries
for (lower, upper) in boundaries:
    # create NumPy arrays from the boundaries
    lower = np.array(lower, dtype = "uint8")
    upper = np.array(upper, dtype = "uint8")

    # find the colors within the specified boundaries and apply
    # the mask
    mask = cv2.inRange(image, lower, upper)
    output = cv2.bitwise_and(image, image, mask = mask)

    # show the images
    cv2.imshow("images", np.hstack([image, output]))
    cv2.waitKey(0)

gray = cv2.cvtColor(output,cv2.COLOR_BGR2GRAY)

kernel_size = 5
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size),0)

low_threshold = 10
high_threshold = 200
edges = cv2.Canny(gray, low_threshold, high_threshold)
dilated = cv2.dilate(edges, np.ones((2,2), dtype=np.uint8))

cv2.imshow('dilated.png', dilated)
cv2.waitKey(0)

rho = 1  # distance resolution in pixels of the Hough grid
theta = np.pi / 180  # angular resolution in radians of the Hough grid
threshold = 10 # minimum number of votes (intersections in Hough grid cell)
min_line_length = 40  # minimum number of pixels making up a line
max_line_gap = 5  # maximum gap in pixels between connectable line segments
line_image = np.copy(output) * 0  # creating a blank to draw lines on

# Run Hough on edge detected image
# Output "lines" is an array containing endpoints of detected line segments

lines = cv2.HoughLinesP(dilated, rho, theta, threshold, np.array([]), min_line_length, max_line_gap)

points = []
for line in lines:
    for x1, y1, x2, y2 in line:
        points.append(((x1 + 0.0, y1 + 0.0), (x2 + 0.0, y2 + 0.0)))
        cv2.line(line_image, (x1, y1), (x2, y2), (255, 0, 0), 5)        

cv2.imshow('houghlines.png', line_image)
cv2.waitKey(0)

lines_edges = cv2.addWeighted(output, 0.8, line_image, 1, 0)
print(lines_edges.shape)

intersections = bot.isect_segments(points)
print(intersections)

for idx, inter in enumerate(intersections):
    a, b = inter
    match = 0
    for other_inter in intersections[idx:]:
        c, d = other_inter
        if abs(c-a) < 8 and abs(d-b) < 8:
            match = 1
            if other_inter in intersections:
                intersections.remove(other_inter)
                intersections[idx] = ((c+a)/2, (d+b)/2)

    if match == 0:
        intersections.remove(inter)

for inter in intersections:
    a, b = inter
    for i in range(6):
        for j in range(6):
            lines_edges[int(b) + i, int(a) + j] = [0, 0, 255]

# Show the result
cv2.imshow('line_intersections.png', lines_edges)
cv2.imwrite('line_intersections.png', lines_edges)
cv2.waitKey(0)

Upvotes: 2

Views: 7834

Answers (1)

Andreas
Andreas

Reputation: 485

Here is my solution, using a different approach. I use the Harris corner detector to detect corners. The parameters were just tweaked in a hurry, so feel free to play with them. Here is a tutorial from OpenCV on it.

I use the OpenCV Wrapper library for some easier OpenCV code. It should be pretty easy to translate if you don't want it.

# import the necessary packages
import numpy as np
import cv2
import opencv_wrapper as cvw

# import  poly_point_isect as bot

# construct the argument parse and parse the arguments
# load the image
image = cv2.imread("tennis.jpg")

# define the list of boundaries
boundaries = [([180, 180, 100], [255, 255, 255])]

# loop over the boundaries
for (lower, upper) in boundaries:
    # create NumPy arrays from the boundaries
    lower = np.array(lower, dtype="uint8")
    upper = np.array(upper, dtype="uint8")

    # find the colors within the specified boundaries and apply
    # the mask
    mask = cv2.inRange(image, lower, upper)
    output = cv2.bitwise_and(image, image, mask=mask)

# Start my code
gray = cvw.bgr2gray(output)

corners = cv2.cornerHarris(gray, 9, 3, 0.01)
corners = cvw.normalize(corners).astype(np.uint8)

thresh = cvw.threshold_otsu(corners)
dilated = cvw.dilate(thresh, 3)

contours = cvw.find_external_contours(dilated)

for contour in contours:
    cvw.circle(image, contour.center, 3, cvw.Color.RED, -1)

cv2.imshow("Image", image)
cv2.waitKey(0)

And the result:

enter image description here

Disclosure: I am the author of OpenCV Wrapper.

Upvotes: 2

Related Questions