3D Object Oriented Bounding Box using PCA - graphics

I am trying to compute the object oriented bounding box for a set of points. I'm using c++ and the Eigen linear algebra library.
I have been using two blog posts as guides yet still my bounding boxes are incorrect (see images).
blog post 1
blog post 2
I hope my commented code is clear of my attempt but the gist of the algorithm is to use PCA to find the basis vectors for the object oriented coordinate frame.
To then project all points into the new frame, find the min and max points that define the box, then project these point into the original coordinate frame and render them.
I can successfully render a box but is isn't a bounding box and appears to be aligned to the normal x,y,z axis. This is clear in the first image for each of the two objects shown.
Any help would be really appreciated. Thanks in advance.
// iglVertices is a X by 3 Eigen::::MatrixXf
// Covariance matrix and eigen decomposition
Eigen::MatrixXf centered = iglVertices.rowwise() - iglVertices.colwise().mean();
Eigen::MatrixXf cov = centered.adjoint() * centered;
Eigen::SelfAdjointEigenSolver<Eigen::MatrixXf> eig(cov);
//Setup homogenous tranformation to act as new basis functions for new coordinate frame
auto basis = Eigen::Matrix4f(eig.eigenvectors().colwise().homogeneous().rowwise().homogeneous());
basis.row(3) = Eigen::Vector4f::Zero();
basis.col(3) = Eigen::Vector4f::Zero();
basis(3,3) = 1.0f;
std::cout << "eig.eigenvectors() " << eig.eigenvectors() << std::endl;
std::cout << "Basis " << basis << std::endl;
//invert matrix and and transform points into new coordinate frame
auto invBasis = basis.inverse();
auto newVertices = invBasis * iglVertices.rowwise().homogeneous().transpose();
//Find max and min for all of the new axis
auto maxP = newVertices.rowwise().maxCoeff();
auto minP = newVertices.rowwise().minCoeff();
std::cout << "max " << maxP << std::endl;
std::cout << "min " << minP << std::endl;
//Find center and half extent in new coordinate frame
auto center = Eigen::Vector4f((maxP + minP) / 2.0);
auto half_extent = Eigen::Vector4f((maxP - minP) / 2.0);
auto t = Eigen::Vector4f((basis * center));
std::cout << "t " << t << std::endl;
//Update basis function with the translation between two coordinate origins
//I don't actually understand why I need this and have tried without it but still my bounding
//box is wrong
basis.col(3) = Eigen::Vector4f(t[0], t[1], t[2], t[3]);
std::cout << "Basis complete " << basis << std::endl;
std::cout << "center " << center << std::endl;
std::cout << "half_extent " << half_extent << std::endl;
//This is the same as the previous minP/maxP but thought i should try this as
// box is paramaterised with center and half-extent
auto max = center + half_extent;
auto min = center - half_extent;
//Transform back into the original coordinates
auto minNormalBasis = (basis * min).hnormalized();
auto maxNormalBasis = (basis * max).hnormalized();
std::cout << "min new coord" << min << std::endl;
std::cout << "max new coord"<< max << std::endl;
std::cout << "min old coord" << minNormalBasis << std::endl;
std::cout << "max old coord"<< maxNormalBasis << std::endl;
//Extract min and max
auto min_x = minNormalBasis[0];
auto min_y = minNormalBasis[1];
auto min_z = minNormalBasis[2];
auto max_x = maxNormalBasis[0];
auto max_y = maxNormalBasis[1];
auto max_z = maxNormalBasis[2];
bBox.clear();
//Build box for rendering
//Ordering specific to the faces I have manually generated
bBox.push_back(trimesh::point(min_x, min_y, min_z));
bBox.push_back(trimesh::point(min_x, max_y, min_z));
bBox.push_back(trimesh::point(min_x, min_y, max_z));
bBox.push_back(trimesh::point(min_x, max_y, max_z));
bBox.push_back(trimesh::point(max_x, min_y, max_z));
bBox.push_back(trimesh::point(max_x, max_y, max_z));
bBox.push_back(trimesh::point(max_x, min_y, min_z));
bBox.push_back(trimesh::point(max_x, max_y, min_z));
The print output for the spray bottle example is
eig.eigenvectors() 0 -0.999992 -0.00411613
-0.707107 -0.00291054 0.707101
0.707107 -0.00291054 0.707101
Basis 0 -0.999992 -0.00411613 0
-0.707107 -0.00291054 0.707101 0
0.707107 -0.00291054 0.707101 0
0 0 0 1
max 2.98023e-08
0.216833
0.582629
1
min -2.98023e-08
-0.215
-0.832446
1
t -0.000402254
-0.0883253
-0.0883253
1
Basis complete 0 -0.999992 -0.00411613 -0.000402254
-0.707107 -0.00291054 0.707101 -0.0883253
0.707107 -0.00291054 0.707101 -0.0883253
0 0 0 1
center 0
0.000916399
-0.124908
1
half_extent 2.98023e-08
0.215916
0.707537
0
min new coord-2.98023e-08
-0.215
-0.832446
1
max new coord2.98023e-08
0.216833
0.582629
1
min old coord 0.218022
-0.676322
-0.676322
max old coord-0.219631
0.323021
0.323021

You have to compute the 8 corners of an axis aligned box within the PCA frame, and then apply the rotation to them:
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), minP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), minP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), minP.y(), maxP.z()));
bBox.push_back(eig.eigenvectors() * Vector3f(minP.x(), maxP.y(), maxP.z()));
...
and you can also directly compute newVertices as:
Matrix<float,3,Dynamic> newVertices = eig.eigenvectors().transpose() * iglVertices.transpose();
After these changes, your code will be reduced by half ;)
And more importantly, please avoid the use of the auto keyword unless you know what you are doing. In your example, most of its usage is very bad practice, not to say wrong. Please read this page.

Related

I want to put my if-else into switch case but the output does not show the correct result that it should be

I am trying to put if-else into switch case, it works but the issue is that the result of the calculation is incorrect
#include "stdafx.h"
#include <stdio.h>
#include <iostream>
using namespace std;
int main()
{
int unit, disc, total, discprice, caseof;
int a = 99;
cout << "Enter number of units sold: ";
cin >> unit;
if (unit > 10 && unit < 19)
{
unit = '1';
}
if (unit > 20 && unit < 49)
{
unit = '2';
}
if (unit > 50 && unit < 99)
{
unit = '3';
}
if (unit > 100)
{
unit = '4';
}
else if (unit <= 10)
{
cout << "Number of units sold" << endl;
cout << "Total amount of units: RM" << total << endl;
}
switch (unit)
{
case '1':
total = unit * a;
disc = total * 0.3;
discprice = total - disc;
cout << "Number of units sold= " << unit << endl;
cout << "Total amount of units: RM" << total << endl;
cout << "Total amount after 30% discount is RM" << discprice << endl;
break;
case '2':
total = unit * a;
disc = total * 0.3;
discprice = total - disc;
cout << "Number of units sold= " << unit << endl;
cout << "Total amount of units: RM" << total << endl;
cout << "Total amount after 30% discount is RM" << discprice << endl;
break;
case '3':
total = unit * a;
disc = total * 0.4;
discprice = total - disc;
cout << "Number of units sold= " << unit << endl;
cout << "Total amount of units: RM" << total << endl;
cout << "Total amount after 40% discount is RM" << discprice << endl;
break;
case '4':
total = unit * 2;
disc = total * 0.5;
discprice = total - disc;
cout << "Number of units sold= " << unit << endl;
cout << "Total amount of units: RM" << total << endl;
cout << "Total amount after 50% discount is RM" << discprice << endl;
break;
}
return 0;
}
The expected output should calculated and show the discounted price of the products. Ex: When I put 15 as unit in the program the number of units sold should appear as 15 and use case 1 to multiply 0.2 and show the discounted price. Instead it shows 49 in Number of units sold.
This is almost just a typo problem, but you defined unit to be int, yet you actually are assigning to it character literals. Instead of:
int unit = '1';
you should use:
int unit = 1;
As an alternative, which I do not recommend, you could cope with the character assignments by converting back to the original integer numbers, e.g.
switch (unit - '0')
{
case 1:
// case 1
// etc., using unit - '0' everywhere for the actual unit integer value
}
First avoid code repetition:
This:
total = unit * 2;
disc = total * 0.5;
discprice = total - disc;
cout << "Number of units sold= " << unit << endl;
cout << "Total amount of units: RM" << total << endl;
cout << "Total amount after 50% discount is RM" << discprice << endl;
can become something like this
total(units, 2, 0.5);
somewhere else you can use:
total(units, a, 0.3);
Declare a function like :
void compute_total(int units, int correction, float disc_ratio ){
...
}
That cleans your code a lot.
Second, c types matter. int unit has int size (at least two bytes), and in some architectures that might not be what you expect giving it char literal '1' (normally 1 byte).
Another type conversion occurs when you do total *0.3, it should return 445.5, but it round to 445
total = 15 * 99
total * 0.3,
Last, your if chain is not working as expected also. This is the real problem, 49 is ascii for '1', as you reassign unit, you are actually giving 49 value to it.
If you chain your ifs with elses as:
if() else if()
still won't solve it.
I advise you to not reassign units variable, create a new one, say "char pricing_strategy".
Notice you can manually do
case 1: case 2: ... case 10: code break; case 11: ... ; case 100: ... break; default: //>100 break;
And avoid if chain, it is not really worth it, and pretty hard on yourself later if you have to change for some reason.

Shape Transformers and Interfaces OpenCV3.0

I was trying to make use of the new Shape Transformers and Interfaces of OpenCV3.0. Unfortunately it doesn't work as expected. To ensure not making any fancy warps and getting strange results cause of that reason I initialized a transformation where nothing at all should happen. But output of the transformation for a testpoint is always [0,0] and the warped image is always completley gray. Any suggestions what could be wrong are welcome.
int main(void){
Mat img1 = imread("C:\\opencv\\sources\\samples\\data\\graf1.png", IMREAD_GRAYSCALE);
std::vector<cv::Point2f> points1, testpoints;
vector<DMatch> good_matches;
Mat respic, resmat;
points1.push_back(Point(0, 0)); //Corners 800x600 pic
points1.push_back(Point(799, 0));
points1.push_back(Point(799, 599));
points1.push_back(Point(0, 599));
Mat pointmatrix1(points1);
good_matches.push_back(DMatch(0, 0, 0));
good_matches.push_back(DMatch(1, 1, 0));
good_matches.push_back(DMatch(2, 2, 0));
good_matches.push_back(DMatch(3, 3, 0));
testpoints.push_back(Point(250, 250));
Mat testpointsmat(testpoints);
// Apply TPS
Ptr<ThinPlateSplineShapeTransformer> mytps = createThinPlateSplineShapeTransformer(0);
mytps->estimateTransformation(pointmatrix1, pointmatrix1, good_matches); // Using same pointmatrix nothing should change in res
mytps->applyTransformation(testpointsmat, resmat);
cout << "pointmatrix1 = " << endl << " " << pointmatrix1 << endl << endl;
cout << "testpointsmat = " << endl << " " << testpointsmat << endl << endl;
cout << "resmat = " << endl << " " << resmat << endl << endl; //Always [0,0] ?
imshow("img1", img1); // Just to see if I have a good picture
mytps->warpImage(img1, respic);
imwrite("Tranformed.png", respic);
imshow("Tranformed", respic); //Always completley grey ?
waitKey(0);
return 0;
}
Don't ask me why but if I add this two lines it works.
// Apply TPS
transpose(pointmatrix1, pointmatrix1); // ADD
transpose(testpoints, testpoints); // ADD
Ptr<ThinPlateSplineShapeTransformer> mytps = createThinPlateSplineShapeTransformer(0);
Now There is something strange in source code here why cols and not rows.
by LBerger

Printing dynamically stored string in assembly mips32

I'm working on a mini-compiler and I reached the code generation level.
I want to store a string dynamically to the heap segment, so I wrote this C++ code (cg is the file I'm generating assembly code to) :
int length = strlen("abc");
cg << "\tori\t$a0,$0," << (length + 3) * 4 << endl; // reserve space for type + length + null + size
cg << "\tori\t$v0,$0,9" << endl;
cg << "\tsyscall" << endl;
increamentSP();
cg << "\tsw\t$v0,0($sp)" << endl;
cg << "\tori\t$t1,$0,1" << endl; // store the type
cg << "\tsw\t$t1,0($v0)" << endl;
cg << "\tori\t$t1,$0," << length << endl; // store the length
cg << "\tsw\t$t1," << 4 << "($v0)" << endl;
for (int i = 0; i < length; i++)
{
cg << "\tori\t$t1,$0," << (int)p->val[i] << endl; // store the char
cg << "\tsw\t$t1," << (2 + i) * 4 << "($v0)" << endl;
}
cg << "\tsw\t$0," << (2 + length) * 4 << "($v0)" << endl;
Basically, what I'm trying to do is :
First, I want to store a flag (1) referring that this type is a string to the first location.
Then, I want to store the size of my string to the second location.
After that, I want to store my strings chars to the next locations.
Finally, I want to store a null-terminating char to the last location.
My problem is that when I try to print my string using a code like this :
la $a0,8($t0)
ori $v0,$0,4
syscall
The result is that mips prints only the letter 'a', how can I print a string stored like this? or is there any better way to store my string?
p.s. I know I can use .asciiz in the .data segment, but the problem is that in my code I might edit the string, so I don't exactly know what my string would become.
Can any one help me with that?

Find angle of right angled triangle

I am trying to work out the angle of a right angled triangle. I have an array containing the lengths of the two sides of the triangle. I also have an array containing the Euclidean Distance between these two points. How would I find the angle of the triangles? In other words, how would I do the sin and then arcsin methods to find the angle? I am just looking for the angle opposite of the hypotenuse. I'm trying to do this in C++.
Solved it now, misinterpreted what I had been asked to do
Solution: How would I find the angle of the triangles
#include <iostream>
#include <cmath>
using namespace std;
#define radians(x) return x * (180/pi)
int main()
{
double opposite, adjacent, angle1, angle2, angle3, choice, radians, hypotenuse;
cout << "Opposite: ";
cin >> opposite;
cout << "Adjacent: ";
cin >> adjacent;
cout << "Radians or Degrees: (R/D)";
cin >> choice;
if(choice == "R")
{
angle1 = arctan(adjacent/opposite);
hypotenuse = opposite\cos(radians(angle1));
angle2 = arcsin(adjacent\hypotenuse);
cout << "Angle 1: "<< radians(angle1) << endl;
cout << "Angle 2: "<< "90\n";
cout << "Angle 3: "<< radians(angle2) << endl;
cout << "Hypotenuse: " << hypotenuse;
}
else if(choice = "D")
{
angle1 = arctan(adjacent/opposite);
hypotenuse = opposite\cos((angle1));
angle2 = arcsin(adjacent\hypotenuse);
cout << "Angle 1: " << (angle1) << endl;
cout << "Angle 2: " << "90\n";
cout << "Angle 3: " << (angle2) << endl;
cout << "Hypotenuse: " << hypotenuse;
}
return 0;
}
or just
angle2 = 180 - (angle1 + 90)
The relation between sides and angles of triangle is:-
a/sinA = b/sinB = c/sinC
where 'a' is the side opposite angle 'A'.
You know one angle let's say it's A = 90. Then you can calculate other two angles from above equation.
You have the lengths of the sides, if you us tangents, you can find the angle for the corresponding side.
Also, once you find one angle, all you need to do is subtract 90 from it to get the final angle:
tan(angle) = opposite/adjacent;
angle = arctan(opposite/adjacent);
otherAngle = 90 - angle;

c++ Sum, Average, Largest, Smallest validation issue

So this program calculates and prints the largest, smallest, average, and sum of the sequence a user enters. My only problem that I have found is that when a symbol is entered, it outputs it is wrong, but still ""adds" it's ascii code to the sum, messing up the results. Also, if someone else a number and letter such as 1361351P, it still reads it. Any help is appreciated.
/** C2.cpp
* Test #2 Problem C2
* Robert Uhde
* This program calculates and prints the largest, smallest, average,
* and sum of a sequence of numbers the user enters.
*/
#include <iostream>
using namespace std;
// Extreme constants to find min/max
const double MAX = 1.7976931348623157e+308;
const double MIN = 2.2250738585072014e-308;
// Create generic variable T for prototype
template <class T>
// Prototype dataSet that with request inputs and calculate sum, average, largest and smallest numbers.
T dataSet(T &sum, T &largest, T &smallest, T avg);
int main(){
// Intro to program
cout << "This program calculates and prints the largest, smallest,"
<< endl << "average, and sum of a sequence of numbers the user enters." << endl << endl;
// defined used variables in longest double format to include as many types as possible with largest range
double avg = 0, sum = 0, max, min;
// Call dataSet which returns avg and return references
avg = dataSet(sum, max, min, avg);
// Output four variables
cout << endl << "The largest of the sequence you entered is: " << max << endl;
cout << "The smallest of the sequence you entered is: " << min << endl;
cout << "The sum of the sequence you entered is: " << sum << endl;
cout << "The average of the sequence you entered is: " << avg << endl;
system("pause");
return 0;
}
// Create generic variable T for dataSet
template <class T>
T dataSet(T &sum, T &max, T &min, T avg){
T num;
min = MAX, max = MIN;
// count number of valid numbers
int count = 0;
// Repeat this loop until ^Z
do{
cout << "Enter a sequence of numbers: (^Z to quit) ";
cin >> num;
// if valid, then increment count by 1, add to sum, find out if it's new max or min
if(cin.good() && (typeid(num) == typeid(int) || typeid(num) == typeid(double))){
count++;
if(num > max)
max = num;
sum += num;
if(num < min)
min = num;
}
// if user enters ^Z break out
else if(cin.eof())
break;
// If there is some sort of type error, print so and clear to request again
else{
cout << "Error. Try Again.\n";
cin.clear();
cin.ignore(80, '\n');
}
}while(true);
// Calculate average and then return
avg = sum / count;
return avg;
}
Your conditioning check needs some more work. I would use more specific condition checkings such as isalpha or isdigit which are part of the since these condition checks below are not good enough
if(cin.good() && (typeid(num) == typeid(int) || typeid(num) == typeid(double)))
Best of luck!

Resources