Skip to content

Commit

Permalink
Merge pull request #427 from unibas-gravis/fix/426-inconsitent-readin…
Browse files Browse the repository at this point in the history
…g-and-writing-in-statismoio

Fix/426 inconsitent reading and writing in statismoio

- fixes #426
- fixes #332
  • Loading branch information
Andreas-Forster authored Mar 21, 2024
2 parents e59696f + 32251fb commit 77d54c5
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 24 deletions.
44 changes: 23 additions & 21 deletions src/main/scala/scalismo/io/statisticalmodel/StatismoDomainIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,19 @@ object StatismoDomainIO {
override def createDomainWithCells(points: IndexedSeq[Point[_2D]],
cellArray: Option[NDArray[Int]]
): Try[TriangleMesh[_2D]] = {
cellArray match {
case None => Failure(new Throwable("Triangle cells missing"))
val triangleList = cellArray match {
case None => Success(TriangleList(IndexedSeq()))
case Some(c) =>
val cellMatrix = ndIntArrayToIntMatrix(c)
if (cellMatrix.cols != 3) Failure(new Exception("Representer cells are not triangles"))
else {
val cells = for (i <- 0 until cellMatrix.rows) yield {
TriangleCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)), PointId(cellMatrix(i, 2)))
}
Success(TriangleMesh2D(UnstructuredPoints(points), TriangleList(cells)))
Success(TriangleList(cells))
}
}

triangleList.map(triangles => TriangleMesh2D(UnstructuredPoints(points), triangles))
}

override def cellsToArray(mesh: TriangleMesh[_2D]): NDArray[Int] = {
Expand All @@ -103,20 +103,19 @@ object StatismoDomainIO {
override def createDomainWithCells(points: IndexedSeq[Point[_3D]],
cellArray: Option[NDArray[Int]]
): Try[TriangleMesh[_3D]] = {
cellArray match {
case None => Failure(new Throwable("Triangle cells missing"))
val triangleList = cellArray match {
case None => Success(TriangleList(IndexedSeq()))
case Some(c) =>
val cellMatrix = ndIntArrayToIntMatrix(c)
if (cellMatrix.cols != 3) Failure(new Exception("Representer cells are not triangles"))
else {
val cells =
for (i <- 0 until cellMatrix.rows)
yield {
TriangleCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)), PointId(cellMatrix(i, 2)))
}
Success(TriangleMesh3D(UnstructuredPoints(points), TriangleList(cells)))
val cells = for (i <- 0 until cellMatrix.rows) yield {
TriangleCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)), PointId(cellMatrix(i, 2)))
}
Success(TriangleList(cells))
}
}
triangleList.map(triangles => TriangleMesh3D(UnstructuredPoints(points), triangles))
}

override def cellsToArray(mesh: TriangleMesh[_3D]): NDArray[Int] = {
Expand All @@ -132,8 +131,8 @@ object StatismoDomainIO {
override def createDomainWithCells(points: IndexedSeq[Point[_3D]],
cellArray: Option[NDArray[Int]]
): Try[TetrahedralMesh[_3D]] = {
cellArray match {
case None => Failure(new Throwable("Tetrahedral cells missing"))
val tetrahedralList = cellArray match {
case None => Success(TetrahedralList(IndexedSeq()))
case Some(c) =>
val cellMatrix = ndIntArrayToIntMatrix(c)
if (cellMatrix.cols != 4) Failure(new Exception("Representer cells are not tetrahedrons"))
Expand All @@ -147,9 +146,10 @@ object StatismoDomainIO {
PointId(cellMatrix(i, 3))
)
}
Success(TetrahedralMesh3D(UnstructuredPoints(points), TetrahedralList(cells)))
Success(TetrahedralList(cells))
}
}
tetrahedralList.map(tetrahedrons => TetrahedralMesh3D(UnstructuredPoints(points), tetrahedrons))
}

override def cellsToArray(mesh: TetrahedralMesh[_3D]): NDArray[Int] = {
Expand All @@ -168,8 +168,8 @@ object StatismoDomainIO {
override def createDomainWithCells(points: IndexedSeq[Point[_2D]],
cellArray: Option[NDArray[Int]]
): Try[LineMesh[_2D]] = {
cellArray match {
case None => Failure(new Throwable("Line cells missing"))
val lineList = cellArray match {
case None => Success(LineList(IndexedSeq()))
case Some(c) =>
val cellMatrix = ndIntArrayToIntMatrix(c)
if (cellMatrix.cols != 2) Failure(new Exception("Representer cells are not lines"))
Expand All @@ -179,9 +179,10 @@ object StatismoDomainIO {
yield {
LineCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)))
}
Try(LineMesh2D(UnstructuredPoints(points), LineList(cells)))
Try(LineList(cells))
}
}
lineList.map(lines => LineMesh2D(UnstructuredPoints(points), lines))
}

override def cellsToArray(mesh: LineMesh[_2D]): NDArray[Int] = {
Expand All @@ -197,8 +198,8 @@ object StatismoDomainIO {
override def createDomainWithCells(points: IndexedSeq[Point[_3D]],
cellArray: Option[NDArray[Int]]
): Try[LineMesh[_3D]] = {
cellArray match {
case None => Failure(new Throwable("Line cells missing"))
val lineList = cellArray match {
case None => Success(LineList(IndexedSeq()))
case Some(c) =>
val cellMatrix = ndIntArrayToIntMatrix(c)
if (cellMatrix.cols != 2) Failure(new Exception("Representer cells are not lines"))
Expand All @@ -208,9 +209,10 @@ object StatismoDomainIO {
yield {
LineCell(PointId(cellMatrix(i, 0)), PointId(cellMatrix(i, 1)))
}
Success(LineMesh3D(UnstructuredPoints(points), LineList(cells)))
Success(LineList(cells))
}
}
lineList.map(lines => LineMesh3D(UnstructuredPoints(points), lines))
}

override def cellsToArray(mesh: LineMesh[_3D]): NDArray[Int] = {
Expand Down
6 changes: 3 additions & 3 deletions src/main/scala/scalismo/io/statisticalmodel/StatismoIO.scala
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,8 @@ object StatismoIO {
points <- Try(
for (i <- 0 until pointsMatrix.cols) yield vectorizer.unvectorize(pointsMatrix(::, i).copy).toPoint
)
cells <- readStandardConnectiveityRepresenterGroup(h5file, modelPath)
domain <- typeHelper.createDomainWithCells(points, Option(cells))
cells = readStandardConnectiveityRepresenterGroup(h5file, modelPath).toOption
domain <- typeHelper.createDomainWithCells(points, cells)
} yield domain
}

Expand All @@ -307,7 +307,7 @@ object StatismoIO {
val cells =
if (h5file.exists(HDFPath(modelPath, "/representer/cells")))
h5file.readNDArrayInt(HDFPath(modelPath, "/representer/cells"))
else Failure(new Throwable("No cells found in representer"))
else Failure[NDArray[Int]](new Throwable("No cells found in representer"))
cells
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ class StatismoDomainIOTests extends ScalismoTestSuite {
t.get
}

it("can convert a 2D TriangleMesh without triangles") {
val unstructuredPoints =
CreateUnstructuredPoints2D.create(IndexedSeq(Point2D(0, 0), Point2D(1, 0), Point2D(1, 1)))
val topology = TriangleList(IndexedSeq())
val input = TriangleMesh2D(unstructuredPoints, topology)
val t = for {
output <- StatismoDomainIO.domainIOTriangleMesh2D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq,
None
)
} yield {
assert(input == output)
}
t.get
}

it("can convert a 3D TriangleMesh") {
val unstructuredPoints =
CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0)))
Expand All @@ -60,6 +75,21 @@ class StatismoDomainIOTests extends ScalismoTestSuite {
t.get
}

it("can convert a 3D TriangleMesh without triangles") {
val unstructuredPoints =
CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0)))
val topology = TriangleList(IndexedSeq())
val input = TriangleMesh3D(unstructuredPoints, topology)
val t = for {
output <- StatismoDomainIO.domainIOTriangleMesh3D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq,
None
)
} yield {
assert(input == output)
}
t.get
}

it("can convert a 3D TetrahedralMesh") {
val unstructuredPoints = CreateUnstructuredPoints3D.create(
IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0), Point3D(1, 1, 1))
Expand All @@ -76,6 +106,21 @@ class StatismoDomainIOTests extends ScalismoTestSuite {
t.get
}

it("can convert a 3D TetrahedralMesh without tetrahedrons") {
val unstructuredPoints = CreateUnstructuredPoints3D.create(
IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0), Point3D(1, 1, 1))
)
val topology = TetrahedralList(IndexedSeq())
val input = TetrahedralMesh3D(unstructuredPoints, topology)
val t = for {
output <- StatismoDomainIO.domainIOTetrahedralMesh3D
.createDomainWithCells(unstructuredPoints.points.toIndexedSeq, None)
} yield {
assert(input == output)
}
t.get
}

it("can convert a 2D LineMesh") {
val unstructuredPoints = CreateUnstructuredPoints2D.create(IndexedSeq(Point2D(0, 0), Point2D(1, 0)))
val topology = LineList(IndexedSeq(LineCell(PointId(0), PointId(1))))
Expand All @@ -91,6 +136,20 @@ class StatismoDomainIOTests extends ScalismoTestSuite {
t.get
}

it("can convert a 2D LineMesh without lines") {
val unstructuredPoints = CreateUnstructuredPoints2D.create(IndexedSeq(Point2D(0, 0), Point2D(1, 0)))
val topology = LineList(IndexedSeq())
val input = LineMesh2D(unstructuredPoints, topology)
val t = for {
output <- StatismoDomainIO.domainIOLineMesh2D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq,
None
)
} yield {
assert(input == output)
}
t.get
}

it("can convert a 3D LineMesh") {
val unstructuredPoints =
CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0)))
Expand All @@ -106,5 +165,20 @@ class StatismoDomainIOTests extends ScalismoTestSuite {
}
t.get
}

it("can convert a 3D LineMesh without lines") {
val unstructuredPoints =
CreateUnstructuredPoints3D.create(IndexedSeq(Point3D(0, 0, 0), Point3D(1, 0, 0), Point3D(1, 1, 0)))
val topology = LineList(IndexedSeq())
val input = LineMesh3D(unstructuredPoints, topology)
val t = for {
output <- StatismoDomainIO.domainIOLineMesh3D.createDomainWithCells(unstructuredPoints.points.toIndexedSeq,
None
)
} yield {
assert(input == output)
}
t.get
}
}
}
65 changes: 65 additions & 0 deletions src/test/scala/scalismo/io/statisticalmodel/StatismoIOTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package scalismo.io.statisticalmodel

import org.scalatest.PrivateMethodTester.*
import scalismo.geometry.{_3D, Point3D}
import scalismo.io.statisticalmodel.StatismoIO
import scalismo.io.StatisticalModelIO
import scalismo.ScalismoTestSuite
import scalismo.common.{UnstructuredPoints, UnstructuredPointsDomain}
import scalismo.hdf5json.HDFPath
import scalismo.mesh.{TriangleList, TriangleMesh, TriangleMesh3D}
import scalismo.statisticalmodel.StatisticalMeshModel

import java.io.File
import java.net.URLDecoder
import java.nio.file.Files
import scala.util.Try

class StatismoIOTests extends ScalismoTestSuite {
def assertModelAlmostEqual[D](m1: UnstructuredPointsDomain[D], m2: UnstructuredPointsDomain[D]): Unit = {
m1.pointSet.pointSequence.zip(m2.pointSet.pointSequence).foreach { case (a, b) =>
assert((a - b).norm < 1e-5)
}
}

describe("the StatismoIO methods") {

it("can write and read UnstructuredPointsDomain") {
val tmpDir = Files.createTempDirectory("test-StatismoIO")
tmpDir.toFile.deleteOnExit()
val tmpFile = new File(tmpDir.toFile, "UnstructuredPointsDomain.h5.json")
tmpFile.deleteOnExit()

val dataToWrite = UnstructuredPointsDomain(
IndexedSeq(
Point3D(0, 1, 2)
)
)

val modelPath = HDFPath("/")
val representerPath = HDFPath(modelPath, "representer")

// helper to test / call private methods
val writerMethod = PrivateMethod[Try[Unit]](Symbol("writeRepresenterStatismov090"))
val readerMethod = PrivateMethod[Try[UnstructuredPointsDomain[_3D]]](Symbol("readStandardMeshRepresentation"))
val ndSpace = scalismo.geometry.Dim.ThreeDSpace
val domainIO = scalismo.io.StatismoDomainIO.domainIOUnstructuredPoints3D
val vectorizer = scalismo.geometry.EuclideanVector.Vector_3DVectorizer

val t = for {
h5Out <- StatisticalModelIOUtils.openFileForWriting(tmpFile)
t <- StatismoIO invokePrivate writerMethod(h5Out, representerPath, dataToWrite, modelPath, ndSpace, domainIO)
_ <- h5Out.write()
_ <- Try {
h5Out.close()
}
h5In <- StatisticalModelIOUtils.openFileForReading(tmpFile)
loaded <- StatismoIO invokePrivate readerMethod(h5In, modelPath, ndSpace, domainIO, vectorizer)
} yield {
assertModelAlmostEqual(dataToWrite, loaded)
}

t.get
}
}
}

0 comments on commit 77d54c5

Please sign in to comment.