milind-u | 959d6bd | 2023-01-21 12:32:52 -0800 | [diff] [blame^] | 1 | import copy |
| 2 | import cv2 |
| 3 | import flatbuffers |
| 4 | import glog |
| 5 | import json |
| 6 | import numpy as np |
| 7 | import os |
| 8 | import sys |
| 9 | |
| 10 | import frc971.vision.calibration.CalibrationData as CalibrationData |
| 11 | import frc971.vision.calibration.CameraCalibration as CameraCalibration |
| 12 | import frc971.vision.calibration.TransformationMatrix as TransformationMatrix |
| 13 | |
| 14 | import glog |
| 15 | |
| 16 | |
| 17 | def bazel_name_fix(filename, year): |
| 18 | """Quick fix to naming games that happen with bazel""" |
| 19 | ret_name = filename |
| 20 | try: |
| 21 | from bazel_tools.tools.python.runfiles import runfiles |
| 22 | r = runfiles.Create() |
| 23 | ret_name = r.Rlocation('org_frc971/y%s/vision/%s' % (year, filename)) |
| 24 | except: |
| 25 | print("Failed bazel_name_fix") |
| 26 | pass |
| 27 | |
| 28 | return ret_name |
| 29 | |
| 30 | |
| 31 | def list_to_transformation_matrix(values, fbb): |
| 32 | """Puts a list of values into an FBB TransformationMatrix.""" |
| 33 | |
| 34 | TransformationMatrix.TransformationMatrixStartDataVector(fbb, len(values)) |
| 35 | for n in reversed(values): |
| 36 | fbb.PrependFloat32(n) |
| 37 | list_offset = fbb.EndVector() |
| 38 | |
| 39 | TransformationMatrix.TransformationMatrixStart(fbb) |
| 40 | TransformationMatrix.TransformationMatrixAddData(fbb, list_offset) |
| 41 | return TransformationMatrix.TransformationMatrixEnd(fbb) |
| 42 | |
| 43 | |
| 44 | def load_camera_definitions(year): |
| 45 | """ |
| 46 | CAMERA DEFINITIONS |
| 47 | We only load in cameras that have a calibration file |
| 48 | These are stored in y<year>/vision/calib_files |
| 49 | |
| 50 | Or better yet, use //y2020/vision:calibration to calibrate the camera |
| 51 | using a Charuco target board |
| 52 | """ |
| 53 | |
| 54 | dir_name = bazel_name_fix("calib_files", year) |
| 55 | if dir_name is not None: |
| 56 | glog.debug("Searching for calibration files in " + dir_name) |
| 57 | else: |
| 58 | glog.fatal("Failed to find calib_files directory") |
| 59 | |
| 60 | camera_calib_list = [] |
| 61 | for filename in sorted(os.listdir(dir_name)): |
| 62 | glog.debug("Inspecting %s", filename) |
| 63 | if ("cam-calib-int" in filename |
| 64 | or 'calibration' in filename) and filename.endswith(".json"): |
| 65 | # Extract intrinsics from file |
| 66 | calib_file = open(dir_name + "/" + filename, 'r') |
| 67 | calib_dict = json.loads(calib_file.read()) |
| 68 | camera_calib_list.append(calib_dict) |
| 69 | return camera_calib_list |
| 70 | |
| 71 | |
| 72 | def generate_header(year): |
| 73 | """ |
| 74 | Generates a header file with a CalibrationData() function |
| 75 | returning the data as a binary flatbuffer. |
| 76 | Expects command line argument: output header path |
| 77 | Parameter year is a string, ex. "2023" |
| 78 | """ |
| 79 | camera_calib_list = load_camera_definitions(year) |
| 80 | |
| 81 | output_path = sys.argv[1] |
| 82 | glog.debug("Writing file to %s", output_path) |
| 83 | |
| 84 | fbb = flatbuffers.Builder(0) |
| 85 | |
| 86 | images_vector = [] |
| 87 | |
| 88 | # Create camera calibration data |
| 89 | camera_calibration_vector = [] |
| 90 | for camera_calib in camera_calib_list: |
| 91 | fixed_extrinsics_list = camera_calib["fixed_extrinsics"] |
| 92 | fixed_extrinsics_vector = list_to_transformation_matrix( |
| 93 | fixed_extrinsics_list, fbb) |
| 94 | |
| 95 | turret_extrinsics_vector = None |
| 96 | if "turret_extrinsics" in camera_calib: |
| 97 | turret_extrinsics_list = camera_calib["turret_extrinsics"] |
| 98 | turret_extrinsics_vector = list_to_transformation_matrix( |
| 99 | turret_extrinsics_list, fbb) |
| 100 | |
| 101 | camera_int_list = camera_calib["intrinsics"] |
| 102 | CameraCalibration.CameraCalibrationStartIntrinsicsVector( |
| 103 | fbb, len(camera_int_list)) |
| 104 | for n in reversed(camera_int_list): |
| 105 | fbb.PrependFloat32(n) |
| 106 | intrinsics_vector = fbb.EndVector() |
| 107 | |
| 108 | dist_coeffs_list = camera_calib["dist_coeffs"] |
| 109 | CameraCalibration.CameraCalibrationStartDistCoeffsVector( |
| 110 | fbb, len(dist_coeffs_list)) |
| 111 | for n in reversed(dist_coeffs_list): |
| 112 | fbb.PrependFloat32(n) |
| 113 | dist_coeffs_vector = fbb.EndVector() |
| 114 | |
| 115 | node_name_offset = fbb.CreateString(camera_calib["node_name"]) |
| 116 | CameraCalibration.CameraCalibrationStart(fbb) |
| 117 | CameraCalibration.CameraCalibrationAddNodeName(fbb, node_name_offset) |
| 118 | CameraCalibration.CameraCalibrationAddTeamNumber( |
| 119 | fbb, camera_calib["team_number"]) |
| 120 | CameraCalibration.CameraCalibrationAddIntrinsics( |
| 121 | fbb, intrinsics_vector) |
| 122 | CameraCalibration.CameraCalibrationAddDistCoeffs( |
| 123 | fbb, dist_coeffs_vector) |
| 124 | CameraCalibration.CameraCalibrationAddFixedExtrinsics( |
| 125 | fbb, fixed_extrinsics_vector) |
| 126 | if turret_extrinsics_vector is not None: |
| 127 | CameraCalibration.CameraCalibrationAddTurretExtrinsics( |
| 128 | fbb, turret_extrinsics_vector) |
| 129 | camera_calibration_vector.append( |
| 130 | CameraCalibration.CameraCalibrationEnd(fbb)) |
| 131 | |
| 132 | CalibrationData.CalibrationDataStartCameraCalibrationsVector( |
| 133 | fbb, len(camera_calibration_vector)) |
| 134 | for camera_calibration in reversed(camera_calibration_vector): |
| 135 | fbb.PrependUOffsetTRelative(camera_calibration) |
| 136 | camera_calibration_vector_table = fbb.EndVector() |
| 137 | |
| 138 | # Fill out TrainingData |
| 139 | CalibrationData.CalibrationDataStart(fbb) |
| 140 | CalibrationData.CalibrationDataAddCameraCalibrations( |
| 141 | fbb, camera_calibration_vector_table) |
| 142 | fbb.Finish(CalibrationData.CalibrationDataEnd(fbb)) |
| 143 | |
| 144 | bfbs = fbb.Output() |
| 145 | |
| 146 | # "year" will get replaced by the variable |
| 147 | output_prefix = [ |
| 148 | '#ifndef Y{year}_VISION_CALIBRATION_DATA_H_', |
| 149 | '#define Y{year}_VISION_CALIBRATION_DATA_H_', |
| 150 | '#include <stdint.h>', |
| 151 | '#include "absl/types/span.h"', |
| 152 | 'namespace y{year} {{', |
| 153 | 'namespace vision {{', |
| 154 | 'inline absl::Span<const uint8_t> CalibrationData() {{', |
| 155 | ] |
| 156 | output_suffix = [ |
| 157 | ' return absl::Span<const uint8_t>(reinterpret_cast<const uint8_t *>(kData), sizeof(kData));', |
| 158 | '}}', |
| 159 | '}} // namespace vision', |
| 160 | '}} // namespace y{year}', |
| 161 | '#endif // Y{year}_VISION_CALIBRATION_DATA_H_', |
| 162 | ] |
| 163 | |
| 164 | # Write out the header file |
| 165 | with open(output_path, 'wb') as output: |
| 166 | for line in output_prefix: |
| 167 | output.write(line.format(year=year).encode("utf-8")) |
| 168 | output.write(b'\n') |
| 169 | output.write(b'alignas(64) static constexpr char kData[] = "') |
| 170 | for byte in fbb.Output(): |
| 171 | output.write(b'\\x' + (b'%x' % byte).zfill(2)) |
| 172 | output.write(b'";\n') |
| 173 | for line in output_suffix: |
| 174 | output.write(line.format(year=year).encode("utf-8")) |
| 175 | output.write(b'\n') |