1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
/*****************************************************************************
* $CAMITK_LICENCE_BEGIN$
*
* CamiTK - Computer Assisted Medical Intervention ToolKit
* (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
*
* Visit http://camitk.imag.fr for more information
*
* This file is part of CamiTK.
*
* CamiTK is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version 3
* only, as published by the Free Software Foundation.
*
* CamiTK is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License version 3 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3 along with CamiTK. If not, see <http://www.gnu.org/licenses/>.
*
* $CAMITK_LICENCE_END$
****************************************************************************/
#ifndef NUMPY_UTILS_H
#define NUMPY_UTILS_H
#include <pybind11/pybind11.h>
#include <pybind11/numpy.h>
namespace py = pybind11;
#include <CamiTKAPI.h>
#include <ImageComponent.h>
#include <MeshComponent.h>
#include <vtkImageData.h>
#include <vtkDataArray.h>
#include <vtkTransform.h>
namespace camitk {
/// Note about memory management on numpy vs VTK
/// In numpy, the indexing is [row, col] for two dimensions, or [slice, row, col] for three dimensions.
/// In VTK, the indexing is [col, row] for two dimensions and [col, row, slice] for three dimensions.
///
/// A numpy array has an ‘array-style’ == 'matrix-style' interface, and is indexed by [row, col] or [slice, row, col].
/// A vtkImageData has an ‘image-style’ interface, and is indexed by (x, y) or (x, y, z).
///
/// In numpy’s terms, VTK expects Fortran-order, while numpy defaults to C-order for its memory layout.
///
/// Beware that numpy also use shapes and strides to define the way it accesses the element memory:
/// - shape is the number of elements in all dimensions
/// - stride is the number of bytes that separate two consecutive elements in each dimension
/// Memory is basically always a 1D array.
///
/// For instance if the memory contains (raw pointer): 0 1 2 3 4 5
/// That can represent two different "images":
//
/// image 1 image 2
/// ^ +-----+-----+ ^ +-----+-----+-----+
/// | | 0 | 1 | | | 0 | 1 | 2 |
/// shape[0] +-----+-----+ shape[0] +-----+-----+-----+
/// | | 2 | 3 | | | 3 | 4 | 5 |
/// | +-----+-----+ v +-----+-----+-----+
/// | | 4 | 5 | <-----shape[1]---->
/// v +-----+-----+
/// <--shape[1]-->
///
/// For image 1: shape[0] is 3, shape[1] is 2
/// For image 2: shape[0] is 2, shape[1] is 3
///
/// Elements are stored using itemsize bytes (char = 1 byte, default int = 8 bytes = 64 bits)
/// numpy also uses stride to iterate in the data structure. Strides are expressed in bytes.
///
/// stride for 2D images is composed by
/// - stride[0] = number of bytes to jump to the next row
/// - stride[1] = number of bytes to jump to the next column
///
/// For image 1 : stride[0] = 2 * itemsize, stride[1] = itemsize
/// For image 2 : stride[0] = 3 * itemsize, stride[1] = itemsize
///
/// But beware when numpy is asked to transpose an array, it does NOT modify the
/// memory storage/layout, it just modifies the stride and shape values
///
/// for instance image3 = np.transpose(image1) = image2, all have the same raw memory representation
///
/// CamiTK conversion is able to take all these cases into account.
///
/// But you have to be extra-careful when mixing numpy with other python image libraries like nibabel,
/// because they may transpose the array in order to use image-style indexing with the arrays.
/// convert a vtkImageData to a numpy array
py::array vtkImageDataToNumpy(vtkSmartPointer<vtkImageData> image);
/// Get the spacing between voxels of a vtkImageData as a python tuple (e.g. (1.5, 1.5, 2.0))
py::array getVtkImageDataSpacing(vtkSmartPointer<vtkImageData> image);
/// Convert a VTKPointSet array to a numpy array.
py::array vtkPointSetToNumpy(vtkSmartPointer<vtkPointSet> pointSet);
/// Convert a numpy array to a vtkImageData
vtkSmartPointer<vtkImageData> numpyToVTKImageData(const pybind11::array& numpyArray);
/// Create a new camitk::ImageComponent from a numpy array and an optional voxel spacing array.
camitk::ImageComponent* newImageComponentFromNumpy(const py::array& numpyArray, const std::string& name, py::object spacingObj = py::none());
/// Convert a numpy array of 3D coordinates to vtkPoints
vtkSmartPointer<vtkPoints> numpyToVtkPoints(py::array_t < double, py::array::c_style | py::array::forcecast > points_array);
/// Convert an array of points (3D coordinates) and an array of cells (3 indices of the points for triangles, 2 indices for lines, 4 for quads, tetrahedrons are not supported) into a vtkPointSet (e.g. a triangle mesh). Only vktPolyData are supported for now.
vtkSmartPointer<vtkPointSet> numpyToVtkPointSet(py::array_t < double, py::array::c_style | py::array::forcecast > points_array, py::array_t < vtkIdType, py::array::c_style | py::array::forcecast > polys_array = py::array());
/// Create a camitk::MeshComponent from an array of points (3D coordinates) and an array of triangles (3 index of the points)
camitk::MeshComponent* newMeshComponentFromNumpy(const std::string& name, py::array_t < double, py::array::c_style | py::array::forcecast > points_array, py::array_t < vtkIdType, py::array::c_style | py::array::forcecast > polys_array = py::array());
/// Create a numpy array from a vtkDataArray (useful for data associated to points/cells in a mesh). Int/Float/Double are supported
py::array vtkDataArrayToNumpy(vtkSmartPointer<vtkDataArray> array);
/// Create a vtkDataArray from a numpy array (usefull to associate data to points/cells of a mesh). Supports Int/Float/Double
vtkSmartPointer<vtkDataArray> numpyToVtkDataArray(py::array array);
/// Create a vtkTransform from a numpy array (the method checks that array is really a 4x4 array)
vtkSmartPointer<vtkTransform> numpyToVtkTransform(py::array array);
} // namespace camitk
#endif // NUMPY_UTILS_H
|