» Home

  » News

  » E57 User Group

  » Downloads

  » Test Data

  » Bugs

  » Documentation

  » Feature Categories

  » Extensions

  » Example

  » Supporting Partners

  » Supporting Products

  » Consultants

  » Licenses

E57 Reader Best Practices

This is a list of the best practices on interpreting the E2807 standard and using the libE57 library.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Click each item to expand its details.

  • Using IntensityLimits
  • Since the intensity unit is unspecified it is necessary to normalize it into a known range before converting it into other usable forms.

    // SimpleAPI

    double intOffset = header.intensityLimits.intensityMinimum; double intRange = header.intensityLimits.intensityMaximum - intOffset; if(intRange <= 0.) intRange = 1.;

    // FoundationAPI

    double intensityMinimum = 0.; double intensityMaximum = 1.; if(scan.isDefined("intensityLimits")) { StructureNode intbox(scan.get("intensityLimits")); if( intbox.get("intensityMaximum").type() == E57_SCALED_INTEGER ) { intensityMaximum = (double) ScaledIntegerNode(intbox.get("intensityMaximum")).scaledValue(); intensityMinimum = (double) ScaledIntegerNode(intbox.get("intensityMinimum")).scaledValue(); } else if( intbox.get("intensityMaximum").type() == E57_FLOAT ){ intensityMaximum = FloatNode(intbox.get("intensityMaximum")).value(); intensityMinimum = FloatNode(intbox.get("intensityMinimum")).value(); } else if( intbox.get("intensityMaximum").type() == E57_INTEGER) { intensityMaximum = (double) IntegerNode(intbox.get("intensityMaximum")).value(); intensityMinimum = (double) IntegerNode(intbox.get("intensityMinimum")).value(); } } double intOffset = intensityMinimum; double intRange = intensityMaximum - intOffset; if(intRange <= 0.) intRange = 1.; ... //for each point this converts intensity data into 0. to 1. range double intensity = (intensityData[i] - intOffset) / intRange;
  • Using ColorLimits
  • Since the color unit is unspecified it is necessary to convert it into a known range before using it.

    // SimpleAPI

    double redOffset = header.colorLimits.colorRedMinimum; double redRange = header.colorLimits.colorRedMaximum - redOffset; if(redRange <= 0.) redRange = 1.; double greenOffset = header.colorLimits.colorGreenMinimum; double greenRange = header.colorLimits.colorGreenMaximum - greenOffset; if(greenRange <= 0.) greenRange = 1.; double blueOffset = header.colorLimits.colorBlueMinimum; double blueRange = header.colorLimits.colorBlueMaximum - blueOffset; if(blueRange <= 0.) blueRange = 1.;

    // FoundationAPI

    double colorRedMinimum = 0.; double colorRedMaximum = 0.; double colorGreenMinimum = 0.; double colorGreenMaximum = 0.; double colorBlueMinimum = 0.; double colorBlueMaximum = 0.; if(scan.isDefined("colorLimits")) { StructureNode colorbox(scan.get("colorLimits")); if( colorbox.get("colorRedMaximum").type() == E57_SCALED_INTEGER ) { colorRedMaximum = (double) ScaledIntegerNode(colorbox.get("colorRedMaximum")).scaledValue(); colorRedMinimum = (double) ScaledIntegerNode(colorbox.get("colorRedMinimum")).scaledValue(); colorGreenMaximum = (double) ScaledIntegerNode(colorbox.get("colorGreenMaximum")).scaledValue(); colorGreenMinimum = (double) ScaledIntegerNode(colorbox.get("colorGreenMinimum")).scaledValue(); colorBlueMaximum = (double) ScaledIntegerNode(colorbox.get("colorBlueMaximum")).scaledValue(); colorBlueMinimum = (double) ScaledIntegerNode(colorbox.get("colorBlueMinimum")).scaledValue(); } else if( colorbox.get("colorRedMaximum").type() == E57_FLOAT ){ colorRedMaximum = FloatNode(colorbox.get("colorRedMaximum")).value(); colorRedMinimum = FloatNode(colorbox.get("colorRedMinimum")).value(); colorGreenMaximum = FloatNode(colorbox.get("colorGreenMaximum")).value(); colorGreenMinimum = FloatNode(colorbox.get("colorGreenMinimum")).value(); colorBlueMaximum = FloatNode(colorbox.get("colorBlueMaximum")).value(); colorBlueMinimum = FloatNode(colorbox.get("colorBlueMinimum")).value(); } else if( colorbox.get("colorRedMaximum").type() == E57_INTEGER) { colorRedMaximum = (double) IntegerNode(colorbox.get("colorRedMaximum")).value(); colorRedMinimum = (double) IntegerNode(colorbox.get("colorRedMinimum")).value(); colorGreenMaximum = (double) IntegerNode(colorbox.get("colorGreenMaximum")).value(); colorGreenMinimum = (double) IntegerNode(colorbox.get("colorGreenMinimum")).value(); colorBlueMaximum = (double) IntegerNode(colorbox.get("colorBlueMaximum")).value(); colorBlueMinimum = (double) IntegerNode(colorbox.get("colorBlueMinimum")).value(); } } double redOffset = colorRedMinimum; double redRange = colorRedMaximum - redOffset; if(redRange <= 0.) redRange = 1.; double greenOffset = colorGreenMinimum; double greenRange = colorGreenMaximum - greenOffset; if(greenRange <= 0.) greenRange = 1.; double blueOffset = colorBlueMinimum; double blueRange = colorBlueMaximum - blueOffset; if(blueRange <= 0.) blueRange = 1.; ... //for each point this converts color data into 0 to 255 range UCHAR red = (UCHAR) (((redData[i] - redOffset) * 255) / redRange); UCHAR green = (UCHAR) (((greenData[i] - greenOffset) * 255) / greenRange); UCHAR blue = (UCHAR) (((blueData[i] - blueOffset) * 255) / blueRange);
  • Using IndexBounds for Structured Size
  • The structure point cloud size can be calculated using the following:

    // SimpleAPI

    int nSizeRows = header.indexBounds.rowMaximum - header.indexBounds.rowMinimum + 1; int nSizeColumns = header.indexBounds.columnMaximum - header.indexBounds.columnMinimum + 1; int nSizeReturns = header.indexBounds.returnMaximum - header.indexBounds.returnMinimum + 1;

    // FoundationAPI

    int rowMaximum = 0.; int rowMinimum = 0.; int columnMaximum = 0.; int columnMinimum = 0.; int returnMaximum = 0.; int returnMinimum = 0.; if(scan.isDefined("indexBounds")) { StructureNode ibox(scan.get("indexBounds")); if(ibox.isDefined("rowMaximum")) { rowMinimum = IntegerNode(ibox.get("rowMinimum")).value(); rowMaximum = IntegerNode(ibox.get("rowMaximum")).value(); } if(ibox.isDefined("columnMaximum")) { columnMinimum = IntegerNode(ibox.get("columnMinimum")).value(); columnMaximum = IntegerNode(ibox.get("columnMaximum")).value(); } if(ibox.isDefined("returnMaximum")) { returnMinimum = IntegerNode(ibox.get("returnMinimum")).value(); returnMaximum = IntegerNode(ibox.get("returnMaximum")).value(); } } int nSizeRows = rowMaximum - rowMinimum + 1; int nSizeColumns = columnMaximum - columnMinimum + 1; int nSizeReturns = returnMaximum - returnMinimum + 1;
  • Converting Quaternion to Matrix
  • Creating a rotation matrix from a quaternion:

    // SimpleAPI

    double w = header.pose.rotation.w; double x = header.pose.rotation.x; double y = header.pose.rotation.y; double z = header.pose.rotation.z;

    // FoundationAPI

    double w,x,y,z; if(scan.isDefined("pose")) { StructureNode pose(scan.get("pose")); if(pose.isDefined("rotation")) { StructureNode rotation(pose.get("rotation")); w = FloatNode(rotation.get("w")).value(); x = FloatNode(rotation.get("x")).value(); y = FloatNode(rotation.get("y")).value(); z = FloatNode(rotation.get("z")).value(); } } // make rotation matrix double mat[3][3]; mat[0][0] = 1. - 2.*y*y - 2.*z*z; mat[0][1] = 2.*x*y - 2.*z*w; mat[0][2] = 2.*x*z + 2.*y*w; mat[1][0] = 2.*x*y + 2.*z*w; mat[1][1] = 1. - 2.*x*x - 2.*z*z; mat[1][2] = 2.*y*z - 2.*x*w; mat[2][0] = 2.*x*z - 2.*y*w; mat[2][1] = 2.*y*z + 2.*x*w; mat[2][2] = 1. - 2.*x*x - 2.*y*y;
  • Reading Guids
  • If the E57 writer used a windows GUID then this code will recover the GUID structure from a string.

    #if defined(_MSC_VER)	// Only for windows
    	GUID guid;	//--- Here is the GUID
    	
    

    // FoundationAPI

    ustring guid = StringNode(root_.get("guid")).value(); CString strGuid = (_bstr_t) guid; //converts char to wchar

    // SimpleAPI

    CString strGuid = (_bstr_t) header.guid.c_str(); //add {} if missing CString winGuid = strGuid; if( strGuid[0] != _T('{')) { winGuid = _T("{"); winGuid += strGuid; winGuid += _T("}"); } //looking for a string like "{6A91E935-5559-477b-BE0C-7CE4E0BDFB7C}" if( winGuid.GetLength() == 38 && winGuid[0] == _T('{') && winGuid[9] == _T('-') && winGuid[14] == _T('-') && winGuid[19] == _T('-') && winGuid[24] == _T('-') && winGuid[37] == _T('}')) { //convert to GUID HRESULT hr = IIDFromString((LPOLESTR) winGuid,(LPIID) &guid); } else { ... //guid is not a windows guid } #else //Non-windows # include "boost/uuid/uuid.hpp" # include "boost/uuid/uuid_generators.hpp" # include "boost/uuid/uuid_io.hpp" boost::uuids::uuid Uuid; //--- Here is the UUID

    // FoundationAPI

    string strUuid = StringNode(root_.get("guid")).value();

    // SimpleAPI

    string strUuid = header.guid.c_str(); //remove {} if present if(strUuid[0] == '{') strUuid.erase(1,0); //looking for a string like "6A91E935-5559-477b-BE0C-7CE4E0BDFB7C" if( strUuid.length() >= 36 && strUuid[8] == '-' && strUuid[13] == '-' && strUuid[18] == '-' && strUuid[23] == '-') { //convert to uuid std::stringstream ss; ss << strUuid; ss >> Uuid; } else { ... //uuid is not a windows guid } #endif
  • Reading GPS Date Time
  • Use this code to read the GPS Date Time fields.

    // SimpleAPI

    #if defined(_MSC_VER) // Only for windows SYSTEMTIME fileDateTime; header.acquisitionStart.dateTimeValue.GetSystemTime(fileDateTime); #else int year, month, day, hour, minute; float seconds; header.acquisitionStart.dateTimeValue.GetUTCDateTime(year, month, day, hour, minute, seconds); #endif

    // FoundationAPI

    double dateTimeValue = 0.; int isAtomicClockReferenced = 0; if(scan.isDefined("acquisitionStart")) { StructureNode acquisitionStart(scan.get("acquisitionStart")); dateTimeValue = FloatNode(acquisitionStart.get("dateTimeValue")).value(); isAtomicClockReferenced = (int32_t) IntegerNode(acquisitionStart.get("isAtomicClockReferenced")).value(); } #ifdef _C_TIMECONV_H_ unsigned short utc_year; //Universal Time Coordinated [year] unsigned char utc_month; //1-12 months unsigned char utc_day; //1-31 days unsigned char utc_hour; //hours unsigned char utc_minute; //minutes float utc_seconds;//seconds unsigned short gps_week; //GPS week (0-1024+) double gps_tow; //GPS time of week(0-604800.0) seconds gps_week = ((int)floor(dateTimeValue))/604800; gps_tow = dateTimeValue - gps_week*604800.; bool result = TIMECONV_GetUTCTimeFromGPSTime( gps_week, gps_tow, &utc_year, &utc_month, &utc_day, &utc_hour, &utc_minute, &utc_seconds); #endif
  • Reading Scaled Integers
  • Use this code to recover the scaled integer point data range. However, the point data has already been converted when you get the data.

    // SimpleAPI

    double pointRangeScaledInteger = header.pointFields.pointRangeScaledInteger; double pointRangeMinimum = header.pointFields.pointRangeMinimum; double pointRangeMaximum = header.pointFields.pointRangeMaximum;

    // FoundationAPI

    double pointRangeScaledInteger = 0.; //FloatNode double pointRangeMinimum = 0.; double pointRangeMaximum = 0.; if( proto.isDefined("cartesianX")) { if( proto.get("cartesianX").type() == E57_SCALED_INTEGER ) { double scale = ScaledIntegerNode(proto.get("cartesianX")).scale(); double offset = ScaledIntegerNode(proto.get("cartesianX")).offset(); int64_t minimum = ScaledIntegerNode(proto.get("cartesianX")).minimum(); int64_t maximum = ScaledIntegerNode(proto.get("cartesianX")).maximum(); pointRangeMinimum = (double) minimum * scale + offset; pointRangeMaximum = (double) maximum * scale + offset; pointRangeScaledInteger = scale; } else if( proto.get("cartesianX").type() == E57_FLOAT ) { pointRangeMinimum = FloatNode(proto.get("cartesianX")).minimum(); pointRangeMaximum = FloatNode(proto.get("cartesianX")).maximum(); } }
  • Reading Cartesian Bounds
  • Use this code to read the CartesianBounds data.

    FoundationAPI

    double MaxX,MinX,MaxY,MinY,MaxZ,MinZ; if(scan.isDefined("cartesianBounds")) { StructureNode bbox(scan.get("cartesianBounds")); if( bbox.get("xMinimum").type() == E57_SCALED_INTEGER ) { MinX = (double) ScaledIntegerNode(bbox.get("xMinimum")).scaledValue(); MaxX = (double) ScaledIntegerNode(bbox.get("xMaximum")).scaledValue(); MinY = (double) ScaledIntegerNode(bbox.get("yMinimum")).scaledValue(); MaxY = (double) ScaledIntegerNode(bbox.get("yMaximum")).scaledValue(); MinZ = (double) ScaledIntegerNode(bbox.get("zMinimum")).scaledValue(); MaxZ = (double) ScaledIntegerNode(bbox.get("zMaximum")).scaledValue(); } else if( bbox.get("xMinimum").type() == E57_INTEGER ) { MinX = (double) IntegerNode(bbox.get("xMinimum")).value(); MaxX = (double) IntegerNode(bbox.get("xMaximum")).value(); MinY = (double) IntegerNode(bbox.get("yMinimum")).value(); MaxY = (double) IntegerNode(bbox.get("yMaximum")).value(); MinZ = (double) IntegerNode(bbox.get("zMinimum")).value(); MaxZ = (double) IntegerNode(bbox.get("zMaximum")).value(); } else if( bbox.get("xMinimum").type() == E57_FLOAT ){ MinX = FloatNode(bbox.get("xMinimum")).value(); MaxX = FloatNode(bbox.get("xMaximum")).value(); MinY = FloatNode(bbox.get("yMinimum")).value(); MaxY = FloatNode(bbox.get("yMaximum")).value(); MinZ = FloatNode(bbox.get("zMinimum")).value(); MaxZ = FloatNode(bbox.get("zMaximum")).value(); } }

    SimpleAPI

    double MinX = header.cartesianBounds.xMinimum; double MaxX = header.cartesianBounds.xMaximum; double MinY = header.cartesianBounds.yMinimum; double MaxY = header.cartesianBounds.yMaximum; double MinZ = header.cartesianBounds.zMinimum; double MaxZ = header.cartesianBounds.zMaximum;
  • Reading Spherical Bounds
  • Use this code to read the SphericalBounds data.

    FoundationAPI

    double MaxRange,MinRange,AzimuthStart,AzimuthEnd,MaxElevation,MinElevation; if(scan.isDefined("sphericalBounds")) { StructureNode sbox(scan.get("sphericalBounds")); if( sbox.get("rangeMinimum").type() == E57_SCALED_INTEGER ) { MinRange = (double) ScaledIntegerNode(sbox.get("rangeMinimum")).scaledValue(); MaxRange = (double) ScaledIntegerNode(sbox.get("rangeMaximum")).scaledValue(); } else if( sbox.get("rangeMinimum").type() == E57_FLOAT ){ MinRange = FloatNode(sbox.get("rangeMinimum")).value(); MaxRange = FloatNode(sbox.get("rangeMaximum")).value(); } if( sbox.get("elevationMinimum").type() == E57_SCALED_INTEGER ) { MinElevation = (double) ScaledIntegerNode(sbox.get("elevationMinimum")).scaledValue(); MaxElevation = (double) ScaledIntegerNode(sbox.get("elevationMaximum")).scaledValue(); } else if( sbox.get("elevationMinimum").type() == E57_FLOAT ){ MinElevation = FloatNode(sbox.get("elevationMinimum")).value(); MaxElevation = FloatNode(sbox.get("elevationMaximum")).value(); } if( sbox.get("azimuthStart").type() == E57_SCALED_INTEGER ) { AzimuthStart = (double) ScaledIntegerNode(sbox.get("azimuthStart")).scaledValue(); AzimuthEnd = (double) ScaledIntegerNode(sbox.get("azimuthEnd")).scaledValue(); } else if( sbox.get("azimuthStart").type() == E57_FLOAT ){ AzimuthStart = FloatNode(sbox.get("azimuthStart")).value(); AzimuthEnd = FloatNode(sbox.get("azimuthEnd")).value(); } }

    SimpleAPI

    double MinRange = header.sphericalBounds.rangeMinimum; double MaxRange = header.sphericalBounds.rangeMaximum; double MinElevation = header.sphericalBounds.elevationMinimum; double MaxElevation = header.sphericalBounds.elevationMaximum; double AzimuthStart = header.sphericalBounds.azimuthStart; double AzimuthEnd = header.sphericalBounds.azimuthEnd;
  • Reading Return Index
  • The standard states that returnIndex is zero based. That is, 0 is the first return, 1 is the second, and so on. Shall be in the interval [0, returnCount - 1]. That means the indexBounds.returnMinimum will always be 0.

    The returnCount is the total number of returns for the pulse that is corresponds to. Shall be in the interval of [0, indexBounds.returnMaximum + 1]. This means the a returnCount of 0 is an invalid point.

    So for a scanner returning upto 3 return points, the returnIndex,returnCount would be 0,3 for the first return, 1,3 for the second return, and 2,3 for the last return. indexBounds.returnMinimum will be 0 and indexBounds.returnMaximum will be 2;

    	int nSize = 1024;  //buffer size;
    	int8_t *pReturnIndex = NULL;
    	int8_t *pReturnCount = NULL;
    

    // SimpleAPI

    int returnMaximum = 0.; int returnMinimum = 0.; if(header.pointFields.returnIndexField) { pReturnIndex = new int8_t[nSize]; returnMinimum = (int) header.indexBounds.returnMinimum; returnMaximum = (int) header.indexBounds.returnMaximum; } if(header.pointFields.returnCountField) pReturnCount = new int8_t[nSize]; ... e57::CompressedVectorReader reader = eReader->SetUpData3DPointsData( scanIndex, // data block index given by the NewData3D nSize, // size of each element buffer. pCartesianX, // pointer to a buffer with the X coordinate (in meters) of the point in Cartesian coordinates pCartesianY, // pointer to a buffer with the Y coordinate (in meters) of the point in Cartesian coordinates pCartesianZ, // pointer to a buffer with the Z coordinate (in meters) of the point in Cartesian coordinates pXYZInvalid, // Value = 0 if the point is considered valid, 1 or 2 otherwise pIntData, // pointer to a buffer with the Point response intensity. Unit is unspecified pIntInvalid, // Value = 0 if the intensity is considered valid, 1 otherwise pRedData, // pointer to a buffer with the Red color coefficient. Unit is unspecified pGreenData, // pointer to a buffer with the Green color coefficient. Unit is unspecified pBlueData, // pointer to a buffer with the Blue color coefficient. Unit is unspecified pColorInvalid, // Value = 0 if the color is considered valid, 1 otherwise pRangeData, // pointer to a buffer with the range (in meters) of points in spherical coordinates. Shall be non-negative pAzimuthData, // pointer to a buffer with the Azimuth angle (in radians) of point in spherical coordinates pElevationData, // pointer to a buffer with the Elevation angle (in radians) of point in spherical coordinates pRAEInvalid, // Value = 0 if the range is considered valid, 1 otherwise pRowIndex, // pointer to a buffer with the row number of point (zero based). // This is useful for data that is stored in a regular grid. Shall be in the interval (0, 2^63). pColumnIndex, // pointer to a buffer with the column number of point (zero based). // This is useful for data that is stored in a regular grid. Shall be in the interval (0, 2^63). pReturnIndex, // pointer to a buffer with the number of this return (zero based). // That is, 0 is the first return, 1 is the second, and so on. Shall be in the interval (0, returnCount). // Only for multi-return sensors. pReturnCount, // pointer to a buffer with the total number of returns for the pulse that this corresponds to. // Shall be in the interval (0, 2^63). Only for multi-return sensors. pTimeStamp, // pointer to a buffer with the time (in seconds) since the start time for the data, // which is given by acquisitionStart in the parent Data3D Structure. Shall be non-negative pTimeInvalid // Value = 0 if the timeStamp is considered valid, 1 otherwise ); ...

    // FoundationAPI

    StructureNode scan(data3D_.get(dataIndex)); int returnMaximum = 0.; int returnMinimum = 0.; if(scan.isDefined("indexBounds")) { StructureNode ibox(scan.get("indexBounds")); ... if(ibox.isDefined("returnMaximum")) { returnMinimum = IntegerNode(ibox.get("returnMinimum")).value(); returnMaximum = IntegerNode(ibox.get("returnMaximum")).value(); } } CompressedVectorNode points(scan.get("points")); StructureNode prototype(points.prototype()); ... int64_t protoCount = prototype.childCount(); int64_t protoIndex; vector destBuffers; for( protoIndex = 0; protoIndex < protoCount; protoIndex++) { ustring name = prototype.get(protoIndex).elementName(); ... if((name.compare("returnIndex") == 0) && prototype.isDefined("returnIndex")) { pReturnIndex = new int8_t[count]; destBuffers.push_back(SourceDestBuffer(imf_, "returnIndex", pReturnIndex, (unsigned) count, true, false)); } else if((name.compare("returnCount") == 0) && prototype.isDefined("returnCount")) { pReturnCount = new int8_t[count]; destBuffers.push_back(SourceDestBuffer(imf_, "returnCount", pReturnCount, (unsigned) count, true, false)); } ... } CompressedVectorReader reader = points.reader(destBuffers); ...

    // Both

    unsigned size = 0; while(size = reader.read()) { for(long i = 0; i < size; i++) { Point P(pCartesianX[i],pCartesianY[i],pCartesianZ[i]); if(pReturnIndex) { int ret = (pReturnIndex[i] - returnMinimum); //cannot deal with more than one return per point // use only the first return if(ret > 0) continue; //discard other returns // or use only the last return if(ret != (pReturnCount[i] - 1)) continue; //discard all others ... //use this point } ... } }






















This site is © Copyright 2010 E57.04 3D Imaging System File Format Committee, All Rights Reserved