Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

VTK Camera Loader #147

Merged
merged 12 commits into from
Sep 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions CMake/FindJsonCpp.cmake
Original file line number Diff line number Diff line change
@@ -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)
199 changes: 192 additions & 7 deletions Documentation/file-specifications/camera-calibration.md
Original file line number Diff line number Diff line change
@@ -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.

NicerNewerCar marked this conversation as resolved.
Show resolved Hide resolved
:::{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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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}
NicerNewerCar marked this conversation as resolved.
Show resolved Hide resolved
```

```{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})}
```
1 change: 1 addition & 0 deletions SuperBuild.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
set(Autoscoper_DEPENDENCIES
GLEW
TIFF
JsonCpp
)
if(Autoscoper_RENDERING_BACKEND STREQUAL "OpenCL")
if(Autoscoper_OPENCL_USE_ICD_LOADER)
Expand Down
75 changes: 75 additions & 0 deletions Superbuild/External_JsonCpp.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@

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=OFF
-DBUILD_STATIC_LIBS:BOOL=ON
# Install directories
-DCMAKE_INSTALL_PREFIX:PATH=<INSTALL_DIR>
-DCMAKE_INSTALL_BINDIR:STRING=${Autoscoper_BIN_DIR}
-DCMAKE_INSTALL_LIBDIR:STRING=${Autoscoper_LIB_DIR}
${EXTERNAL_PROJECT_OPTIONAL_CMAKE_CACHE_ARGS}
DEPENDS
${${proj}_DEPENDENCIES}
)
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}")
5 changes: 5 additions & 0 deletions libautoscoper/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,14 @@ 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 PRIVATE ${JsonCpp_LIBRARY})
target_include_directories(libautoscoper PRIVATE ${JsonCpp_INCLUDE_DIR})

target_include_directories(libautoscoper PUBLIC
${CMAKE_CURRENT_BINARY_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/src
../math
)

install(TARGETS libautoscoper
Expand Down
Loading
Loading