From 5d9879950e54290a493bcb7f3094370f98e6c9ad Mon Sep 17 00:00:00 2001 From: Anthony Lombardi Date: Tue, 11 Jul 2023 14:41:33 -0400 Subject: [PATCH 01/12] STYLE: Refactor mayacam readers * Both loadMayaCam1 & 2 had duplicated code. * Add new helper methods `calculateViewport` and `calculateImagePlane` to remove this duplication. * Simplified some of the math by checking in loadMayaCam1 if z is negative. --- libautoscoper/src/Camera.cpp | 165 ++++++++++++++--------------------- libautoscoper/src/Camera.hpp | 4 + 2 files changed, 68 insertions(+), 101 deletions(-) diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index f3f26b32..e3c045bf 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -200,64 +200,14 @@ void Camera::loadMayaCam1(const std::string& mayacam) coord_frame_ = CoordFrame::from_xyzypr(xyzypr); // Calculate the viewport - viewport_[0] = (2.0f*u0 - size_[0]) / z; - viewport_[1] = (2.0f*v0 - size_[1]) / z; - viewport_[2] = -2.0f*size_[0] / z; - viewport_[3] = -2.0f*size_[1] / z; - - // Choose the scaling factor such that the image plane will be on the - // other side of the origin from the camera. The values in the mayacam - // file are discarded. - double distance = sqrt(translation[0] * translation[0] + - translation[1] * translation[1] + - translation[2] * translation[2]); - double scale = -1.5*distance / z; - - image_plane_trans[0] = scale*(size_[0] / 2.0 - u0); - image_plane_trans[1] = scale*(size_[1] / 2.0 - v0); - image_plane_trans[2] = scale*z; - - // Calculate the vertices at the corner of the image plane. - double image_plane_center[3]; - coord_frame_.point_to_world_space(image_plane_trans, image_plane_center); - - double half_width = scale*size_[0] / 2.0; - double half_height = scale*size_[1] / 2.0; - - double right[3] = { coord_frame_.rotation()[0], - coord_frame_.rotation()[1], - coord_frame_.rotation()[2] }; - double up[3] = { coord_frame_.rotation()[3], - coord_frame_.rotation()[4], - coord_frame_.rotation()[5] }; + if (z < 0) { + calculateViewport(u0, v0, -z, -z); + } else { + calculateViewport(u0, v0, z, z); + } - image_plane_[0] = image_plane_center[0] - half_width*right[0] + - half_height*up[0]; - image_plane_[1] = image_plane_center[1] - half_width*right[1] + - half_height*up[1]; - image_plane_[2] = image_plane_center[2] - half_width*right[2] + - half_height*up[2]; - - image_plane_[3] = image_plane_center[0] - half_width*right[0] - - half_height*up[0]; - image_plane_[4] = image_plane_center[1] - half_width*right[1] - - half_height*up[1]; - image_plane_[5] = image_plane_center[2] - half_width*right[2] - - half_height*up[2]; - - image_plane_[6] = image_plane_center[0] + half_width*right[0] - - half_height*up[0]; - image_plane_[7] = image_plane_center[1] + half_width*right[1] - - half_height*up[1]; - image_plane_[8] = image_plane_center[2] + half_width*right[2] - - half_height*up[2]; - - image_plane_[9] = image_plane_center[0] + half_width*right[0] + - half_height*up[0]; - image_plane_[10] = image_plane_center[1] + half_width*right[1] + - half_height*up[1]; - image_plane_[11] = image_plane_center[2] + half_width*right[2] + - half_height*up[2]; + // Calculate the Image Plane + calculateImagePlane(u0, v0, z); } @@ -398,32 +348,45 @@ void Camera::loadMayaCam1(const std::string& mayacam) coord_frame_ = CoordFrame(&rotation[0][0], translation); // Calculate the viewport - viewport_[0] = -(2.0f*K[2][0] - size_[0]) / K[0][0]; - viewport_[1] = -(2.0f*K[2][1] - size_[1]) / K[1][1]; - viewport_[2] = 2.0f*size_[0] / K[0][0]; - viewport_[3] = 2.0f*size_[1] / K[1][1]; + calculateViewport(K[2][0], K[2][1], K[0][0], K[1][1]); + + // Calculate the image plane + double z = -0.5* (K[0][0] + K[1][1]); // Average focal length, negated to be consistent with MayaCam 1.0 + calculateImagePlane(K[2][0], K[2][1], z); + } + + + void Camera::calculateViewport(const double& cx, const double& cy, const double& fx, const double& fy) { + // Calculate the viewport + // Validate that neither fx nor fy are zero + if (fx == 0 || fy == 0) { + throw std::runtime_error("Invalid camera parameters (fx or fy is zero)"); + } + viewport_[0] = -(2.0f*cx - size_[0]) / fx; + viewport_[1] = -(2.0f*cy - size_[1]) / fy; + viewport_[2] = 2.0f* size_[0] / fx; + viewport_[3] = 2.0f* size_[1] / fy; + } - // Choose the scaling factor such that the image plane will be on the - // other side of the origin from the camera. The values in the mayacam - // file are discarded. - double z = - 0.5* (K[0][0] + K[1][1]); - double distance = sqrt(translation[0] * translation[0] + - translation[1] * translation[1] + - translation[2] * translation[2]); - double scale = -1.5*distance / z; + void Camera::calculateImagePlane(const double& cx, const double& cy, const double& z) { + // Pick a scale factor that places the image plane on the other side of the origin from the camera. + double distance = sqrt(coord_frame_.translation()[0] * coord_frame_.translation()[0] + + coord_frame_.translation()[1] * coord_frame_.translation()[1] + + coord_frame_.translation()[2] * coord_frame_.translation()[2]); + double scale = -1.5 * distance / z; double image_plane_trans[3]; - image_plane_trans[0] = scale*(size_[0] / 2.0 - K[2][0]); - image_plane_trans[1] = scale*(size_[1] / 2.0 - K[2][1]); - image_plane_trans[2] = scale*z; + image_plane_trans[0] = scale * (size_[0] / 2.0 - cx); + image_plane_trans[1] = scale * (size_[1] / 2.0 - cy); + image_plane_trans[2] = scale * z; // Calculate the vertices at the corner of the image plane. double image_plane_center[3]; coord_frame_.point_to_world_space(image_plane_trans, image_plane_center); - double half_width = scale*size_[0] / 2.0; - double half_height = scale*size_[1] / 2.0; + double half_width = scale * size_[0] / 2.0; + double half_height = scale * size_[1] / 2.0; double right[3] = { coord_frame_.rotation()[0], coord_frame_.rotation()[1], @@ -432,33 +395,33 @@ void Camera::loadMayaCam1(const std::string& mayacam) coord_frame_.rotation()[4], coord_frame_.rotation()[5] }; - image_plane_[0] = image_plane_center[0] - half_width*right[0] + - half_height*up[0]; - image_plane_[1] = image_plane_center[1] - half_width*right[1] + - half_height*up[1]; - image_plane_[2] = image_plane_center[2] - half_width*right[2] + - half_height*up[2]; - - image_plane_[3] = image_plane_center[0] - half_width*right[0] - - half_height*up[0]; - image_plane_[4] = image_plane_center[1] - half_width*right[1] - - half_height*up[1]; - image_plane_[5] = image_plane_center[2] - half_width*right[2] - - half_height*up[2]; - - image_plane_[6] = image_plane_center[0] + half_width*right[0] - - half_height*up[0]; - image_plane_[7] = image_plane_center[1] + half_width*right[1] - - half_height*up[1]; - image_plane_[8] = image_plane_center[2] + half_width*right[2] - - half_height*up[2]; - - image_plane_[9] = image_plane_center[0] + half_width*right[0] + - half_height*up[0]; - image_plane_[10] = image_plane_center[1] + half_width*right[1] + - half_height*up[1]; - image_plane_[11] = image_plane_center[2] + half_width*right[2] + - half_height*up[2]; + image_plane_[0] = image_plane_center[0] - half_width * right[0] + + half_height * up[0]; + image_plane_[1] = image_plane_center[1] - half_width * right[1] + + half_height * up[1]; + image_plane_[2] = image_plane_center[2] - half_width * right[2] + + half_height * up[2]; + + image_plane_[3] = image_plane_center[0] - half_width * right[0] - + half_height * up[0]; + image_plane_[4] = image_plane_center[1] - half_width * right[1] - + half_height * up[1]; + image_plane_[5] = image_plane_center[2] - half_width * right[2] - + half_height * up[2]; + + image_plane_[6] = image_plane_center[0] + half_width * right[0] - + half_height * up[0]; + image_plane_[7] = image_plane_center[1] + half_width * right[1] - + half_height * up[1]; + image_plane_[8] = image_plane_center[2] + half_width * right[2] - + half_height * up[2]; + + image_plane_[9] = image_plane_center[0] + half_width * right[0] + + half_height * up[0]; + image_plane_[10] = image_plane_center[1] + half_width * right[1] + + half_height * up[1]; + image_plane_[11] = image_plane_center[2] + half_width * right[2] + + half_height * up[2]; } } // namespace XROMM diff --git a/libautoscoper/src/Camera.hpp b/libautoscoper/src/Camera.hpp index 50b1e113..2630fd63 100644 --- a/libautoscoper/src/Camera.hpp +++ b/libautoscoper/src/Camera.hpp @@ -89,6 +89,10 @@ class Camera void loadMayaCam2(const std::string& mayacam); + // helper functions + void calculateViewport(const double &cx, const double &cy, const double &fx, const double &fy); + void calculateImagePlane(const double &cx, const double &cy, const double &z); + }; } // namespace xromm From c2a92f5c571fd2a6d8431629d2049a8d788318e0 Mon Sep 17 00:00:00 2001 From: Anthony Lombardi Date: Tue, 11 Jul 2023 15:06:38 -0400 Subject: [PATCH 02/12] DOC: Fix incorrect statements in the camera calibration file specs --- .../file-specifications/camera-calibration.md | 199 +++++++++++++++++- 1 file changed, 192 insertions(+), 7 deletions(-) diff --git a/Documentation/file-specifications/camera-calibration.md b/Documentation/file-specifications/camera-calibration.md index 0a82e0ba..bd77bd15 100644 --- a/Documentation/file-specifications/camera-calibration.md +++ b/Documentation/file-specifications/camera-calibration.md @@ -1,5 +1,44 @@ # Camera Calibration File Format +## VTKCam 1.0 + +### Overview + +The camera calibration file is a yaml and contains all of the information to load a camera into Autoscoper. The file is organized with key value pairs. + +:::{warning} +The VTKCam 1.0 format is non-standard and exists purely as an +implementation detail. While we are documenting it, the file +organization may change from version to version without notice. +We mean it. +::: + +### Key value pairs + +| Key | Value | +| --- | --- | +|`version`| The version of the file. Currently only 1.0 is supported. | +|`focal-point`| The XYZ coordinates of the focal point. | +|`camera-position`| The XYZ coordinates of the camera position. | +|`view-up`| The up vector of the camera. | +|`view-angle`| The view angle of the camera. | +|`image-width`| The width of the image. | +|`image-height`| The height of the image. | + +### Example + +```{code-block} json +{ + "@schema": "https://autoscoperm.slicer.org/vtk-schema-1.0.json", + "version": 1.0, + "focal-point": [-7.9999999999999964, -245.50000000000006, -186.65000000000006], + "camera-position": [104.71926635196253, -255.22259800818924, -179.66771669788898], + "view-up": [0.0, 1.0, 0.0], + "view-angle": 30.0, + "image-width": 1760, + "image-height": 1760, +} +``` ## MayaCam 2.0 ### Overview @@ -17,6 +56,15 @@ The camera calibration file is a txt and contains all of the information to load ### Template +```{note} +The rotation is applied to the camera after the translation. Therefore, the final camera position is calculated by the following equation: + +``` + +```{math} +camera\_position = rotation * -translation +``` + ``` image size height,width @@ -28,13 +76,13 @@ fx,0,cx rotation r00,r01,r02 -r10,r11,r12 -r20,r21,r22 +-r10,-r11,-r12 +-r20,-r21,-r22 translation -t0 -t1 -t2 +-x +y +-z ``` ### Example @@ -70,10 +118,10 @@ The camera calibration file is a csv and contains all of the information to load | Line Number | Description | | --- | --- | | 1 | The camera location in world space. | -| 2 | Rotations around the local x, y, and z axes of the camera. The order of rotation is z, y, x. | +| 2 | Rotations around the local x, y, and z axes of the camera. The order of rotation is z, y, x (Roll, Pitch, Yaw).| | 3 | The position of the film plane relative to the camera. Given in the camera's local space. | | 4 | u0, v0, z | -| 5 | scale, height, width | +| 5 | scale, height, width. Scale is ignored and is computed from distance and z. | ### Example @@ -85,3 +133,140 @@ The camera calibration file is a csv and contains all of the information to load 840.2,839.2,-6325.8 0.1,1760,1760 ``` + +## Backend Calculations + +### Overview + +This section describes the calculations that are performed by the backend to convert the camera calibration file into an internal Camera object. + +### Image Plane + + +The image plane is the location of the DRR image in world space. The center of the image plane is calculated by the following equation: + +```{note} +The below formula applies to VTKCam 1.0 and MayaCam 2.0. For MayaCam 1.0, `c_x`` and `c_y` are replaced by `u0` and `v0` respectively. The value for `z` is also not calculated and is instead pulled from the camera calibration file. + +`DRR_X` and `DRR_Y` are the width and height of the DRR image respectively. + +The translation matrix is represented by `t` and the rotation matrix is represented by `R`. +``` + +The constant `-0.5` is used so that the `z` value is the average of the `f_x` and `f_y` values, and it is negated to be consistent with the MayaCam 1.0 file format. + +```{math} +z = -0.5 * (f_x + f_y) +``` + +```{math} +distance = \sqrt{t_x^2 + t_y^2 + t_z^2} +``` + +The constant `-1.5` is used so that the image plane is placed across the origin from the camera. + +```{math} +scale = \frac{-1.5 * distance}{z} +``` + +```{math} +image\_plane\_translation[0] = scale * (\frac{DRR_X}{2} - c_x) +``` + +```{math} +image\_plane\_translation[1] = scale * (\frac{DRR_Y}{2} - c_y) +``` + +```{math} +image\_plane\_translation[2] = scale * z +``` + +```{math} +image\_plane\_center = R * image\_plane\_translation + t +``` + +### Viewport + +The viewport defines the position of the 2D viewer. The viewport is calculated by the following equation: + +```{note} +The below formula applies to VTKCam 1.0 and MayaCam 2.0. For MayaCam 1.0, c_x and c_y are replaced by u0 and v0 respectively. While f_x and f_y are replaced by z. + +DRR_X and DRR_Y are the width and height of the DRR image respectively. +``` + +```{math} +viewport[0] = -(2 * c_x - DRR_X) / f_x +``` + +```{math} +viewport[1] = -(2 * c_y - DRR_Y) / f_y +``` + +```{math} +viewport[2] = 2 * DRR_X / f_x +``` + +```{math} +viewport[3] = 2 * DRR_Y / f_y +``` + +### VTKCam 1.0 Unique Calculations + +The VTKCam 1.0 file format calculates the camera orientation and the focal length from the camera position, focal point, and view angle. + +#### Camera Orientation + +```{note} +The translation matrix is assumed to be the camera position. +``` + +First, a vector is calculated from the camera position to the focal point. + +```{math} +f = \text{focal_point} - \text{camera_position} + +\hat{f} = \frac{f}{\lVert f \rVert} +``` + +Next, a side vector is calculated from the cross product of the focal vector and the up vector. The up vector is assumed to be (0, 1, 0). + +```{math} +s = \hat{f} \times \begin{bmatrix} 0 \\ 1 \\ 0 \end{bmatrix} + +\hat{s} = \frac{s}{\lVert s \rVert} +``` + +Finally, the up vector is calculated from the cross product of the side vector and the focal vector. + +```{math} +\text{up} = \hat{s} \times \hat{f} +``` + +The rotation matrix is then calculated from the side, up, and focal vectors. + +```{math} +\text{rotation_matrix} = \begin{bmatrix} +s_x & s_y & s_z \\ +up_x & up_y & up_z \\ +-f_X & -f_y & -f_z \\ +\end{bmatrix} +``` + +#### Focal Length + +```{note} +The principal point (c_x, c_y) is assumed to be half of the image size in the x and y directions respectively. + +The view angle is assumed to be in radians (This is converted from degrees to radians in the backend.). +``` + +The focal length is calculated from the view angle and the image size. + +```{math} +f_x = \frac{image\_size_x}{2 \cdot \tan(\frac{view\_angle}{2})} +``` + +```{math} +f_y = \frac{image\_size_y}{2 \cdot \tan(\frac{view\_angle}{2})} +``` \ No newline at end of file From 3e72db188087eb69aa3075b6f8a710e64cd369ab Mon Sep 17 00:00:00 2001 From: Anthony Lombardi Date: Tue, 11 Jul 2023 20:49:51 -0400 Subject: [PATCH 03/12] ENH: Add VTKCam Reader --- libautoscoper/CMakeLists.txt | 1 + libautoscoper/src/Camera.cpp | 226 +++++++++++++++++++++--- libautoscoper/src/Camera.hpp | 8 + libautoscoper/src/gpu/opencl/OpenCL.cpp | 3 +- 4 files changed, 217 insertions(+), 21 deletions(-) diff --git a/libautoscoper/CMakeLists.txt b/libautoscoper/CMakeLists.txt index f822bc4b..331ff84d 100644 --- a/libautoscoper/CMakeLists.txt +++ b/libautoscoper/CMakeLists.txt @@ -108,6 +108,7 @@ target_compile_definitions(libautoscoper PUBLIC -DUSE_LIBTIFF) target_include_directories(libautoscoper PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src + ../math ) install(TARGETS libautoscoper diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index e3c045bf..cffdccb1 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -100,29 +100,58 @@ namespace xromm "See https://autoscoper.readthedocs.io/en/latest/file-specifications/camera-calibration.html#mayacam-" + version + "-0"; } -Camera::Camera(const std::string& mayacam) : mayacam_(mayacam) -{ - // Load the mayacam.csv file into an array of doubles + std::string vtkCamReadingError(const std::string& version, int line, const std::string& filename, const std::string& message) { + return std::string("Invalid VTKCam ") + version + ".0 file. " + message + "\n" + "See line " + std::to_string(line) + " in " + filename + ".\n" + "\n" + "Please check the VTKCam " + version + ".0 specification.\n" + "See https://autoscoper.readthedocs.io/en/latest/file-specifications/camera-calibration.html#vtkcam-" + version + "-0"; + } - std::fstream file(mayacam.c_str(), std::ios::in); - if (file.is_open() == false) { - throw std::runtime_error("File not found: " + mayacam); + bool parseArray(std::string& value, double* a, int n) { + value.erase(std::remove(value.begin(), value.end(), '['), value.end()); + value.erase(std::remove(value.begin(), value.end(), ']'), value.end()); + value.erase(std::remove(value.begin(), value.end(), ','), value.end()); + std::istringstream value_stream(value); + for (int i = 0; i < n; ++i) { + if (!(value_stream >> a[i])) { + return false; + } } - - std::string csv_line; - safeGetline(file, csv_line); - file.close(); - if (csv_line.compare("image size") == 0) - { - loadMayaCam2(mayacam); - } - else - { - loadMayaCam1(mayacam); + return true; } -} +Camera::Camera(const std::string& mayacam) : mayacam_(mayacam) +{ + // Check the file extension + std::string::size_type ext_pos = mayacam_.find_last_of('.'); + if (ext_pos == std::string::npos) { + throw std::runtime_error("Invalid MayaCam file"); + } + std::string ext = mayacam_.substr(ext_pos + 1); + // if its a yaml file load it as a vtk camera + if (ext.compare("yaml") == 0) { + loadVTKCamera(mayacam_); + } + else { + std::fstream file(mayacam.c_str(), std::ios::in); + if (file.is_open() == false) { + throw std::runtime_error("File not found: " + mayacam); + } + std::string csv_line; + safeGetline(file, csv_line); + file.close(); + if (csv_line.compare("image size") == 0) + { + loadMayaCam2(mayacam); + } + else + { + loadMayaCam1(mayacam); + } + } +} void Camera::loadMayaCam1(const std::string& mayacam) { @@ -210,7 +239,6 @@ void Camera::loadMayaCam1(const std::string& mayacam) calculateImagePlane(u0, v0, z); } - void Camera::loadMayaCam2(const std::string& mayacam) { std::cout << "Reading MayaCam 2.0 file: " << mayacam << std::endl; @@ -227,7 +255,6 @@ void Camera::loadMayaCam1(const std::string& mayacam) std::fstream file(mayacam.c_str(), std::ios::in); - double csv_vals[5][3]; std::string csv_line, csv_val; for (int i = 0; i < 17 && safeGetline(file, csv_line); ++i) { std::istringstream csv_line_stream(csv_line); @@ -355,6 +382,140 @@ void Camera::loadMayaCam1(const std::string& mayacam) calculateImagePlane(K[2][0], K[2][1], z); } + void Camera::loadVTKCamera(const std::string& filename) { + // Open and parse the file + double version = -1.0, view_angle = -1.0, image_width = -1.0 , image_height = -1.0; + double focal_point[3] = {-1.0, -1.0, -1.0}, camera_position[3] = { -1.0, -1.0, -1.0 }, view_up[3] = { -1.0, -1.0, -1.0 }; + std::fstream file(filename.c_str(), std::ios::in); + if (!file.is_open()) { + throw std::runtime_error("Error opening VTKCam file: " + filename); + } + std::string line; + // The file is a series of key value pairs separated by a colon, # denotes a comment + int line_num = 1; + while (safeGetline(file, line)) { + // Ignore comments and empty lines + if (line.empty() || line[0] == '#') { + line_num++; + continue; + } + // Split the line into key and value + std::string key, value; + std::istringstream line_stream(line); + if (!getline(line_stream, key, ':')) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing key.")); + } + if (!getline(line_stream, value, ':')) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing value.")); + } + // Parse the key value pair + if (key == "version") { + std::istringstream value_stream(value); + if (!(value_stream >> version)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing version number.")); + } + } + else if (key == "focal-point") { + if (!parseArray(value, focal_point, 3)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing focal-point.")); + } + } + else if (key == "camera-position") { + if (!parseArray(value, camera_position, 3)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing camera-position.")); + } + } + else if (key == "view-up") { + if (!parseArray(value, view_up, 3)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing view-up.")); + } + } + else if (key == "view-angle") { + std::istringstream value_stream(value); + if (!(value_stream >> view_angle)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing view-angle.")); + } + } + else if (key == "image-width") { + std::istringstream value_stream(value); + if (!(value_stream >> image_width)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing image-width.")); + } + } + else if (key == "image-height") { + std::istringstream value_stream(value); + if (!(value_stream >> image_height)) { + file.close(); + throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing image-height.")); + } + } + line_num++; + } + + // Close the file + file.close(); + + // Check that all the values were read + if (version == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing version number.")); + } + if (view_angle == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing view-angle.")); + } + if (image_width == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing image-width.")); + } + if (image_height == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing image-height.")); + } + if (focal_point[0] == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing focal-point.")); + } + if (camera_position[0] == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing camera-position.")); + } + if (view_up[0] == -1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing view-up.")); + } + + // Check the version number + if (version != 1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Unsupported version number.")); + } + + + // Set the size + size_[0] = image_width; + size_[1] = image_height; + + Vec3d cam_pos(camera_position); + Vec3d focal(focal_point); + Vec3d up(view_up); + double* rot = lookAt(cam_pos, focal, up); + coord_frame_ = CoordFrame(rot, cam_pos); + + // Calculate the focal length + double* focal_lengths = calculateFocalLength(view_angle); + + // Calculate the principal point + double cx = image_width / 2.0; + double cy = image_height / 2.0; + + // Calculate the viewport + calculateViewport(cx, cy , focal_lengths[0], focal_lengths[1]); + + // Calculate the image plane + double z = -0.5* (focal_lengths[0] + focal_lengths[1]); + calculateImagePlane(cx, cy, z); + } void Camera::calculateViewport(const double& cx, const double& cy, const double& fx, const double& fy) { // Calculate the viewport @@ -424,4 +585,29 @@ void Camera::loadMayaCam1(const std::string& mayacam) half_height * up[2]; } + double* Camera::calculateFocalLength(const double& view_angle) { + double focal_lengths[2]; + + // Convert from deg to rad + double angle_rad = view_angle * (M_PI / 180); + + focal_lengths[0] = size_[0] / (2 * std::tan(angle_rad / 2)); + focal_lengths[1] = size_[1] / (2 * std::tan(angle_rad / 2)); + + return focal_lengths; + } + + double* Camera::lookAt(Vec3d eye, Vec3d center, Vec3d up) { + // Implementation based off of: + // https://www.khronos.org/opengl/wiki/GluLookAt_code + Vec3d forward = unit(center - eye); + Vec3d side = unit(cross(forward, up)); + up = cross(side, forward); + double matrix[9]; + matrix[0] = side.x; matrix[1] = side.y; matrix[2] = side.z; + matrix[3] = up.x; matrix[4] = up.y; matrix[5] = up.z; + matrix[6] = -forward.x; matrix[7] = -forward.y; matrix[8] = -forward.z; + return matrix; + } + } // namespace XROMM diff --git a/libautoscoper/src/Camera.hpp b/libautoscoper/src/Camera.hpp index 2630fd63..f671333c 100644 --- a/libautoscoper/src/Camera.hpp +++ b/libautoscoper/src/Camera.hpp @@ -44,6 +44,9 @@ #include +#include +#include + #include "CoordFrame.hpp" namespace xromm @@ -89,9 +92,14 @@ class Camera void loadMayaCam2(const std::string& mayacam); + void loadVTKCamera(const std::string& filename); + // helper functions void calculateViewport(const double &cx, const double &cy, const double &fx, const double &fy); void calculateImagePlane(const double &cx, const double &cy, const double &z); + double* calculateRotationMatrix(const double* camera_pos, const double* focal_point); + double* calculateFocalLength(const double& view_angle); + double* lookAt(Vec3d eye, Vec3d center, Vec3d up); }; diff --git a/libautoscoper/src/gpu/opencl/OpenCL.cpp b/libautoscoper/src/gpu/opencl/OpenCL.cpp index 4023c6a2..f3a7d2db 100644 --- a/libautoscoper/src/gpu/opencl/OpenCL.cpp +++ b/libautoscoper/src/gpu/opencl/OpenCL.cpp @@ -993,7 +993,8 @@ void Buffer::copy(const Buffer* dst, size_t size) const { if (size == 0) size = size_; if (size > dst->size_) - ERROR("Destination buffer does not have enough room!"); + ERROR("Destination buffer does not have enough room! (" + << size << " > " << dst->size_ << ")"); err_ = clEnqueueCopyBuffer( queue_, buffer_, dst->buffer_, 0, 0, size, 0, NULL, NULL); CHECK_CL From 7fb27f8f8c9f8d1e21e93a4f761b9842200a2eae Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 30 Aug 2023 04:05:40 -0400 Subject: [PATCH 04/12] COMP: Fix Camera.cpp build error including "algorithm" required for std::remove MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix build error like the following reported when using GCC 9.4.0 on Ubuntu 20.04: /path/to/Autoscoper/libautoscoper/src/Camera.cpp:397:44: error: cannot convert ‘std::__cxx11::basic_string::iterator’ {aka ‘__gnu_cxx::__normal_iterator >’} to ‘const char*’ 397 | value.erase(std::remove(value.begin(), value.end(), ']'), value.end()); | ~~~~~~~~~~~^~ | | | std::__cxx11::basic_string::iterator {aka __gnu_cxx::__normal_iterator >} --- libautoscoper/src/Camera.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index cffdccb1..11e9c921 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -44,6 +44,7 @@ #define M_PI 3.14159265358979323846 #endif +#include #include #include #include From faa68a8a3a1918b7b3e4f59889e54f84cbb9df33 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 30 Aug 2023 04:38:53 -0400 Subject: [PATCH 05/12] COMP: Fix -Wreturn-local-addr in Camera class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the following warning: /path/to/Autoscoper/libautoscoper/src/Camera.cpp: In member function ‘double* xromm::Camera::calculateFocalLength(const double&)’: /path/to/Autoscoper/libautoscoper/src/Camera.cpp:602:12: warning: address of local variable ‘focal_lengths’ returned [-Wreturn-local-addr] 602 | return focal_lengths; | ^~~~~~~~~~~~~ /path/to/Autoscoper/libautoscoper/src/Camera.cpp: In member function ‘double* xromm::Camera::lookAt(Vec3d, Vec3d, Vec3d)’: /path/to/Autoscoper/libautoscoper/src/Camera.cpp:615:12: warning: address of local variable ‘matrix’ returned [-Wreturn-local-addr] 615 | return matrix; | ^~~~~~ --- libautoscoper/src/Camera.cpp | 15 ++++++--------- libautoscoper/src/Camera.hpp | 4 ++-- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index 11e9c921..9577576c 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -500,11 +500,13 @@ void Camera::loadMayaCam1(const std::string& mayacam) Vec3d cam_pos(camera_position); Vec3d focal(focal_point); Vec3d up(view_up); - double* rot = lookAt(cam_pos, focal, up); + double rot[9] = { 0.0 }; + lookAt(cam_pos, focal, up, rot); coord_frame_ = CoordFrame(rot, cam_pos); // Calculate the focal length - double* focal_lengths = calculateFocalLength(view_angle); + double focal_lengths[2] = { 0.0 }; + calculateFocalLength(view_angle, focal_lengths); // Calculate the principal point double cx = image_width / 2.0; @@ -586,19 +588,15 @@ void Camera::loadMayaCam1(const std::string& mayacam) half_height * up[2]; } - double* Camera::calculateFocalLength(const double& view_angle) { - double focal_lengths[2]; - + void Camera::calculateFocalLength(const double& view_angle, double focal_lengths[2]) { // Convert from deg to rad double angle_rad = view_angle * (M_PI / 180); focal_lengths[0] = size_[0] / (2 * std::tan(angle_rad / 2)); focal_lengths[1] = size_[1] / (2 * std::tan(angle_rad / 2)); - - return focal_lengths; } - double* Camera::lookAt(Vec3d eye, Vec3d center, Vec3d up) { + void Camera::lookAt(Vec3d& eye, Vec3d& center, Vec3d& up, double matrix[9]) { // Implementation based off of: // https://www.khronos.org/opengl/wiki/GluLookAt_code Vec3d forward = unit(center - eye); @@ -608,7 +606,6 @@ void Camera::loadMayaCam1(const std::string& mayacam) matrix[0] = side.x; matrix[1] = side.y; matrix[2] = side.z; matrix[3] = up.x; matrix[4] = up.y; matrix[5] = up.z; matrix[6] = -forward.x; matrix[7] = -forward.y; matrix[8] = -forward.z; - return matrix; } } // namespace XROMM diff --git a/libautoscoper/src/Camera.hpp b/libautoscoper/src/Camera.hpp index f671333c..c84e9345 100644 --- a/libautoscoper/src/Camera.hpp +++ b/libautoscoper/src/Camera.hpp @@ -98,8 +98,8 @@ class Camera void calculateViewport(const double &cx, const double &cy, const double &fx, const double &fy); void calculateImagePlane(const double &cx, const double &cy, const double &z); double* calculateRotationMatrix(const double* camera_pos, const double* focal_point); - double* calculateFocalLength(const double& view_angle); - double* lookAt(Vec3d eye, Vec3d center, Vec3d up); + void calculateFocalLength(const double& view_angle, double focal_lengths[2]); + void lookAt(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]); }; From 16f08dfa5fdfa34a564093e3d6f4068ad7d38105 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 30 Aug 2023 04:42:13 -0400 Subject: [PATCH 06/12] COMP: Fix constness of "lookAt" function Also improve readability by splitting setting of each matrix element on its own line. --- libautoscoper/src/Camera.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index 9577576c..e4485511 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -596,16 +596,22 @@ void Camera::loadMayaCam1(const std::string& mayacam) focal_lengths[1] = size_[1] / (2 * std::tan(angle_rad / 2)); } - void Camera::lookAt(Vec3d& eye, Vec3d& center, Vec3d& up, double matrix[9]) { + void Camera::lookAt(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]) { // Implementation based off of: // https://www.khronos.org/opengl/wiki/GluLookAt_code Vec3d forward = unit(center - eye); + // This vector points to the right-hand side of the camera's orientation. Vec3d side = unit(cross(forward, up)); - up = cross(side, forward); - double matrix[9]; - matrix[0] = side.x; matrix[1] = side.y; matrix[2] = side.z; - matrix[3] = up.x; matrix[4] = up.y; matrix[5] = up.z; - matrix[6] = -forward.x; matrix[7] = -forward.y; matrix[8] = -forward.z; + Vec3d perpendicularUp = cross(side, forward); + matrix[0] = side.x; + matrix[1] = side.y; + matrix[2] = side.z; + matrix[3] = perpendicularUp.x; + matrix[4] = perpendicularUp.y; + matrix[5] = perpendicularUp.z; + matrix[6] = -forward.x; + matrix[7] = -forward.y; + matrix[8] = -forward.z; } } // namespace XROMM From d670c362093fe294d3133151e3d11fd46777a720 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 30 Aug 2023 04:43:24 -0400 Subject: [PATCH 07/12] STYLE: Improve name consistency renaming "lookAt" to "calculateLookAtMatrix" --- libautoscoper/src/Camera.cpp | 4 ++-- libautoscoper/src/Camera.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index e4485511..b78991af 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -501,7 +501,7 @@ void Camera::loadMayaCam1(const std::string& mayacam) Vec3d focal(focal_point); Vec3d up(view_up); double rot[9] = { 0.0 }; - lookAt(cam_pos, focal, up, rot); + calculateLookAtMatrix(cam_pos, focal, up, rot); coord_frame_ = CoordFrame(rot, cam_pos); // Calculate the focal length @@ -596,7 +596,7 @@ void Camera::loadMayaCam1(const std::string& mayacam) focal_lengths[1] = size_[1] / (2 * std::tan(angle_rad / 2)); } - void Camera::lookAt(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]) { + void Camera::calculateLookAtMatrix(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]) { // Implementation based off of: // https://www.khronos.org/opengl/wiki/GluLookAt_code Vec3d forward = unit(center - eye); diff --git a/libautoscoper/src/Camera.hpp b/libautoscoper/src/Camera.hpp index c84e9345..19d0ee77 100644 --- a/libautoscoper/src/Camera.hpp +++ b/libautoscoper/src/Camera.hpp @@ -99,7 +99,7 @@ class Camera void calculateImagePlane(const double &cx, const double &cy, const double &z); double* calculateRotationMatrix(const double* camera_pos, const double* focal_point); void calculateFocalLength(const double& view_angle, double focal_lengths[2]); - void lookAt(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]); + void calculateLookAtMatrix(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]); }; From df41ba2930fbef0d791bf21660edd045bb91fe82 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Wed, 30 Aug 2023 04:46:09 -0400 Subject: [PATCH 08/12] COMP: Remove unused and undefined Camera::calculateRotationMatrix function --- libautoscoper/src/Camera.hpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libautoscoper/src/Camera.hpp b/libautoscoper/src/Camera.hpp index 19d0ee77..e662f519 100644 --- a/libautoscoper/src/Camera.hpp +++ b/libautoscoper/src/Camera.hpp @@ -97,7 +97,6 @@ class Camera // helper functions void calculateViewport(const double &cx, const double &cy, const double &fx, const double &fy); void calculateImagePlane(const double &cx, const double &cy, const double &z); - double* calculateRotationMatrix(const double* camera_pos, const double* focal_point); void calculateFocalLength(const double& view_angle, double focal_lengths[2]); void calculateLookAtMatrix(const Vec3d& eye, const Vec3d& center, const Vec3d& up, double matrix[9]); From fcfad15c7cfd4e3cd5c9843ee252a6d2db7ed3ee Mon Sep 17 00:00:00 2001 From: Anthony Lombardi Date: Fri, 15 Sep 2023 11:00:32 -0400 Subject: [PATCH 09/12] ENH: Add Schema for vtkCamera JSON file --- .../src/schema/vtkCamera-schema-v1.0.json | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 libautoscoper/src/schema/vtkCamera-schema-v1.0.json diff --git a/libautoscoper/src/schema/vtkCamera-schema-v1.0.json b/libautoscoper/src/schema/vtkCamera-schema-v1.0.json new file mode 100644 index 00000000..d0300c8f --- /dev/null +++ b/libautoscoper/src/schema/vtkCamera-schema-v1.0.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://autoscoperm.slicer.org/vtk-schema-1.0.json", + "title": "VTK Camera", + "description": "Parameters for recreating a VTK Camera Object", + "type": "object", + "properties": { + "version": { + "description": "The version of the file", + "type": "number" + }, + "focal-point": { + "description": "The point the camera is looking at", + "type": "array", + "items" : { + "type": "number" + }, + "minItems": 3, + "maxItems": 3 + }, + "camera-position": { + "description": "The point the camera is at", + "type": "array", + "items" : { + "type": "number" + }, + "minItems": 3, + "maxItems": 3 + }, + "view-up": { + "description": "The up vector of the camera", + "type": "array", + "items" : { + "type": "number" + }, + "minItems": 3, + "maxItems": 3 + }, + "view-angle" : { + "description": "The viewing angle", + "type": "number" + }, + "image-width": { + "description": "The width of the DRR image", + "type": "integer" + }, + "image-height": { + "description": "The height of the DRR image", + "type": "integer" + } + }, + "required": [ "version","focal-point","camera-position","view-up","view-angle","image-width","image-height" ] +} \ No newline at end of file From eb8f5613cf0431e0e61e17b41a2c066fb36f2263 Mon Sep 17 00:00:00 2001 From: Anthony Lombardi Date: Mon, 18 Sep 2023 12:51:13 -0400 Subject: [PATCH 10/12] ENH: Refactor VTK Cam loader for JSON files --- SuperBuild.cmake | 1 + Superbuild/External_JsonCpp.cmake | 66 ++++++++++++++++ libautoscoper/CMakeLists.txt | 3 + libautoscoper/src/Camera.cpp | 125 +++++------------------------- 4 files changed, 90 insertions(+), 105 deletions(-) create mode 100644 Superbuild/External_JsonCpp.cmake diff --git a/SuperBuild.cmake b/SuperBuild.cmake index d883dec3..7c66bf49 100644 --- a/SuperBuild.cmake +++ b/SuperBuild.cmake @@ -2,6 +2,7 @@ set(Autoscoper_DEPENDENCIES GLEW TIFF + JsonCpp ) if(Autoscoper_RENDERING_BACKEND STREQUAL "OpenCL") if(Autoscoper_OPENCL_USE_ICD_LOADER) diff --git a/Superbuild/External_JsonCpp.cmake b/Superbuild/External_JsonCpp.cmake new file mode 100644 index 00000000..cbe0f229 --- /dev/null +++ b/Superbuild/External_JsonCpp.cmake @@ -0,0 +1,66 @@ + +set(proj JsonCpp) + +set(${proj}_DEPENDENCIES "") + +# Include dependent projects if any +ExternalProject_Include_Dependencies(${proj} PROJECT_VAR proj DEPENDS_VAR ${proj}_DEPENDENCIES) + +if(Autoscoper_USE_SYSTEM_${proj}) + message(FATAL_ERROR "Enabling Autoscoper_USE_SYSTEM_${proj} is not supported !") +endif() + +# Sanity checks +if(DEFINED JsonCpp_INCLUDE_DIR AND NOT EXISTS ${JsonCpp_INCLUDE_DIR}) + message(FATAL_ERROR "JsonCpp_INCLUDE_DIR variable is defined but corresponds to nonexistent directory") +endif() +if(DEFINED JsonCpp_LIBRARY AND NOT EXISTS ${JsonCpp_LIBRARY}) + message(FATAL_ERROR "JsonCpp_LIBRARY variable is defined but corresponds to nonexistent file") +endif() + +if((NOT DEFINED JsonCpp_INCLUDE_DIR + OR NOT DEFINED JsonCpp_LIBRARY) AND NOT Autoscoper_USE_SYSTEM_${proj}) + + set(EP_SOURCE_DIR ${CMAKE_BINARY_DIR}/${proj}) + set(EP_BINARY_DIR ${CMAKE_BINARY_DIR}/${proj}-build) + set(EP_INSTALL_DIR ${CMAKE_BINARY_DIR}/${proj}-install) + + set(EXTERNAL_PROJECT_OPTIONAL_CMAKE_CACHE_ARGS) + + ExternalProject_Add(${proj} + ${${proj}_EP_ARGS} + GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git + GIT_TAG 5defb4ed1a4293b8e2bf641e16b156fb9de498cc # 1.9.5 + SOURCE_DIR ${EP_SOURCE_DIR} + BINARY_DIR ${EP_BINARY_DIR} + INSTALL_DIR ${EP_INSTALL_DIR} + CMAKE_CACHE_ARGS + # Compiler settings + -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} + -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} + -DCMAKE_CXX_EXTENSIONS:BOOL=${CMAKE_CXX_EXTENSIONS} + -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} + -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED} + # Options + -DJSONCPP_WITH_TESTS:BOOL=OFF + -DJSONCPP_WITH_POST_BUILD_UNITTEST:BOOL=OFF + -DJSONCPP_WITH_WARNING_AS_ERROR:BOOL=OFF + -DJSONCPP_WITH_PKGCONFIG_SUPPORT:BOOL=OFF + -DJSONCPP_WITH_CMAKE_PACKAGE:BOOL=ON + -DBUILD_SHARED_LIBS:BOOL=ON + -DBUILD_STATIC_LIBS:BOOL=OFF + # Install directories + -DCMAKE_INSTALL_PREFIX:PATH= + -DCMAKE_INSTALL_BINDIR:STRING=${Autoscoper_BIN_DIR} + -DCMAKE_INSTALL_LIBDIR:STRING=${Autoscoper_LIB_DIR} + ${EXTERNAL_PROJECT_OPTIONAL_CMAKE_CACHE_ARGS} + DEPENDS + ${${proj}_DEPENDENCIES} + ) + set(JSONCPP_DIR ${EP_INSTALL_DIR}) + set(JSONCPP_SOURCE_DIR ${EP_SOURCE_DIR}) + set(JSONCPP_INCLUDE_DIR ${JSONCPP_DIR}/include) + set(JSONCPP_LIBRARY ${JSONCPP_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}jsoncpp${CMAKE_SHARED_LIBRARY_SUFFIX}) + + mark_as_superbuild(JSONCPP_DIR JSONCPP_SOURCE_DIR JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) +endif() diff --git a/libautoscoper/CMakeLists.txt b/libautoscoper/CMakeLists.txt index 331ff84d..c44b55db 100644 --- a/libautoscoper/CMakeLists.txt +++ b/libautoscoper/CMakeLists.txt @@ -105,6 +105,9 @@ find_package(TIFF REQUIRED MODULE) target_link_libraries(libautoscoper PUBLIC TIFF::TIFF) target_compile_definitions(libautoscoper PUBLIC -DUSE_LIBTIFF) +find_package(JsonCpp REQUIRED) +target_link_libraries(libautoscoper PUBLIC JsonCpp::JsonCpp) + target_include_directories(libautoscoper PUBLIC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/src diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index b78991af..905f98d7 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -44,11 +44,14 @@ #define M_PI 3.14159265358979323846 #endif +#define VTK_CAMERA_SCHEMA_URL "https://autoscoperm.slicer.org/vtkCamera-schema-1.0.json" + #include #include #include #include #include +#include #include "Camera.hpp" @@ -131,7 +134,7 @@ Camera::Camera(const std::string& mayacam) : mayacam_(mayacam) } std::string ext = mayacam_.substr(ext_pos + 1); // if its a yaml file load it as a vtk camera - if (ext.compare("yaml") == 0) { + if (ext.compare("json") == 0) { loadVTKCamera(mayacam_); } else { @@ -384,133 +387,45 @@ void Camera::loadMayaCam1(const std::string& mayacam) } void Camera::loadVTKCamera(const std::string& filename) { - // Open and parse the file - double version = -1.0, view_angle = -1.0, image_width = -1.0 , image_height = -1.0; - double focal_point[3] = {-1.0, -1.0, -1.0}, camera_position[3] = { -1.0, -1.0, -1.0 }, view_up[3] = { -1.0, -1.0, -1.0 }; + std::cout << "Loading VTKCam file: " << filename << std::endl; std::fstream file(filename.c_str(), std::ios::in); if (!file.is_open()) { throw std::runtime_error("Error opening VTKCam file: " + filename); } - std::string line; - // The file is a series of key value pairs separated by a colon, # denotes a comment - int line_num = 1; - while (safeGetline(file, line)) { - // Ignore comments and empty lines - if (line.empty() || line[0] == '#') { - line_num++; - continue; - } - // Split the line into key and value - std::string key, value; - std::istringstream line_stream(line); - if (!getline(line_stream, key, ':')) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing key.")); - } - if (!getline(line_stream, value, ':')) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing value.")); - } - // Parse the key value pair - if (key == "version") { - std::istringstream value_stream(value); - if (!(value_stream >> version)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing version number.")); - } - } - else if (key == "focal-point") { - if (!parseArray(value, focal_point, 3)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing focal-point.")); - } - } - else if (key == "camera-position") { - if (!parseArray(value, camera_position, 3)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing camera-position.")); - } - } - else if (key == "view-up") { - if (!parseArray(value, view_up, 3)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing view-up.")); - } - } - else if (key == "view-angle") { - std::istringstream value_stream(value); - if (!(value_stream >> view_angle)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing view-angle.")); - } - } - else if (key == "image-width") { - std::istringstream value_stream(value); - if (!(value_stream >> image_width)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing image-width.")); - } - } - else if (key == "image-height") { - std::istringstream value_stream(value); - if (!(value_stream >> image_height)) { - file.close(); - throw std::runtime_error(vtkCamReadingError("1", line_num, filename, "Error parsing image-height.")); - } - } - line_num++; - } - // Close the file + Json::Value calibrationFile; + file >> calibrationFile; file.close(); - // Check that all the values were read - if (version == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing version number.")); - } - if (view_angle == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing view-angle.")); - } - if (image_width == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing image-width.")); - } - if (image_height == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing image-height.")); - } - if (focal_point[0] == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing focal-point.")); - } - if (camera_position[0] == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing camera-position.")); - } - if (view_up[0] == -1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Missing view-up.")); + // Check the schema + if (calibrationFile["@schema"].asString() != VTK_CAMAERA_SCHEMA_URL) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Unsupported schema. " + calibrationFile["@schema"].asString() + " is not " + VTK_CAMERA_SCHEMA_URL)); } // Check the version number - if (version != 1.0) { - throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Unsupported version number.")); + if (calibrationFile["version"].asDouble() != 1.0) { + throw std::runtime_error(vtkCamReadingError("1", -1, filename, "Unsupported version number. "+ calibrationFile["version"].asString() + " is not 1.0")); } // Set the size - size_[0] = image_width; - size_[1] = image_height; + size_[0] = calibrationFile["image-width"].asDouble(); + size_[1] = calibrationFile["image-height"].asDouble(); - Vec3d cam_pos(camera_position); - Vec3d focal(focal_point); - Vec3d up(view_up); + Vec3d cam_pos(calibrationFile["camera-position"][0].asDouble(), calibrationFile["camera-position"][1].asDouble(), calibrationFile["camera-position"][2].asDouble()); + Vec3d focal(calibrationFile["focal-point"][0].asDouble(), calibrationFile["focal-point"][1].asDouble(), calibrationFile["focal-point"][2].asDouble()); + Vec3d up(calibrationFile["view-up"][0].asDouble(), calibrationFile["view-up"][1].asDouble(), calibrationFile["view-up"][2].asDouble()); double rot[9] = { 0.0 }; calculateLookAtMatrix(cam_pos, focal, up, rot); coord_frame_ = CoordFrame(rot, cam_pos); // Calculate the focal length double focal_lengths[2] = { 0.0 }; - calculateFocalLength(view_angle, focal_lengths); + calculateFocalLength(calibrationFile["view-angle"].asDouble(), focal_lengths); // Calculate the principal point - double cx = image_width / 2.0; - double cy = image_height / 2.0; + double cx = size_[0] / 2.0; + double cy = size_[1] / 2.0; // Calculate the viewport calculateViewport(cx, cy , focal_lengths[0], focal_lengths[1]); From 155886c10fcebc3acca63840909068702406f78b Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Mon, 25 Sep 2023 16:21:18 -0400 Subject: [PATCH 11/12] COMP: Fix standalone build updating JsonCpp integration For sake of consistency with how Slicer integrates JsonCpp, we ignore the "jsoncppConfig.cmake" file provided by jsoncpp CMake project and instead set JsonCpp_INCLUDE_DIR and JsonCpp_LIBRARY expected by the "FindJsonCpp" CMake module. To streamline distribution of the standalone Autoscoper executable, update JsonCpp project to statically build the JsonCpp library. --- CMake/FindJsonCpp.cmake | 40 +++++++++++++++++++++++++++++++ Superbuild/External_JsonCpp.cmake | 25 ++++++++++++------- libautoscoper/CMakeLists.txt | 3 ++- 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 CMake/FindJsonCpp.cmake diff --git a/CMake/FindJsonCpp.cmake b/CMake/FindJsonCpp.cmake new file mode 100644 index 00000000..7115c8d7 --- /dev/null +++ b/CMake/FindJsonCpp.cmake @@ -0,0 +1,40 @@ +# Find the JsonCpp include files and library. +# +# JsonCpp is a C++ library that can read/write JSON (JavaScript Object Notation) +# documents. See http://jsoncpp.sourceforge.net/ for more details. +# +# This module defines: +# JsonCpp_INCLUDE_DIRS - where to find json/json.h +# JsonCpp_LIBRARIES - the libraries to link against to use JsonCpp +# JsonCpp_FOUND - if false the library was not found. + +find_path(JsonCpp_INCLUDE_DIR "json/json.h" + PATH_SUFFIXES "jsoncpp" + DOC "Specify the JsonCpp include directory here") + +find_library(JsonCpp_LIBRARY + NAMES jsoncpp + PATHS + DOC "Specify the JsonCpp library here") +set(JsonCpp_INCLUDE_DIRS ${JsonCpp_INCLUDE_DIR}) +set(JsonCpp_LIBRARIES "${JsonCpp_LIBRARY}") + +set(_JsonCpp_version_args) +if (EXISTS "${JsonCpp_INCLUDE_DIR}/json/version.h") + file(STRINGS "${JsonCpp_INCLUDE_DIR}/json/version.h" _JsonCpp_version_contents REGEX "JSONCPP_VERSION_[A-Z]+") + foreach (_JsonCpp_version_part MAJOR MINOR PATCH) + string(REGEX REPLACE ".*# *define +JSONCPP_VERSION_${_JsonCpp_version_part} +([0-9]+).*" + "\\1" JsonCpp_VERSION_${_JsonCpp_version_part} "${_JsonCpp_version_contents}") + endforeach () + + set(JsonCpp_VERSION_STRING "${JsonCpp_VERSION_MAJOR}.${JsonCpp_VERSION_MINOR}.${JsonCpp_VERSION_PATCH}") + + set(_JsonCpp_version_args VERSION_VAR JsonCpp_VERSION_STRING) +endif () + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(JsonCpp + REQUIRED_VARS JsonCpp_LIBRARIES JsonCpp_INCLUDE_DIRS + ${_JsonCpp_version_args}) + +mark_as_advanced(JsonCpp_INCLUDE_DIR JsonCpp_LIBRARY) diff --git a/Superbuild/External_JsonCpp.cmake b/Superbuild/External_JsonCpp.cmake index cbe0f229..4531a888 100644 --- a/Superbuild/External_JsonCpp.cmake +++ b/Superbuild/External_JsonCpp.cmake @@ -47,8 +47,8 @@ if((NOT DEFINED JsonCpp_INCLUDE_DIR -DJSONCPP_WITH_WARNING_AS_ERROR:BOOL=OFF -DJSONCPP_WITH_PKGCONFIG_SUPPORT:BOOL=OFF -DJSONCPP_WITH_CMAKE_PACKAGE:BOOL=ON - -DBUILD_SHARED_LIBS:BOOL=ON - -DBUILD_STATIC_LIBS:BOOL=OFF + -DBUILD_SHARED_LIBS:BOOL=OFF + -DBUILD_STATIC_LIBS:BOOL=ON # Install directories -DCMAKE_INSTALL_PREFIX:PATH= -DCMAKE_INSTALL_BINDIR:STRING=${Autoscoper_BIN_DIR} @@ -57,10 +57,19 @@ if((NOT DEFINED JsonCpp_INCLUDE_DIR DEPENDS ${${proj}_DEPENDENCIES} ) - set(JSONCPP_DIR ${EP_INSTALL_DIR}) - set(JSONCPP_SOURCE_DIR ${EP_SOURCE_DIR}) - set(JSONCPP_INCLUDE_DIR ${JSONCPP_DIR}/include) - set(JSONCPP_LIBRARY ${JSONCPP_DIR}/lib/${CMAKE_SHARED_LIBRARY_PREFIX}jsoncpp${CMAKE_SHARED_LIBRARY_SUFFIX}) - - mark_as_superbuild(JSONCPP_DIR JSONCPP_SOURCE_DIR JSONCPP_INCLUDE_DIR JSONCPP_LIBRARY) + set(${proj}_INCLUDE_DIR ${EP_INSTALL_DIR}/include) + set(${proj}_LIBRARY ${EP_INSTALL_DIR}/${Autoscoper_LIB_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}jsoncpp${CMAKE_STATIC_LIBRARY_SUFFIX}) +else() + ExternalProject_Add_Empty(${proj} DEPENDS ${${proj}_DEPENDENCIES}) endif() + +# For sake of consistency with how Slicer integrates JsonCpp, we ignore the "jsoncppConfig.cmake" file provided by jsoncpp and +# instead set JsonCpp_INCLUDE_DIR and JsonCpp_LIBRARY expected by the "FindJsonCpp" CMake module. +mark_as_superbuild( + VARS + ${proj}_INCLUDE_DIR:PATH + ${proj}_LIBRARY:FILEPATH +) + +ExternalProject_Message(${proj} "${proj}_INCLUDE_DIR:${${proj}_INCLUDE_DIR}") +ExternalProject_Message(${proj} "${proj}_LIBRARY:${${proj}_LIBRARY}") diff --git a/libautoscoper/CMakeLists.txt b/libautoscoper/CMakeLists.txt index c44b55db..5cf231c7 100644 --- a/libautoscoper/CMakeLists.txt +++ b/libautoscoper/CMakeLists.txt @@ -106,7 +106,8 @@ target_link_libraries(libautoscoper PUBLIC TIFF::TIFF) target_compile_definitions(libautoscoper PUBLIC -DUSE_LIBTIFF) find_package(JsonCpp REQUIRED) -target_link_libraries(libautoscoper PUBLIC JsonCpp::JsonCpp) +target_link_libraries(libautoscoper PRIVATE ${JsonCpp_LIBRARY}) +target_include_directories(libautoscoper PRIVATE ${JsonCpp_INCLUDE_DIR}) target_include_directories(libautoscoper PUBLIC ${CMAKE_CURRENT_BINARY_DIR} From 0c112077c037771fbd2d11cc0dcc64f3fc42f968 Mon Sep 17 00:00:00 2001 From: Jean-Christophe Fillion-Robin Date: Mon, 25 Sep 2023 17:12:32 -0400 Subject: [PATCH 12/12] STYLE: Consistently indent Camera.cpp --- libautoscoper/src/Camera.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libautoscoper/src/Camera.cpp b/libautoscoper/src/Camera.cpp index 905f98d7..9184c757 100644 --- a/libautoscoper/src/Camera.cpp +++ b/libautoscoper/src/Camera.cpp @@ -125,8 +125,8 @@ namespace xromm return true; } -Camera::Camera(const std::string& mayacam) : mayacam_(mayacam) -{ + Camera::Camera(const std::string& mayacam) : mayacam_(mayacam) + { // Check the file extension std::string::size_type ext_pos = mayacam_.find_last_of('.'); if (ext_pos == std::string::npos) { @@ -155,9 +155,9 @@ Camera::Camera(const std::string& mayacam) : mayacam_(mayacam) loadMayaCam1(mayacam); } } -} + } -void Camera::loadMayaCam1(const std::string& mayacam) + void Camera::loadMayaCam1(const std::string& mayacam) { std::cout << "Reading MayaCam 1.0 file: " << mayacam << std::endl;