opencv triangulatePoints, strange result - c++

I am using the triagulatePoints function in opencv. Finally I have it working, after much despair, but the results do not look right. I have read some other questions on this, but I still don't understand it!
I am running:
cv::Mat Q,P1,P2,LP,RP,D1,D2,M1,M2;
char *CALIB_FILE = (char *)"extrinsics.xml";
FileStorage fs(CALIB_FILE, FileStorage::READ);
fs["Q"] >> Q;
fs["P1"] >> P1;
fs["P2"] >> P2;
cv::Mat cam0pnts(1, 5, CV_64FC2); //681 432 479 419 550 320 682 274 495 254
cv::Mat cam1pnts(1, 5, CV_64FC2); //800 466 587 451 657 352 791 311 592 283
cv::Mat points_3D(1, 5, CV_64FC4);
cv::triangulatePoints(P1, P2, cam0pnts, cam1pnts, points_3D);
P1 and P2 are the calculated extrinsics from the stereo_calib function.
There are 5 points, a rough square with a point in each corner and one in the middle.
The resulting Matrix is:
[-0.6620691274599629, 0.6497615623177577, -0.6585234150236594, 0.6529909432980171, -0.6604373884239706;
-0.7091492226203088, 0.7208075295879011, -0.7119285643550911, 0.7174438199266364, -0.710244308941275;
0.242429054072024, -0.2413429417514131, 0.2439357048056051, -0.2426462227979475, 0.2436708320163396;
-6.52928664505207e-005, -4.348043360405063e-005, -5.515313727475824e-005, -6.149577656504346e-005, -5.668087253108842e-005]
Which, when plotted in 3d, gives two positions that look almost correct, if completely scaled wrong, then three duplicates of those two.
Where am I going wrong here? DO I need to do something to the resulting matrix to get an xyz coordinate? Or have I implemented the function incorrectly?

cancel that, i managed to do it by ignoring the cv::triangulate function and using this method here:
http://www.morethantechnical.com/2012/01/04/simple-triangulation-with-opencv-from-harley-zisserman-w-code/
with a small change to fix some code that was in wrong place...
Mat_<double> IterativeLinearLSTriangulation(Point3d u, //homogenous image point (u,v,1)
Matx34d P, //camera 1 matrix
Point3d u1, //homogenous image point in 2nd camera
Matx34d P1 //camera 2 matrix
) {
double wi = 1, wi1 = 1;
Mat_<double> X(4, 1);
for (int i = 0; i < 10; i++) { //Hartley suggests 10 iterations at most
Mat_<double> X_ = LinearLSTriangulation(u, P, u1, P1);
X(0) = X_(0); X(1) = X_(1); X(2) = X_(2); X(3) = 1.0;
//recalculate weights
double p2x = Mat_<double>(Mat_<double>(P).row(2)*X)(0);
double p2x1 = Mat_<double>(Mat_<double>(P1).row(2)*X)(0);
//breaking point
if (fabsf(wi - p2x) <= EPSILON && fabsf(wi1 - p2x1) <= EPSILON) break;
wi = p2x;
wi1 = p2x1;
//reweight equations and solve
Matx43d A((u.x*P(2, 0) - P(0, 0)) / wi, (u.x*P(2, 1) - P(0, 1)) / wi, (u.x*P(2, 2) - P(0, 2)) / wi,
(u.y*P(2, 0) - P(1, 0)) / wi, (u.y*P(2, 1) - P(1, 1)) / wi, (u.y*P(2, 2) - P(1, 2)) / wi,
(u1.x*P1(2, 0) - P1(0, 0)) / wi1, (u1.x*P1(2, 1) - P1(0, 1)) / wi1, (u1.x*P1(2, 2) - P1(0, 2)) / wi1,
(u1.y*P1(2, 0) - P1(1, 0)) / wi1, (u1.y*P1(2, 1) - P1(1, 1)) / wi1, (u1.y*P1(2, 2) - P1(1, 2)) / wi1
);
Mat_<double> B = (Mat_<double>(4, 1) << -(u.x*P(2, 3) - P(0, 3)) / wi,
-(u.y*P(2, 3) - P(1, 3)) / wi,
-(u1.x*P1(2, 3) - P1(0, 3)) / wi1,
-(u1.y*P1(2, 3) - P1(1, 3)) / wi1
);
solve(A, B, X_, DECOMP_SVD);
X(0) = X_(0); X(1) = X_(1); X(2) = X_(2); X(3) = 1.0;
}
return X;
}
and this:
Mat_<double> LinearLSTriangulation(Point3d u, //homogenous image point (u,v,1)
Matx34d P, //camera 1 matrix
Point3d u1, //homogenous image point in 2nd camera
Matx34d P1 //camera 2 matrix
)
{
//build matrix A for homogenous equation system Ax = 0
//assume X = (x,y,z,1), for Linear-LS method
//which turns it into a AX = B system, where A is 4x3, X is 3x1 and B is 4x1
Matx43d A(u.x*P(2, 0) - P(0, 0), u.x*P(2, 1) - P(0, 1), u.x*P(2, 2) - P(0, 2),
u.y*P(2, 0) - P(1, 0), u.y*P(2, 1) - P(1, 1), u.y*P(2, 2) - P(1, 2),
u1.x*P1(2, 0) - P1(0, 0), u1.x*P1(2, 1) - P1(0, 1), u1.x*P1(2, 2) - P1(0, 2),
u1.y*P1(2, 0) - P1(1, 0), u1.y*P1(2, 1) - P1(1, 1), u1.y*P1(2, 2) - P1(1, 2)
);
Mat_<double> B = (Mat_<double>(4, 1) << -(u.x*P(2, 3) - P(0, 3)),
-(u.y*P(2, 3) - P(1, 3)),
-(u1.x*P1(2, 3) - P1(0, 3)),
-(u1.y*P1(2, 3) - P1(1, 3)));
Mat_<double> X;
solve(A, B, X, DECOMP_SVD);
return X;
}

Related

Converting for loop in R to Rcpp

I've been playing around with using more efficient data structures and parallel processing and a few other things. I've made good progress getting a script from running in ~60 seconds down to running in about ~9 seconds.
The one thing I can't for the life of me get my head around though is writing a loop in Rcpp. Specifically, a loop that calculates line-by-line depending on previous-line results and updates the data as it goes.
Wondering if someone could convert my code into Rcpp that way I can back-engineer and figure out, with an example that I'm very familiar with, how its done.
It's a loop that calculates the result of 3 variables at each line. Line 1 has to be calculated separately, and then line 2 onwards calculates based on values from the current and previous lines.
This example code is just 6 lines long but my original code is many thousands:
temp <- matrix(c(0, 0, 0, 2.211, 2.345, 0, 0.8978, 1.0452, 1.1524, 0.4154,
0.7102, 0.8576, 0, 0, 0, 1.7956, 1.6348, 0,
rep(NA, 18)), ncol=6, nrow=6)
const1 <- 0.938
for (p in 1:nrow(temp)) {
if (p==1) {
temp[p, 4] <- max(min(temp[p, 2],
temp[p, 1]),
0)
temp[p, 5] <- max(temp[p, 3] + (0 - const1),
0)
temp[p, 6] <- temp[p, 1] - temp[p, 4] - temp[p, 5]
}
if (p>1) {
temp[p, 4] <- max(min(temp[p, 2],
temp[p, 1] + temp[p-1, 6]),
0)
temp[p, 5] <- max(temp[p, 3] + (temp[p-1, 6] - const1),
0)
temp[p, 6] <- temp[p-1, 6] + temp[p, 1] - temp[p, 4] - temp[p, 5]
}
}
Thanks in advance, hopefully this takes someone with Rcpp skills just a minute or two!
Here is an the sample Rcpp equivalent code:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericMatrix getResult(NumericMatrix x, double const1){
for (int p = 0; p < x.nrow(); p++){
if (p == 0){
x(p, 3) = std::max(std::min(x(p, 1), x(p, 0)), 0.0);
x(p, 4) = std::max(x(p, 2) + (0.0 - const1), 0.0);
x(p, 5) = x(p, 0) - x(p, 3) - x(p, 4);
}
if (p > 0){
x(p, 3) = std::max(std::min(x(p, 1), x(p, 0) + x(p - 1, 5)), 0.0);
x(p, 4) = std::max(x(p, 2) + (x(p - 1, 5) - const1), 0.0);
x(p, 5) = x(p - 1, 5) + x(p, 0) - x(p, 3) - x(p, 4);
}
}
return x;
}
A few notes:
Save this in a file and do Rcpp::sourceCpp("myCode.cpp") in your session to compile it and make it available within the session.
We use NumericMatrix here to represent the matrix.
You'll see that we call std::max and std::min respectively. These functions require two common data types, i.e. if we do max(x, y), both x and y must be of the same type. Numeric matrix entries are double (I believe), so you need to provide a double; hence, the change from 0 (an int in C++) to 0.0 (a double)
In C++, indexing starts from 0 instead of 1. As such, you convert R code like temp[1, 4] to temp(0, 3)
Have a look at http://adv-r.had.co.nz/Rcpp.html for more information to support your development
Update: If x was a list of vectors, here's an approach:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
List getResult(List x, double const1){
// Create a new list from x called `res`
Rcpp::List res(x);
for (int p = 0; p < x.size(); p++){
// Initiate a NumericVector `curr` with the contents of `res[p]`
Rcpp::NumericVector curr(res[p]);
if (p == 0){
curr(3) = std::max(std::min(curr(1), curr(0)), 0.0);
curr(4) = std::max(curr(2) + (0.0 - const1), 0.0);
curr(5) = curr(0) - curr(3) - curr(4);
}
if (p > 0){
// Initiate a NumericVector `prev` with the contents of `res[p-1]`
Rcpp::NumericVector prev(res[p-1]);
curr(3) = std::max(std::min(curr(1), curr(0) + prev(5)), 0.0);
curr(4) = std::max(curr(2) + (prev(5) - const1), 0.0);
curr(5) = prev(5) + curr(0) - curr(3) - curr(4);
}
}
return x;
}
So I tried both jav's answers, and did a little bit of reading. Looks to me like Lists are a R-kinda-thing and Rcpp seems to prefer simple vectors and matrices and whatnot.
So I decided to pass my vectors from the list directly into the Rcpp script. The whole thing works wonders. My ~70 second script which I got down to about ~5 seconds with parallel processing is now running in 0.3 seconds. So Rcpp is pretty awesome at this as I had read.
Here's the code I went with:
(temp is a list of 3 vectors that feed into the calculation of the other 3 vectors, and that const 1 is a constant defined earlier in the code)
R code that calls the script:
temp <- getResult(zero=temp[[i]][, 1],
one=temp[[i]][, 2],
two=temp[[i]][, 3],
const1=constant, rows=(as.double(rowslength)))
Output is a matrix with 3 columns calculated by the following Rcpp script:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericMatrix AWBMgetResult(NumericVector zero, NumericVector one,
NumericVector two, double const1, double rows){
// create numericmatrix x
Rcpp::NumericMatrix x(rows, 3);
// compute loop
for (int p = 0; p < rows; p++){
if (p == 0){
x(p, 0) = std::max(std::min(one(p), zero(p)), 0.0);
x(p, 1) = std::max(two(p) + (0.0 - const1), 0.0);
x(p, 2) = zero(p) - x(p, 0) - x(p, 1);
}
else{
x(p, 0) = std::max(std::min(one(p), zero(p) + x(p - 1, 2)), 0.0);
x(p, 1) = std::max(two(p) + (x(p - 1, 2) - const1), 0.0);
x(p, 2) = x(p - 1, 2) + zero(p) - x(p, 0) - x(p, 1);
}
}
return x;
}
I went with if else in the Rcpp code because I couldn't find out how to do 2 loops in a row, 1 of for (p in 1:1) and 1 of for (p in 2:rowslength). But it doesn't seem to matter from a speed point of view. I assumed an if else would still be quicker than an if = 1, if > 1 (since that is checking the value of p at every row)

Halide Error: Input buffer b0 is accessed at -1, which is before the min (0) in dimension 0 - sobel edge detector

I'm trying to develop a sobel edge detector using Halide.
Because I need to compute the 3x3 square around each pixel, I'm trying to realize the function over a smaller rectangle (and avoid the outer border). Here's what I came up with so far:
Buffer<uint8_t> image;
//Read image into buffer (omitted)
gamma(x, y, c) = cast<uint8_t>(255 * pow(image(x, y, c) * 1.0f / 255, gamma_exponent)); //Gamma correction
//Sobel edge detector
h(x, y, c) = gamma(x + 1, y - 1, c) + 2 * gamma(x + 1, y, c) +
gamma(x + 1, y + 1, c) - gamma(x - 1, y - 1, c) -
2 * gamma(x - 1, y, c) - gamma(x - 1, y + 1, c);
v(x, y, c) = -gamma(x - 1, y + 1, c) - 2 * gamma(x, y + 1, c) -
gamma(x + 1, y + 1, c) + gamma(x - 1, y - 1, c) +
2 * gamma(x, y - 1, c) + gamma(x + 1, y - 1, c);
sobel_ed(x, y, c) = (h(x, y, c) + v(x, y, c)) / 4;
sobel_bounded(x, y, c) = BoundaryConditions::constant_exterior(sobel_ed, 0, 2, image.width() - 4, 2, image.height() - 4)(x, y, c);
Buffer<uint8_t> tmp(image.width() - 4, image.height() - 4, image.channels());
tmp.set_min(2, 2, 0);
sobel_bounded.realize(tmp);
When I run this code, I get the following error:
Error: Input buffer b0 is accessed at -1, which is before the min (0) in dimension 0
It seems like Halide evaluates the statement for every value of x and y, even though I set boundary conditions.
What can I do?

How do I get correct answers using my code with the barycentric formula?

My function getHeightOfTerrain() is calling a barycentric formula function that is not returning the correct height for the one set test height in : heightMapFromArray[][].
I've tried watching OpenGL JAVA Game tutorials 14,21, 22, by "thin matrix" and I am confused on how to use my array: heightMapforBaryCentric in both of the supplied functions, and how to set the arguments that are passed to the baryCentic() function in some sort of manner so that I can solve the problem.
int creaateTerrain(int height, int width)
{
float holderY[6] = { 0.f ,0.f,0.f,0.f,0.f,0.f };
float scaleit = 1.5f;
float holder[6] = { 0.f,0.f,0.f,0.f,0.f,0.f };
for (int z = 0, z2 =0; z < iterationofHeightMap;z2++)
{
//each loop is two iterations and creates one quad (two triangles)
//however because each iteration is by two (i.e. : x=x+2) om bottom
//the amount of triangles is half the x value
//
//number of vertices : 80 x 80 x 6.
//column
for (int x = 0, x2 = 0; x < iterationofHeightMap;x2++)
{
//relevant - A : first triangle - on left triangle
//[row] [colum[]
holder[0] = heightMapFromArray[z][x];
//holder[0] = (float)imageData[(z / 2 * MAP_Z + (x / 2)) * 3];
//holder[0] = holder[0] / 255;// *scaleit;
vertices.push_back(glm::vec3(x, holder[0], z));
//match height map with online barycentric use
heightMapforBaryCentric[x2][z2] = holder[0];
holder[1] = heightMapFromArray[z+2][x];
//holder[1] = (float)imageData[(((z + 2) / 2 * MAP_Z + ((x) / 2))) * 3];
//holder[1] = holder[1] / 255;// 6 * scaleit;
vertices.push_back(glm::vec3(x, holder[1], z + 2));
//match height map with online barycentric use
heightMapforBaryCentric[x2][z2+1] = holder[1];
holder[2] = heightMapFromArray[z+2][x+2];
//holder[2] = (float)imageData[(((z + 2) / 2 * MAP_Z + ((x + 2) / 2))) * 3];
//holder[2] = holder[2] / 255;// *scaleit;
vertices.push_back(glm::vec3(x + 2, holder[2], z + 2));
////match height map with online barycentric use
heightMapforBaryCentric[x2+1][z2+1] = holder[2];
//relevant - B - second triangle (on right side)
holder[3] = heightMapFromArray[z][x];
//holder[3] = (float)imageData[((z / 2)*MAP_Z + (x / 2)) * 3];
//holder[3] = holder[3] / 255;// 256 * scaleit;
vertices.push_back(glm::vec3(x, holder[3], z));
holder[4] = heightMapFromArray[x+2][z+2];
//holder[4] = (float)imageData[(((z + 2) / 2 * MAP_Z + ((x + 2) / 2))) * 3];
//holder[4] = holder[4] / 255;// *scaleit;
vertices.push_back(glm::vec3(x + 2, holder[4], z + 2));
holder[5] = heightMapFromArray[x+2][z];
//holder[5] = (float)imageData[((z / 2)*MAP_Z + ((x + 2) / 2)) * 3];
//holder[5] = holder[5] / 255;// *scaleit;
vertices.push_back(glm::vec3(x + 2, holder[5], z));
x = x + 2;
}
z = z + 2;
}
return(1);
}
float getHeightOfTerrain(float worldX, float worldZ) {
float terrainX = worldX;
float terrainZ = worldZ;
int gridSquareSize = 2.0f;
gridX = (int)floor(terrainX / gridSquareSize);
gridZ = (int)floor(terrainZ / gridSquareSize);
xCoord = ((float)(fmod(terrainX, gridSquareSize)) / (float)gridSquareSize);
zCoord = ((float)(fmod(terrainZ, gridSquareSize)) / (float)gridSquareSize);
if (xCoord <= (1 - zCoord))
{
answer = baryCentric(
//left triangle
glm::vec3(0.0f, heightMapforBaryCentric[gridX][gridZ], 0.0f),
glm::vec3(0.0f, heightMapforBaryCentric[gridX][gridZ+1], 1.0f),
glm::vec3(1.0f, heightMapforBaryCentric[gridX+1][gridZ+1], 1.0f),
glm::vec2(xCoord, zCoord));
// if (answer != 1)
// {
// fprintf(stderr, "Z:gridx: %d gridz: %d answer: %f\n", gridX, gridZ,answer);
//
// }
}
else
{
//right triangle
answer = baryCentric(glm::vec3(0, heightMapforBaryCentric[gridX][gridZ], 0),
glm::vec3(1,heightMapforBaryCentric[gridX+1][gridZ+1], 1),
glm::vec3(1,heightMapforBaryCentric[gridX+1][gridZ], 0),
glm::vec2(xCoord, zCoord));
}
if (answer == 1)
{
answer = 0;
}
//answer = abs(answer - 1);
return(answer);
}
float baryCentric(glm::vec3 p1, glm::vec3 p2, glm::vec3 p3 , glm::vec2 pos) {
float det = (p2.z - p3.z) * (p1.x - p3.x) + (p3.x - p2.x) * (p1.z - p3.z);
float l1 = ((p2.z - p3.z) * (pos.x - p3.x) + (p3.x - p2.x) * (pos.y - p3.z)) / det;
float l2 = ((p3.z - p1.z) * (pos.x - p3.x) + (p1.x - p3.x) * (pos.y - p3.z)) / det;
float l3 = 1.0f - l1 - l2;
return (l1 * p1.y + l2 * p2.y + l3 * p3.y);
}
My expected results were that the center of the test grid's height to be the set value .5 and gradually less as the heights declined. My results were the heights being all the same, varied, or increasing. Usually these heights were under the value of one.

how to control Eigen Precision

I'm trying to convert an Eigen 3x3 rotation matrix to a quaternion using this code:
//m_labelMatrix : raw data of vtk4x4 matrix4d.
//m_transformationMatrix : Eigen4x4 matrix4d.
m_transformationMatrix = Eigen::Map<Eigen::Matrix4d>(m_labelMatrix);
m_transformationMatrix.transposeInPlace();
//m_affinedMatrix : affine3d matrix.
m_affinedMatrix = m_transformationMatrix;
auto label_pos = m_affinedMatrix.translation();
auto rotationMatrix = m_affinedMatrix.linear();
auto scaleX = rotationMatrix.col(0).norm();
auto scaleY = rotationMatrix.col(1).norm();
auto scaleZ = rotationMatrix.col(2).norm();
// Make my rotation matrix orthogonal.
rotationMatrix.col(0).normalize();
rotationMatrix.col(1).normalize();
rotationMatrix.col(2) = rotationMatrix.col(0).cross(rotationMatrix.col(1));
rotationMatrix.col(2).normalize();
rotationMatrix.col(0) = rotationMatrix.col(1).cross(rotationMatrix.col(2));
rotationMatrix.col(0).normalize();
Eigen::Quaterniond q(rotationMatrix);
But, when I try to convert back to rotation matrix i get the same matrix with some different values(I think it is an Eigen rounding problem):
rotationMatrix = q.normalized().matrix();
/*3.02303 0.484642 -0.124911
-0.559522 2.94976 -0.217941
0.259569 0.71415 0.984962 */
There is a method for conversion the rotation matrix with noise
https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Conversion_to_and_from_the_matrix_representation
"Fitting quaternions"
with Eigen , it will look like
Eigen::Matrix<Scalar, 4, 4> c;
c(0, 0) = xx - yy - zz; c(0, 1) = yx + xy; c(0, 2) = zx + xz; c(0, 3) = yz - zy;
c(1, 0) = yx + xy; c(1, 1) = yy - xx - zz; c(1, 2) = zy + yz; c(1, 3) = zx - xz;
c(2, 0) = zx + xz; c(2, 1) = zy + yz; c(2, 2) = zz - xx - yy; c(2, 3) = xy - yx;
c(3, 0) = yz - zy; c(3, 1) = zx - xz; c(3, 2) = xy - yx; c(3, 3) = xx + yy + zz;
c /= 3.0;
Eigen::SelfAdjointEigenSolver<Matrix44> es(c);
typename Vector4::Index maxIdx;
es.eigenvalues().maxCoeff(&maxIdx);
Vector4 xyzw = es.eigenvectors().col(maxIdx);

C++ Points of Vertices in Cuboid (Bitwise AND)

I'm trying to calculate the points in a cuboid given its centre (which is a Vector3) and the lengths of the sides along the x, y and z axis. I found the following on math.stackexchange.com: https://math.stackexchange.com/questions/107778/simplest-equation-for-drawing-a-cube-based-on-its-center-and-or-other-vertices which says I can use the following formulae:
The constructor for the World class is:
World::World(Vector3 o, float d1, float d2, float d3) : origin(o)
{
// If we consider an edge length to be d, we need to find r such that
// 2r = d in order to calculate the positions of each vertex in the world.
float r1 = d1 / 2,
r2 = d2 / 2,
r3 = d3 / 2;
for (int i = 0; i < 8; i++)
{
/* Sets up the vertices of the cube.
*
* #see http://bit.ly/1cc2RPG
*/
float x = o.getX() + (std::pow(-1, i&1) * r1),
y = o.getY() + (std::pow(-1, i&2) * r2),
z = o.getZ() + (std::pow(-1, i&4) * r3);
points[i] = Vector3(x, y, z);
std::cout << points[i] << "\n";
}
}
And I passing the following parameters to the constructor:
Vector3 o(0, 0, 0);
World w(o, 100.f, 100.f, 100.f);
The coordinates being output for all 8 vertices are:
(50, 50, 50)
(-50, 50, 50)
(50, 50, 50)
(-50, 50, 50)
(50, 50, 50)
(-50, 50, 50)
(50, 50, 50)
(-50, 50, 50)
Which cannot be correct. Any guidance would be very much appreciated!
The problem lies in the bitwise & inside your pow calls:
In the y and z components, they always return 0 and 2 or 4, respectively. -1^2 = -1^4 = 1, which is why the sign of these components is always positive. You could try (i&2)!=0 or (i&2) >> 1 for the y component instead. The same goes for the z component.
Change this:
float x = o.getX() + (std::pow(-1, i&1) * r1),
y = o.getY() + (std::pow(-1, i&2) * r2),
z = o.getZ() + (std::pow(-1, i&4) * r3);
To this:
float x = o.getX() + (std::pow(-1, (i ) & 1) * r1), // pow(-1, 0) == 1, pow(-1, 1) == -1
y = o.getY() + (std::pow(-1, (i >> 1) & 1) * r2), // pow(-1, 0) == 1, pow(-1, 1) == -1
z = o.getZ() + (std::pow(-1, (i >> 2) & 1) * r3); // pow(-1, 0) == 1, pow(-1, 1) == -1
Or even to this:
float x = o.getX() + (std::pow(-1, (i )) * r1), // pow(-1, {0, 2, 4, 6}) == 1, pow(-1, {1, 3, 5, 7}) == -1
y = o.getY() + (std::pow(-1, (i >> 1)) * r2), // pow(-1, {0, 2}) == 1, pow(-1, {1, 3}) == -1
z = o.getZ() + (std::pow(-1, (i >> 2)) * r3); // pow(-1, 0) == 1, pow(-1, 1) == -1
The problem is that as written even though the values you mask out identify weather or not the lengths need to be negated. They are not in the correct place value to get the desired properties from the exponentiation of -1.
Rewriting the code as I have above will solve this issue, however it would be more readable and in general more permanent just to unroll the loop and manually write if each one is an addition or subtraction without using the pow function.