Page 1 of 2

PTS to LAS format conversion problem

Posted: Tue Feb 16, 2021 3:58 pm
by PSpohn
Hello everyone

I have encoutered a little problem while I tried to change the point clouds formats in CC command line mode from PTS to LAS/LAZ.

The PTS containing the following information:

X,Y,Z,Intensity

The problem is that the intensity values are imported as scalar values into a scalar field (SF). And when I save this imported PTS pointcloud as LAS the scalar values in the SF are not saved as LAS intensity values. Instead they are saved as an additional SF to the LAS and the LAS intensities are filled with zeros. So something like this:

X,Y,Z, Intensity (all 0.0), SF0 (with intensity values)

I would need the SF to be saved in the LAS as intensity instead:

X,Y,Z, Intensity (with intensity values)

I need the LAS to be saved in this way to read the LAS intensities with another tool I have.

I have found out that renaming the SF manually to "intensity" will do the trick. But this is not possible to do in command line mode.

Any help?!

Re: PTS to LAS format conversion problem

Posted: Sat Feb 20, 2021 4:55 pm
by daniel
Yes, it's because LAS expect scalar fields with particular names. And your intensity SF is probably named 'Intensity' by default.

What you can do is to add a comment line at the beginning of the PTS file with the names of the columns:

Code: Select all

#X Y Z Intensity
This should let CC deduce the right names/roles of each column.

Re: PTS to LAS format conversion problem

Posted: Wed Feb 24, 2021 4:47 pm
by PSpohn
Thanks for your help, Daniel!

I unterstand your idea how to solve my problem. But if I would do that, I currently would have to manually insert that line into every PTS file I have. And I have usually saveral PTS files to convert at once. That's also why I would like to do the PTS to LAS format conversion with the CC command line mode.

I saw in another forum topic, that someone suggested to implement a command line function to rename scalar fields. This would be a fabulous solution for my problem, too! Don't you think?

Or, a even better solution would be to name the first scalar automatically as "intensity", when CC imports a PTS file. As far as I know the sequence "X Y Z Intensity R G B" is the defined order of the PTS format.

Re: PTS to LAS format conversion problem

Posted: Wed Feb 24, 2021 10:59 pm
by WargodHernandez
@Daniel
What would you think about when saving to LAS looking for Intensity and falling back to intensity if it exists and Intensity does not?

I know I've seen this issue pop up a few times over the years.

Re: PTS to LAS format conversion problem

Posted: Thu Feb 25, 2021 5:03 pm
by PSpohn
daniel wrote: Sat Feb 20, 2021 4:55 pm What you can do is to add a comment line at the beginning of the PTS file with the names of the columns:

Code: Select all

#X Y Z Intensity
This should let CC deduce the right names/roles of each column.
Just btw.,
I cannot get this to work with a PTS file. Even not with a simple TXT file like this:

Code: Select all

#X Y Z Intensity
3537692.999 5151074.527 377.571 136.000
3537693.031 5151074.521 377.565 71.000
3537693.040 5151074.519 377.565 94.000
The intensity values remain labeled as "Scalar field" after the import.


In my opinion the best way would still be to label the fourth value in a PTS file as intensity (or to load them into a SF and name the SF "Intensity"). As daniel mentioned back in 2015 (see here: http://www.danielgm.net/cc/forum/viewtopic.php?t=1307) the fourth value is defined as intensity in a PTS file. And just btw, I also heard that Leica was the first one who defined the PTS format.

Or implement the command line function to rename scalar fields. I would favour this idea, because this might become handy for other problems, too :)

@WargodHernandez
Thanks for your support. And I can imagine you are rigth, that I am not the first one who points to this issue.
But I think your solution to check for intensities at the LAS export, might lead to more problems. LAS intensities must be saved as unsigned short (0 - 65535). Scalar values or intensities from other scources (like TXT, E57) might not be in that given range. That might also be the reason why Daniel choose to save them in the LAS under additional data.
In the current version (CC 2.12) all values out of that range (0-65535) are saved as 0, if you rename the scalar field to "Intensity" before the LAS export.

Re: PTS to LAS format conversion problem

Posted: Thu Feb 25, 2021 6:19 pm
by daniel
Oh, so I did a mistake, it was '//' instead of '#'.

But you are right, having a command line method to rename a scalar field would probably be the simplest solution in the end.

@Chris: the issue with auto assigning a scalar field to 'Intensity' is that LAS files have some expectations regarding intensity values (unsigned short integer). But on the other side, if the user does it on its side, I can't remember if we check the values either or if PDAL will complain if the values are outside of the specifications.

Re: PTS to LAS format conversion problem

Posted: Thu Feb 25, 2021 6:39 pm
by WargodHernandez
In the current version (CC 2.12) all values out of that range (0-65535) are saved as 0, if you rename the scalar field to "Intensity" before the LAS export.
if renaming the Scalar field doesn't solve your problem then changing its name at the time of import shouldn't fix anything, when we load something like intensity it gets loaded into a scalar field there isn't a specific Intensity field.

but if the range is 0-65535, we should include an option to remap the SF to that range we do something like that in the enhanceRGBWithIntensitySF function which remaps the SF to 0-255

Code: Select all

double newI = 255 * ((sf->getValue(i) - minI) / intRange); //in [0 ; 1]
intRange is just

Code: Select all

double intRange = maxI - minI;
we could easily do a similar function when saving to LAS first check for a intensity/Intensity scalar field and if it exists and max/min is outside the 0-65535 limits then remap before saving

Re: PTS to LAS format conversion problem

Posted: Thu Feb 25, 2021 6:48 pm
by WargodHernandez
@Daniel

they do not appear to check here is LASFWFFilter.cpp

Code: Select all

case LAS_INTENSITY:
					laspoint.set_intensity(static_cast<U16>(it->sf->getValue(i)));
					break;

and here is saving for LASFilter.cpp

Code: Select all

// standard las fields
		uint8_t classFlags = 0;
		uint8_t classification = 0;
		for (const LasField &lasField: fieldsToSave)
		{
			assert(lasField.sf);
			Id pdalId = typeToId(lasField.type, minPointFormat);
			switch (lasField.type)
			{
			case LAS_X:
			case LAS_Y:
			case LAS_Z:
			case LAS_RED:
			case LAS_GREEN:
			case LAS_BLUE:
				assert(false);
				break;
			case LAS_TIME:
				point.setField(pdalId, lasField.sf->getValue(ptsWritten) + lasField.sf->getGlobalShift());
				break;
			case LAS_CLASSIFICATION:
				classification = static_cast<uint8_t>(static_cast<int>(lasField.sf->getValue(ptsWritten)) & 255);
				break;
			case LAS_CLASSIF_VALUE:
				classification = static_cast<uint8_t>(static_cast<int>(lasField.sf->getValue(ptsWritten)) & (minPointFormat < 6 ? 31 : 255));
				break;
			case LAS_CLASSIF_SYNTHETIC:
				if (lasField.sf->getValue(ptsWritten) != 0)
				{
					if (minPointFormat < 6)
						classification |= 32; //bit #5 of the 'Classification' field
					else
						classFlags |= 1;      //bit #0 of the 'Classification Flags' field
				}
				break;
			case LAS_CLASSIF_KEYPOINT:
				if (lasField.sf->getValue(ptsWritten) != 0)
				{
					if (minPointFormat < 6)
						classification |= 64; //bit #6 of the 'Classification' field
					else
						classFlags |= 2;      //bit #1 of the 'Classification Flags' field
				}
				break;
			case LAS_CLASSIF_WITHHELD:
				if (lasField.sf->getValue(ptsWritten) != 0)
				{
					if (minPointFormat < 6)
						classification |= 128; //bit #7 of the 'Classification' field
					else
						classFlags |= 4;       //bit #2 of the 'Classification Flags' field
				}
				break;
			case LAS_CLASSIF_OVERLAP:
				if (lasField.sf->getValue(ptsWritten) != 0)
				{
					if (minPointFormat >= 6)
						classFlags |= 8;      //bit #3 of the 'Classification Flags' field
					else
						assert(false);
				}
				break;
			case LAS_INVALID:
				break;
			default:
				point.setField(pdalId, lasField.sf->getValue(ptsWritten));
				break;
			}
		}
It appears to use the default

Re: PTS to LAS format conversion problem

Posted: Thu Feb 25, 2021 7:10 pm
by WargodHernandez
@Daniel btw while looking at LASFilter.cpp I noticed the following at line #351

Code: Select all

	if (hasOffsetMetaData & ccGlobalShiftManager::NeedShift(bbMax - lasOffset))
Shouldn't that be the logical && instead of bitwise &


Edit: Nevermind this, my local copy was outdated, you changed that in commit 31d5c1d

Re: PTS to LAS format conversion problem

Posted: Thu Feb 25, 2021 9:39 pm
by daniel
@PSpohn I've added a -RENAME_SF {sf index} {sf name} option to the command line. I'll let you test it (with the latest 2.12.alpha version online).