torch_em.model.cryozoo

CryoZoo: pretrained models for cryo-electron tomography (cryo-ET) in the torch-em segmentation framework.

CryoZoo integrates published pretrained cryo-ET models into torch-em, so that you can finetune and use them for segmentation like the other torch-em models. CryoSiam is one of these integrations, and the CryoZooUNet uses its pretrained encoder.

CryoSiam is described in https://doi.org/10.1101/2025.11.11.687379. Please cite it if you use this piece of code or pretrained weights in your research.

The authors release the weights under the MIT license at https://huggingface.co/frosinastojanovska/cryosiam_v1.0. This module does not contain code from CryoSiam. It builds the encoder from the ResNet of MONAI, like CryoSiam does. Thus, the weights of the CryoSiam checkpoint load without changes.

  1"""CryoZoo: pretrained models for cryo-electron tomography (cryo-ET) in the torch-em segmentation framework.
  2
  3CryoZoo integrates published pretrained cryo-ET models into torch-em, so that you can finetune and use them
  4for segmentation like the other torch-em models.
  5CryoSiam is one of these integrations, and the CryoZooUNet uses its pretrained encoder.
  6
  7CryoSiam is described in https://doi.org/10.1101/2025.11.11.687379.
  8Please cite it if you use this piece of code or pretrained weights in your research.
  9
 10The authors release the weights under the MIT license at https://huggingface.co/frosinastojanovska/cryosiam_v1.0.
 11This module does not contain code from CryoSiam. It builds the encoder from the ResNet of MONAI, like CryoSiam does.
 12Thus, the weights of the CryoSiam checkpoint load without changes.
 13"""
 14
 15import os
 16from collections import OrderedDict
 17from typing import List, Optional, Union
 18
 19import numpy as np
 20
 21import torch
 22import torch.nn as nn
 23
 24try:
 25    from monai.networks.nets import ResNet
 26except ImportError:
 27    ResNet = None
 28
 29from ..util.util import get_cache_directory
 30from .unet import UNetBase, Decoder, ConvBlock3d, Upsampler3d
 31
 32
 33CRYOSIAM_URL = "https://huggingface.co/frosinastojanovska/cryosiam_v1.0/resolve/main/dense_simsiam_pretrained.ckpt"
 34CRYOSIAM_CHECKSUM = "19957ef7eba45658432a81d37ac1764db65f97ddbfcd55bee3c190f51a9b17e7"
 35
 36ENCODERS = {}  # The encoders of the CryoZooUNet by name. The register_encoder decorator fills it at import.
 37
 38
 39def register_encoder(name: str):
 40    """Add an encoder class to `ENCODERS` under the given name.
 41
 42    Use this function as a decorator for the encoder class.
 43
 44    Args:
 45        name: The name of the encoder.
 46
 47    Returns:
 48        The decorator, which adds the encoder class and returns it unchanged.
 49    """
 50    def register(encoder_class):
 51        ENCODERS[name] = encoder_class
 52        return encoder_class
 53    return register
 54
 55
 56class CryoZooEncoder(nn.Module):
 57    """The base class for the CNN encoders of the CryoZooUNet.
 58
 59    To add an encoder, make a subclass and decorate it with `register_encoder`. The subclass must:
 60    - set `features`: the number of channels of each skip connection and of the bottleneck, from fine to coarse.
 61    - set `scale_factors`: the downsampling factor between two consecutive entries of `features`.
 62    - set `stem_scale_factor`: the downsampling factor between the input and the first skip connection.
 63    - implement `forward`: it returns the bottleneck and the list of skip connections, from fine to coarse.
 64
 65    If pretrained weights exist, the subclass must also:
 66    - set `url`, `checksum` and `cache_name`: the URL and the SHA256 checksum of the checkpoint,
 67      and the name of its folder in the torch-em cache directory.
 68    - implement `load_pretrained`.
 69
 70    A scale factor is an integer or a list with one integer for each spatial axis (Z, Y, X).
 71
 72    Args:
 73        in_channels: The number of input channels.
 74    """
 75    url: Optional[str] = None
 76    checksum: Optional[str] = None
 77    cache_name: Optional[str] = None
 78
 79    def __init__(self, in_channels: int = 1):
 80        super().__init__()
 81        self.in_channels = in_channels
 82        self.return_outputs = True
 83        self.features: List[int] = []
 84        self.scale_factors: List[Union[int, List[int]]] = []
 85        self.stem_scale_factor: Union[int, List[int]] = 1
 86
 87    @property
 88    def out_channels(self):
 89        """The number of channels of the bottleneck."""
 90        return self.features[-1]
 91
 92    def __len__(self):
 93        return len(self.scale_factors)
 94
 95    @classmethod
 96    def get_checkpoint(cls, checkpoint_path: Optional[Union[str, os.PathLike]] = None, download: bool = True) -> str:
 97        """Get the filepath to the pretrained checkpoint.
 98
 99        If you pass `checkpoint_path`, this method uses this file.
100        Otherwise, it uses the checkpoint in the torch-em cache directory, and it downloads the checkpoint if needed.
101        See `torch_em.util.util.get_cache_directory` for the cache directory.
102
103        Args:
104            checkpoint_path: The filepath to a checkpoint on disk.
105            download: Whether to download the checkpoint if it is not in the cache yet.
106
107        Returns:
108            The filepath to the checkpoint.
109        """
110        if checkpoint_path is not None:
111            if not os.path.exists(checkpoint_path):
112                raise FileNotFoundError(f"The checkpoint {checkpoint_path} does not exist.")
113            return str(checkpoint_path)
114
115        if cls.url is None:
116            raise NotImplementedError(f"The encoder {cls.__name__} does not have pretrained weights.")
117
118        import pooch  # We import pooch here, so that 'import torch_em' does not need it.
119
120        folder = os.path.join(get_cache_directory(), "models", cls.cache_name)
121        fname = cls.url.split("/")[-1]
122        if not download and not os.path.exists(os.path.join(folder, fname)):
123            raise RuntimeError(f"The checkpoint is not in {folder}. Set download=True to download it.")
124
125        return pooch.retrieve(url=cls.url, known_hash=f"sha256:{cls.checksum}", fname=fname, path=folder)
126
127    def load_pretrained(self, checkpoint: Union[str, os.PathLike, OrderedDict]) -> None:
128        """Load the pretrained weights into the encoder.
129
130        Args:
131            checkpoint: The filepath to a checkpoint or a state dict for the encoder.
132        """
133        raise NotImplementedError(f"The encoder {type(self).__name__} does not have pretrained weights.")
134
135
136def load_cryosiam_encoder_state(checkpoint: Union[str, os.PathLike]) -> OrderedDict:
137    """Load the encoder weights from a CryoSiam checkpoint.
138
139    Args:
140        checkpoint: The filepath to the CryoSiam checkpoint.
141
142    Returns:
143        The state dict for the MONAI ResNet of `CryoSiamEncoder`.
144    """
145    state = torch.load(checkpoint, map_location="cpu", weights_only=True)["state_dict"]
146    prefix = "_model.encoder."
147    # The 'fc' layer belongs to the global projection head of the pretraining.
148    return OrderedDict(
149        (k[len(prefix):], v) for k, v in state.items() if k.startswith(prefix) and not k.startswith(prefix + "fc.")
150    )
151
152
153@register_encoder("cryosiam")
154class CryoSiamEncoder(CryoZooEncoder):
155    """The 3D ResNet encoder of CryoSiam, which uses the ResNet from MONAI.
156
157    It has a 7x7x7 stem without stride and four stages with one basic block each.
158    The first three stages give the skip connections and the last stage gives the bottleneck.
159
160    Args:
161        in_channels: The number of input channels. The pretrained weights expect one channel.
162    """
163    url = CRYOSIAM_URL
164    checksum = CRYOSIAM_CHECKSUM
165    cache_name = "cryosiam"
166
167    def __init__(self, in_channels: int = 1):
168        super().__init__(in_channels=in_channels)
169        if ResNet is None:
170            raise RuntimeError("The CryoSiam encoder needs MONAI. Install it from conda-forge or with pip.")
171
172        self.features = [64, 128, 256, 512]
173        self.scale_factors = [2, 2, 2]
174        self.stem_scale_factor = 1
175
176        # We pin all the settings that the CryoSiam checkpoint depends on, in case the MONAI defaults change.
177        self.resnet = ResNet(
178            block="basic", layers=[1, 1, 1, 1], block_inplanes=self.features, spatial_dims=3,
179            n_input_channels=in_channels, conv1_t_size=7, conv1_t_stride=1, no_max_pool=True, shortcut_type="B",
180            feed_forward=False, bias_downsample=True, act=("relu", {"inplace": False}), norm="batch",
181        )
182
183    def load_pretrained(self, checkpoint: Union[str, os.PathLike, OrderedDict]) -> None:
184        state = checkpoint if isinstance(checkpoint, OrderedDict) else load_cryosiam_encoder_state(checkpoint)
185        self.resnet.load_state_dict(state)
186
187    def forward(self, x):
188        """Apply the encoder to the input data.
189
190        Args:
191            x: The input tensor with the shape (B, C, Z, Y, X).
192
193        Returns:
194            The bottleneck, and the list of skip connections from fine to coarse if `return_outputs` is True.
195        """
196        net = self.resnet
197        x = net.act(net.bn1(net.conv1(x)))
198        encoder_out = []
199        for layer in (net.layer1, net.layer2, net.layer3):
200            x = layer(x)
201            encoder_out.append(x)
202        x = net.layer4(x)
203
204        if self.return_outputs:
205            return x, encoder_out
206        else:
207            return x
208
209
210class OutputHead(nn.Module):
211    """@private
212    """
213    def __init__(self, in_channels, out_channels, scale_factor):
214        super().__init__()
215        self.out_channels = out_channels
216        self.upsampler = Upsampler3d(scale_factor, in_channels, in_channels)
217        self.conv = nn.Conv3d(in_channels, out_channels, 1)
218
219    def forward(self, x):
220        return self.conv(self.upsampler(x))
221
222
223class CryoZooUNet(UNetBase):
224    """A 3D U-Net with a pretrained CNN encoder and a convolutional decoder from torch-em.
225
226    The decoder always uses the skip connections of the encoder.
227    Each spatial input dimension must be divisible by the total downsampling factor of the encoder.
228    For the CryoSiam encoder, this factor is 8.
229    The pretrained CryoSiam weights expect tomograms with inverted contrast and intensities scaled to [0, 1].
230
231    `init_kwargs` does not contain `encoder_checkpoint`. When torch-em restores a trained model,
232    it loads all the weights from the saved training state. Thus it does not load the pretrained weights again.
233
234    Args:
235        out_channels: The number of output channels.
236        encoder: The name of the encoder. See `ENCODERS` for the supported names.
237        in_channels: The number of input channels. The pretrained CryoSiam weights expect one channel.
238        encoder_checkpoint: The filepath to a checkpoint or a state dict for the encoder.
239            The model loads the encoder weights from it. Use `get_cryozoounet` to get the pretrained weights.
240            If you do not pass it, the encoder starts with random weights.
241        final_activation: The activation applied after the output convolution.
242        postprocessing: A postprocessing function to apply after the U-Net output.
243        check_shape: Whether to check the input shape to the U-Net forward call.
244        conv_block_kwargs: The keyword arguments for the convolutional blocks of the decoder.
245    """
246    def __init__(
247        self,
248        out_channels: int,
249        encoder: str = "cryosiam",
250        in_channels: int = 1,
251        encoder_checkpoint: Optional[Union[str, os.PathLike, OrderedDict]] = None,
252        final_activation: Optional[Union[nn.Module, str]] = None,
253        postprocessing: Optional[Union[nn.Module, str]] = None,
254        check_shape: bool = True,
255        **conv_block_kwargs,
256    ):
257        if encoder not in ENCODERS:
258            raise ValueError(f"The encoder '{encoder}' is not supported. Choose one of {list(ENCODERS)}.")
259        encoder_model = ENCODERS[encoder](in_channels=in_channels)
260
261        features_decoder = encoder_model.features[::-1]
262        decoder = Decoder(
263            features=features_decoder,
264            scale_factors=encoder_model.scale_factors[::-1],
265            conv_block_impl=ConvBlock3d,
266            sampler_impl=Upsampler3d,
267            skip_channels=features_decoder[1:],
268            **conv_block_kwargs,
269        )
270
271        if np.all(np.array(encoder_model.stem_scale_factor) == 1):
272            out_conv = nn.Conv3d(features_decoder[-1], out_channels, 1)
273        else:
274            out_conv = OutputHead(features_decoder[-1], out_channels, encoder_model.stem_scale_factor)
275
276        super().__init__(
277            encoder=encoder_model,
278            base=nn.Identity(),
279            decoder=decoder,
280            out_conv=out_conv,
281            final_activation=final_activation,
282            postprocessing=postprocessing,
283            check_shape=check_shape,
284        )
285        self.init_kwargs = {
286            "out_channels": out_channels, "encoder": encoder, "in_channels": in_channels,
287            "final_activation": final_activation, "postprocessing": postprocessing, "check_shape": check_shape,
288            **conv_block_kwargs,
289        }
290
291        if encoder_checkpoint is not None:
292            self.encoder.load_pretrained(encoder_checkpoint)
293
294    def _check_shape(self, x):
295        spatial_shape = tuple(x.shape)[2:]
296        factor = np.ones(len(spatial_shape), dtype=int) * np.array(self.encoder.stem_scale_factor)
297        for scale_factor in self.encoder.scale_factors:
298            factor = factor * np.array(scale_factor)
299        if any(sh % fac != 0 for sh, fac in zip(spatial_shape, factor)):
300            raise ValueError(f"The input shape {spatial_shape} must be divisible by {factor.tolist()}.")
301
302
303def get_cryozoounet(
304    out_channels: int,
305    encoder: str = "cryosiam",
306    pretrained: bool = True,
307    checkpoint_path: Optional[Union[str, os.PathLike]] = None,
308    **kwargs,
309) -> CryoZooUNet:
310    """Get a CryoZooUNet with a pretrained CNN encoder.
311
312    Args:
313        out_channels: The number of output channels.
314        encoder: The name of the encoder. See `ENCODERS` for the supported names.
315        pretrained: Whether to initialize the encoder with its pretrained weights.
316        checkpoint_path: The filepath to a checkpoint on disk.
317            If you do not pass it, the function downloads the pretrained weights to the torch-em cache directory
318            when you use them the first time.
319        kwargs: Additional keyword arguments for `CryoZooUNet`.
320
321    Returns:
322        The CryoZooUNet.
323    """
324    if encoder not in ENCODERS:
325        raise ValueError(f"The encoder '{encoder}' is not supported. Choose one of {list(ENCODERS)}.")
326    encoder_checkpoint = ENCODERS[encoder].get_checkpoint(checkpoint_path) if pretrained else None
327    return CryoZooUNet(out_channels=out_channels, encoder=encoder, encoder_checkpoint=encoder_checkpoint, **kwargs)
CRYOSIAM_URL = 'https://huggingface.co/frosinastojanovska/cryosiam_v1.0/resolve/main/dense_simsiam_pretrained.ckpt'
CRYOSIAM_CHECKSUM = '19957ef7eba45658432a81d37ac1764db65f97ddbfcd55bee3c190f51a9b17e7'
ENCODERS = {'cryosiam': <class 'CryoSiamEncoder'>}
def register_encoder(name: str):
40def register_encoder(name: str):
41    """Add an encoder class to `ENCODERS` under the given name.
42
43    Use this function as a decorator for the encoder class.
44
45    Args:
46        name: The name of the encoder.
47
48    Returns:
49        The decorator, which adds the encoder class and returns it unchanged.
50    """
51    def register(encoder_class):
52        ENCODERS[name] = encoder_class
53        return encoder_class
54    return register

Add an encoder class to ENCODERS under the given name.

Use this function as a decorator for the encoder class.

Arguments:
  • name: The name of the encoder.
Returns:

The decorator, which adds the encoder class and returns it unchanged.

class CryoZooEncoder(torch.nn.modules.module.Module):
 57class CryoZooEncoder(nn.Module):
 58    """The base class for the CNN encoders of the CryoZooUNet.
 59
 60    To add an encoder, make a subclass and decorate it with `register_encoder`. The subclass must:
 61    - set `features`: the number of channels of each skip connection and of the bottleneck, from fine to coarse.
 62    - set `scale_factors`: the downsampling factor between two consecutive entries of `features`.
 63    - set `stem_scale_factor`: the downsampling factor between the input and the first skip connection.
 64    - implement `forward`: it returns the bottleneck and the list of skip connections, from fine to coarse.
 65
 66    If pretrained weights exist, the subclass must also:
 67    - set `url`, `checksum` and `cache_name`: the URL and the SHA256 checksum of the checkpoint,
 68      and the name of its folder in the torch-em cache directory.
 69    - implement `load_pretrained`.
 70
 71    A scale factor is an integer or a list with one integer for each spatial axis (Z, Y, X).
 72
 73    Args:
 74        in_channels: The number of input channels.
 75    """
 76    url: Optional[str] = None
 77    checksum: Optional[str] = None
 78    cache_name: Optional[str] = None
 79
 80    def __init__(self, in_channels: int = 1):
 81        super().__init__()
 82        self.in_channels = in_channels
 83        self.return_outputs = True
 84        self.features: List[int] = []
 85        self.scale_factors: List[Union[int, List[int]]] = []
 86        self.stem_scale_factor: Union[int, List[int]] = 1
 87
 88    @property
 89    def out_channels(self):
 90        """The number of channels of the bottleneck."""
 91        return self.features[-1]
 92
 93    def __len__(self):
 94        return len(self.scale_factors)
 95
 96    @classmethod
 97    def get_checkpoint(cls, checkpoint_path: Optional[Union[str, os.PathLike]] = None, download: bool = True) -> str:
 98        """Get the filepath to the pretrained checkpoint.
 99
100        If you pass `checkpoint_path`, this method uses this file.
101        Otherwise, it uses the checkpoint in the torch-em cache directory, and it downloads the checkpoint if needed.
102        See `torch_em.util.util.get_cache_directory` for the cache directory.
103
104        Args:
105            checkpoint_path: The filepath to a checkpoint on disk.
106            download: Whether to download the checkpoint if it is not in the cache yet.
107
108        Returns:
109            The filepath to the checkpoint.
110        """
111        if checkpoint_path is not None:
112            if not os.path.exists(checkpoint_path):
113                raise FileNotFoundError(f"The checkpoint {checkpoint_path} does not exist.")
114            return str(checkpoint_path)
115
116        if cls.url is None:
117            raise NotImplementedError(f"The encoder {cls.__name__} does not have pretrained weights.")
118
119        import pooch  # We import pooch here, so that 'import torch_em' does not need it.
120
121        folder = os.path.join(get_cache_directory(), "models", cls.cache_name)
122        fname = cls.url.split("/")[-1]
123        if not download and not os.path.exists(os.path.join(folder, fname)):
124            raise RuntimeError(f"The checkpoint is not in {folder}. Set download=True to download it.")
125
126        return pooch.retrieve(url=cls.url, known_hash=f"sha256:{cls.checksum}", fname=fname, path=folder)
127
128    def load_pretrained(self, checkpoint: Union[str, os.PathLike, OrderedDict]) -> None:
129        """Load the pretrained weights into the encoder.
130
131        Args:
132            checkpoint: The filepath to a checkpoint or a state dict for the encoder.
133        """
134        raise NotImplementedError(f"The encoder {type(self).__name__} does not have pretrained weights.")

The base class for the CNN encoders of the CryoZooUNet.

To add an encoder, make a subclass and decorate it with register_encoder. The subclass must:

  • set features: the number of channels of each skip connection and of the bottleneck, from fine to coarse.
  • set scale_factors: the downsampling factor between two consecutive entries of features.
  • set stem_scale_factor: the downsampling factor between the input and the first skip connection.
  • implement forward: it returns the bottleneck and the list of skip connections, from fine to coarse.

If pretrained weights exist, the subclass must also:

A scale factor is an integer or a list with one integer for each spatial axis (Z, Y, X).

Arguments:
  • in_channels: The number of input channels.
CryoZooEncoder(in_channels: int = 1)
80    def __init__(self, in_channels: int = 1):
81        super().__init__()
82        self.in_channels = in_channels
83        self.return_outputs = True
84        self.features: List[int] = []
85        self.scale_factors: List[Union[int, List[int]]] = []
86        self.stem_scale_factor: Union[int, List[int]] = 1

Initialize internal Module state, shared by both nn.Module and ScriptModule.

url: Optional[str] = None
checksum: Optional[str] = None
cache_name: Optional[str] = None
in_channels
return_outputs
features: List[int]
scale_factors: List[Union[int, List[int]]]
stem_scale_factor: Union[int, List[int]]
out_channels
88    @property
89    def out_channels(self):
90        """The number of channels of the bottleneck."""
91        return self.features[-1]

The number of channels of the bottleneck.

@classmethod
def get_checkpoint( cls, checkpoint_path: Union[str, os.PathLike, NoneType] = None, download: bool = True) -> str:
 96    @classmethod
 97    def get_checkpoint(cls, checkpoint_path: Optional[Union[str, os.PathLike]] = None, download: bool = True) -> str:
 98        """Get the filepath to the pretrained checkpoint.
 99
100        If you pass `checkpoint_path`, this method uses this file.
101        Otherwise, it uses the checkpoint in the torch-em cache directory, and it downloads the checkpoint if needed.
102        See `torch_em.util.util.get_cache_directory` for the cache directory.
103
104        Args:
105            checkpoint_path: The filepath to a checkpoint on disk.
106            download: Whether to download the checkpoint if it is not in the cache yet.
107
108        Returns:
109            The filepath to the checkpoint.
110        """
111        if checkpoint_path is not None:
112            if not os.path.exists(checkpoint_path):
113                raise FileNotFoundError(f"The checkpoint {checkpoint_path} does not exist.")
114            return str(checkpoint_path)
115
116        if cls.url is None:
117            raise NotImplementedError(f"The encoder {cls.__name__} does not have pretrained weights.")
118
119        import pooch  # We import pooch here, so that 'import torch_em' does not need it.
120
121        folder = os.path.join(get_cache_directory(), "models", cls.cache_name)
122        fname = cls.url.split("/")[-1]
123        if not download and not os.path.exists(os.path.join(folder, fname)):
124            raise RuntimeError(f"The checkpoint is not in {folder}. Set download=True to download it.")
125
126        return pooch.retrieve(url=cls.url, known_hash=f"sha256:{cls.checksum}", fname=fname, path=folder)

Get the filepath to the pretrained checkpoint.

If you pass checkpoint_path, this method uses this file. Otherwise, it uses the checkpoint in the torch-em cache directory, and it downloads the checkpoint if needed. See torch_em.util.util.get_cache_directory for the cache directory.

Arguments:
  • checkpoint_path: The filepath to a checkpoint on disk.
  • download: Whether to download the checkpoint if it is not in the cache yet.
Returns:

The filepath to the checkpoint.

def load_pretrained( self, checkpoint: Union[str, os.PathLike, collections.OrderedDict]) -> None:
128    def load_pretrained(self, checkpoint: Union[str, os.PathLike, OrderedDict]) -> None:
129        """Load the pretrained weights into the encoder.
130
131        Args:
132            checkpoint: The filepath to a checkpoint or a state dict for the encoder.
133        """
134        raise NotImplementedError(f"The encoder {type(self).__name__} does not have pretrained weights.")

Load the pretrained weights into the encoder.

Arguments:
  • checkpoint: The filepath to a checkpoint or a state dict for the encoder.
def load_cryosiam_encoder_state(checkpoint: Union[str, os.PathLike]) -> collections.OrderedDict:
137def load_cryosiam_encoder_state(checkpoint: Union[str, os.PathLike]) -> OrderedDict:
138    """Load the encoder weights from a CryoSiam checkpoint.
139
140    Args:
141        checkpoint: The filepath to the CryoSiam checkpoint.
142
143    Returns:
144        The state dict for the MONAI ResNet of `CryoSiamEncoder`.
145    """
146    state = torch.load(checkpoint, map_location="cpu", weights_only=True)["state_dict"]
147    prefix = "_model.encoder."
148    # The 'fc' layer belongs to the global projection head of the pretraining.
149    return OrderedDict(
150        (k[len(prefix):], v) for k, v in state.items() if k.startswith(prefix) and not k.startswith(prefix + "fc.")
151    )

Load the encoder weights from a CryoSiam checkpoint.

Arguments:
  • checkpoint: The filepath to the CryoSiam checkpoint.
Returns:

The state dict for the MONAI ResNet of CryoSiamEncoder.

@register_encoder('cryosiam')
class CryoSiamEncoder(CryoZooEncoder):
154@register_encoder("cryosiam")
155class CryoSiamEncoder(CryoZooEncoder):
156    """The 3D ResNet encoder of CryoSiam, which uses the ResNet from MONAI.
157
158    It has a 7x7x7 stem without stride and four stages with one basic block each.
159    The first three stages give the skip connections and the last stage gives the bottleneck.
160
161    Args:
162        in_channels: The number of input channels. The pretrained weights expect one channel.
163    """
164    url = CRYOSIAM_URL
165    checksum = CRYOSIAM_CHECKSUM
166    cache_name = "cryosiam"
167
168    def __init__(self, in_channels: int = 1):
169        super().__init__(in_channels=in_channels)
170        if ResNet is None:
171            raise RuntimeError("The CryoSiam encoder needs MONAI. Install it from conda-forge or with pip.")
172
173        self.features = [64, 128, 256, 512]
174        self.scale_factors = [2, 2, 2]
175        self.stem_scale_factor = 1
176
177        # We pin all the settings that the CryoSiam checkpoint depends on, in case the MONAI defaults change.
178        self.resnet = ResNet(
179            block="basic", layers=[1, 1, 1, 1], block_inplanes=self.features, spatial_dims=3,
180            n_input_channels=in_channels, conv1_t_size=7, conv1_t_stride=1, no_max_pool=True, shortcut_type="B",
181            feed_forward=False, bias_downsample=True, act=("relu", {"inplace": False}), norm="batch",
182        )
183
184    def load_pretrained(self, checkpoint: Union[str, os.PathLike, OrderedDict]) -> None:
185        state = checkpoint if isinstance(checkpoint, OrderedDict) else load_cryosiam_encoder_state(checkpoint)
186        self.resnet.load_state_dict(state)
187
188    def forward(self, x):
189        """Apply the encoder to the input data.
190
191        Args:
192            x: The input tensor with the shape (B, C, Z, Y, X).
193
194        Returns:
195            The bottleneck, and the list of skip connections from fine to coarse if `return_outputs` is True.
196        """
197        net = self.resnet
198        x = net.act(net.bn1(net.conv1(x)))
199        encoder_out = []
200        for layer in (net.layer1, net.layer2, net.layer3):
201            x = layer(x)
202            encoder_out.append(x)
203        x = net.layer4(x)
204
205        if self.return_outputs:
206            return x, encoder_out
207        else:
208            return x

The 3D ResNet encoder of CryoSiam, which uses the ResNet from MONAI.

It has a 7x7x7 stem without stride and four stages with one basic block each. The first three stages give the skip connections and the last stage gives the bottleneck.

Arguments:
  • in_channels: The number of input channels. The pretrained weights expect one channel.
CryoSiamEncoder(in_channels: int = 1)
168    def __init__(self, in_channels: int = 1):
169        super().__init__(in_channels=in_channels)
170        if ResNet is None:
171            raise RuntimeError("The CryoSiam encoder needs MONAI. Install it from conda-forge or with pip.")
172
173        self.features = [64, 128, 256, 512]
174        self.scale_factors = [2, 2, 2]
175        self.stem_scale_factor = 1
176
177        # We pin all the settings that the CryoSiam checkpoint depends on, in case the MONAI defaults change.
178        self.resnet = ResNet(
179            block="basic", layers=[1, 1, 1, 1], block_inplanes=self.features, spatial_dims=3,
180            n_input_channels=in_channels, conv1_t_size=7, conv1_t_stride=1, no_max_pool=True, shortcut_type="B",
181            feed_forward=False, bias_downsample=True, act=("relu", {"inplace": False}), norm="batch",
182        )

Initialize internal Module state, shared by both nn.Module and ScriptModule.

url = 'https://huggingface.co/frosinastojanovska/cryosiam_v1.0/resolve/main/dense_simsiam_pretrained.ckpt'
checksum = '19957ef7eba45658432a81d37ac1764db65f97ddbfcd55bee3c190f51a9b17e7'
cache_name = 'cryosiam'
features
scale_factors
stem_scale_factor
resnet
def load_pretrained( self, checkpoint: Union[str, os.PathLike, collections.OrderedDict]) -> None:
184    def load_pretrained(self, checkpoint: Union[str, os.PathLike, OrderedDict]) -> None:
185        state = checkpoint if isinstance(checkpoint, OrderedDict) else load_cryosiam_encoder_state(checkpoint)
186        self.resnet.load_state_dict(state)

Load the pretrained weights into the encoder.

Arguments:
  • checkpoint: The filepath to a checkpoint or a state dict for the encoder.
def forward(self, x):
188    def forward(self, x):
189        """Apply the encoder to the input data.
190
191        Args:
192            x: The input tensor with the shape (B, C, Z, Y, X).
193
194        Returns:
195            The bottleneck, and the list of skip connections from fine to coarse if `return_outputs` is True.
196        """
197        net = self.resnet
198        x = net.act(net.bn1(net.conv1(x)))
199        encoder_out = []
200        for layer in (net.layer1, net.layer2, net.layer3):
201            x = layer(x)
202            encoder_out.append(x)
203        x = net.layer4(x)
204
205        if self.return_outputs:
206            return x, encoder_out
207        else:
208            return x

Apply the encoder to the input data.

Arguments:
  • x: The input tensor with the shape (B, C, Z, Y, X).
Returns:

The bottleneck, and the list of skip connections from fine to coarse if return_outputs is True.

class CryoZooUNet(torch_em.model.unet.UNetBase):
224class CryoZooUNet(UNetBase):
225    """A 3D U-Net with a pretrained CNN encoder and a convolutional decoder from torch-em.
226
227    The decoder always uses the skip connections of the encoder.
228    Each spatial input dimension must be divisible by the total downsampling factor of the encoder.
229    For the CryoSiam encoder, this factor is 8.
230    The pretrained CryoSiam weights expect tomograms with inverted contrast and intensities scaled to [0, 1].
231
232    `init_kwargs` does not contain `encoder_checkpoint`. When torch-em restores a trained model,
233    it loads all the weights from the saved training state. Thus it does not load the pretrained weights again.
234
235    Args:
236        out_channels: The number of output channels.
237        encoder: The name of the encoder. See `ENCODERS` for the supported names.
238        in_channels: The number of input channels. The pretrained CryoSiam weights expect one channel.
239        encoder_checkpoint: The filepath to a checkpoint or a state dict for the encoder.
240            The model loads the encoder weights from it. Use `get_cryozoounet` to get the pretrained weights.
241            If you do not pass it, the encoder starts with random weights.
242        final_activation: The activation applied after the output convolution.
243        postprocessing: A postprocessing function to apply after the U-Net output.
244        check_shape: Whether to check the input shape to the U-Net forward call.
245        conv_block_kwargs: The keyword arguments for the convolutional blocks of the decoder.
246    """
247    def __init__(
248        self,
249        out_channels: int,
250        encoder: str = "cryosiam",
251        in_channels: int = 1,
252        encoder_checkpoint: Optional[Union[str, os.PathLike, OrderedDict]] = None,
253        final_activation: Optional[Union[nn.Module, str]] = None,
254        postprocessing: Optional[Union[nn.Module, str]] = None,
255        check_shape: bool = True,
256        **conv_block_kwargs,
257    ):
258        if encoder not in ENCODERS:
259            raise ValueError(f"The encoder '{encoder}' is not supported. Choose one of {list(ENCODERS)}.")
260        encoder_model = ENCODERS[encoder](in_channels=in_channels)
261
262        features_decoder = encoder_model.features[::-1]
263        decoder = Decoder(
264            features=features_decoder,
265            scale_factors=encoder_model.scale_factors[::-1],
266            conv_block_impl=ConvBlock3d,
267            sampler_impl=Upsampler3d,
268            skip_channels=features_decoder[1:],
269            **conv_block_kwargs,
270        )
271
272        if np.all(np.array(encoder_model.stem_scale_factor) == 1):
273            out_conv = nn.Conv3d(features_decoder[-1], out_channels, 1)
274        else:
275            out_conv = OutputHead(features_decoder[-1], out_channels, encoder_model.stem_scale_factor)
276
277        super().__init__(
278            encoder=encoder_model,
279            base=nn.Identity(),
280            decoder=decoder,
281            out_conv=out_conv,
282            final_activation=final_activation,
283            postprocessing=postprocessing,
284            check_shape=check_shape,
285        )
286        self.init_kwargs = {
287            "out_channels": out_channels, "encoder": encoder, "in_channels": in_channels,
288            "final_activation": final_activation, "postprocessing": postprocessing, "check_shape": check_shape,
289            **conv_block_kwargs,
290        }
291
292        if encoder_checkpoint is not None:
293            self.encoder.load_pretrained(encoder_checkpoint)
294
295    def _check_shape(self, x):
296        spatial_shape = tuple(x.shape)[2:]
297        factor = np.ones(len(spatial_shape), dtype=int) * np.array(self.encoder.stem_scale_factor)
298        for scale_factor in self.encoder.scale_factors:
299            factor = factor * np.array(scale_factor)
300        if any(sh % fac != 0 for sh, fac in zip(spatial_shape, factor)):
301            raise ValueError(f"The input shape {spatial_shape} must be divisible by {factor.tolist()}.")

A 3D U-Net with a pretrained CNN encoder and a convolutional decoder from torch-em.

The decoder always uses the skip connections of the encoder. Each spatial input dimension must be divisible by the total downsampling factor of the encoder. For the CryoSiam encoder, this factor is 8. The pretrained CryoSiam weights expect tomograms with inverted contrast and intensities scaled to [0, 1].

init_kwargs does not contain encoder_checkpoint. When torch-em restores a trained model, it loads all the weights from the saved training state. Thus it does not load the pretrained weights again.

Arguments:
  • out_channels: The number of output channels.
  • encoder: The name of the encoder. See ENCODERS for the supported names.
  • in_channels: The number of input channels. The pretrained CryoSiam weights expect one channel.
  • encoder_checkpoint: The filepath to a checkpoint or a state dict for the encoder. The model loads the encoder weights from it. Use get_cryozoounet to get the pretrained weights. If you do not pass it, the encoder starts with random weights.
  • final_activation: The activation applied after the output convolution.
  • postprocessing: A postprocessing function to apply after the U-Net output.
  • check_shape: Whether to check the input shape to the U-Net forward call.
  • conv_block_kwargs: The keyword arguments for the convolutional blocks of the decoder.
CryoZooUNet( out_channels: int, encoder: str = 'cryosiam', in_channels: int = 1, encoder_checkpoint: Union[str, os.PathLike, collections.OrderedDict, NoneType] = None, final_activation: Union[torch.nn.modules.module.Module, str, NoneType] = None, postprocessing: Union[torch.nn.modules.module.Module, str, NoneType] = None, check_shape: bool = True, **conv_block_kwargs)
247    def __init__(
248        self,
249        out_channels: int,
250        encoder: str = "cryosiam",
251        in_channels: int = 1,
252        encoder_checkpoint: Optional[Union[str, os.PathLike, OrderedDict]] = None,
253        final_activation: Optional[Union[nn.Module, str]] = None,
254        postprocessing: Optional[Union[nn.Module, str]] = None,
255        check_shape: bool = True,
256        **conv_block_kwargs,
257    ):
258        if encoder not in ENCODERS:
259            raise ValueError(f"The encoder '{encoder}' is not supported. Choose one of {list(ENCODERS)}.")
260        encoder_model = ENCODERS[encoder](in_channels=in_channels)
261
262        features_decoder = encoder_model.features[::-1]
263        decoder = Decoder(
264            features=features_decoder,
265            scale_factors=encoder_model.scale_factors[::-1],
266            conv_block_impl=ConvBlock3d,
267            sampler_impl=Upsampler3d,
268            skip_channels=features_decoder[1:],
269            **conv_block_kwargs,
270        )
271
272        if np.all(np.array(encoder_model.stem_scale_factor) == 1):
273            out_conv = nn.Conv3d(features_decoder[-1], out_channels, 1)
274        else:
275            out_conv = OutputHead(features_decoder[-1], out_channels, encoder_model.stem_scale_factor)
276
277        super().__init__(
278            encoder=encoder_model,
279            base=nn.Identity(),
280            decoder=decoder,
281            out_conv=out_conv,
282            final_activation=final_activation,
283            postprocessing=postprocessing,
284            check_shape=check_shape,
285        )
286        self.init_kwargs = {
287            "out_channels": out_channels, "encoder": encoder, "in_channels": in_channels,
288            "final_activation": final_activation, "postprocessing": postprocessing, "check_shape": check_shape,
289            **conv_block_kwargs,
290        }
291
292        if encoder_checkpoint is not None:
293            self.encoder.load_pretrained(encoder_checkpoint)

Initialize internal Module state, shared by both nn.Module and ScriptModule.

init_kwargs
def get_cryozoounet( out_channels: int, encoder: str = 'cryosiam', pretrained: bool = True, checkpoint_path: Union[str, os.PathLike, NoneType] = None, **kwargs) -> CryoZooUNet:
304def get_cryozoounet(
305    out_channels: int,
306    encoder: str = "cryosiam",
307    pretrained: bool = True,
308    checkpoint_path: Optional[Union[str, os.PathLike]] = None,
309    **kwargs,
310) -> CryoZooUNet:
311    """Get a CryoZooUNet with a pretrained CNN encoder.
312
313    Args:
314        out_channels: The number of output channels.
315        encoder: The name of the encoder. See `ENCODERS` for the supported names.
316        pretrained: Whether to initialize the encoder with its pretrained weights.
317        checkpoint_path: The filepath to a checkpoint on disk.
318            If you do not pass it, the function downloads the pretrained weights to the torch-em cache directory
319            when you use them the first time.
320        kwargs: Additional keyword arguments for `CryoZooUNet`.
321
322    Returns:
323        The CryoZooUNet.
324    """
325    if encoder not in ENCODERS:
326        raise ValueError(f"The encoder '{encoder}' is not supported. Choose one of {list(ENCODERS)}.")
327    encoder_checkpoint = ENCODERS[encoder].get_checkpoint(checkpoint_path) if pretrained else None
328    return CryoZooUNet(out_channels=out_channels, encoder=encoder, encoder_checkpoint=encoder_checkpoint, **kwargs)

Get a CryoZooUNet with a pretrained CNN encoder.

Arguments:
  • out_channels: The number of output channels.
  • encoder: The name of the encoder. See ENCODERS for the supported names.
  • pretrained: Whether to initialize the encoder with its pretrained weights.
  • checkpoint_path: The filepath to a checkpoint on disk. If you do not pass it, the function downloads the pretrained weights to the torch-em cache directory when you use them the first time.
  • kwargs: Additional keyword arguments for CryoZooUNet.
Returns:

The CryoZooUNet.