package gdsc.smlm.ij;
import java.awt.Rectangle;
import org.apache.commons.math3.util.FastMath;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
import gdsc.smlm.ij.utils.ImageConverter;
import gdsc.smlm.results.ImageSource;
import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.WindowManager;
import ij.io.FileInfo;
import ij.process.ImageProcessor;
/*-----------------------------------------------------------------------------
* GDSC SMLM Software
*
* Copyright (C) 2013 Alex Herbert
* Genome Damage and Stability Centre
* University of Sussex, UK
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*---------------------------------------------------------------------------*/
/**
* Represent an ImageJ image as a results source. Supports all greyscale images. Only processes channel 0 of 32-bit
* colour images.
*/
public class IJImageSource extends ImageSource
{
@XStreamOmitField
private int slice;
private int singleFrame = 0;
private int extraFrames = 0;
@XStreamOmitField
private Object[] imageArray;
@XStreamOmitField
private ImageStack imageStack;
private String path;
/**
* Create a new image source using the name of the image file
*
* @param name
*/
public IJImageSource(String name)
{
super(name);
}
/**
* Create a new image source using the path to the image file
*
* @param name
* @param path
*/
public IJImageSource(String name, String path)
{
super(name);
this.path = path;
}
/**
* Create a new image source from an ImagePlus
*
* @param imp
*/
public IJImageSource(ImagePlus imp)
{
super(imp.getTitle());
initialise(imp);
}
private boolean initialise(ImagePlus imp)
{
imageArray = null;
imageStack = null;
if (imp == null)
return false;
ImageStack s = imp.getImageStack();
if (s.isVirtual())
{
// We must use the image stack to get the image data for virtual images
imageStack = s;
}
else
{
// We can access the image array directly
imageArray = s.getImageArray();
}
width = imp.getWidth();
height = imp.getHeight();
// Store the number of valid frames
if (singleFrame > 0)
frames = 1 + this.extraFrames;
else
frames = imp.getStackSize();
slice = 0;
FileInfo info = imp.getOriginalFileInfo();
if (info != null)
path = info.directory + info.fileName;
return true;
}
/**
* Create a new image source from an ImageProcessor
*
* @param name
* @param ip
*/
public IJImageSource(String name, ImageProcessor ip)
{
super(name);
imageArray = new Object[] { ip.getPixels() };
width = ip.getWidth();
height = ip.getHeight();
frames = 1;
}
/**
* Create a new image source from an ImagePlus. Specify a single frame for processing.
*
* @param imp
* @param frame
*/
public IJImageSource(ImagePlus imp, int frame)
{
this(imp, frame, 0);
}
/**
* Create a new image source from an ImagePlus. Specify a start frame and any additional frames (after the start)
* for processing.
*
* @param imp
* @param startFrame
* The first frame to process
* @param extraFrames
* The number of additional frames to process
*/
public IJImageSource(ImagePlus imp, int startFrame, int extraFrames)
{
super(imp.getTitle());
// Ensure only a single frame is processed
singleFrame = startFrame;
this.extraFrames = FastMath.max(0, extraFrames);
initialise(imp);
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#openSource()
*/
@Override
protected boolean openSource()
{
if (nullImageArray() && imageStack == null)
{
// Try and find the image using the name or path
ImagePlus imp = null;
if (getName() != null)
imp = WindowManager.getImage(getName());
if (imp == null)
{
// Try and open the original image from file
if (path != null && path.length() > 0)
{
imp = IJ.openImage(path);
if (imp == null)
{
// Some readers return null and display the image, e.g. BioFormats.
// Add code to handle this.
}
else
{
// Ensure the image has the correct name
if (getName() != null)
imp.setTitle(getName());
}
}
}
return initialise(imp);
}
slice = 0;
return true;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#close()
*/
@Override
public void close()
{
imageArray = null;
imageStack = null;
}
/**
* @return True is the image array or any part of it is null
*/
private boolean nullImageArray()
{
if (imageArray == null)
return true;
// Check the image array. This is set to null when an image is closed by ImageJ
// allowing us to detect when the image is still available
for (Object o : imageArray)
if (o == null)
return true;
return false;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#nextFrame(java.awt.Rectangle)
*/
@Override
protected float[] nextFrame(Rectangle bounds)
{
++slice;
if (singleFrame > 0)
{
// Return frames from the starting frame until the extra frames limit is reached
return (slice - 1 <= extraFrames) ? get(singleFrame + slice - 1, bounds) : null;
}
return get(slice, bounds);
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getFrame(int, java.awt.Rectangle)
*/
@Override
protected float[] getFrame(int frame, Rectangle bounds)
{
if (imageArray != null)
{
if (frame > 0 && frame <= imageArray.length)
{
return ImageConverter.getData(imageArray[frame - 1], width, height, bounds, null);
}
}
else if (imageStack != null)
{
// This is a virtual stack so access the image processor through the virtual stack object
if (frame > 0 && frame <= imageStack.getSize())
{
return ImageConverter.getData(imageStack.getPixels(frame), width, height, bounds, null);
}
}
return null;
}
@Override
public boolean isValid(int frame)
{
if (singleFrame > 0)
{
return (frame >= singleFrame && frame <= singleFrame + extraFrames);
}
return frame > 0 && frame <= frames;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#toString()
*/
@Override
public String toString()
{
String s = super.toString();
if (path != null)
s += String.format(" (%s)", path);
return s;
}
}