@@ -34,9 +34,106 @@ __global__ void rotateKernel_pull(const float* __restrict__ in_im,
3434 float3 origin,
3535 float angle_rad)
3636 {
37+ // parallelise the operation across all image voxels
38+ int i = threadIdx .x + blockDim .x * blockIdx .x ;
39+ int j = threadIdx .y + blockDim .y * blockIdx .y ;
40+ int k = threadIdx .z + blockDim .z * blockIdx .z ;
3741
42+ // check we are not outside the image
43+ if (i >= dim.x || j >= dim.y || k >= dim.z ) {
44+ return ;
45+ };
3846
39- }
47+ // get the 1-dimensional index
48+ int idx = i + (j * dim.x ) + (k * dim.x * dim.y );
49+
50+ // move to centre of voxel and convert from voxel to image space
51+ float Xs_rel_x = (i + 0 .5f ) * spacing.x - origin.x ;
52+ float Xs_rel_y = (j + 0 .5f ) * spacing.y - origin.y ;
53+ float Xs_rel_z = (k + 0 .5f ) * spacing.z - origin.z ;
54+
55+ // perform the rotation by 'angle_rad'
56+ float X_rot_x = (Xs_rel_x * cosf (angle_rad)) - (Xs_rel_y * sinf (angle_rad));
57+ float X_rot_y = (Xs_rel_x * sinf (angle_rad)) + (Xs_rel_y * cosf (angle_rad));
58+ float X_rot_z = Xs_rel_z;
59+
60+ // convert back to voxel space origin (corner of image) but keep image space dimensions
61+ float Xr_x = X_rot_x + origin.x ;
62+ float Xr_y = X_rot_y + origin.y ;
63+ float Xr_z = X_rot_z + origin.z ;
64+
65+ // now we go fully back to voxel space (but now it's been rotated)
66+ float p_r = Xr_x / spacing.x ;
67+ float q_r = Xr_y / spacing.y ;
68+ float r_r = Xr_z / spacing.z ;
69+
70+ // first loop is for finding the value of a gaussian kernel at
71+ // each nearest neighbour position and summing them all. This will allow normalisation
72+ // of each NN contribution in the next loop.
73+ float G = 0 ; // accumulator variable
74+ float sigma = 1 ; // gaussian kernel sigma
75+
76+ for (int dr = -1 ; dr <= 1 ; dr++) {
77+ int r = (int )roundf (r_r) + dr; // nearest neighbour z coordinate in rotated voxel space
78+ float x_r = (r * spacing.z ) - origin.z ; // converted to image space
79+
80+ for (int dq = -1 ; dq <= 1 ; dq++) {
81+ int q = (int )roundf (q_r) + dq;
82+ float x_q = (q * spacing.y ) - origin.y ;
83+
84+ for (int dp = -1 ; dp <= 1 ; dp++) {
85+ int p = (int )roundf (p_r) + dp;
86+ float x_p = (p * spacing.x ) - origin.x ;
87+
88+ // get distance between nearest neighbour and central voxel
89+ // both need to be in image space
90+ float delta_x = X_rot_x - x_p;
91+ float delta_y = X_rot_y - x_q;
92+ float delta_z = X_rot_z - x_r;
93+
94+ // caulculate gaussian kernel
95+ float g = expf (-1 . * ((delta_x*delta_x) + (delta_y*delta_y) + (delta_z*delta_z)) / (2 *sigma*sigma))
96+ G += g;
97+ };
98+ };
99+ };
100+
101+ // loop again but this time actually fetch the image values
102+ float accumulation = 0 ;
103+
104+ for (int dr = -1 ; dr <= 1 ; dr++) {
105+ float r = dr + (int )roundf (r_r);
106+ float x_r = origin.z + (r * spacing.z );
107+
108+ for (int dq = -1 ; dq <= 1 ; dq++) {
109+ float q = dq + (int )roundf (q_r);
110+ float x_q = origin.y + (q * spacing.y );
111+
112+ for (int dp = -1 ; dp <= 1 ; dp++) {
113+ float p = dp + (int )roundf (p_r);
114+ float x_p = origin.x + (p * spacing.x );
115+
116+ float delta_x = Xr_x - x_p;
117+ float delta_y = Xr_y - x_q;
118+ float delta_z = Xr_z - x_r;
119+
120+ // get input image voxel index that we are at
121+ int i_idx = p + (q * dim.x ) + (r * dim.x * dim.y );
122+
123+ // calculate gaussian weighting for this voxel
124+ float g = expf (-1 . * ((delta_x*delta_x) + (delta_y*delta_y) + (delta_z*delta_z)) / (2 *sigma*sigma));
125+
126+ // record the weighted value from this voxel
127+ accumulation += in_im[i_idx] * g / G;
128+
129+ };
130+ };
131+ };
132+
133+ // assign the pulled voxel values to a single voxel in the output image
134+ out_im[idx] = accumulation;
135+
136+ };
40137
41138// the following is the adjoint operation (push)
42139__global__ void rotateKernel_push (const float * __restrict__ in_im,
0 commit comments