Skip to content

Commit 622558b

Browse files
committed
Fix bug reported on issue #87 of watershed-labeled voxels out of the
valid mask. The problem was in the creation of markers, where neighbors of the original markers were also added to the list without checking the valid mask.
1 parent f08e484 commit 622558b

2 files changed

Lines changed: 81 additions & 3 deletions

File tree

‎src/main/java/inra/ijpb/watershed/MarkerControlledWatershedTransform3D.java‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -555,7 +555,7 @@ public ImagePlus applyWithPriorityQueue()
555555
+ cDist2cur ) );
556556
}
557557
}
558-
else if ( tabLabels[u][v][w] > 0 &&
558+
else if ( tabLabels[u][v][w] > 0 && maskStack.getVoxel( u, v, w ) > 0 &&
559559
! neighborLabels.contains(
560560
tabLabels[ u ][ v ][ w ] ) )
561561
{
@@ -572,8 +572,14 @@ else if ( tabLabels[u][v][w] > 0 &&
572572
// now that we know the voxel is labeled, add neighbors to list
573573
for( VoxelRecord v : neighborVoxels )
574574
{
575-
tabLabels[ v.getCursor().getX() ][ v.getCursor().getY() ][ v.getCursor().getZ() ] = INQUEUE;
576-
voxelList.add( v );
575+
final int x = v.getCursor().getX();
576+
final int y = v.getCursor().getY();
577+
final int z = v.getCursor().getZ();
578+
if ( maskStack.getVoxel( x, y, z ) > 0 )
579+
{
580+
tabLabels[ x ][ y ][ z ] = INQUEUE;
581+
voxelList.add( v );
582+
}
577583
}
578584
}
579585
}
@@ -1050,6 +1056,7 @@ public PriorityQueue<VoxelRecord> extractVoxelValuesPriorityQueue(
10501056
if ( u >= 0 && u < size1 &&
10511057
v >= 0 && v < size2 &&
10521058
w >= 0 && w < size3 &&
1059+
ipMask.getf( u, v ) > 0 &&
10531060
(int) seedStack.getVoxel( u, v, w ) == 0 &&
10541061
tabLabels[ u ][ v ][ w ] != INQUEUE )
10551062
{
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*-
2+
* #%L
3+
* Mathematical morphology library and plugins for ImageJ/Fiji.
4+
* %%
5+
* Copyright (C) 2014 - 2026 INRA.
6+
* %%
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU Lesser General Public License as
9+
* published by the Free Software Foundation, either version 3 of the
10+
* License, or (at your option) any later version.
11+
* #L%
12+
*/
13+
package inra.ijpb.plugins;
14+
15+
import static org.junit.Assert.assertEquals;
16+
17+
import org.junit.Test;
18+
19+
import ij.ImagePlus;
20+
import ij.ImageStack;
21+
import ij.process.FloatProcessor;
22+
import ij.process.ImageProcessor;
23+
import ij.process.ByteProcessor;
24+
import inra.ijpb.watershed.Watershed;
25+
26+
public class Watershed3DMaskTest {
27+
28+
@Test
29+
public void testWatershedRespectsMask3D() {
30+
final int w = 5, h = 5, d = 3;
31+
32+
ImageStack inputStack = new ImageStack(w, h);
33+
ImageStack maskStack = new ImageStack(w, h);
34+
35+
for (int z = 0; z < d; z++) {
36+
FloatProcessor fp = new FloatProcessor(w, h);
37+
ByteProcessor mp = new ByteProcessor(w, h);
38+
for (int y = 0; y < h; y++) {
39+
for (int x = 0; x < w; x++) {
40+
// simple gradient
41+
fp.setf(x, y, (float) (z * 10 + x + y));
42+
// mask center 3x3 is 1, border is 0
43+
if (x >= 1 && x <= 3 && y >= 1 && y <= 3)
44+
mp.setf(x, y, 1);
45+
else
46+
mp.setf(x, y, 0);
47+
}
48+
}
49+
inputStack.addSlice(fp);
50+
maskStack.addSlice(mp);
51+
}
52+
53+
ImagePlus input = new ImagePlus("input", inputStack);
54+
ImagePlus mask = new ImagePlus("mask", maskStack);
55+
56+
ImagePlus ws = Watershed.computeWatershed(input, mask, 6);
57+
// verify no labels outside mask
58+
ImageStack out = ws.getImageStack();
59+
for (int z = 0; z < d; z++) {
60+
ImageProcessor outIp = out.getProcessor(z+1);
61+
ImageProcessor maskIp = maskStack.getProcessor(z+1);
62+
for (int y = 0; y < h; y++) {
63+
for (int x = 0; x < w; x++) {
64+
if (maskIp.getf(x, y) == 0) {
65+
assertEquals("Label outside mask at ("+x+","+y+","+z+")", 0f, outIp.getf(x, y), 0f);
66+
}
67+
}
68+
}
69+
}
70+
}
71+
}

0 commit comments

Comments
 (0)