package com.srbenoit.microscopy;
import java.awt.image.BufferedImage;
import com.srbenoit.filter.AbstractFilter;
import com.srbenoit.filter.FilterException;
import com.srbenoit.filter.FilterInput;
import com.srbenoit.filter.FilterOutput;
import com.srbenoit.filter.FilterTreeExecutor;
import com.srbenoit.filter.Pipe;
import com.srbenoit.filter.items.ImageArrayPipeItem;
/**
* A filter that merges all z planes in an image array, producing a new image array that has 1
* plane per time point.
*/
public class ZPlaneMergerFilter extends AbstractFilter {
/** version number for serialization */
private static final long serialVersionUID = 4347190635894369722L;
/**
* Constructs a new ZPlaneMergerFilter
.
*/
public ZPlaneMergerFilter() {
super("Z-plane merger", ZPlaneMergerFilter.class.getName());
this.inputs.add(new FilterInput(ImageArrayPipeItem.class, "Images to be merged"));
this.outputs.add(new FilterOutput(ImageArrayPipeItem.class, "Merged images",
"merged_images"));
makeRenderer();
}
/**
* Duplicates the filter including all of its settings, but returns an independent object.
*
* @return the duplicated object
*/
@Override public AbstractFilter duplicate() {
return new ZPlaneMergerFilter();
}
/**
* Performs the filter operation.
*
* @param executor the FilterTreeExecutor
that is executing the filter
* @param pipe a pipe containing the input data items
* @throws FilterException if the filter cannot complete
*/
@Override public void filter(final FilterTreeExecutor executor, final Pipe pipe)
throws FilterException {
ImageArrayPipeItem input;
ImageArrayPipeItem output;
validateInputs(pipe);
executor.indicateProgress(1);
input = (ImageArrayPipeItem) pipe.get(this.inputs.get(0).getKey());
executor.indicateProgress(2);
output = new ImageArrayPipeItem(this.outputs.get(0).getKey(),
"Z-plane merged images (PNG format)", pipe, input.getXLabel(), input.getYLabel(),
input.getXSize(), 1, "png");
pipe.add(output);
executor.indicateProgress(3);
runFilter(executor, input, output);
executor.indicateProgress(80);
if (!executor.isCancelled()) {
pipe.save(executor);
}
executor.indicateProgress(100);
}
/**
* Runs the filter, reading the source Metamorph TIF files and extracting an array of images,
* the first dimension of which is time, and the second dimension of which is z plane.
*
* @param executor the FilterTreeExecutor
that is executing the filter
* @param input the images to process
* @param output the image array in which to store the merged images
*/
private void runFilter(final FilterTreeExecutor executor, final ImageArrayPipeItem input,
final ImageArrayPipeItem output) {
int width;
BufferedImage[] src;
BufferedImage merged;
width = input.getXSize();
for (int x = 0; x < width; x++) {
if (executor.isCancelled()) {
break;
}
executor.indicateProgress(5 + (75 * x / width));
src = input.getImages(x);
merged = mergeImages(src);
output.setImage(x, 0, merged);
}
}
/**
* Merge the images in all planes into a composite merged image. The maximum intensity value
* from the set of planes is accepted as the merged image intensity value.
*
* @param source the source images to be merged
* @return the merged image
*/
public static BufferedImage mergeImages(final BufferedImage[] source) {
BufferedImage result;
int width;
int height;
int effWidth;
int effHeight;
int rgb;
int red;
int green;
int blue;
width = source[0].getWidth();
height = source[0].getHeight();
result = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// Determine the effective width and height of the merged image
effWidth = width;
effHeight = height;
for (int i = 0; i < source.length; i++) {
if (source[i].getWidth() < effWidth) {
effWidth = source[i].getWidth();
}
if (source[i].getHeight() < effHeight) {
effHeight = source[i].getHeight();
}
}
// Perform the merge operation
for (int x = 0; x < effWidth; x++) {
for (int y = 0; y < effHeight; y++) {
red = 0;
green = 0;
blue = 0;
for (int i = 0; i < source.length; i++) {
rgb = source[i].getRGB(x, y);
if ((rgb & 0x000000FF) > red) {
red = rgb & 0x000000FF;
}
if ((rgb & 0x0000FF00) > green) {
green = rgb & 0x0000FF00;
}
if ((rgb & 0x00FF0000) > blue) {
blue = rgb & 0x00FF0000;
}
}
rgb = red | green | blue;
result.setRGB(x, y, rgb);
}
}
return result;
}
/**
* Generates the string representation of the filter.
*
* @return the string representation
*/
@Override public String toString() {
return "ZPlaneMergerFilter";
}
}