Reputation: 97
I am trying to detect the full lines on an American football field in order to figure out player positions and warp the field with homography. I'm using python and opencv, but I can't get the full field lines. Here is the input image:
I've tried to use other methods I've seen (How to detect lines in a football field using OpenCV) but this is all I've been able to get:
While this is close, I would like to be able to have a single blue line going all thr way across the field for each of the horizontal lines on the field. My process so far has been:
Mask out field to only leave white sections
Use canny to get edge detection
Get hough lines from the canny detection
lines_p = cv2.HoughLinesP(canny, 1, np.pi / 180, 100, None, 0, 20)
Extend and then merge close lines to get the final result shown at the top
def extend_lines(lines, extension_length=20):
"""
Extend short line segments by a specified length.
Parameters:
- lines: List of line segments in the format ((x1, y1), (x2, y2)).
- extension_length: Length by which to extend the lines.
Returns:
- List of extended line segments.
"""
extended_lines = []
for line in lines:
print(line)
x1, y1, x2, y2 = line[0]
# Calculate the vector representing the line segment
dx, dy = x2 - x1, y2 - y1
# Normalize the vector
length = np.sqrt(dx**2 + dy**2)
if length > 0:
dx /= length
dy /= length
# Extend the line segment by the specified length
x1_extended = int(x1 - extension_length * dx)
y1_extended = int(y1 - extension_length * dy)
x2_extended = int(x2 + extension_length * dx)
y2_extended = int(y2 + extension_length * dy)
extended_lines.append([[x1_extended, y1_extended, x2_extended, y2_extended]])
else:
extended_lines.append(line)
for i in reversed(range(len(extended_lines))):
line = extended_lines[i]
print(line[0])
length = np.sqrt((line[0][2] - line[0][0])**2 + (line[0][3] - line[0][1])**2)
if length < 150:
del extended_lines[i]
return extended_lines
def merge_close_lines(lines, epsilon=50, min_samples=2):
"""
Merge lines that are close to each other.
Parameters:
- lines: List of line segments in the format ((x1, y1), (x2, y2)).
- epsilon: Maximum distance to consider for merging lines.
- min_samples: Minimum number of samples required to form a cluster.
Returns:
- List of merged line segments.
"""
points = np.array([[(line[0][0] + line[0][2]) / 2, (line[0][1] + line[0][3]) / 2] for line in lines])
# Apply DBSCAN clustering
db = DBSCAN(eps=epsilon, min_samples=min_samples).fit(points)
labels = db.labels_
# Group lines by cluster label
grouped_lines = {}
for i, label in enumerate(labels):
if label in grouped_lines:
grouped_lines[label].append(lines[i])
else:
grouped_lines[label] = [lines[i]]
# Fit a line to each cluster
merged_lines = []
for label, lines_in_cluster in grouped_lines.items():
merged_line = find_longest_line(lines_in_cluster)
merged_lines.append(merged_line)
return merged_lines
def find_longest_line(lines):
longest_line = None
max_length = 0
for line in lines:
x1, y1, x2, y2 = line[0]
length = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
if length > max_length:
max_length = length
longest_line = line
return longest_line
I would like a more consistent and accurate way to get the lines, or some way to warp the field using just this. Thank y'all!
Upvotes: 3
Views: 270