Skip to content

Commit fbf6598

Browse files
author
dcyoung
committed
Added binary PLY support and support for variable CSV column formats
1 parent 448662d commit fbf6598

File tree

6 files changed

+160
-17
lines changed

6 files changed

+160
-17
lines changed

Assets/Scripts/CSVReader.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ public static List<Vector4> ReadPoints(string file)
2626
if (numPoints <= 0)
2727
return null;
2828

29+
// check if the .csv contains a column for scan index
30+
string header = lines[0];
31+
int offset = header.Contains("SCAN_INDEX") ? 1 : 0;
32+
bool bContainsSigStrength = header.Contains("SIGNAL_STRENGTH");
33+
2934
List<Vector4> points = new List<Vector4>();
3035
float x, y, z, normalizedSignalStrength;
3136
for (int i = 0; i < numPoints; i++)
@@ -35,11 +40,12 @@ public static List<Vector4> ReadPoints(string file)
3540

3641
// Read the position
3742
// convert from the centimeters to meters, and flip z and y (RHS to LHS)
38-
x = 0.01f * float.Parse(row[1]);
39-
z = 0.01f * float.Parse(row[2]);
40-
y = 0.01f * float.Parse(row[3]);
43+
x = 0.01f * float.Parse(row[0 + offset]);
44+
z = 0.01f * float.Parse(row[1 + offset]);
45+
y = 0.01f * float.Parse(row[2 + offset]);
46+
4147
// Read the signal strength and normalize it. ie: [0 : 254] => [0.0f : 1.0f]
42-
normalizedSignalStrength = float.Parse(row[4]) / 254.0f;
48+
normalizedSignalStrength = bContainsSigStrength ? (float.Parse(row[3 + offset]) / 254.0f) : 0.75f;
4349

4450
// Package the position and signal strength into a single 4 element vector
4551
points.Add(new Vector4(x, y, z, normalizedSignalStrength));

Assets/Scripts/PLYReader.cs

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using System;
2+
using System.IO;
3+
using System.Collections;
4+
using System.Collections.Generic;
5+
using System.Text.RegularExpressions;
6+
using UnityEngine;
7+
8+
/// <summary>
9+
/// Naively parses a ply file of an expected structure into an array of Vector4 objects.
10+
/// Expected structure is vertices with following attributes ONLY: float x, float y, float z, u_char signal_strength
11+
/// Each Vector4 represents a point, where the x,y,z components are the position and the w component holds normalized signal strength.
12+
/// Handles the conversion from centimeters to meters.
13+
/// Handles the conversion from right handed coordinates where z is up, to unity's left handed coordinates where y is up.
14+
/// </summary>
15+
public class PLYReader
16+
{
17+
public static List<Vector4> ReadPoints(string file)
18+
{
19+
// Check file exists
20+
if (!File.Exists(file))
21+
{
22+
return new List<Vector4>();
23+
}
24+
25+
try
26+
{
27+
// Interpret File
28+
using (BinaryReader reader = new BinaryReader(File.Open(file, FileMode.Open)))
29+
{
30+
int fileLength = (int)reader.BaseStream.Length;
31+
string buildingLine = "";
32+
int vertexCount = 0;
33+
int charSize = sizeof(char);
34+
35+
// read the header
36+
int numRead = 0;
37+
while ((numRead += charSize) < fileLength)
38+
{
39+
char nextChar = reader.ReadChar();
40+
if (nextChar == '\n')
41+
{
42+
if (buildingLine.Contains("end_header"))
43+
{
44+
break;
45+
}
46+
else if (buildingLine.Contains("element vertex"))
47+
{
48+
string[] array = Regex.Split(buildingLine, @"\s+");
49+
if (array.Length - 2 > 0)
50+
{
51+
int target = Array.IndexOf(array, "vertex") + 1;
52+
vertexCount = Convert.ToInt32(array[target]);
53+
buildingLine = "";
54+
}
55+
else
56+
{
57+
return new List<Vector4>();
58+
}
59+
}
60+
}
61+
else
62+
{
63+
buildingLine += nextChar;
64+
}
65+
}
66+
67+
// Read the vertices
68+
List<Vector4> points = new List<Vector4>();
69+
float x, y, z, normalizedSignalStrength;
70+
for (int i = 0; i < vertexCount; i++)
71+
{
72+
// Read the position
73+
// convert from the centimeters to meters, and flip z and y (RHS to LHS)
74+
x = 0.01f * reader.ReadSingle();
75+
z = 0.01f * reader.ReadSingle();
76+
y = 0.01f * reader.ReadSingle();
77+
78+
// Read the signal strength and normalize it. ie: [0 : 254] => [0.0f : 1.0f]
79+
normalizedSignalStrength = 1.0f * reader.ReadByte() / 254.0f;
80+
81+
// Package the position and signal strength into a single 4 element vector
82+
points.Add(new Vector4(x, y, z, normalizedSignalStrength));
83+
}
84+
return points;
85+
}
86+
}
87+
catch (Exception e)
88+
{
89+
return new List<Vector4>();
90+
}
91+
}
92+
}

Assets/Scripts/PLYReader.cs.meta

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Scripts/PointCloudGenerator.cs

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,36 @@ public class PointCloudGenerator : MonoBehaviour
2828
void Awake()
2929
{
3030
// Open a native file dialog, so the user can specify the file location
31-
SFB.ExtensionFilter[] extensions = new[] { new ExtensionFilter("Point Cloud Files", "csv") };
31+
SFB.ExtensionFilter[] extensions = new[] { new ExtensionFilter("Point Cloud Files", "csv", "ply") };
3232
string[] paths = StandaloneFileBrowser.OpenFilePanel("Open File", "", extensions, false);
3333
if (paths.Length < 1)
3434
{
35-
print("Error: Please specify a CSV file.");
35+
print("Error: Please specify a properly formatted CSV or binary PLY file.");
3636
Application.Quit();
3737
return;
3838
}
3939

40+
string filePath = paths[0];
41+
string[] parts = filePath.Split('.');
42+
string extension = parts[parts.Length - 1];
43+
4044
// Read the points from the file
41-
data = CSVReader.ReadPoints(paths[0]);
45+
switch (extension)
46+
{
47+
case "csv":
48+
case "CSV":
49+
data = CSVReader.ReadPoints(filePath);
50+
break;
51+
case "ply":
52+
case "PLY":
53+
data = PLYReader.ReadPoints(filePath);
54+
break;
55+
default:
56+
print("Error: Please specify a properly formatted CSV or binary PLY file.");
57+
Application.Quit();
58+
return;
59+
}
60+
4261
numPoints = data.Count;
4362
if (numPoints <= 0)
4463
{
@@ -68,7 +87,7 @@ void Awake()
6887
for (int j = 0; j < numPointsPerCloud; j++)
6988
{
7089
// normalzied signal strength stored in the 4th component of the vector
71-
normalizedSignalStrength[j] = data[offset + j].w;
90+
normalizedSignalStrength[j] = data[offset + j].w * 0.3f;
7291

7392
// position stored in the first 3 elements of the vector (conversion handled by implicit cast)
7493
positions[j] = data[offset + j];

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
## 9/13/2017
4+
- Added ability to read binary `.ply` files.
5+
- Added support for `.csv` files with or without `SCAN_INDEX` and `SIGNAL_STRENGTH` columns.

README.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ Main Viewer | Miniature Viewer
1414
### Quickstart
1515
- Run the executable `Point_Cloud_Viewer.exe` or `Miniature_Point_Cloud_Viewer.exe`.
1616
- Once the app loads, a file browser should open automatically.
17-
- Select a CSV pointcloud file, with the expected format.
18-
- Wait for the application to parse the file and render the points.
17+
- Select a PLY (binary) or CSV pointcloud file, with the [expected format](#compatible-file-format).
18+
- Wait for the application to parse the file and render the points.
19+
> **Note:** binary `.ply` files parse significantly faster than `.csv`
1920
- For the main viewer, use `Q` & `E` to adjust the vertical position of the point cloud so the camera appears to be at head height, and the floor in the scan matches the ground plane. Then walk around inside the scan.
2021
- For the miniature viewer, experiment with the transform controls listed below to view the diorama.
2122
- To exit the application and close the executable, press `ESC`.
@@ -36,21 +37,26 @@ Main Viewer | Miniature Viewer
3637
- The scan will not appear until you put on the headset.
3738

3839
### Compatible file format
39-
Only files with the expected format will open correctly. Attempting to open unexpected files will terminate the unity application. The application expects `.csv` files downloaded from the `sweep-3d-scanner` or exported from the `Sweep Visualizer` desktop application.
40+
Only files with the expected format will open correctly. Attempting to open unexpected files will terminate the unity application. The application expects either `.csv` or `.ply (binary)` files downloaded from the `sweep-3d-scanner` or exported from the `Sweep Visualizer` desktop application.
4041

41-
If you want to convert custom data to make a compatible file, create a CSV file with the following format:
42+
If you want to convert custom data to make a compatible file, create a CSV file where the first line is a header, subsequent rows represent points, and the first 3 columns are `X`, `Y` and `Z`. Optionally, you can include a 4th column called `SIGNAL_STRENGTH`. Example file contents shown below.
4243
```csv
43-
SCAN_INDEX,X,Y,Z,SIGNAL_STRENGTH
44-
0,6.7,-124.2,-71.3,183
45-
0,6.7,-125.4,-69.2,187
44+
X,Y,Z
45+
6.7,-124.2,-71.3
46+
6.7,-125.4,-69.2
47+
...
48+
49+
or
50+
51+
X,Y,Z,SIGNAL_STRENGTH
52+
6.7,-124.2,-71.3,199
53+
6.7,-125.4,-69.2,199
4654
...
4755
```
4856

49-
- SCAN_INDEX: is the index of the 2D slice of a 3D scan. This is a reference to the data acquisition process used by the 3D scanner, and is NOT used by the unity viewer. If you are making a file from scratch, simply use 0 for every entry.
5057
- X,Y,Z: Integer or floating point values representing the x, y and z coordinates for each point. Units are in cm. Assumes a right handed coordinate system, where z is up. Note: this is NOT the same as unity's coordinate system, which is left handed with y up.
5158
- SIGNAL_STRENGTH: integer value between [0:254] indicating strength/confidence in the sensor reading. Higher values are stronger readings, lower values are weaker readings. The color of points in the viewer are determined by this value, by mapping the range [0:254] to the entirety of the HUE spectrum in HSV color space. If you are generating a custom file, and you do NOT have signal strength values for your data points, simply use the same value for every point.
5259

53-
5460
## Unity Project
5561
### Packages/Dependencies
5662
- `Standard Assets/Characters`: provides first person character controller
@@ -66,6 +72,9 @@ SCAN_INDEX,X,Y,Z,SIGNAL_STRENGTH
6672
- `Scripts/CSVReader`:
6773
- Naively parses a csv file of the expected structure into an array of `Vector4` objects.
6874
- Each `Vector4` represents a point, where the first 3 elements (x,y,z) are position, and the 4th (w) is normalized signal strength.
75+
- `Scripts/PLYReader`:
76+
- Naively parses a ply (binary) file of the expected structure into an array of `Vector4` objects.
77+
- Each `Vector4` represents a point, where the first 3 elements (x,y,z) are position, and the 4th (w) is normalized signal strength.
6978
- `Scripts/PointCloudGenerator`:
7079
- Requests the user select a `.csv` file.
7180
- Reads the points from the file.

0 commit comments

Comments
 (0)