package gdsc.smlm.results;
import java.awt.Rectangle;
import java.util.Arrays;
import com.thoughtworks.xstream.annotations.XStreamOmitField;
/*-----------------------------------------------------------------------------
* 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.
*---------------------------------------------------------------------------*/
/**
* Wraps an image source and allows aggregation of consecutive frames.
*/
public class AggregatedImageSource extends ImageSource
{
private final int aggregate;
private final ImageSource imageSource;
// Used for frame-based read
@XStreamOmitField
private int lastFrame;
@XStreamOmitField
private int lastStartFrame;
@XStreamOmitField
private int lastEndFrame;
@XStreamOmitField
private float[] lastImage = null;
/**
* Create a new aggregated image source using the given image source
*
* @param imageSource
* The image source to aggregate (must not be null)
* @param aggregate
* The number of frames to aggregate (must be above 1)
*/
public AggregatedImageSource(ImageSource imageSource, int aggregate)
{
super("");
if (imageSource == null)
throw new IllegalArgumentException("Image source must not be null");
if (aggregate < 2)
throw new IllegalArgumentException("The number of frames to aggregate ({0}) must be above 1");
setName("Aggregated (" + aggregate + ") " + imageSource.getName());
this.imageSource = imageSource;
this.aggregate = aggregate;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getWidth()
*/
@Override
public int getWidth()
{
return imageSource.getWidth();
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getHeight()
*/
@Override
public int getHeight()
{
return imageSource.getHeight();
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getFrames()
*/
@Override
public int getFrames()
{
return (int) Math.ceil((double) imageSource.getFrames() / aggregate);
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getParent()
*/
@Override
public ImageSource getParent()
{
return imageSource;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getOriginal()
*/
@Override
public ImageSource getOriginal()
{
return imageSource.getOriginal();
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ResultsSource#openSource()
*/
@Override
protected boolean openSource()
{
return imageSource.openSource();
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ResultsSource#close()
*/
@Override
public void close()
{
imageSource.close();
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#nextFrame(java.awt.Rectangle)
*/
@Override
protected float[] nextFrame(Rectangle bounds)
{
// Aggregate frames consecutive frames
float[] image = imageSource.next(bounds);
if (image != null)
{
// Ensure the original image is not updated by creating a copy
image = Arrays.copyOf(image, image.length);
final int start = imageSource.getStartFrameNumber();
int end = imageSource.getEndFrameNumber();
for (int n = 1; n < aggregate; n++)
{
float[] image2 = imageSource.next(bounds);
if (image2 == null)
break;
end = imageSource.getEndFrameNumber();
for (int i = 0; i < image.length; i++)
image[i] += image2[i];
}
// Ensure that the frame number is recorded
setFrameNumber(start, end);
//System.out.printf("Aggregated %d-%d\n", start, end);
}
return image;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#getFrame(int, java.awt.Rectangle)
*/
@Override
protected float[] getFrame(int frame, Rectangle bounds)
{
if (frame < 1)
return null;
// Calculate if the cache is invalid
if (frame != lastFrame || lastImage == null)
{
// Try and get the desired frame
float[] image = imageSource.get(frame, bounds);
if (image == null)
return null;
lastStartFrame = imageSource.getStartFrameNumber();
lastEndFrame = imageSource.getEndFrameNumber();
// Ensure the original image is not updated by creating a copy
image = Arrays.copyOf(image, image.length);
// Go forwards until the desired number of frames have been collated
int collated = 1;
int nextFrame = frame;
while (collated < aggregate && imageSource.isValid(++nextFrame))
{
float[] image2 = imageSource.get(nextFrame, bounds);
if (image2 != null)
{
lastEndFrame = imageSource.getEndFrameNumber();
for (int i = 0; i < image.length; i++)
image[i] += image2[i];
collated++;
}
}
// Cache the image
lastImage = image;
lastFrame = frame;
}
// Ensure that the frame number is recorded
setFrameNumber(lastStartFrame, lastEndFrame);
return lastImage;
}
/**
* @param frame
* @return
*/
@Override
public boolean isValid(int frame)
{
return imageSource.isValid(frame);
}
/**
* @return The number of frames to aggregate
*/
public int getAggregate()
{
return aggregate;
}
/*
* (non-Javadoc)
*
* @see gdsc.smlm.results.ImageSource#toString()
*/
@Override
public String toString()
{
return String.format("%s (Aggregate %d images)", imageSource.toString(), aggregate);
}
}