Visualization & Data Export
After solve (or Sentinel.invert) you have an in-memory reconstruction. This page is the deep-dive on reading that result and getting it out — to MATLAB (.mat), to ParaView (.vtu), or as the standard ReconProps.mat — plus a runnable example.
See also
The User Guide has a brief Postprocessing section and Getting Started Tutorial 6 shows quick VTK export; this page is the authoritative reference for all export paths.
Understanding inversion outputs
What you get back depends on which entry point you used:
| You want… | Use | You get |
|---|---|---|
| Full programmatic access (mesh, state, export handles) | solve | an InversionResult |
| Shear modulus on the image voxel grid (FORGE one-shot) | Sentinel.invert | a NamedTuple (see below) |
solve → InversionResult. Key fields: material (the reconstructed properties), displacement (final calculated field, metres), state (converged, global_obj, disp_error), and setup — the InverseProblemSetup you pass to the export helpers below. See InversionResult for the full field list.
Sentinel.invert → NamedTuple. The FORGE-facing one-shot returns (; real_shear, imag_shear, voxel_size, dims, stats):
real_shear,imag_shear— storage (μ′) and loss (μ″) shear modulus in Pa, as 3-D arrays already sampled on the original image voxel grid.voxel_size— image voxel size in mm;dims—size(real_shear).stats— a QANamedTuple(converged,iterations,max_iterations,final_objective,final_disp_error,mesh_elements,mesh_nodes).
Voxels with no reconstruction (outside the mask / beyond the material mesh) are NaN — an honest "no value" sentinel, not a fill value. Don't assume the arrays are finite.
Physical units: the scalar-factor rule
Stored property values are normalized. The physical value is material.prop[i].scalar[1] * rvalue (real) and scalar[2] * ivalue (imag) — Pa for shear/bulk, kg/m³ for density. The export helpers below (interpolate_to_grid, interpolate_properties, export_reconprops, and the Material method of export_vtk) apply this scaling for you. Only raw material.prop[i].rvalue/.ivalue need it applied by hand.
Exporting to MATLAB (.mat)
Sample the reconstructed properties onto a regular grid with interpolate_to_grid, then write a .mat with MAT.jl. The grid arrays are already in Pa — write them directly, do not re-scale.
using Sentinel, MAT
result = solve(prob)
mat = result.material
# material mesh carrying property 1 (shear for Model 1):
mesh = result.setup.meshes[result.setup.config.meshind[1, 1]]
# Sample onto a regular grid — here the mesh's own extent/resolution (metres).
# Each property may live on a DIFFERENT material mesh, so interpolate one
# property at a time, passing the mesh that carries it via prop_indices = [i].
origin = collect(mesh.origin)
res = collect(mesh.res)
dims = mesh.dims # (nx, ny, nz)
grid = interpolate_to_grid(mat, mesh, origin, res, dims; prop_indices = [1])
# grid.data["prop1_real"] / ["prop1_imag"] are (nx,ny,nz) Float64 in Pa (NaN outside)
# coordinate axes (metres) for MATLAB
x = origin[1] .+ (0:dims[1]-1) .* res[1]
y = origin[2] .+ (0:dims[2]-1) .* res[2]
z = origin[3] .+ (0:dims[3]-1) .* res[3]
matwrite("recon_props.mat", Dict(
"mu_storage" => grid.data["prop1_real"], # μ′ (Pa)
"mu_loss" => grid.data["prop1_imag"], # μ″ (Pa)
"mu_mag" => hypot.(grid.data["prop1_real"], grid.data["prop1_imag"]),
"x_m" => collect(x), "y_m" => collect(y), "z_m" => collect(z),
))Julia and MATLAB are both column-major and MAT.jl writes MATLAB-native order, so the [nx, ny, nz] arrays load with the same indexing — no transpose needed. For values at arbitrary points rather than a grid, use interpolate_properties.
Load and visualize in MATLAB:
S = load('recon_props.mat');
mu = S.mu_storage; % Pa, [nx,ny,nz]
k = round(size(mu,3)/2); % axial slice
figure; imagesc(S.x_m, S.y_m, mu(:,:,k).'); axis image; colorbar
title('\mu'' storage modulus (Pa)');
figure; volshow(mu); % volume render (R2018b+)
figure; p = patch(isosurface(mu, 3000)); % 3 kPa isosurface
isonormals(mu, p); p.FaceColor = 'red'; p.EdgeColor = 'none';
camlight; lighting gouraud; axis imageNaN voxels render as transparent in imagesc/isosurface.
The standard ReconProps.mat
export_reconprops writes the canonical ReconProps.mat consumed by the MATLAB MRE-analysis pipeline. Unlike the ad-hoc grid above, it samples onto the MR voxel grid defined by sidecar files and applies the brain mask, so it requires those sidecars in basedir:
<stem>.InterpLocations.mat—xout,yout,zout,maskint<stem>.InterpData.mat—MagIm_intthe dataset's
.meshindfile
result = solve(prob)
export_reconprops(result.material, result.setup, "/path/to/dataset", "ReconProps.mat")Output keys (3-D arrays on the MR voxel grid, physical units, masked voxels = NaN): RealShear/ImagShear/ShearMag (Pa), RealDens/ImagDens (kg/m³), RealBulk/ImagBulk (or RealLambda/ImagLambda), DR (damping ratio 0.5*(μ″/μ′ − ρ″/ρ′)), RC, MagIm, plus meshind, meshres, recondir.
Use this for the standard pipeline product; use interpolate_to_grid + MAT.jl for grids you define yourself (no sidecars needed).
Visualizing in ParaView (VTK)
export_vtk writes a .vtu that opens directly in ParaView. Three methods cover the common cases:
# 1. A generated mesh (preprocessing) — mesh + displacement/anatomy/region
export_vtk("mesh", hex_mesh_result; write_disp=true)
# 2. A Ferrite grid + a solved displacement field (disp in metres; dispset 1-indexed)
export_vtk("forward", prob.grid, prob.dh, disp)
# 3. Reconstructed material on its mesh (values scalar-scaled to Pa).
# Scope to property 1 (shear) — its mesh; other properties may be on others.
export_vtk("recon", result.material,
result.setup.meshes[result.setup.config.meshind[1, 1]]; prop_indices=[1])The displacement methods write disp_real/disp_imag vector fields (metres). In ParaView: use Threshold to hide NaN/empty cells, Slice/Clip for an ROI, and Glyph to draw displacement vectors. (Generated-mesh VTK is also covered in Getting Started Tutorial 6.)
Convergence output
Convergence history is summarized and exported via print_convergence_table, convergence_statistics, and export_convergence_csv (objective is unitless; disp_error is in metres).
print_convergence_table(history)
export_convergence_csv("convergence.csv", history)Runnable example
examples/export_outputs.jl runs a short inversion on the bundled fixture and demonstrates the MATLAB (.mat) and ParaView (.vtu) exports end-to-end:
julia --project=. examples/export_outputs.jl