57 switch (orientation) {
59 return Vector3f::UnitZ();
61 return Vector3f::UnitY();
63 return Vector3f::UnitX();
65 return Vector3f::UnitZ();
70 switch (orientation) {
72 return Vector3f::UnitX();
74 return Vector3f::UnitX();
76 return Vector3f::UnitY();
78 return Vector3f::UnitX();
83 switch (orientation) {
85 return Vector3f::UnitY();
87 return Vector3f::UnitZ();
89 return Vector3f::UnitZ();
91 return Vector3f::UnitY();
94int bestVoxelAxisForDirection(
const Matrix4f& vox2ras,
95 const Vector3f& target,
96 const std::array<bool, 3>& used)
99 float bestScore = -1.0f;
101 for (
int axis = 0; axis < 3; ++axis) {
105 Vector3f axisDirection = vox2ras.block<3, 1>(0, axis);
106 const float norm = axisDirection.norm();
108 axisDirection /= norm;
110 const float score = std::abs(axisDirection.dot(target));
111 if (score > bestScore) {
120PlaneSpec planeSpecForOrientation(
const Matrix4f& vox2ras,
123 std::array<bool, 3> used = {
false,
false,
false};
124 const int fixedAxis = bestVoxelAxisForDirection(vox2ras,
125 anatomicalNormal(orientation),
127 used[fixedAxis] =
true;
129 const int columnAxis = bestVoxelAxisForDirection(vox2ras,
130 anatomicalColumnDirection(orientation),
132 used[columnAxis] =
true;
134 const int rowAxis = bestVoxelAxisForDirection(vox2ras,
135 anatomicalRowDirection(orientation),
138 return {columnAxis, rowAxis, fixedAxis};
141int axisDimension(
const QVector<int>& dims,
int axis)
146int flatIndex(
int x,
int y,
int z,
int dimX,
int dimY)
148 return x + dimX * (y + dimY * z);
158 const QVector<float>& volData,
159 const QVector<int>& dims,
160 const Matrix4f& vox2ras,
164 const int dimX = dims[0];
165 const int dimY = dims[1];
166 const PlaneSpec spec = planeSpecForOrientation(vox2ras, orientation);
167 const int width = axisDimension(dims, spec.columnAxis);
168 const int height = axisDimension(dims, spec.rowAxis);
169 const int fixedDim = axisDimension(dims, spec.fixedAxis);
171 sliceIndex = std::clamp(sliceIndex, 0, fixedDim - 1);
173 MriSliceImage result;
175 result.
width = width;
178 result.
pixels.resize(width, height);
180 for (
int row = 0; row < height; ++row) {
181 for (
int col = 0; col < width; ++col) {
182 std::array<int, 3> voxel = {0, 0, 0};
183 voxel[spec.columnAxis] = col;
184 voxel[spec.rowAxis] = row;
185 voxel[spec.fixedAxis] = sliceIndex;
186 result.
pixels(col, row) = volData[flatIndex(voxel[0], voxel[1], voxel[2], dimX, dimY)];
191 result.
sliceToRas.col(0) = vox2ras.col(spec.columnAxis);
192 result.
sliceToRas.col(1) = vox2ras.col(spec.rowAxis);
193 result.
sliceToRas.col(2) = vox2ras.col(spec.fixedAxis);
194 Vector4f origin = Vector4f::Zero();
195 origin(spec.fixedAxis) =
static_cast<float>(sliceIndex);
200 float minVal = result.
pixels.minCoeff();
201 float maxVal = result.
pixels.maxCoeff();
202 if (maxVal > minVal) {
203 result.
pixels = (result.
pixels.array() - minVal) / (maxVal - minVal);
216 return planeSpecForOrientation(vox2ras, orientation).fixedAxis;
222 const Matrix4f& vox2ras,
232 const Vector3i& voxel)
240 const QVector<float>& volData,
241 const QVector<int>& dims,
242 const Matrix4f& vox2ras,
243 const Vector3f& rasPoint)
245 Vector3i voxel =
rasToVoxel(vox2ras, rasPoint);
247 QVector<MriSliceImage> slices;
262 const Vector3f& rasPoint)
264 Matrix4f ras2vox = vox2ras.inverse();
266 rasH << rasPoint, 1.0f;
267 Vector4f voxH = ras2vox * rasH;
270 static_cast<int>(std::round(voxH.x())),
271 static_cast<int>(std::round(voxH.y())),
272 static_cast<int>(std::round(voxH.z())));
278 const Vector3i& voxel)
281 voxH << static_cast<float>(voxel.x()),
282 static_cast<float>(voxel.y()),
283 static_cast<float>(voxel.z()),
285 Vector4f rasH = vox2ras * voxH;
287 return rasH.head<3>();
322 const Vector3i& voxel)
330 const Vector3f& rasPoint)
339 const Vector3f& rasPoint)
347 const Vector3i& voxel)
Orthogonal-plane resampler that turns a 3D MRILIB::MriVolData into the 2D textures consumed by the sl...
Format-agnostic in-memory representation of a 3D MRI volume plus its slice decomposition.
Volume I/O, voxel geometry and slice resampling for structural MRI data inside mne-cpp.
Single 2D MRI cross-section produced by MriSlicer (pixel buffer + RAS metadata).
Eigen::Matrix4f sliceToRas
SliceOrientation orientation
static Eigen::Vector3f voxelToRas(const Eigen::Matrix4f &vox2ras, const Eigen::Vector3i &voxel)
static MriSliceImage extractSlice(const QVector< float > &volData, const QVector< int > &dims, const Eigen::Matrix4f &vox2ras, SliceOrientation orientation, int sliceIndex)
static Eigen::Vector3i rasToVoxel(const Eigen::Matrix4f &vox2ras, const Eigen::Vector3f &rasPoint)
static int voxelAxisForOrientation(const Eigen::Matrix4f &vox2ras, SliceOrientation orientation)
static int sliceIndexForOrientation(const Eigen::Matrix4f &vox2ras, SliceOrientation orientation, const Eigen::Vector3i &voxel)
static QVector< MriSliceImage > extractOrthogonal(const QVector< float > &volData, const QVector< int > &dims, const Eigen::Matrix4f &vox2ras, const Eigen::Vector3f &rasPoint)
static int dimensionForOrientation(const QVector< int > &dims, const Eigen::Matrix4f &vox2ras, SliceOrientation orientation)
Format-agnostic 3D MRI volume: header geometry, voxel buffer (as a vector of MriSlice),...
Eigen::Matrix4f computeVox2RasTkr() const
QVector< int > dims() const
QVector< float > voxelDataAsFloat() const