Skip to content

Can’t reproduce SoccerNetGS test results #1

@Lehsuby

Description

@Lehsuby

Can’t reproduce SoccerNetGS test results

Description

I’m unable to reproduce the results reported in the paper using the SoccerNetGS 2024 test set and the Jaccard@5 metric from the sn-calibration repository.

Image

Steps to Reproduce

  1. Build the Docker image

    docker build . -t broadtrack
  2. Run the container (mounting the dataset and scripts)

    docker run --rm -it --gpus=all \
      -v SoccerNetGS:/root/SoccerNetGS \
      -v broadtrack/scripts:/root/scripts \
      broadtrack /bin/bash
  3. Install Python and dependencies

    apt-get update && apt-get install -y python3 python3-pip
    pip install -r /root/scripts/requirements.txt
  4. For each video folder:
    a. Initial run with camera parameters (X=0, Y=55, Z=-12)

    broadtrack --f /root/SoccerNetGS/test/<video_folder>/img1 \
      --X 0 --Y 55 --Z -12 \
      --o /root/SoccerNetGS/test/<video_folder>/output/json_video.json

    b. Compute tripod parameters

    python /root/scripts/compute_tripod.py \
      -i /root/SoccerNetGS/test/<video_folder>/output/json_video.json \
      -o /root/SoccerNetGS/test/<video_folder>/output/json_tripod.json

    c. Re-run with computed tripod parameters

    broadtrack --f /root/SoccerNetGS/test/<video_folder>/img1 \
      --t /root/SoccerNetGS/test/<video_folder>/output/json_tripod.json \
      --o /root/SoccerNetGS/test/<video_folder>/output/json_video_with_tripod.json
  5. Convert json to camera params from sn-calibration

    class Camera:
        def __init__(self, calib_json_object):
            """
            Loads camera parameters from dictionary.
            :param calib_json_object: the dictionary containing camera parameters.
            """
            self.image_width = calib_json_object["sensorResolutionWidthPixels"]
            self.image_height = calib_json_object["sensorResolutionHeightPixels"]
            self.principal_point = (self.image_width/2., self.image_height/2. )
            hfov = calib_json_object["horizontalFieldOfViewDegrees"]*np.pi/180.
            focal = 2 * self.principal_point[0] / (2 * np.tan(hfov / 2.))
            self.xfocal_length = focal
            self.yfocal_length = focal
    
            self.calibration = np.array([
                [self.xfocal_length, 0, self.principal_point[0]],
                [0, self.yfocal_length, self.principal_point[1]],
                [0, 0, 1]
            ], dtype='float')
    
            self.pan = calib_json_object['panDegrees']
            self.tilt = calib_json_object['tiltDegrees']
            self.roll = calib_json_object['rollDegrees']
            pan = calib_json_object['panDegrees'] * np.pi / 180.
            tilt = calib_json_object['tiltDegrees'] * np.pi / 180.
            roll = calib_json_object['rollDegrees'] * np.pi / 180.
    
            self.rotation = np.array([
                [-np.sin(pan) * np.sin(roll) * np.cos(tilt) + np.cos(pan) * np.cos(roll),
                 np.sin(pan) * np.cos(roll) + np.sin(roll) * np.cos(pan) * np.cos(tilt), np.sin(roll) * np.sin(tilt)],
                [-np.sin(pan) * np.cos(roll) * np.cos(tilt) - np.sin(roll) * np.cos(pan),
                 -np.sin(pan) * np.sin(roll) + np.cos(pan) * np.cos(roll) * np.cos(tilt), np.sin(tilt) * np.cos(roll)],
                [np.sin(pan) * np.sin(tilt), -np.sin(tilt) * np.cos(pan), np.cos(tilt)]
            ], dtype='float')
    
            self.rotation = np.transpose(self.pan_tilt_roll_to_orientation(pan, tilt, roll))
    
            self.position = np.array([
                calib_json_object['positionXMeters'],
                calib_json_object['positionYMeters'],
                calib_json_object['positionZMeters']
                                      ], dtype='float')
    
            normalized_radial_distortion_k1 = calib_json_object['normalizedRadialDistortionCoefficients'][0]
            normalized_focal = self.image_height / self.yfocal_length
            self.radial_distortion_k1 = normalized_radial_distortion_k1 / (normalized_focal ** 2)
    
        def to_json_parameters(self):
            return {
                "pan_degrees": self.pan,
                "tilt_degrees": self.tilt,
                "roll_degrees": self.roll,
                "x_focal_length": self.calibration[0, 0],
                "y_focal_length": self.calibration[1, 1],
                "principal_point": [self.principal_point[0], self.principal_point[1]],
                "position_meters": [self.position[0], self.position[1], self.position[2]],
                "rotation_matrix": [[self.rotation[0, 0], self.rotation[0, 1], self.rotation[0, 2]],
                                  [self.rotation[1, 0], self.rotation[1, 1], self.rotation[1, 2]],
                                  [self.rotation[2, 0], self.rotation[2, 1], self.rotation[2, 2]]],
                "radial_distortion": [self.radial_distortion_k1, 0., 0., 0., 0., 0.],
                "tangential_distortion": [0., 0.],
                "thin_prism_distortion": [0., 0., 0., 0.]
            }
    
        def get_look_at_vector(self):
            return self.rotation[2]
    
        @staticmethod
        def pan_tilt_roll_to_orientation(pan, tilt, roll):
            """
            Conversion from euler angles to orientation matrix.
            :param pan:
            :param tilt:
            :param roll:
            :return: orientation matrix
            """
            Rpan = np.array([
                [np.cos(pan), -np.sin(pan), 0],
                [np.sin(pan), np.cos(pan), 0],
                [0, 0, 1]])
            Rroll = np.array([
                [np.cos(roll), -np.sin(roll), 0],
                [np.sin(roll), np.cos(roll), 0],
                [0, 0, 1]])
            Rtilt = np.array([
                [1, 0, 0],
                [0, np.cos(tilt), -np.sin(tilt)],
                [0, np.sin(tilt), np.cos(tilt)]])
            rotMat = np.dot(Rpan, np.dot(Rtilt, Rroll))
            return rotMat
  6. Evaluate with sn-calibration

Observed Behavior

  • I commented out the block around line 111 in compute_tripod.py (to bypass the assertion) because it fails:
    Traceback (most recent call last):
      File "/root/scripts/compute_tripod.py", line 259, in <module>
        analyse_sequence(args.input, args.output)
      File "/root/scripts/compute_tripod.py", line 177, in analyse_sequence
        assert len(intersect_optical_axes_match)
    AssertionError
    
  • Final metrics differ significantly from those in the paper:
    {
      "completeness": 1.0,
      "meanRecall": 0.44478709,
      "meanPrecision": 0.10040818,
      "meanAccuracies": 0.09859953, # Jaccard @ 5
      "finalScore": 0.09859953075647354
    }

Expected Behavior

The reported Jaccard@5 metrics on the SoccerNetGS 2024 test set should match the values presented in the paper.

Environment

  • BroadTrack: main branch (as of February 17, 2025)
  • sn-calibration: main branch (as of June 18, 2024)
  • Python: 3.10.12
  • GPU: NVIDIA 4090
  • Driver Version: 570.133.20
  • CUDA Version: 12.8

Additional Context

Any guidance on required preprocessing, parameter tuning, or code adjustments to reproduce the published results would be greatly appreciated.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions