Skip to content

Commit

Permalink
add workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
akaszynski committed Jul 19, 2023
1 parent 03e50c4 commit 5feccec
Show file tree
Hide file tree
Showing 6 changed files with 383 additions and 2 deletions.
73 changes: 73 additions & 0 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
name: Build and upload

on:
pull_request:
push:
tags:
- "*"
branches:
- "main"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
build_wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v3
with:
submodules: true

- name: Build wheels
uses: pypa/cibuildwheel@v2.14.1

- uses: actions/upload-artifact@v3
with:
path: ./wheelhouse/*.whl

build_sdist:
name: Build source distribution
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: true

- name: Build sdist
run: pipx run build --sdist

- uses: actions/upload-artifact@v3
with:
path: dist/*.tar.gz

upload_pypi:
needs: [build_wheels, build_sdist]
runs-on: ubuntu-latest
# upload to PyPI on every tag
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
# alternatively, to publish when a GitHub Release is created, use the following rule:
# if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/download-artifact@v3
with:
name: artifact
path: dist

- uses: pypa/gh-action-pypi-publish@v1.8.6
with:
user: __token__
password: ${{ secrets.PYPI_TOKEN }}

- name: Release
uses: softprops/action-gh-release@v1
with:
generate_release_notes: true
files: |
./dist/*.whl
5 changes: 5 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
include pyminiply/wrapper.hpp
include pyminiply/wrapper.pyx
include pyminiply/miniply/miniply.cpp
include pyminiply/miniply/miniply.h
global-exclude pyminiply/_wrapper.cpp
257 changes: 257 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
###########
pyminiply
###########

|pypi| |MIT|

.. |pypi| image:: https://img.shields.io/pypi/v/pyminiply.svg?logo=python&logoColor=white
:target: https://pypi.org/project/pyminiply/

.. |MIT| image:: https://img.shields.io/badge/License-MIT-yellow.svg
:target: https://opensource.org/licenses/MIT

``pyminiply`` is a Python library for rapidly reading PLY files. It is a
Python wrapper around the fast C++ PLY reading library provided by
`miniply <ehttps://github.com/vilya/miniply>`_. Thanks @vilya!

The main advantage of ``pyminiply`` over other PLY reading libraries is
its performance. See the benchmarks below for more details.

**************
Installation
**************

The recommended way to install ``pyminiply`` is via PyPI:

.. code:: sh
pip install pyminiply
You can also clone the repository and install it from source:

.. code:: sh
git clone https://github.com/pyvista/pyminiply.git
cd pyminiply
git submodule update --init --recursive
pip install .
*******
Usage
*******

Load in the vertices, indices, normals, UV, and color information from a
PLY file:

.. code:: pycon
>>> import pyminiply
>>> vertices, indices, normals, uv, color = pyminiply.read("example.ply")
>>> vertices
array([[ 5.0000000e-01, -5.0000000e-01, -5.5511151e-17],
[ 4.0000001e-01, -5.0000000e-01, -4.4408922e-17],
[ 3.0000001e-01, -5.0000000e-01, -3.3306692e-17],
...,
[-4.2500001e-01, 5.0000000e-01, 4.7184480e-17],
[-4.7499999e-01, 4.4999999e-01, 5.2735593e-17],
[-5.0000000e-01, 4.2500001e-01, 5.5511151e-17]], dtype=float32)
>>> indices
array([[ 0, 442, 441],
[ 442, 122, 443],
[ 443, 121, 441],
...,
[1677, 438, 1679],
[1679, 439, 1676],
[1677, 1679, 1676]], dtype=int32)
>>> normals
array([[-1.110223e-16, 0.000000e+00, -1.000000e+00],
[-1.110223e-16, 0.000000e+00, -1.000000e+00],
[-1.110223e-16, 0.000000e+00, -1.000000e+00],
...,
[-1.110223e-16, 0.000000e+00, -1.000000e+00],
[-1.110223e-16, 0.000000e+00, -1.000000e+00],
[-1.110223e-16, 0.000000e+00, -1.000000e+00]], dtype=float32)
>>> uv
array([[0. , 0. ],
[0.1 , 0. ],
[0.2 , 0. ],
...,
[0.92499995, 1. ],
[0.975 , 0.95 ],
[1. , 0.92499995]], dtype=float32)
>>> color
array([[ 0, 0, 0],
[ 0, 0, 0],
[ 0, 0, 0],
...,
[254, 254, 254],
[254, 254, 254],
[255, 255, 255]], dtype=uint8)
You can also read in the PLY file as a `PyVista
<https://github.com/pyvista>`_ PolyData and immediately plot it.

.. code:: pycon
>>> import pyminiply
>>> mesh = pyminiply.read_as_mesh("example.ply")
>>> mesh
PolyData (0x7f0653579c00)
N Cells: 200
N Points: 121
N Strips: 0
X Bounds: -5.000e-01, 5.000e-01
Y Bounds: -5.000e-01, 5.000e-01
Z Bounds: -5.551e-17, 5.551e-17
N Arrays: 2
>>> mesh.plot()
.. image:: https://github.com/pyvista/pyminiply/raw/main/demo.png

***********
Benchmark
***********

The main reason behind writing yet another PLY file reader for Python is
to leverage the highly performant ``miniply`` library.

There is already an benchmark demonstrating how ``miniply`` outperforms
in comparison to competing C and C++ libraries at `ply_io_benchmark
<https://github.com/mhalber/ply_io_benchmark>`_ when reading PLY files.
The benchmark here shows how ``pyminiply`` performs relative to other
Python PLY file readers.

Here are the timings from reading in a 1,000,000 point binary PLY file:

+-------------+-----------------+
| Library | Time (seconds) |
+=============+=================+
| pyminiply | 0.046 |
+-------------+-----------------+
| open3d | 0.149 |
+-------------+-----------------+
| PyVista | 0.409 |
| (VTK) | |
+-------------+-----------------+
| meshio | 0.512 |
+-------------+-----------------+
| plyfile | 8.939 |
+-------------+-----------------+

**Benchmark source:**

.. code:: python
import time
import numpy as np
import pyvista as pv
import pyminiply
import plyfile
import meshio
import open3d
filename = 'tmp.ply'
mesh = pv.Plane(i_resolution=999, j_resolution=999).triangulate()
mesh.clear_data()
mesh.save(filename)
# pyminiply
tstart = time.time()
pyminiply.read(filename)
tend = time.time() - tstart; print(f'pyminiply: {tend:.3f}')
# open3d
tstart = time.time()
open3d.io.read_point_cloud(filename)
tend = time.time() - tstart; print(f'open3d: {tend:.3f}')
# VTK/PyVista
tstart = time.time()
pv.read(filename)
tend = time.time() - tstart; print(f'VTK/PyVista: {tend:.3f}')
tstart = time.time()
meshio.read(filename)
tend = time.time() - tstart; print(f'meshio: {tend:.3f}')
# plyfile
tstart = time.time()
plyfile.PlyData.read(filename)
tend = time.time() - tstart; print(f'plyfile: {tend:.3f}')
Comparison with VTK and PyVista
===============================

Here's an additional benchmark comparing VTK/PyVista with ``pyminiply``:

.. code:: python
import numpy as np
import time
import pyvista as pv
import matplotlib.pyplot as plt
import pyminiply
times = []
filename = 'tmp.ply'
for res in range(50, 800, 50):
mesh = pv.Plane(i_resolution=res, j_resolution=res).triangulate().subdivide(2)
mesh.clear_data()
mesh.save(filename)
tstart = time.time()
pv_mesh = pv.read(filename)
vtk_time = time.time() - tstart
tstart = time.time()
ply_mesh = pyminiply.read_as_mesh(filename)
ply_reader_time = time.time() - tstart
assert np.allclose(pv_mesh['Normals'], ply_mesh['Normals'])
assert np.allclose(pv_mesh.points, ply_mesh.points)
assert np.allclose(pv_mesh._connectivity_array, ply_mesh._connectivity_array)
times.append([mesh.n_points, vtk_time, ply_reader_time])
print(times[-1])
times = np.array(times)
plt.figure(1)
plt.title('PLY load time')
plt.plot(times[:, 0], times[:, 1], label='VTK')
plt.plot(times[:, 0], times[:, 2], label='pyminiply')
plt.xlabel('Number of Points')
plt.ylabel('Time to Load (seconds)')
plt.legend()
plt.figure(2)
plt.title('PLY load time (Log-Log)')
plt.loglog(times[:, 0], times[:, 1], label='VTK')
plt.loglog(times[:, 0], times[:, 2], label='pyminiply')
plt.xlabel('Number of Points')
plt.ylabel('Time to Load (seconds)')
plt.legend()
plt.show()
.. image:: https://github.com/pyvista/pyminiply/raw/main/bench0.png

.. image:: https://github.com/pyvista/pyminiply/raw/main/bench1.png

*****************************
License and Acknowledgments
*****************************

This project relies on ``miniply`` and credit goes to the original
author for the excellent C++ library. That work is licensed under the
MIT License.

The work in this repository is also licensed under the MIT License.

*********
Support
*********

If you are having issues, please feel free to raise an `Issue
<https://github.com/pyvista/pyminiply/issues>`_.
Binary file added demo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
45 changes: 45 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
[build-system]
requires = [
"setuptools>=42",
"wheel>=0.33.0",
"cython>=3",
"oldest-supported-numpy"
]

build-backend = "setuptools.build_meta"

[tool.pytest.ini_options]
junit_family= "legacy"
filterwarnings = [
# bogus numpy ABI warning (see numpy/#432)
"ignore:.*numpy.dtype size changed.*:RuntimeWarning",
"ignore:.*numpy.ufunc size changed.*:RuntimeWarning"
]

[tool.cibuildwheel]
archs = ["auto64"] # 64-bit only
skip = "pp* *musllinux*" # disable PyPy and musl-based wheels
test-requires = "pytest pyvista"
test-command = "pytest {project}/tests"

[tool.cibuildwheel.macos]
# https://cibuildwheel.readthedocs.io/en/stable/faq/#apple-silicon
archs = ["x86_64", "universal2"]
test-skip = ["*_arm64", "*_universal2:arm64"]

[tool.codespell]
skip = '*.cxx,*.h,*.gif,*.png,*.jpg,*.js,*.html,*.doctree,*.ttf,*.woff,*.woff2,*.eot,*.mp4,*.inv,*.pickle,*.ipynb,flycheck*,./.git/*,./.hypothesis/*,*.yml,./doc/build/*,./doc/images/*,./dist/*,*~,.hypothesis*,*.cpp,*.c'
quiet-level = 3

[tool.isort]
profile = "black"
force_sort_within_sections = true
default_section = "THIRDPARTY"
skip_glob = ["__init__.py"]
src_paths = ["doc", "src", "tests"]

[tool.black]
line-length = 100
skip-string-normalization = true
target-version = ['py39']
exclude='\.eggs|\.git|\.mypy_cache|\.tox|\.venv|_build|buck-out|build|dist|node_modules'
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Setup for stl-reader."""
"""Setup for pyminiply."""
from io import open as io_open
import os
import sys
Expand Down Expand Up @@ -76,7 +76,8 @@
]
),
package_data={
"pyminiply": ["*.pyx"], # include all .pyx files in the package
"pyminiply": ["*.pyx", "*.hpp"],
"pyminiply/wrapper": ["*.c", "*.h"],
},
keywords="read ply",
install_requires=["numpy>1.11.0"],
Expand Down

0 comments on commit 5feccec

Please sign in to comment.