0
votes

After searching almost every topic on Catmull-Rom splines and finally implementing it successfully I'm now stuck at a point where I don't know if I made a logical mistake or if my code is simply wrong.

My problem is, that my code won't accept the alpha parameter for the centripetal catmull-rom spline interpolation. The results with different alpha values are always the same.

My main goal is to interpolate 4 points (6 when I double the first and last points) with the centripetal catmull-rom algorithm. I implemented basically the C# version which can be found here (Centripetal Catmull-Rom Spline) or also in a couple of other threads throughout SO (Catmull-rom curve with no cusps and no self-intersections, Catmull-Rom interpolation on SVG Paths).

My implementation:

First I have an 3D image with 4 different voxels set to one. I try to identify those voxels and save the coordinates. For example z=250, y=100, x=323. I save those values and cast them as double to use them as control points for my CR-algorithm.

for(int nx=0; nx<Nx; ++nx)
for(int ny=0; ny<Ny; ++ny)
for(int nz=0; nz<Nz; ++nz)
{
if(TempArray[nz*Ny*Nx+ny*Nx+nx] > 0)
{
Coordinates[counter*3+0] = nz;  // +0 = z0
Coordinates[counter*3+1] = ny;  // +1 = y0
Coordinates[counter*3+2] = nx;  // +2 = x0
++counter;
}
}

Just to clearify. The arrays have the following layout: Coordinates[z0,y0,x0,z1,y1,x1,...,zn,yn,xn].

My implementation of the catamull-rom interpolation is as follows:

double P0z, P0y, P0x, P1z, P1y, P1x, P2z, P2y, P2x, P3z, P3y, P3x;
P0z = Coordinates[n*3+0];   
P1z = Coordinates[n*3+3];   
P2z = Coordinates[n*3+6];   
P3z = Coordinates[n*3+9];
P0y = Coordinates[n*3+1];   
P1y = Coordinates[n*3+4];   
P2y = Coordinates[n*3+7];   
P3y = Coordinates[n*3+10];
P0x = Coordinates[n*3+2];   
P1x = Coordinates[n*3+5];   
P2x = Coordinates[n*3+8];   
P3x = Coordinates[n*3+11];

double disAxBx, disAyBy, disAzBz; disAzBz=disAyBy=disAxBx=0.0;

disAzBz = pow( P1z - P0z, 2 );
disAyBy = pow( P1y - P0y, 2 );
disAxBx = pow( P1x - P0x, 2 );

double distanceVec = pow( disAzBz + disAyBy + disAxBx, 0.5 );

t0 = 0.0;
t1 = pow( distanceVec, alpha) + t0;  // This is the part where the alpha comes in
t2 = pow( distanceVec, alpha) + t1;
t3 = pow( distanceVec, alpha) + t2;

for ( double t=t1; t<t2; t+= (t2-t1)/(NumberOfPoints) )  // t starts at t1 and runs till t2 (like in the examples)
{
z=y=x=0;

A1[0] = ( ( (t1-t)/(t1-t0) ) * P0z ) + ( ( (t-t0)/(t1-t0) ) * P1z );
A1[1] = ( ( (t1-t)/(t1-t0) ) * P0y ) + ( ( (t-t0)/(t1-t0) ) * P1y );
A1[2] = ( ( (t1-t)/(t1-t0) ) * P0x ) + ( ( (t-t0)/(t1-t0) ) * P1x );

A2[0] = ( ( (t2-t)/(t2-t1) ) * P1z ) + ( ( (t-t1)/(t2-t1) ) * P2z );
A2[1] = ( ( (t2-t)/(t2-t1) ) * P1y ) + ( ( (t-t1)/(t2-t1) ) * P2y );
A2[2] = ( ( (t2-t)/(t2-t1) ) * P1x ) + ( ( (t-t1)/(t2-t1) ) * P2x );

A3[0] = ( ( (t3-t)/(t3-t2) ) * P2z ) + ( ( (t-t2)/(t3-t2) ) * P3z );
A3[1] = ( ( (t3-t)/(t3-t2) ) * P2y ) + ( ( (t-t2)/(t3-t2) ) * P3y );
A3[2] = ( ( (t3-t)/(t3-t2) ) * P2x ) + ( ( (t-t2)/(t3-t2) ) * P3x );

B1[0] = ( ( (t2-t)/(t2-t0) ) * A1[0] ) + ( ( (t-t0)/(t2-t0) ) * A2[0] );
B1[1] = ( ( (t2-t)/(t2-t0) ) * A1[1] ) + ( ( (t-t0)/(t2-t0) ) * A2[1] );
B1[2] = ( ( (t2-t)/(t2-t0) ) * A1[2] ) + ( ( (t-t0)/(t2-t0) ) * A2[2] );

B2[0] = ( ( (t3-t)/(t3-t1) ) * A2[0] ) + ( ( (t-t1)/(t3-t1) ) * A3[0] );
B2[1] = ( ( (t3-t)/(t3-t1) ) * A2[1] ) + ( ( (t-t1)/(t3-t1) ) * A3[1] );
B2[2] = ( ( (t3-t)/(t3-t1) ) * A2[2] ) + ( ( (t-t1)/(t3-t1) ) * A3[2] );

C[0] =int ( ( ( (t2-t)/(t2-t1) ) * B1[0] ) + ( ( (t-t1)/(t2-t1) ) * B2[0] ) +0.5 );
C[1] =int ( ( ( (t2-t)/(t2-t1) ) * B1[1] ) + ( ( (t-t1)/(t2-t1) ) * B2[1] ) +0.5 );
C[2] =int ( ( ( (t2-t)/(t2-t1) ) * B1[2] ) + ( ( (t-t1)/(t2-t1) ) * B2[2] ) +0.5 );

z=C[0]; y=C[1]; x=C[2];

OutputArray[z*Nx*Ny+y*Nx+x] = 1.f;          
}
} // Loop over 4 consecutive points

Now what I try to do is, to set every voxel (or pixel in 2D) to 1, which is calculated by the CR-interpolation method. I basically want to calculate the coordinates with the CR algorithm. If I set alpha=0, the results are as expected (I used similar points to the wikipedia example). I get a nice self-intersection at the top. But if I change the value to 0.5 or 1, I still get the same results.

Now I suspect that there is something wrong with the types I use. Probably it is not wise to cast the integer coordinates as double or to cast them back to integer (with +0.5). But that would not explain the self-intersection I get. I actually did not provide an image, because it is very similar to the images we have in 1 and 2. Thanks to everyone who even considers to read this.

1

1 Answers

0
votes

After looking over the code a couple of hours I finally found my mistake. My code was simply wrong in calculating the t2 and t3 values. Looking at the formula for t (Centripetal Catmull-Rom Spline) provided the solution.

t2 and t3 are dependent on |P2 - P1| respectively on |P3 - P2|. I put the values for t1 also on t2 and t3, thus leading to the same uniform result of the CR algorithm no matter which alpha was used. This makes also sense, why it was right for alpha = 0.

I changed the code as follows and now I get the right results:

disAzBz0 = pow( P1z - P0z, 2 ); disAzBz1 = pow( P2z - P1z, 2 ); disAzBz2 = pow( P3z - P2z, 2 );  // Value for distance calculation for P0 and P1
disAyBy0 = pow( P1y - P0y, 2 ); disAyBy1 = pow( P2y - P1y, 2 ); disAyBy2 = pow( P3y - P2y, 2 );  // Value for distance calculation for P1 and P2
disAxBx0 = pow( P1x - P0x, 2 ); disAxBx1 = pow( P2x - P1x, 2 ); disAxBx2 = pow( P3x - P2x, 2 );  // Value for distance calculation for P2 and P3

double distanceVec01 = pow( disAzBz0 + disAyBy0 + disAxBx0, 0.5 ); // Distance P0-P1
double distanceVec12 = pow( disAzBz1 + disAyBy1 + disAxBx1, 0.5 ); // Distance P1-P2
double distanceVec23 = pow( disAzBz2 + disAyBy2 + disAxBx2, 0.5 ); // Distance P2-P3

t0 = 0.0;
t1 = pow( distanceVec01, alpha) + t0;  // This was right in the code above
t2 = pow( distanceVec12, alpha) + t1;  // Here was the mistake. I used distanceVec01 instead of distanceVec12
t3 = pow( distanceVec23, alpha) + t2;  // Same here