Reputation: 73
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 :-)
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
Reputation: 46600
Here's an approach
cv2.HoughLinesP()
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
cv2.inRange()
to isolate the white lines.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 thistop_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
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