-
Notifications
You must be signed in to change notification settings - Fork 65
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: native ply writer implementation (20 times faster than VTK) * feat: native ply binary reader for triangleMesh * feat: added writing support of VertexColorMesh3D * feat: added support for ascii format
- Loading branch information
Dennis Madsen
authored
Jan 22, 2024
1 parent
0c3ac5b
commit c93cfde
Showing
9 changed files
with
656 additions
and
132 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/* | ||
* Copyright 2015 University of Basel, Graphics and Vision Research Group | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package scalismo.io.ply | ||
|
||
import scalismo.geometry._3D | ||
import scalismo.mesh.{TriangleMesh, VertexColorMesh3D} | ||
|
||
import java.io.{File, IOException} | ||
import scala.util.{Failure, Try} | ||
|
||
object PLY { | ||
|
||
def write(mesh: TriangleMesh[_3D], filename: File): Try[Unit] = { | ||
PLYMeshWriter.write(mesh, None, filename) | ||
} | ||
|
||
def write(mesh: VertexColorMesh3D, filename: File): Try[Unit] = { | ||
PLYMeshWriter.write(mesh.shape, Option(mesh.color.pointData.iterator), filename) | ||
} | ||
|
||
def read(file: File): Try[Either[TriangleMesh[_3D], VertexColorMesh3D]] = { | ||
if (!file.exists()) { | ||
val filename = file.getCanonicalFile | ||
Failure(new IOException(s"Could not read ply file with name $filename. Reason: The file does not exist")) | ||
} else { | ||
PLYMeshReader.readFileAndParseHeader(file) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
/* | ||
* Copyright 2015 University of Basel, Graphics and Vision Research Group | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package scalismo.io.ply | ||
|
||
import java.io.IOException | ||
import scala.collection.mutable.ArrayBuffer | ||
import scala.util.{Failure, Success, Try} | ||
|
||
object PLYHeader { | ||
|
||
def createHeader(numVertices: Int, numFaces: Int, vertexColors: Boolean): String = { | ||
val header = new StringBuilder | ||
header.append("ply\nformat binary_little_endian 1.0\ncomment Scalismo generated PLY File\n") | ||
header.append(f"element vertex $numVertices\nproperty float x\nproperty float y\nproperty float z\n") | ||
if (vertexColors) { | ||
header.append("property uchar red\nproperty uchar green\nproperty uchar blue\nproperty uchar alpha\n") | ||
} | ||
header.append(f"element face $numFaces\nproperty list uchar int vertex_indices\nend_header\n") | ||
header.toString() | ||
} | ||
|
||
def parseHeader(header: Array[String]): HeaderInfo = { | ||
val fileComments: ArrayBuffer[String] = ArrayBuffer.empty[String] | ||
var fileFormat: Option[PLYFormat] = None | ||
val fileElements: ArrayBuffer[PLYElement] = ArrayBuffer.empty[PLYElement] | ||
|
||
var cnt = 0 | ||
while (cnt < header.length) { | ||
val line = header(cnt) | ||
val lineSplit = line.split(" ") | ||
val name = lineSplit.head | ||
if (name == "comment" || name == "obj_info") { | ||
fileComments += lineSplit.drop(1).mkString(" ") | ||
} else if (lineSplit.length > 2) { | ||
if (name == "format") { | ||
lineSplit(1) match { | ||
case PLY_FORMAT_ASCII => | ||
fileFormat = Option(PLYFormat(PLYTypeFormat.ASCII, lineSplit(2))) | ||
case PLY_FORMAT_BIG => | ||
fileFormat = Option(PLYFormat(PLYTypeFormat.BINARY_BIG_ENDIAN, lineSplit(2))) | ||
case PLY_FORMAT_LITTLE => | ||
fileFormat = Option(PLYFormat(PLYTypeFormat.BINARY_LITTLE_ENDIAN, lineSplit(2))) | ||
case _ => Failure(new IOException(s"Unsupported PLY format")) | ||
} | ||
} | ||
if (name == "element") { | ||
val elementFormat = lineSplit(1) match { | ||
case PLY_ELEMENT_VERTEX => | ||
Success(PLYElementFormat.VERTEX) | ||
case PLY_ELEMENT_FACE => | ||
Success(PLYElementFormat.FACE) | ||
case _ => Failure(new IOException(s"Unsupported property format")) | ||
} | ||
val elementCount = lineSplit(2).toInt | ||
val elementProperties: ArrayBuffer[PLYProperty] = ArrayBuffer.empty[PLYProperty] | ||
while (cnt < header.length - 1 && (header(cnt + 1).split(" ").head) == "property") { | ||
val nextLine = header(cnt + 1).split(" ") | ||
if (nextLine(1) == "list") { | ||
elementProperties += PLYProperty(nextLine(2), nextLine(4), Option(nextLine(3))) | ||
} else { | ||
elementProperties += PLYProperty(nextLine(1), nextLine(2)) | ||
} | ||
cnt += 1 | ||
} | ||
fileElements += PLYElement(elementFormat.get, elementCount, elementProperties.toArray) | ||
} | ||
} | ||
cnt += 1 | ||
} | ||
|
||
val vertexInfo = fileElements.find(_.format == PLYElementFormat.VERTEX).get | ||
val faceInfo = fileElements.find(_.format == PLYElementFormat.FACE).get | ||
val faceDefined = validateElementFace(faceInfo) | ||
val vertexDefined = validateElementVertex(vertexInfo) | ||
if (!vertexDefined.status) { | ||
throw new IOException( | ||
"Unsupported element property provided" | ||
) | ||
} else if (!vertexDefined.is3DVertex) { | ||
throw new IOException( | ||
"Vertex (x,y,z) not defined in file." | ||
) | ||
} else if (!faceDefined) { | ||
throw new IOException( | ||
"Face element defined but no property defined." | ||
) | ||
} | ||
HeaderInfo( | ||
format = fileFormat.get, | ||
vertexInfo = vertexInfo, | ||
faceInfo = faceInfo, | ||
comments = fileComments.toArray, | ||
headerLength = header.map(_.getBytes("UTF-8").length).sum + header.length | ||
) | ||
} | ||
|
||
private def validateElementFace(element: PLYElement): Boolean = { | ||
if (element.count == 0) true | ||
else { | ||
element.properties.nonEmpty && element.properties.exists(prop => VERTEX_INDEX_PROPERTY_NAMES.contains(prop.name)) | ||
} | ||
} | ||
|
||
private def validateElementVertex(element: PLYElement): PLYItemsDefined = { | ||
val xyz = Seq("x", "y", "z") | ||
val n = Seq("nx", "ny", "nz") | ||
val color = Seq("red", "green", "blue") | ||
val colora = Seq("red", "green", "blue", "alpha") | ||
val st = Seq("s", "t") | ||
val uv = Seq("u", "v") | ||
val texture = Seq("texture_u", "texture_v") | ||
val all = xyz ++ n ++ color ++ colora ++ st ++ uv ++ texture | ||
val names = element.properties.map(_.name) | ||
val is3DVertexDefined = xyz.forall(names.contains) | ||
val is3DNormalDefined = n.forall(names.contains) | ||
val is3DVertexColorDefined = color.forall(names.contains) || colora.forall(names.contains) | ||
val is3DUVsDefined = | ||
st.forall(names.contains) || | ||
uv.forall(names.contains) || | ||
texture.forall(names.contains) | ||
val status = element.properties.map(_.name).forall(item => all.contains(item)) | ||
PLYItemsDefined(status, is3DVertexDefined, is3DNormalDefined, is3DVertexColorDefined, is3DUVsDefined) | ||
} | ||
|
||
} |
Oops, something went wrong.