From 2363283852efdad9ec2209698c00280a70366527 Mon Sep 17 00:00:00 2001 From: Andreas-Forster Date: Sat, 3 Feb 2024 13:53:15 +0100 Subject: [PATCH] modularized asm IO as proposed in scalismo --- .../scalismo/asm/io/ActiveShapeModelIO.scala | 89 +++++++++++-------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/src/main/scala/scalismo/asm/io/ActiveShapeModelIO.scala b/src/main/scala/scalismo/asm/io/ActiveShapeModelIO.scala index dceeebe..db17c25 100644 --- a/src/main/scala/scalismo/asm/io/ActiveShapeModelIO.scala +++ b/src/main/scala/scalismo/asm/io/ActiveShapeModelIO.scala @@ -13,25 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package scalismo.asm.io +package scalismo.io +import java.io.{File, IOException} import breeze.linalg.{DenseMatrix, DenseVector} import io.jhdf.api.Group -import scalismo.asm.statisticalmodel.{ - ActiveShapeModel, - FeatureExtractorIOHandlers, - ImagePreprocessorIOHandlers, - Profile, - Profiles -} import scalismo.common.PointId import scalismo.geometry._3D import scalismo.hdf5json.{FloatArray1D, HDFPath, IntArray1D} -import scalismo.io.statisticalmodel.* +import scalismo.io.statisticalmodel.{HDF5Writer, NDArray, StatismoIO, StatisticalModelIOUtils, StatisticalModelReader} import scalismo.mesh.TriangleMesh import scalismo.statisticalmodel.{MultivariateNormalDistribution, PointDistributionModel, StatisticalMeshModel} +import scalismo.statisticalmodel.asm.* -import java.io.{File, IOException} import scala.collection.immutable import scala.util.{Failure, Success, Try} @@ -40,7 +34,7 @@ object ActiveShapeModelIO { object Names { object Group { - val ActiveShapeModel = "/activeShapeModel" + val ActiveShapeModel = "activeShapeModel" val FeatureExtractor = "featureExtractor" val ImagePreprocessor = "imagePreprocessor" val Profiles = "profiles" @@ -61,24 +55,38 @@ object ActiveShapeModelIO { } } - def writeActiveShapeModel(asm: ActiveShapeModel, file: File): Try[Unit] = { + for { + h5file <- StatisticalModelIOUtils.createFile(file) + _ <- writeAcitveShapeModel(asm, h5file, HDFPath("/")) + _ <- Try(h5file.close()) + } yield () + } + private[io] def writeAcitveShapeModel(asm: ActiveShapeModel, h5file: HDF5Writer, path: HDFPath): Try[Unit] = { val pointModel = PointDistributionModel[_3D, TriangleMesh](asm.statisticalModel.gp) + + for { + _ <- StatismoIO.writeStatismoPDM(pointModel, h5file, path) + _ <- writeAppearanceModel(asm, h5file, path) + _ <- h5file.write() + } yield () + } + + private def writeAppearanceModel(asm: ActiveShapeModel, h5file: HDF5Writer, path: HDFPath): Try[Unit] = { + val asmPath = HDFPath(path, Names.Group.ActiveShapeModel) + val fePath = HDFPath(asmPath, Names.Group.FeatureExtractor) + val ppPath = HDFPath(asmPath, Names.Group.ImagePreprocessor) + val profilesPath = HDFPath(asmPath, Names.Group.Profiles) + for { - _ <- StatismoIO.writeStatismoPDM(pointModel, file) - h5 <- StatisticalModelIOUtils.openFileForWriting(file) - asmPath = HDFPath(Names.Group.ActiveShapeModel) - fePath = HDFPath(asmPath, Names.Group.FeatureExtractor) - ppPath = HDFPath(asmPath, Names.Group.ImagePreprocessor) - profilesPath = HDFPath(asmPath, Names.Group.Profiles) // for now, the version information is fixed to 1.0 - _ <- h5.writeIntAttribute(asmPath, Names.Attribute.MajorVersion, 1) - _ <- h5.writeIntAttribute(asmPath, Names.Attribute.MinorVersion, 0) - _ <- ImagePreprocessorIOHandlers.save(asm.preprocessor, h5, ppPath) - _ <- FeatureExtractorIOHandlers.save(asm.featureExtractor, h5, fePath) - _ <- writeProfiles(h5, profilesPath, asm.profiles) - _ <- h5.write() + _ <- h5file.writeIntAttribute(asmPath, Names.Attribute.MajorVersion, 1) + _ <- h5file.writeIntAttribute(asmPath, Names.Attribute.MinorVersion, 0) + _ <- ImagePreprocessorIOHandlers.save(asm.preprocessor, h5file, ppPath) + _ <- FeatureExtractorIOHandlers.save(asm.featureExtractor, h5file, fePath) + _ <- writeProfiles(h5file, profilesPath, asm.profiles) + _ <- h5file.write() } yield () } @@ -87,11 +95,11 @@ object ActiveShapeModelIO { val numberOfPoints = profiles.data.length val profileLength = if (numberOfPoints > 0) profiles.data.head.distribution.mean.size else 0 val means: NDArray[Float] = new NDArray(IndexedSeq[Long](numberOfPoints, profileLength), - profiles.data.flatMap(_.distribution.mean.toArray).toArray.map(_.toFloat) + profiles.data.flatMap(_.distribution.mean.toArray).toArray.map(_.toFloat) ) val covariances: NDArray[Float] = new NDArray(IndexedSeq[Long](numberOfPoints * profileLength, profileLength), - profiles.data.flatMap(_.distribution.cov.toArray).toArray.map(_.toFloat) + profiles.data.flatMap(_.distribution.cov.toArray).toArray.map(_.toFloat) ) val result = for { @@ -110,10 +118,24 @@ object ActiveShapeModelIO { }.flatten def readActiveShapeModel(fn: File): Try[ActiveShapeModel] = { + val modelPath = HDFPath("/") for { - pdm <- StatismoIO.readStatismoPDM[_3D, TriangleMesh](fn) modelReader <- StatisticalModelIOUtils.openFileForReading(fn) - asmPath = HDFPath(Names.Group.ActiveShapeModel) + pointModel <- StatismoIO.readStatismoPDM[_3D, TriangleMesh](modelReader, modelPath) + model <- readActiveShapeModel(modelReader, modelPath, pointModel) + } yield model + } + + private[io] def readActiveShapeModel(modelReader: StatisticalModelReader, + modelPath: HDFPath, + pdm: PointDistributionModel[_3D, TriangleMesh] + ): Try[ActiveShapeModel] = { + val asmPath = HDFPath(Names.Group.ActiveShapeModel) + val fePath = HDFPath(asmPath, Names.Group.FeatureExtractor) + val ppPath = HDFPath(asmPath, Names.Group.ImagePreprocessor) + val profilesGroup = HDFPath(asmPath, Names.Group.Profiles) + + for { asmVersionMajor <- modelReader.readIntAttribute(asmPath, Names.Attribute.MajorVersion) asmVersionMinor <- modelReader.readIntAttribute(asmPath, Names.Attribute.MinorVersion) _ <- { @@ -122,23 +144,16 @@ object ActiveShapeModelIO { case _ => Failure(new IOException(s"Unsupported ActiveShapeModel version: $asmVersionMajor.$asmVersionMinor")) } } - fePath = HDFPath(asmPath, Names.Group.FeatureExtractor) - ppPath = HDFPath(asmPath, Names.Group.ImagePreprocessor) preprocessor <- ImagePreprocessorIOHandlers.load(modelReader, ppPath) - profilesGroup = HDFPath(asmPath, Names.Group.Profiles) featureExtractor <- FeatureExtractorIOHandlers.load(modelReader, fePath) profiles <- readProfiles(modelReader, profilesGroup, pdm.reference) - } yield { - val shapeModel = PointDistributionModel(pdm.gp) - ActiveShapeModel(shapeModel, profiles, preprocessor, featureExtractor) - } - + } yield ActiveShapeModel(pdm, profiles, preprocessor, featureExtractor) } private[this] def readProfiles(modelReader: StatisticalModelReader, path: HDFPath, referenceMesh: TriangleMesh[_3D] - ): Try[Profiles] = { + ): Try[Profiles] = { for { profileLength <- modelReader.readIntAttribute(path, Names.Attribute.ProfileLength)