flyingchicken
flyingchicken

Reputation: 97

Get full lines on football field in order to warp to birds eye view

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: 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: Extended lines on field

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:

  1. Mask out field to only leave white sections

  2. Use canny to get edge detection

    canny = cv2.Canny(img_mask, 50, 150, apertureSize=3) Canny image detection

  3. Get hough lines from the canny detection

    lines_p = cv2.HoughLinesP(canny, 1, np.pi / 180, 100, None, 0, 20) Hough line detection

  4. 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

Answers (0)

Related Questions