Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/426 inconsitent reading and writing in statismoio #427

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}
}
}
Loading