Skip to content

Commit a9c0729

Browse files
authored
Merge pull request #16 from dancergraham/feature/color
feature: Now in technicolor! πŸ³οΈβ€πŸŒˆ
2 parents 0809775 + 63421ac commit a9c0729

5 files changed

Lines changed: 56 additions & 15 deletions

File tree

β€ŽCargo.tomlβ€Ž

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "e57-python"
3-
version = "0.1.0-a4"
3+
version = "0.1.0-a5"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

β€ŽREADME.mdβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ This python library wraps the [rust e57 library](https://github.com/cry-inc/e57)
1010

1111
- [x] Proof of concept xml reading
1212
- [x] Read e57 point coordinates to numpy array - see `read_points` method.
13+
- [x] Read color field to numpy array.
1314
- [ ] Read intensity and other fields to numpy array.
1415
- [ ] Write to e57 (format ?)
1516

β€Žsrc/lib.rsβ€Ž

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,18 @@ use std::fs::File;
22
use std::io::BufReader;
33

44
use ::e57::{CartesianCoordinate, E57Reader};
5-
use ndarray::Ix2;
6-
use numpy::{PyArray};
5+
use ndarray::{Ix2};
6+
use numpy::PyArray;
77
use pyo3::prelude::*;
88

9+
#[pyclass]
10+
pub struct E57 {
11+
#[pyo3(get)]
12+
pub points: Py<PyArray<f64, Ix2>>,
13+
#[pyo3(get)]
14+
pub color: Py<PyArray<f32, Ix2>>,
15+
}
16+
917
/// Extracts the xml contents from an e57 file.
1018
#[pyfunction]
1119
fn raw_xml(filepath: &str) -> PyResult<String> {
@@ -26,7 +34,7 @@ fn raw_xml(filepath: &str) -> PyResult<String> {
2634

2735
/// Extracts the point data from an e57 file.
2836
#[pyfunction]
29-
fn read_points<'py>(py: Python<'py>, filepath: &str) -> PyResult<&'py PyArray<f64, Ix2>> {
37+
unsafe fn read_points<'py>(py: Python<'py>, filepath: &str) -> PyResult<E57> {
3038
let file = E57Reader::from_file(filepath);
3139
let mut file = match file {
3240
Ok(file) => file,
@@ -38,8 +46,8 @@ fn read_points<'py>(py: Python<'py>, filepath: &str) -> PyResult<&'py PyArray<f6
3846
};
3947
let pc = file.pointclouds();
4048
let pc = pc.first().expect("files contain pointclouds");
41-
let ncols = 3;
42-
let mut vec = Vec::with_capacity(pc.records as usize * ncols);
49+
let mut color_vec = Vec::with_capacity(pc.records as usize * 3);
50+
let mut vec = Vec::with_capacity(pc.records as usize * 3);
4351
let mut nrows = 0;
4452
for pointcloud in file.pointclouds() {
4553
let mut iter = file
@@ -56,15 +64,31 @@ fn read_points<'py>(py: Python<'py>, filepath: &str) -> PyResult<&'py PyArray<f6
5664
vec.extend([x, y, z]);
5765
nrows += 1
5866
}
67+
// if let Some(intensity) = p.intensity{
68+
// vec.append(intensity as f64)
69+
// }
70+
if let Some(color) = p.color {
71+
color_vec.extend([color.red, color.green, color.blue])
72+
}
5973
}
6074
}
61-
62-
Ok(PyArray::from_vec(py, vec).reshape((nrows, ncols)).unwrap())
75+
if color_vec.len() == vec.len() {
76+
Ok(E57 {
77+
points: Py::from(PyArray::from_vec(py, vec).reshape((nrows, 3)).unwrap()),
78+
color: Py::from(PyArray::from_vec(py, color_vec).reshape((nrows, 3)).unwrap()),
79+
})
80+
} else {
81+
Ok(E57 {
82+
points: Py::from(PyArray::from_vec(py, vec).reshape((nrows, 3)).unwrap()),
83+
color: Py::from(PyArray::new(py, (0,3), false)),
84+
})
85+
}
6386
}
6487

6588
/// e57 pointcloud file reading.
6689
#[pymodule]
67-
fn e57(_py: Python, m: &PyModule) -> PyResult<()> {
90+
fn e57(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
91+
m.add_class::<E57>()?;
6892
m.add_function(wrap_pyfunction!(raw_xml, m)?)?;
6993
m.add_function(wrap_pyfunction!(read_points, m)?)?;
7094
Ok(())

β€Žtestdata/pipeSpherical.e57β€Ž

26 KB
Binary file not shown.

β€Žtests/test_e57.pyβ€Ž

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,30 @@ def test_raw_xml():
1010

1111
def test_read_points():
1212
pointcloud = e57.read_points(r"testdata/bunnyFloat.e57")
13-
assert isinstance(pointcloud, np.ndarray)
14-
assert len(pointcloud) == 30_571
13+
points = pointcloud.points
14+
assert isinstance(points, np.ndarray)
15+
assert len(points) == 30_571
16+
17+
18+
def test_read_spherical():
19+
pointcloud = e57.read_points(r"testdata/pipeSpherical.e57")
20+
points = pointcloud.points
21+
assert isinstance(points, np.ndarray)
22+
assert len(points) == 1_220
23+
24+
25+
def test_read_color():
26+
pointcloud = e57.read_points(r"testdata/pipeSpherical.e57")
27+
color = pointcloud.color
28+
assert isinstance(color, np.ndarray)
29+
assert len(color) == 1_220
1530

1631

1732
def test_box_dimensions():
1833
pointcloud: np.ndarray = e57.read_points(r"testdata/bunnyFloat.e57")
19-
max_coords = pointcloud.max(0, None, False, -np.inf)
20-
min_coords = pointcloud.min(0, None, False, np.inf)
34+
points = pointcloud.points
35+
max_coords = points.max(0, None, False, -np.inf)
36+
min_coords = points.min(0, None, False, np.inf)
2137
X, Y, Z = max_coords - min_coords
2238
assert X == pytest.approx(0.155698)
2339
assert Y == pytest.approx(0.14731)
@@ -26,8 +42,8 @@ def test_box_dimensions():
2642

2743
def test_global_box_center():
2844
pointcloud: np.ndarray = e57.read_points(r"testdata/bunnyFloat.e57")
29-
max_coords = pointcloud.max(0, None, False, -np.inf)
30-
min_coords = pointcloud.min(0, None, False, np.inf)
45+
max_coords = pointcloud.points.max(0, None, False, -np.inf)
46+
min_coords = pointcloud.points.min(0, None, False, np.inf)
3147
X, Y, Z = (max_coords + min_coords) / 2
3248
assert X == pytest.approx(-0.016840)
3349
assert Y == pytest.approx(0.113666)

0 commit comments

Comments
Β (0)