wingnut
wingnut

Reputation: 73

Detecting and isolating lines on green tennis table board

I am trying to detect lines on a table tennis board, but there is allot of "noise" detected off the board (sides) and on the board as well.

There is also the issue of getting rid of the brand/wording on the table.

Basically want detect the white lines on the green table.

I have been looking at some examples, but being new to OpenCV doesn't help :-)

Table Tennis BoardEdgesenter image description here

import cv2
import numpy as np

img = cv2.imread("table.jpg")
imgS = cv2.resize(img, (960, 1024))
#
# # Convert to grayscale first beforeget edges of the image

kernel_size = 5
gray = cv2.cvtColor(imgS, cv2.COLOR_BGR2GRAY)
blur_gray = cv2.GaussianBlur(gray,(kernel_size, kernel_size), 0)
# get edges of the image

edges = cv2.Canny(blur_gray, 75, 150)
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 50, maxLineGap=100)


# print(lines)

# draw "lines"
for line in lines:
    x1, y1, x2, y2 = line[0]
    # print(line[0])
    # Draw the line on original image (img) and set color to blue and thickness 2
    cv2.line(imgS, (x1, y1), (x2, y2), (0, 255,0), 2)


cv2.imshow("Edges", edges)
cv2.imshow("img", imgS)
cv2.waitKey(0)
cv2.destroyAllWindows()

Upvotes: 4

Views: 1351

Answers (1)

nathancy
nathancy

Reputation: 46600

Here's an approach

  • Convert image to grayscale and Gaussian blur
  • Canny edge detection
  • Create mask to keep white line contours
  • Perform morphological transformations with a vertical kernel to isolate vertical lines
  • Perform morphological transformations with a horizontal kernel to isolate horizontal lines
  • Detect lines with cv2.HoughLinesP()
  • Iterate through mask and find contours
  • Draw contours onto original image

Canny edge detection

Next we use a special vertical kernel using cv2.getStructuringElement() to filter out horizontal lines and extract only the vertical lines

vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,3))
remove_horizontal = cv2.morphologyEx(canny, cv2.MORPH_OPEN, vertical_kernel)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate_vertical = cv2.morphologyEx(remove_horizontal, cv2.MORPH_CLOSE, kernel, iterations=5)

We do the same thing with a horizontal kernel to extract only horizontal lines

horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,1))
remove_vertical = cv2.morphologyEx(canny, cv2.MORPH_OPEN, horizontal_kernel)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
dilate_horizontal = cv2.morphologyEx(remove_vertical, cv2.MORPH_CLOSE, kernel, iterations=3)

We combine the two to get the resulting mask

Finally we find contours on the mask and draw it on the original image. Here's the result

import cv2
import numpy as np

image = cv2.imread('1.jpg')
image = cv2.resize(image, (960, 1024))
mask = np.zeros(image.shape, np.uint8)
gray = cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
canny = cv2.Canny(blur, 150, 255, 1)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))

# Find Vertical Lines
# ------ 
vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,3))
remove_horizontal = cv2.morphologyEx(canny, cv2.MORPH_OPEN, vertical_kernel)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5,5))
dilate_vertical = cv2.morphologyEx(remove_horizontal, cv2.MORPH_CLOSE, kernel, iterations=5)

minLineLength = 10
maxLineGap = 150
lines = cv2.HoughLinesP(dilate_vertical,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(mask,(x1,y1),(x2,y2),(255,255,255),3)
cv2.imwrite('vertical_mask.png', mask)
# ------ 

# Find Horizontal Lines
# ------ 
horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,1))
remove_vertical = cv2.morphologyEx(canny, cv2.MORPH_OPEN, horizontal_kernel)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9,9))
dilate_horizontal = cv2.morphologyEx(remove_vertical, cv2.MORPH_CLOSE, kernel, iterations=3)

minLineLength = 10
maxLineGap = 300
horizontal_mask = np.zeros(image.shape, np.uint8)
lines = cv2.HoughLinesP(dilate_horizontal,1,np.pi/180,100,minLineLength,maxLineGap)
for line in lines:
    for x1,y1,x2,y2 in line:
        cv2.line(mask,(x1,y1),(x2,y2),(255,255,255),3)
        cv2.line(horizontal_mask,(x1,y1),(x2,y2),(255,255,255),3)
cv2.imwrite('horizontal_mask.png', horizontal_mask)
# ------ 

mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
    cv2.drawContours(image, [c], -1, (36,255,12), 2)

cv2.imwrite('remove_vertical.png', remove_vertical)
cv2.imwrite('remove_horizontal.png', remove_horizontal)
cv2.imwrite('dilate_horizontal.png', dilate_horizontal)
cv2.imwrite('mask.png', mask)
cv2.imwrite('image.png', image)
cv2.waitKey()

Notes: Other potential strategies and thoughts

  • Use color thresholding and cv2.inRange() to isolate the white lines.
  • To filter out the net, you can use a modified version of the above method but instead of using cv2.HoughLinesP(), use Numpy slicing to extract only the top/bottom halves and only search for lines using the top 1/4 and bottom 1/4 of image. Maybe something like this
top_half = remove_vertical[0:int(image.shape[0] * .25), 0:image.shape[1]]
bottom_half = remove_vertical[int(image.shape[0] * .75):image.shape[0], 0:image.shape[1]]

Strategy to isolate only the middle line

  • Convert image to grayscale and Gaussian blur
  • Canny edge detection
  • Perform morphological transformations with a vertical kernel
  • Dilate to enhance contour
  • Find contours and filter using contour area

Canny edge detection

Next we use a special vertical kernel using cv2.getStructuringElement() to filter out horizontal lines and extract only the vertical lines

vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,9))
remove_horizontal = cv2.morphologyEx(canny, cv2.MORPH_OPEN, vertical_kernel)

Finally we dilate to enhance contours and filter using a minimum threshold area. Here's the result

import cv2

image = cv2.imread('1.jpg')
image = cv2.resize(image, (960, 1024))
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (7,7), 0)
canny = cv2.Canny(blur, 120, 255, 1)

vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1,9))
remove_horizontal = cv2.morphologyEx(canny, cv2.MORPH_OPEN, vertical_kernel)

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
dilate = cv2.morphologyEx(remove_horizontal, cv2.MORPH_CLOSE, kernel)

cnts = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]

for c in cnts:
    area = cv2.contourArea(c)
    if area > 50:
        cv2.drawContours(image, [c], -1, (36,255,12), 3)

cv2.imwrite('result.png', image)
cv2.imwrite('canny.png', canny)
cv2.imwrite('remove_horizontal.png', remove_horizontal)
cv2.waitKey()

Upvotes: 4

Related Questions