diff --git a/fsl/fslview/displaycontext/display.py b/fsl/fslview/displaycontext/display.py index 15781f8730aff23d98462d5872c063da3a22eef5..9616e3cbe87fa93efbe3ce2d9853c3d0b4672f82 100644 --- a/fsl/fslview/displaycontext/display.py +++ b/fsl/fslview/displaycontext/display.py @@ -4,7 +4,15 @@ # # Author: Paul McCarthy <pauldmccarthy@gmail.com> # -""" +"""This module provides definitions of an important class - the +:class:`Display` class. + +A ``Display`` contains a specification for the way in which an +:class:`~fsl.data.image.Image` instance is to be displayed. + + +..note:: Put a description of the three coordinate systems which + exist in the system. """ import logging @@ -295,12 +303,6 @@ class Display(props.SyncableHasProperties): elif self.transform == 'affine': voxToDisplayMat = self.voxToWorldMat - # for pixdim/identity transformations, we want the world - # location (0, 0, 0) to map to voxel location (0, 0, 0) - if self.transform in ('id', 'pixdim'): - for i in range(3): - voxToDisplayMat[3, i] = pixdim[i] * 0.5 - # Transformation matrices for moving between the voxel # coordinate system and the display coordinate system self.voxToDisplayMat = np.array(voxToDisplayMat, dtype=np.float32) diff --git a/fsl/fslview/gl/gl21/generic_vert.glsl b/fsl/fslview/gl/gl21/generic_vert.glsl index 719683b2c0c5b22b685e21bdf6491f71a7f95742..abef9271bbcd979692143e8ce21706b683c88221 100644 --- a/fsl/fslview/gl/gl21/generic_vert.glsl +++ b/fsl/fslview/gl/gl21/generic_vert.glsl @@ -8,6 +8,9 @@ */ #version 120 + +uniform mat4 displayToVoxMat; + /* * Optional transformation matrix which is applied to all * vertex coordinates (used by the e.g. lightbox canvas to @@ -38,6 +41,12 @@ uniform float zCoord; varying vec3 fragTexCoords; +/* + * Image voxel coordinates corresponding to this vertex. + */ +varying vec3 fragVoxCoords; + + void main(void) { vec4 worldLoc = vec4(0, 0, 0, 1); @@ -45,6 +54,26 @@ void main(void) { worldLoc[yax] = worldCoords.y; worldLoc[zax] = zCoord; + /* + * Transform the texture coordinates into voxel coordinates + */ + fragVoxCoords = (displayToVoxMat * worldLoc).xyz; + + /* + * Centre voxel coordinates - the display space of a voxel + * at a particular location (x, y, z) extends from + * + * (x-0.5, y-0.5, z-0.5) + * + * to + * + * (x+0.5, y+0.5, z+0.5), + * + * so we need to offset the coordinates by 0.5 to + * make the coordinates usable as voxel indices + */ + fragVoxCoords.xyz = fragVoxCoords.xyz + 0.5; + /* * Pass the vertex coordinates as texture * coordinates to the fragment shader diff --git a/fsl/fslview/gl/gl21/gltensor_rgb_frag.glsl b/fsl/fslview/gl/gl21/gltensor_rgb_frag.glsl index 76b28d8243a52bad5e95de4756be44b435606e0f..0cd2cdfe070b3e273b423daade81a18280691818 100644 --- a/fsl/fslview/gl/gl21/gltensor_rgb_frag.glsl +++ b/fsl/fslview/gl/gl21/gltensor_rgb_frag.glsl @@ -7,6 +7,8 @@ #pragma include spline_interp.glsl +#pragma include test_in_bounds.glsl + /* * image data texture */ @@ -29,50 +31,26 @@ uniform vec3 imageShape; */ uniform bool useSpline; -/* - * Transformation from display space to voxel coordinates. - */ -uniform mat4 displayToVoxMat; - /* * Image texture coordinates. */ varying vec3 fragTexCoords; +/* + * Image voxel coordinates + */ +varying vec3 fragVoxCoords; void main(void) { - /* - * Transform the texture coordinates into voxel coordinates - */ - vec4 voxCoords = displayToVoxMat * vec4(fragTexCoords, 1); + vec3 voxCoords = fragVoxCoords; - /* - * Centre voxel coordinates - */ - voxCoords.xyz = voxCoords.xyz + 0.5; + if (!test_in_bounds(voxCoords, imageShape)) { - /* - * Don't render the fragment if it's outside the image space - */ - if (voxCoords.x < -0.01 || voxCoords.x >= imageShape.x + 0.01 || - voxCoords.y < -0.01 || voxCoords.y >= imageShape.y + 0.01 || - voxCoords.z < -0.01 || voxCoords.z >= imageShape.z + 0.01) { - gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); return; } - /* - * Be lenient at voxel boundaries - */ - if (voxCoords.x < 0.0) voxCoords.x = 0.01; - if (voxCoords.y < 0.0) voxCoords.y = 0.01; - if (voxCoords.z < 0.0) voxCoords.z = 0.01; - if (voxCoords.x >= imageShape.x) voxCoords.x = imageShape.x - 0.01; - if (voxCoords.y >= imageShape.y) voxCoords.y = imageShape.y - 0.01; - if (voxCoords.z >= imageShape.z) voxCoords.z = imageShape.z - 0.01; - /* * Normalise voxel coordinates to (0.0, 1.0) */ diff --git a/fsl/fslview/gl/gl21/glvolume_frag.glsl b/fsl/fslview/gl/gl21/glvolume_frag.glsl index e850a297a4c45bce0bbebc6f1bc0e22d22e015d2..15a508dd10a92fd0f2324fca9b0ce13983a81ac4 100644 --- a/fsl/fslview/gl/gl21/glvolume_frag.glsl +++ b/fsl/fslview/gl/gl21/glvolume_frag.glsl @@ -6,6 +6,7 @@ #version 120 #pragma include spline_interp.glsl +#pragma include test_in_bounds.glsl /* * image data texture @@ -27,11 +28,6 @@ uniform sampler1D colourTexture; */ uniform bool useSpline; -/* - * Transformation from display space to voxel coordinates. - */ -uniform mat4 displayToVoxMat; - /* * Transformation matrix to apply to the 1D texture coordinate. */ @@ -43,39 +39,22 @@ uniform mat4 voxValXform; varying vec3 fragTexCoords; -void main(void) { +/* + * Image voxel coordinates + */ +varying vec3 fragVoxCoords; - /* - * Transform the texture coordinates into voxel coordinates - */ - vec4 voxCoords = displayToVoxMat * vec4(fragTexCoords, 1); - /* - * Centre voxel coordinates - */ - voxCoords.xyz = voxCoords.xyz + 0.5; +void main(void) { - /* - * Don't render the fragment if it's outside the image space - */ - if (voxCoords.x < -0.01 || voxCoords.x >= imageShape.x + 0.01 || - voxCoords.y < -0.01 || voxCoords.y >= imageShape.y + 0.01 || - voxCoords.z < -0.01 || voxCoords.z >= imageShape.z + 0.01) { + vec3 voxCoords = fragVoxCoords; + + if (!test_in_bounds(voxCoords, imageShape)) { gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); return; } - /* - * Be lenient at voxel boundaries - */ - if (voxCoords.x < 0.0) voxCoords.x = 0.01; - if (voxCoords.y < 0.0) voxCoords.y = 0.01; - if (voxCoords.z < 0.0) voxCoords.z = 0.01; - if (voxCoords.x >= imageShape.x) voxCoords.x = imageShape.x - 0.01; - if (voxCoords.y >= imageShape.y) voxCoords.y = imageShape.y - 0.01; - if (voxCoords.z >= imageShape.z) voxCoords.z = imageShape.z - 0.01; - /* * Normalise voxel coordinates to (0.0, 1.0) */ diff --git a/fsl/fslview/gl/gl21/test_in_bounds.glsl b/fsl/fslview/gl/gl21/test_in_bounds.glsl new file mode 100644 index 0000000000000000000000000000000000000000..d01b2dd56e853a5e7c7fec4591b78ae66fcb7626 --- /dev/null +++ b/fsl/fslview/gl/gl21/test_in_bounds.glsl @@ -0,0 +1,29 @@ +/** + * Simple bounds test to determine whether the given voxel coordinates + * are within the bounds specified by the given array/image shape. + */ +bool test_in_bounds(inout vec3 coords, vec3 shape) { + + /* + * Don't render the fragment if it's outside the image space + */ + if (coords.x < -0.01 || coords.x >= shape.x + 0.01 || + coords.y < -0.01 || coords.y >= shape.y + 0.01 || + coords.z < -0.01 || coords.z >= shape.z + 0.01) { + + return false; + } + + /* + * Be lenient at voxel boundaries + */ + if (coords.x < 0.0) coords.x = 0.01; + if (coords.y < 0.0) coords.y = 0.01; + if (coords.z < 0.0) coords.z = 0.01; + if (coords.x >= shape.x) coords.x = shape.x - 0.01; + if (coords.y >= shape.y) coords.y = shape.y - 0.01; + if (coords.z >= shape.z) coords.z = shape.z - 0.01; + + + return true; +} \ No newline at end of file diff --git a/fsl/utils/transform.py b/fsl/utils/transform.py index 1ffea567dda9e29ae8efd9df46219a4135db71f6..f8d427ca3bc09276442ded2312020db754b64969 100644 --- a/fsl/utils/transform.py +++ b/fsl/utils/transform.py @@ -51,7 +51,21 @@ def scaleOffsetXform(scales, offsets): def axisBounds(shape, xform, axes=None): - """Returns the (lo, hi) bounds of the specified axis/axes.""" + """Returns the (lo, hi) bounds of the specified axis/axes. + + This function assumes that voxel indices correspond to the voxel + centre. For example, the voxel at ``(4, 5, 6)`` covers the space: + + ``[3.5 - 4.5, 4.5 - 5.5, 5.5 - 6.5]`` + + So the bounds of the specified shape extends from the corner at + + ``(-0.5, -0.5, -0.5)`` + + to the corner at + + ``(shape[0] - 0.5, shape[1] - 0.5, shape[1] - 0.5)`` + """ scalar = False @@ -64,12 +78,12 @@ def axisBounds(shape, xform, axes=None): x, y, z = shape[:3] + points = np.zeros((8, 3), dtype=np.float32) + x -= 0.5 y -= 0.5 z -= 0.5 - points = np.zeros((8, 3), dtype=np.float32) - points[0, :] = [-0.5, -0.5, -0.5] points[1, :] = [-0.5, -0.5, z] points[2, :] = [-0.5, y, -0.5]