Volumes and point clouds
Overview
Two data families complete Rasmah's file I/O. Scalar volumes represent a field over a regular 3D grid — the natural form for medical imaging, where a CT or MRI scan is a stack of grayscale slices. Point clouds represent raw scan data — a set of points, optionally with normals, from a laser or photogrammetry scan.
Both enter through the implicit (SDF) path rather than the BRep path: a volume is sampled into a level set, and a point cloud is reconstructed into a surface, each becoming a mesh you can simulate.
Scalar volumes (DICOM & NIfTI)
A DicomVolume is a regular grid of values plus the voxel spacing and the origin of the first voxel. It is callable (trilinearly interpolating the field) and has a bounding box:
Rasmah.DicomVolume — Type
DicomVolume(values, spacing, origin)A regular 3D grid of scalar values values (indexed [rows, cols, frames]) with per-axis voxel spacing and the origin (centre of the first voxel). The volume is callable at a physical point via trilinear interpolation, and segment turns it into an implicit field for meshing.
Fields
values: the 3D voxel grid.spacing: voxel size per axis (millimetres when read from DICOM).origin: position of the first voxel centre.
vol = DicomVolume(zeros(4, 4, 4), [1.0, 1.0, 1.0], [0.0, 0.0, 0.0])
bounds(vol)([-0.5, -0.5, -0.5], [3.5, 3.5, 3.5])DICOM (ISO 12052) is the medical-imaging exchange format; NIfTI is its research-side counterpart. Rasmah maps both onto the same DicomVolume:
Rasmah.read_dicom — Function
read_dicom(io) -> DicomVolume
read_dicom(path) -> DicomVolumeRead one DICOM file (Explicit or Implicit VR Little Endian, monochrome 8-/16-bit pixel data) into a DicomVolume.
Rasmah.read_dicom_series — Function
read_dicom_series(paths) -> DicomVolume
read_dicom_series(dir; ext=".dcm") -> DicomVolumeAssemble a DICOM series of 2D slices into one DicomVolume, sorted by image position and stacked along z. The z spacing is the median inter-slice distance (or the in-file slice spacing for a single slice).
Rasmah.write_dicom — Function
write_dicom(vol, io; instance_number=1, series_number=1) -> io
write_dicom(vol, path; kwargs...) -> pathWrite a DicomVolume as a single DICOM file (CT storage, Explicit VR Little Endian, 16-bit monochrome pixel data).
Rasmah.write_dicom_series — Function
write_dicom_series(vol, dir) -> dirWrite each frame of a DicomVolume as a separate DICOM slice file (slice_0001.dcm, slice_0002.dcm, …) in dir, offsetting each slice along z by the voxel spacing.
Rasmah.read_nifti — Function
read_nifti(path)
read_nifti(io::IO)Read a NIfTI-1/.nii/.nii.gz volume, returning a DicomVolume (or a Vector{DicomVolume} for 4D time series).
Rasmah.write_nifti — Function
write_nifti(vol::DicomVolume, path)
write_nifti(vol::DicomVolume, io::IO)Write a DicomVolume as a single-file NIfTI-1 .nii (float64 voxels).
A volume becomes mesheable geometry in two steps — threshold it into a level set and convert that to an SDF:
Rasmah.segment — Function
segment(vol, threshold; β=nothing) -> ThresholdFieldA level-set field whose zero set is the vol == threshold isosurface. With β = nothing the field is the hard level set threshold - vol; a positive β gives the smooth sigmoid σ((threshold - vol)/β) - 1/2, differentiable in the threshold and volume.
Rasmah.sample_grid — Function
sample_grid(f, lo, hi, dims) -> DicomVolumeSample a callable f(x) on a regular dims = (nx, ny, nz) grid over the box [lo, hi], returning the result as a DicomVolume with the implied spacing and origin.
vol = DicomVolume(zeros(8, 8, 8), [1.0, 1.0, 1.0], [0.0, 0.0, 0.0])
seg = segment(vol, 0.5)
bounds(seg)([-0.5, -0.5, -0.5], [7.5, 7.5, 7.5])Point clouds
Point-cloud readers and writers cover the common scan formats:
Rasmah.read_xyz — Function
read_xyz(path) -> PointCloudRead a plain ASCII XYZ point-cloud file (one x y z per line, optionally followed by nx ny nz normals) into a PointCloud.
Rasmah.write_xyz — Function
write_xyz(points, io; normals = nothing)
write_xyz(points, path; normals = nothing)Write a 3×n point matrix (optionally with normals) as a plain ASCII XYZ file.
Rasmah.read_pcd — Function
read_pcd(path) -> PointCloud
read_pcd(io) -> PointCloudRead a PCL Point Cloud Data (PCD) file (ASCII, binary, or LZF-compressed) into a PointCloud.
Rasmah.write_pcd — Function
write_pcd(points, io; normals = nothing, binary = false)
write_pcd(points, path; normals = nothing, binary = false)Write a 3×n point matrix (optionally with normals) as a PCD file, ASCII by default or binary with binary = true.
Rasmah.read_pointcloud — Function
read_pointcloud(path) -> PointCloudRead any supported point-cloud file (.xyz/.txt, .pcd, .ptx, .las, .ply, .obj, .stl), dispatching on the file extension. Mesh formats contribute their vertices only.
A point cloud is a PointCloud (a 3×N matrix of points, optionally with normals); the reconstruction and primitive-fitting machinery that turns it into a mesh lives in the Meshing chapters.
Time series (PVD)
A sequence of meshes — an animation or a transient simulation — is written as a ParaView Data (.pvd) collection, which the interactive viewer can play:
Rasmah.write_pvd — Function
write_pvd(meshes, path; times=nothing, format=:ascii) -> pathSerialize a sequence of meshes (all TriangleMesh or all TetMesh) to a ParaView Data (.pvd) collection plus one .vtp/.vtu file per frame. times optionally supplies the timestep per frame.
Rasmah.read_pvd — Function
read_pvd(path) -> (dir, entries)Read the manifest of a ParaView Data (.pvd) collection: the directory the frames live in plus the ordered (file, timestep) entries (parsing only the <DataSet> list, not geometry).
Next steps
With a volume or point cloud converted to a mesh, the natural follow-ons are the meshing and simulation chapters. For the triangle-mesh and exact-CAD formats, return to File I/O and Exact CAD exchange.
