Skip to content

Commit 0105428

Browse files
author
anna-grim
committed
feat: n2v2unet subclass
1 parent f352c0c commit 0105428

1 file changed

Lines changed: 109 additions & 0 deletions

File tree

  • src/aind_exaspim_image_compression/machine_learning

src/aind_exaspim_image_compression/machine_learning/unet3d.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,112 @@ def forward(self, x):
377377
(B, 1, D, H, W).
378378
"""
379379
return self.conv(x)
380+
381+
382+
class N2V2UNet(UNet):
383+
"""
384+
Noise2Void2 variant of the 3D U-Net.
385+
386+
This implementation makes two architectural modifications relative to
387+
the standard U-Net:
388+
389+
1. Replaces max pooling with anti-aliased MaxBlurPool3D.
390+
2. Removes the highest-resolution skip connection.
391+
392+
Residual learning is disabled by default to avoid reintroducing an
393+
identity path from the input to the output.
394+
"""
395+
396+
def __init__(
397+
self,
398+
width_multiplier=1,
399+
trilinear=True,
400+
residual=True,
401+
):
402+
# Call parent class
403+
super().__init__(
404+
width_multiplier=width_multiplier,
405+
trilinear=trilinear,
406+
residual=residual,
407+
)
408+
409+
factor = 2 if trilinear else 1
410+
411+
# Encoder
412+
self.down1 = DownBlur(self.channels[0], self.channels[1])
413+
self.down2 = DownBlur(self.channels[1], self.channels[2])
414+
self.down3 = DownBlur(self.channels[2], self.channels[3])
415+
self.down4 = DownBlur(self.channels[3], self.channels[4] // factor)
416+
417+
@property
418+
def config(self):
419+
config = super().config
420+
config["model"] = "N2V2UNet"
421+
return config
422+
423+
424+
class DownBlur(nn.Module):
425+
"""Downsampling block using MaxBlurPool3D."""
426+
427+
def __init__(self, in_channels, out_channels):
428+
super().__init__()
429+
430+
self.maxpool_conv = nn.Sequential(
431+
MaxBlurPool3D(in_channels),
432+
DoubleConv(in_channels, out_channels),
433+
)
434+
435+
def forward(self, x):
436+
return self.maxpool_conv(x)
437+
438+
439+
class MaxBlurPool3D(nn.Module):
440+
"""
441+
Anti-aliased 3D max pooling (BlurPool).
442+
"""
443+
444+
def __init__(self, channels):
445+
# Call parent class
446+
super().__init__()
447+
448+
# Create kernel
449+
kernel = torch.tensor([1.0, 2.0, 1.0])
450+
kernel = (
451+
kernel[:, None, None]
452+
* kernel[None, :, None]
453+
* kernel[None, None, :]
454+
)
455+
kernel /= kernel.sum()
456+
457+
self.register_buffer(
458+
"kernel",
459+
kernel[None, None],
460+
persistent=False,
461+
)
462+
463+
# Instance attributes
464+
self.channels = channels
465+
self.pool = nn.MaxPool3d(2, stride=1)
466+
467+
def forward(self, x):
468+
x = self.pool(x)
469+
x = F.pad(
470+
x,
471+
[1, 1, 1, 1, 1, 1],
472+
mode="replicate",
473+
)
474+
475+
kernel = self.kernel.expand(
476+
self.channels,
477+
-1,
478+
-1,
479+
-1,
480+
-1,
481+
)
482+
483+
return F.conv3d(
484+
x,
485+
kernel,
486+
stride=2,
487+
groups=self.channels,
488+
)

0 commit comments

Comments
 (0)