0

Background

I've defined a tennis court with 3D points, and I'm trying to use OpenCV to project the 3D points as a 2D image.

Below are the 3D points I'm using to define the tennis court. For reference:

  • Left on the court is -X and Right is +X,
  • Up is the -Y and Down is +Y, and
  • Far side of net is +Z and Close side is -Z.
  • Point (0,0,0) is the intersection of the left side line with the net line (basically the left net post).
def DefineCourtPoints():
    objp = {}
    objp[1] = [0,0,39] # 1.  Intersection of the upper base line with the left side line
    objp[2] = [0,0,-39] # 2.  Intersection of the lower base line with the left side line
    objp[3] = [36,0,-39] # 3.  Intersection of the lower base line with the right side line
    objp[4] = [36,0,39] # 4.  Intersection of the upper base line with the right side line  
    objp[5] = [4.5,0,39] # 5.  Intersection of the upper base line with the left singles line
    objp[6] = [4.5,0,-39] # 6.  Intersection of the lower base line with the left singles line
    objp[7] = [31.5,0,-39] # 7.  Intersection of the lower base line with the right singles line
    objp[8] = [31.5,0,39] # 8.  Intersection of the upper base line with the right singles line
    objp[9] = [4.5,0,21] # 9.  Intersection of the left singles line with the upper service line
    objp[10] = [31.5,0,21] # 10.  Intersection of the right singles line with the upper service line
    objp[11] = [4.5,0,-21] # 11.  Intersection of the left singles line with the lower service line
    objp[12] = [31.5,0,-21] # 12.  Intersection of the right singles line with the lower service line
    objp[13] = [18,0,21] # 13.  Intersection of the upper service line with the center service line
    objp[14] = [18,0,-21] # 14.  Intersection of the lower service line with the center service line
    objp[15] = [0,0,0] # 15.  Intersection of the left side line with the net line (this will be considered (0,0) )
    objp[16] = [36,0,0] # 16.  Intersection of the right side line with the net line

    return objp

For additional reference, here are the intrinsic matrix and distortion parameters for my camera (which were obtained using OpenCV and have been validated):

intrinsic_mtx = np.array([
    [1882.77177, 0.0, 973.572122],
    [0.0, 1880.83035, 537.299982],
    [0.0, 0.0, 1.0]
])

distortion = np.array([0.232714433, -1.35119878, -0.00188551612, 0.00166434182, 2.50351701])

Here is the code I use to project each of the 3D points to 2D, and to graph the points/lines using Matplotlib.

def ProjectPoints(intrinsic_mtx, distortion, R, T, objp)
    x_points = []
    y_points = []

    # Loop through each of the 3D points and project them to 2D.
    for index in objp: 
        2d_point = cv2.projectPoints(
                        np.array(objp[index], dtype=np.float64),
                        cv2.Rodrigues(R)[0],
                        T,
                        intrinsic_mtx, 
                        distortion
                     )[0][0][0]

        print(2d_point)
        x_points.append(2d_point[0])
        y_points.append(2d_point[1])

    # Graph the court boundary.
    lines = [(1,2), # A line exists between point 1 and 2...
            (2,3), # A line exists between point 2 and 3...
            (3,4), # A line exists between point 3 and 4...
            (4,1)] # A line exists between point 4 and 1...

    x_lines = list()
    y_lines = list()
    for pair in lines:
        for i in range(2):
            x_lines.append(x_points[pair[i]-1])
            y_lines.append(y_points[pair[i]-1])
        # Append None to separate the lines.
        x_lines.append(None)
        y_lines.append(None)

    fig, ax = plt.subplots()
    ax.scatter(x_points, y_points)
    ax.axis('equal')
    ax.plot(x_lines, y_lines, c = 'r')
    plt.show()

Problem

When I project the points and graph them, I expect to get something looking like a tennis court, but I just get a blob of points!

To troubleshoot, I decided to graph just outer edges of the court using the four corners of the court (which are the first four points in DefineCourtPoints). I expected to see a rectangle or parallelogram as these are valid projections of a rectangle. However, I get stuff like the following, which doesn't really make any sense since it has crossed lines:

Projection output... not what I'm expecting

The above result was obtained using R = [0, 0, 0] and a T = [0, -10, 0]

Here are the coordinates of the four points for the above result, showing that I didn't cause the weird shape by somehow graphing the lines in the wrong order:

# Intersection of the upper base line with the left side line
[973.572122 , 1019.56417431]

# Intersection of the lower base line with the left side 
line
[973.572122 , 55.03578969]

# Intersection of the lower base line with the right side 
line
[-764.37105031, 55.03578969]

# Intersection of the upper base line with the right side line
[2711.51529431 , 1019.56417431]

Question

Why am I getting such weird projections for such a simple 3D object as a rectangle/tennis court boundary?

Is anyone getting the same results that I'm getting?

Thanks!

Try it yourself

import numpy as np
import cv2
import matplotlib.pyplot as plt

def DefineCourtPoints(): # Just the corners of the court.
    objp = {}
    objp[1] = [0,0,39] # 1.  Intersection of the upper base line with the left side line
    objp[2] = [0,0,-39] # 2.  Intersection of the lower base line with the left side line
    objp[3] = [36,0,-39] # 3.  Intersection of the lower base line with the right side line
    objp[4] = [36,0,39] # 4.  Intersection of the upper base line with the right side line  

objp = DefineCourtPoints()
intrinsic_mtx = np.array([
    [1882.77177, 0.0, 973.572122],
    [0.0, 1880.83035, 537.299982],
    [0.0, 0.0, 1.0]
])

distortion = np.array([0.232714433, -1.35119878, -0.00188551612, 0.00166434182, 2.50351701])
R = np.array([0,0,0])
T = np.array([0,-10,0])
ProjectPoints(intrinsic_mtx, distortion, R, T, objp)
jjasper
  • 191
  • 1
  • 9
  • a [mre] would be handy. – Christoph Rackwitz Dec 07 '21 at 14:06
  • @ChristophRackwitz - Thank you, I just added a "Try it yourself" section that I hope helps. – jjasper Dec 07 '21 at 17:53
  • (And I added the graphing code to my post) – jjasper Dec 07 '21 at 17:59
  • strange flavor of python. identifiers like `2d_point` can't start with a number. please try to avoid pseudo-code. best remove all uses of dictionaries too. – Christoph Rackwitz Dec 07 '21 at 18:14
  • the coordinate systems look unusual. actually, the whole code feels weird. suddenly appending `None`s for some reason... and some other stuff. I'd have to rewrite it all to make sense of it and narrow the potential error space. – Christoph Rackwitz Dec 07 '21 at 18:21
  • I can tell that you're way too close to the object, (78 x 36, when you're 10 units away...) so your picture can't possibly have been made with these values. – Christoph Rackwitz Dec 07 '21 at 18:39
  • 1
    a likely issue is that you're iterating over a dictionary's keys, but you use some oldish python where dictionary keys don't maintain insertion order... or you have an off-by-one because you chose your indices to start at 1. or a combination of that. – Christoph Rackwitz Dec 07 '21 at 18:49
  • 2
    https://gist.github.com/crackwitz/dca6099bfa5203ca77698ee87c8795a1 – Christoph Rackwitz Dec 07 '21 at 19:02
  • The main issue appears to have been the values I used for the rotation and translation matrices. After going through @ChristophRackwitz 's demo on github, I changed my rotation and translation matrices to match what Christoph had in his demo, and I also used an array instead of a dictionary for storing the object points. These changes gave me the result I was looking for. Thanks Christoph! – jjasper Dec 14 '21 at 21:16

0 Answers0