Models¶
Collection of (generative) models for ultrasound imaging.
zea contains a collection of models for various tasks, all located in the zea.models package.
See the following dropdown for a list of available models:
Available models
zea.models.able.ABLE: An adaptive beamformer that learns per-pixel apodization weights.zea.models.echonet.EchoNetDynamic: A model for left ventricle segmentation.zea.models.carotid_segmenter.CarotidSegmenter: A model for carotid artery segmentation.zea.models.echonetlvh.EchoNetLVH: A model for left ventricle hypertrophy segmentation.zea.models.unet.UNet: A simple U-Net implementation.zea.models.lista.LISTA: An unfolded convolutional ISTA (LISTA) model.zea.models.lpips.LPIPS: A model implementing the perceptual similarity metric.zea.models.ultrapips.UltraPIPS: A perceptual similarity metric for B-mode ultrasound with an ultrasound foundation model backbone.zea.models.taesd.TinyAutoencoder: A tiny autoencoder model for image compression.zea.models.regional_quality.MobileNetv2RegionalQuality: A scoring model for myocardial regions in apical views.zea.models.lv_segmentation.AugmentedCamusSeg: A nnU-Net based left ventricle and myocardium segmentation model.zea.models.speckle2self.Speckle2Self: A self-supervised speckle reduction model for ultrasound images.zea.models.inversionnet.InversionNet: A speed-of-sound reconstruction model for ultrasound computed tomography.
Presets for these models can be found in zea.models.presets. Presets are pre-trained weights for the models, which can be used to initialize the models for inference or further training. Each model class has a presets attribute that lists the available presets for that model. We store the presets on Hugging Face Hub, and they are downloaded automatically when loading a model with a preset.
To use these models, you can import them directly from the zea.models module and load the pretrained weights using the from_preset() method. For example:
>>> from zea.models.unet import UNet
>>> model = UNet.from_preset("unet-echonet-inpainter")
You can list all available presets using the presets attribute:
>>> from zea.models.unet import UNet
>>> presets = list(UNet.presets.keys())
>>> print(f"Available built-in zea presets for UNet: {presets}")
Available built-in zea presets for UNet: ['unet-echonet-inpainter']
Generative models¶
In addition to regular models, zea provides generative models for tasks such as image
generation, inpainting, and denoising. The key difference is that generative models have
sampling methods implemented. There are two base classes:
GenerativeModel for classical models (e.g. a Gaussian
mixture model) and DeepGenerativeModel for
neural-network-based models — the latter also inherits from
BaseModel, adding Keras features like weight saving and preset
loading. Both expose the following methods:
fit()for training the model on datasample()for generating new samples from the learned distributionposterior_sample()for drawing samples from the posterior given measurementslog_density()for computing the log-probability of data under the model
See the following dropdown for a list of available generative models:
Available models
zea.models.diffusion.DiffusionModel: A deep generative diffusion model for ultrasound image generation.zea.models.flow_matching.FlowMatchingModel: A flow matching generative model for ultrasound image generation.zea.models.gmm.GaussianMixtureModel: A Gaussian Mixture Model.zea.models.hvae.HierarchicalVAE: A hierarchical variational autoencoder for ultrasound image generation.
An example of how to use the zea.models.diffusion.DiffusionModel is shown below:
>>> from zea.models.diffusion import DiffusionModel
>>> model = DiffusionModel.from_preset("diffusion-echonet-dynamic")
>>> samples = model.sample(n_samples=4)
Adding a new model¶
New models are welcome! Please follow the Contributing guide for the general workflow (forking, branches, pull requests, etc.). The steps below walk you through what is specific to adding a model.
Create a new file in
zea/models/for your model, e.g.zea/models/mymodel.py.Add a model class that inherits from
zea.models.base.BaseModel. For generative models, useGenerativeModelorDeepGenerativeModelas the base class. Implement thecallmethod.Upload the pretrained weights to our Hugging Face. The expected files are a
config.jsonand amodel.weights.h5. See the Keras documentation for how to save these. You can drag and drop the files directly on the Hugging Face website.Tip
Alternate saving methods are also possible. See
zea.models.echonet.EchoNetfor an example — in that case you need to implement acustom_load_weightsmethod in your model class.Add a preset for your model in
zea.models.presets. Presets let you register multiple sets of weights for the same model architecture.In your model file, import the presets module and call
register_presetswith your model class to activate the presets.Import your model in
zea/models/__init__.pyto make it part of the package.
Adding non-Keras (custom) models¶
The recommended approach for any model is to implement it as a native Keras 3 model. This gives you backend-agnostic execution (JAX, TensorFlow, PyTorch) and the full preset/weight-loading infrastructure for free.
For models originally trained in PyTorch, the typical workflow is:
Vendor the architecture — keep the PyTorch network code at hand while you port it. If you want it in the module long-term (e.g. for ONNX export), put it in a
_build_torch_classes()helper that importstorchlazily. PyTorch is then required for weight conversion and ONNX export, but never for inference — which is what keeps it out of the runtime dependencies.Implement the Keras architecture — write
keras.layers.Layersubclasses that replicate each block. Key API differences to handle:Padding for strided Conv2D: Keras
padding='same'is asymmetric forstride > 1; useZeroPadding2D(p) + Conv2D(padding='valid')to match PyTorch’s symmetricpadding=p.ConvTranspose: Keras
Conv2DTranspose(padding='valid')gives the full output, from which PyTorch’spadding=pcropspon every side — so follow it withCropping2D(p). A PyTorchoutput_paddingadds to the end only, sopadding=1, output_padding=1becomesx[:, 1:, 1:, :].Normalization:
BatchNormalizationneedsepsilon=1e-5to match PyTorch’sBatchNorm2ddefault, and takes its weights as[weight, bias, running_mean, running_var]. ForInstanceNorm2d(affine=False)useGroupNormalization(groups=C, scale=False, center=False, epsilon=1e-5).Weight axes: Conv2D —
(2,3,1,0); Conv2DTranspose —(2,3,1,0)(same permutation, different semantics).Input format: Keras defaults to channels-last (NHWC); transpose NCHW → NHWC in
call()and back before returning.Layer names: do not name custom layer classes with a leading underscore. Keras derives the layer name from the class name, and the TensorFlow backend rejects names starting with
_.
Write a weight-loading helper — a function that maps PyTorch state-dict keys to the Keras layer tree and calls
layer.set_weights([...]). The weights only exist once the model is built; building it symbolically withself(keras.Input(batch_shape=...))avoids pushing real data through it.Add
from_pth(path)classmethod — wraps the weight loader for convenient local testing.Verify against the reference implementation — before uploading the converted weights, run both models on the same input and compare. A correct port agrees to ~1e-5 in float32. If it does not, compare block by block: the layer where the difference appears is the one that was ported wrong.
Warning
Run that comparison on CPU. On a supported NVIDIA GPU all three backends — JAX, TensorFlow and PyTorch — use TF32 for convolutions by default, which costs ~3 decimal digits and makes a correct port look broken (differences of ~1e-2 instead of ~1e-5). This applies to any pair of them, so a TensorFlow-versus-PyTorch comparison is no safer than a JAX-versus-PyTorch one.
Optionally add an ONNX fallback — for environments that have
onnxruntimebut nottorch, you can keep afrom_onnx(path)classmethod and an_onnx_sessattribute; overridecall()to dispatch to the ONNX path when the session is set.Follow steps 3-6 from the standard guide above for HF upload, presets, and registration.
Two worked examples of this pattern: zea.models.inversionnet (native Keras
plus a PyTorch state-dict converter) and zea.models.speckle2self (the same,
plus a vendored PyTorch architecture and an optional ONNX fallback).