You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
6.2 KiB
240 lines
6.2 KiB
package ij.process; |
|
import ij.IJ; |
|
import ij.ImagePlus; |
|
import ij.ImageStack; |
|
import ij.gui.Roi; |
|
import ij.plugin.filter.ThresholdToSelection; |
|
|
|
/* |
|
* This plugin takes a binary stack as input, where some slices are |
|
* labeled (i.e. contain white regions), and some are not. The unlabaled |
|
* regions are interpolated by weighting the signed integer distance |
|
* transformed labeled slices. |
|
* |
|
* from: |
|
* http://fiji.sc/cgi-bin/gitweb.cgi?p=fiji.git;a=blob_plain;f=src-plugins/VIB-lib/vib/BinaryInterpolator.java;h=f6a610659ad624d13f94639bc5c0149712071f9f;hb=refs/heads/master |
|
*/ |
|
|
|
public class BinaryInterpolator { |
|
int[][] idt; |
|
int w, h; |
|
|
|
public void run(ImagePlus image, Roi[] rois) { |
|
w = image.getWidth(); |
|
h = image.getHeight(); |
|
ImageStack stack = new ImageStack(w, h); |
|
int firstIndex = -1, lastIndex = -1; |
|
for(int i = 1; i < rois.length; i++) { |
|
if(rois[i] != null) { |
|
firstIndex = (firstIndex == -1) ? i : firstIndex; |
|
lastIndex = i; |
|
} |
|
} |
|
if (firstIndex == -1) { |
|
IJ.error("There must be at least one selection in order to interpolate."); |
|
return; |
|
} |
|
|
|
for(int i = firstIndex; i <= lastIndex; i++) { |
|
ByteProcessor bp = new ByteProcessor(w, h); |
|
if(rois[i] != null) { |
|
bp.copyBits(rois[i].getMask(), |
|
rois[i].getBounds().x, |
|
rois[i].getBounds().y, |
|
ij.process.Blitter.ADD); |
|
} |
|
stack.addSlice("", bp); |
|
} |
|
run(stack); |
|
ImagePlus roiImage = new ImagePlus("bla", stack); |
|
|
|
ThresholdToSelection ts = new ThresholdToSelection(); |
|
ts.setup("", roiImage); |
|
for(int i = firstIndex; i <= lastIndex; i++) { |
|
ImageProcessor bp = stack.getProcessor(1); |
|
stack.deleteSlice(1); |
|
int threshold = 255; |
|
bp.setThreshold(threshold, threshold, ImageProcessor.NO_LUT_UPDATE); |
|
ts.run(bp); |
|
rois[i] = roiImage.getRoi(); |
|
} |
|
} |
|
|
|
public void run(ImageStack stack) { |
|
int sliceCount = stack.getSize(); |
|
if (sliceCount < 3) { |
|
IJ.error("Too few slices to interpolate!"); |
|
return; |
|
} |
|
|
|
IJ.showStatus("getting signed integer distance transform"); |
|
w = stack.getWidth(); |
|
h = stack.getHeight(); |
|
idt = new int[sliceCount][]; |
|
int first = sliceCount, last = -1; |
|
|
|
for (int z = 0; z < sliceCount; z++) { |
|
idt[z] = getIDT(stack.getProcessor(z + 1).getPixels()); |
|
if (idt[z] != null) { |
|
if (z < first) |
|
first = z; |
|
last = z; |
|
} |
|
} |
|
|
|
if (first == last || last < 0) { |
|
IJ.error("Not enough to interpolate"); |
|
return; |
|
} |
|
|
|
IJ.showStatus("calculating weights"); |
|
int current = 0, next = first; |
|
for (int z = first; z < last; z++) { |
|
if (z == next) { |
|
current = z; |
|
for (next = z + 1; idt[next] == null; next++); |
|
continue; |
|
} |
|
|
|
byte[] p = |
|
(byte[])stack.getProcessor(z + 1).getPixels(); |
|
for (int i = 0; i < w * h; i++) |
|
if (0 <= idt[current][i] * (next - z) |
|
+ idt[next][i] * (z - current)) |
|
p[i] = (byte)255; |
|
IJ.showProgress(z - first + 1, last - z); |
|
} |
|
} |
|
|
|
/* |
|
* The following calculates the signed integer distance transform. |
|
* Distance transform means that each pixel is assigned the distance |
|
* to the boundary. |
|
* IDT means that the distance is not the Euclidean, but the minimal |
|
* sum of neighbour distances with 3 for horizontal and neighbours, |
|
* and 4 for diagonal neighbours (in 3d, the 3d diagonal neighbour |
|
* would be 5). |
|
* Signed means that the outside pixels have a negative sign. |
|
*/ |
|
class IDT { |
|
int[] result; |
|
|
|
IDT() { |
|
result = new int[w * h]; |
|
int infinity = (w + h) * 9; |
|
|
|
for (int i = 0; i < result.length; i++) |
|
result[i] = infinity; |
|
} |
|
|
|
int init(byte[] p) { |
|
int count = 0; |
|
|
|
for (int j = 0; j < h; j++) |
|
for (int i = 0; i < w; i++) { |
|
int idx = i + w * j; |
|
if (isBoundary(p, i, j)) { |
|
result[idx] = 0; |
|
count++; |
|
} else if (isJustOutside(p, i, j)) |
|
result[idx] = -1; |
|
} |
|
return count; |
|
} |
|
|
|
final void idt(int x, int y, int dx, int dy) { |
|
if (x + dx < 0 || y + dy < 0 || |
|
x + dx >= w || y + dy >= h) |
|
return; |
|
int value = result[x + dx + w * (y + dy)]; |
|
int distance = (dx == 0 || dy == 0 ? 3 : 4); |
|
value += distance * (value < 0 ? -1 : 1); |
|
if (Math.abs(result[x + w * y]) > Math.abs(value)) |
|
result[x + w * y] = value; |
|
} |
|
|
|
void propagate() { |
|
for (int j = 0; j < h; j++) |
|
for (int i = 0; i < w; i++) { |
|
idt(i, j, -1, 0); |
|
idt(i, j, -1, -1); |
|
idt(i, j, 0, -1); |
|
} |
|
|
|
for (int j = h - 1; j >= 0; j--) |
|
for (int i = w - 1; i >= 0; i--) { |
|
idt(i, j, +1, 0); |
|
idt(i, j, +1, +1); |
|
idt(i, j, 0, +1); |
|
} |
|
|
|
for (int i = w - 1; i >= 0; i--) |
|
for (int j = h - 1; j >= 0; j--) { |
|
idt(i, j, +1, 0); |
|
idt(i, j, +1, +1); |
|
idt(i, j, 0, +1); |
|
} |
|
|
|
for (int i = 0; i < w; i++) |
|
for (int j = 0; j < h; j++) { |
|
idt(i, j, -1, 0); |
|
idt(i, j, -1, -1); |
|
idt(i, j, 0, -1); |
|
} |
|
} |
|
} |
|
|
|
int[] getIDT(Object pixels) { |
|
IDT idt = new IDT(); |
|
if (idt.init((byte[])pixels) == 0) |
|
return null; |
|
idt.propagate(); |
|
return idt.result; |
|
} |
|
|
|
final boolean isBoundary(byte[] pixels, int x, int y) { |
|
if (pixels[x + w * y] == 0) |
|
return false; |
|
if (x <= 0 || pixels[x - 1 + w * y] == 0) |
|
return true; |
|
if (x >= w - 1 || pixels[x + 1 + w * y] == 0) |
|
return true; |
|
if (y <= 0 || pixels[x + w * (y - 1)] == 0) |
|
return true; |
|
if (y >= h - 1 || pixels[x + w * (y + 1)] == 0) |
|
return true; |
|
if (x <= 0 || y <= 0 || pixels[x - 1 + w * (y - 1)] == 0) |
|
return true; |
|
if (x <= 0 || y >= h - 1 || pixels[x - 1 + w * (y + 1)] == 0) |
|
return true; |
|
if (x >= w - 1 || y <= 0 || pixels[x + 1 + w * (y - 1)] == 0) |
|
return true; |
|
if (x >= w - 1 || y >= h - 1 || |
|
pixels[x + 1 + w * (y + 1)] == 0) |
|
return true; |
|
return false; |
|
} |
|
|
|
final boolean isJustOutside(byte[] pixels, int x, int y) { |
|
if (pixels[x + w * y] != 0) |
|
return false; |
|
if (x > 0 && pixels[x - 1 + w * y] != 0) |
|
return true; |
|
if (x < w - 1 && pixels[x + 1 + w * y] != 0) |
|
return true; |
|
if (y > 0 && pixels[x + w * (y - 1)] != 0) |
|
return true; |
|
if (y < h - 1 && pixels[x + w * (y + 1)] != 0) |
|
return true; |
|
if (x > 0 && y > 0 && pixels[x - 1 + w * (y - 1)] != 0) |
|
return true; |
|
if (x > 0 && y < h - 1 && pixels[x - 1 + w * (y + 1)] != 0) |
|
return true; |
|
if (x < w - 1 && y > 0 && pixels[x + 1 + w * (y - 1)] != 0) |
|
return true; |
|
if (x < w - 1 && y < h - 1 && |
|
pixels[x + 1 + w * (y + 1)] != 0) |
|
return true; |
|
return false; |
|
} |
|
}
|
|
|