asmaloney/libE57Format

question: how to read multiple scans ?

Closed this issue · 11 comments

Hello,

I would like to know how to properly read a e57 file containing multiple scans accross multiple scanner poses.

I tried to multiply each point group by a matrix (built by rotation and position of scanner), but the result seems to be bad :
Original file :
Capture d’écran 2019-05-22 à 17 50 15

The file exported by my reader (groups seems to be well-oriented although) :
Capture d’écran 2019-05-24 à 16 01 54

Should we accumulate each pose along met groups ?
Should we apply a scale factor on translate parameter ?...

Each scan in an e57 file has an optional pose RigidBodyTransform structure in Cartesian coordinates.

If it is present, the pose structure contains two required fields: rotation (a unit-length quaternion) and a translation.

In the XML part of the file, it looks like this:

<pose type="Structure">
  <rotation type="Structure">
    <w type="Float">8.8372441406806668e-001</w>
    <x type="Float">1.0619827779317424e-003</x>
    <y type="Float">-1.3312975367466115e-003</y>
    <z type="Float">4.6800455106708111e-001</z>
  </rotation>
  <translation type="Structure">
    <x type="Float">6.5880000000000001e-003</x>
    <y type="Float">-1.1521999999999999e-002</y>
    <z type="Float">3.9709999999999997e-003</z>
  </translation>
</pose>

So you should check for the existence of a pose structure and apply it accordingly to each scan.

Okay, this is effectively what I did.
By refering to this page for the rotation :
http://www.libe57.org/bestReader.html (Converting Quaternion to Matrix)

So I build a matrix 3x3 for rotation and for each point :

  • apply rotation matrix inverse
  • apply translation inverse
  • if there is no pose structure, I apply the identity matrix for rotation (and no translation).

And I have the result above.
So if all of these steps should be correct, It could be a math mistake from me.

With the caveat that I'm not a 3D math guru (at all):

  1. Why do you apply the inverse?
  2. Have you checked that you aren't mismatching row-major vs. column-major on the matrix you're creating?
  1. I thought this would be the right way, but you're right, I just checked it, and result is the same !
  2. This is a good point. I'm using this method:

double resX = x*mR[0][0] + y*mR[0][1] + z*mR[0][2] + t[0];
double resY = x*mR[1][0] + y*mR[1][1] + z*mR[1][2] + t[1];
double resZ = x*mR[2][0] + y*mR[2][1] + z*mR[2][2] + t[2];

but if a change by:
double resX = x*mR[0][0] + y*mR[1][0] + z*mR[2][0] + t[0];
double resY = x*mR[0][1] + y*mR[1][1] + z*mR[2][1] + t[1];
double resZ = x*mR[0][2] + y*mR[1][2] + z*mR[2][2] + t[2];
I got a worse result:
secondMMethod

Another thing to check is the order of application of the rotation & translation.

You can side-step this issue by using a 4x4 matrix for the whole transform.

Are you using a matrix library of some sort?

If you want to download/trace through an example that works, CloudComapre uses this library in its E57 handling. It is using a 4x4 matrix.

I have some news !
In fact this calculation is right, the pb is that the values XYZ has not the same scaling as the translate parameters, so the translation is invisible !

The pb should come from data extraction : I receive data in [1000;5000] range , whereas translation is in [1;5].
What do you advise for this function ?
SourceDestBuffer(imf, "cartesianX", x, N,...)

(It looks like you're working with one of the pumpA example files - can you attach it?)

Sounds like maybe the cartesian coordinates are stored as scaled integers? The point prototype for the scan will look something like this in the file's XML:

<cartesianX type="ScaledInteger" minimum="0" maximum="32767" scale="1e-003"/>
<cartesianY type="ScaledInteger" minimum="0" maximum="32767" scale="1e-003"/>
<cartesianZ type="ScaledInteger" minimum="0" maximum="32767" scale="1e-003"/>

So if it is a scaled integer node, you'll need to read it as such using ScaledIntegerNode and apply the scaling.

You can see where CloudCompare does this here.

You might also want to take a look at the E57 Simple API referenced in the comment there. It provides a higher-level interface that might help you with your implementation.

That's it !
I'm using this file:
pump.e57

which has effectively scaledinteger for cartesianXYZ.
Maybe I have to change reading method to an other..

So, the solution:

when you build your SourceDestBuffer, ensure to activate the auto-scaling on cartesian values!

vector<SourceDestBuffer> destBuffers;
double x[N]; destBuffers.push_back(SourceDestBuffer(imf, "cartesianX", x, N, true, true));
double y[N]; destBuffers.push_back(SourceDestBuffer(imf, "cartesianY", y, N, true, true));
double z[N]; destBuffers.push_back(SourceDestBuffer(imf, "cartesianZ", z, N, true, true));

Thanks for your help!

Great! Thank you for posting your solution as well.

@jean-noelp I already make a tool to do that XDD...
https://github.com/dogod621/E57Converter

PC