/*-
* #%L
* Fiji distribution of ImageJ for the life sciences.
* %%
* Copyright (C) 2007 - 2017 Fiji developers.
* %%
* 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 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/gpl-2.0.html>.
* #L%
*/
package mpicbg.spim.fusion;
import ij.ImagePlus;
import java.util.ArrayList;
import spim.vecmath.Point3f;
import mpicbg.imglib.image.Image;
import mpicbg.imglib.image.display.imagej.ImageJFunctions;
import mpicbg.imglib.type.numeric.real.FloatType;
import mpicbg.models.AbstractAffineModel3D;
import mpicbg.spim.io.IOFunctions;
import mpicbg.spim.io.ImgLibSaver;
import mpicbg.spim.io.SPIMConfiguration;
import mpicbg.spim.registration.ViewDataBeads;
import mpicbg.spim.registration.ViewStructure;
public abstract class SPIMImageFusion
{
protected SPIMConfiguration conf;
final protected ArrayList <IsolatedPixelWeightenerFactory<?>> isolatedWeightenerFactories;
final protected ArrayList <CombinedPixelWeightenerFactory<?>> combinedWeightenerFactories;
protected Point3f min = null, max = null, size = null, location000 = null;
protected int cropOffsetX, cropOffsetY, cropOffsetZ, imgW, imgH, imgD, scale;
final protected ViewStructure viewStructure;
public SPIMImageFusion( ViewStructure viewStructure, ViewStructure referenceViewStructure,
ArrayList<IsolatedPixelWeightenerFactory<?>> isolatedWeightenerFactories,
ArrayList<CombinedPixelWeightenerFactory<?>> combinedWeightenerFactories )
{
this.conf = viewStructure.getSPIMConfiguration();
this.viewStructure = viewStructure;
this.scale = conf.scale;
this.isolatedWeightenerFactories = isolatedWeightenerFactories;
this.combinedWeightenerFactories = combinedWeightenerFactories;
// compute the final image size
computeFinalImageSize( referenceViewStructure.getViews() );
// compute cropped image size
initFusion();
// compute location of point (0,0,0) in the global coordinate system
location000 = new Point3f();
location000.x = cropOffsetX * scale + min.x;
location000.y = cropOffsetY * scale + min.y;
location000.z = cropOffsetZ * scale + min.z;
if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN )
IOFunctions.println( "Location of pixel (0,0,0) in global coordinates is: " + location000 );
}
public abstract void fuseSPIMImages( int channelIndex );
public abstract Image<FloatType> getFusedImage();
public ImagePlus getFusedImageCopy() { return ImageJFunctions.copyToImagePlus( getFusedImage() ); }
public ImagePlus getFusedImageVirtual() { return ImageJFunctions.displayAsVirtualStack( getFusedImage() ); }
public void closeImages() { getFusedImage().close(); }
public boolean saveAsTiffs( final String dir, final String name, final int channelIndex ) { return ImgLibSaver.saveAsTiffs( getFusedImage(), dir, name + "_ch" + viewStructure.getChannelNum( channelIndex ), ImageJFunctions.GRAY32 ); }
public Point3f getOutputImageMinCoordinate() { return min; }
public Point3f getOutputImageMaxCoordinate() { return max; }
public Point3f getOutputImageSize() { return size; }
protected void initFusion()
{
cropOffsetX = conf.cropOffsetX/scale;
cropOffsetY = conf.cropOffsetY/scale;
cropOffsetZ = conf.cropOffsetZ/scale;
if (conf.cropSizeX == 0)
imgW = (Math.round((float)Math.ceil(size.x)) + 1)/scale;
else
imgW = conf.cropSizeX/scale;
if (conf.cropSizeY == 0)
imgH = (Math.round((float)Math.ceil(size.y)) + 1)/scale;
else
imgH = conf.cropSizeY/scale;
if (conf.cropSizeZ == 0)
imgD = (Math.round((float)Math.ceil(size.z)) + 1)/scale;
else
imgD = conf.cropSizeZ/scale;
}
public void computeFinalImageSize( final ArrayList <ViewDataBeads> views )
{
min = new Point3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
max = new Point3f(-Float.MAX_VALUE, -Float.MAX_VALUE, -Float.MAX_VALUE);
size = new Point3f();
computeImageSize( views, min, max, size, conf.scale, conf.cropSizeX, conf.cropSizeY, conf.cropSizeZ, viewStructure.getDebugLevel() );
}
public static void computeImageSize( final ArrayList <ViewDataBeads> views, final Point3f min, final Point3f max, final Point3f size, final int scale,
final int cropSizeX, final int cropSizeY, final int cropSizeZ, final int debugLevel )
{
min.x = Float.MAX_VALUE;
min.y = Float.MAX_VALUE;
min.z = Float.MAX_VALUE;
max.x = -Float.MAX_VALUE;
max.y = -Float.MAX_VALUE;
max.z = -Float.MAX_VALUE;
for ( final ViewDataBeads view : views )
{
if ( Math.max( view.getViewErrorStatistics().getNumConnectedViews(), view.getTile().getConnectedTiles().size() ) <= 0 && view.getViewStructure().getNumViews() > 1 )
{
if ( view.getUseForRegistration() == true )
if ( debugLevel <= ViewStructure.DEBUG_ERRORONLY )
IOFunctions.printErr( "Cannot use view " + view + ", it is not connected to any other view!" );
continue;
}
else if ( view.getViewStructure().getNumViews() == 1 )
{
if ( debugLevel <= ViewStructure.DEBUG_ERRORONLY )
IOFunctions.printErr( "Warning: Only one view given: " + view );
}
final int[] dim = view.getImageSize();
// transform the corner points of the current view
final double[] minCoordinate = new double[]{ 0, 0, 0 };
final double[] maxCoordinate = new double[]{ dim[0], dim[1], dim[2] };
((AbstractAffineModel3D<?>)view.getTile().getModel()).estimateBounds( minCoordinate, maxCoordinate );
min.x = (float)Math.min( minCoordinate[ 0 ], min.x );
min.y = (float)Math.min( minCoordinate[ 1 ], min.y );
min.z = (float)Math.min( minCoordinate[ 2 ], min.z );
max.x = (float)Math.max( maxCoordinate[ 0 ], max.x );
max.y = (float)Math.max( maxCoordinate[ 1 ], max.y );
max.z = (float)Math.max( maxCoordinate[ 2 ], max.z );
}
size.sub(max, min);
if ( debugLevel <= ViewStructure.DEBUG_MAIN )
{
IOFunctions.println("Dimension of final output image:");
IOFunctions.println("From : " + min + " to " + max);
double ram = (4l * size.x * size.y * size.z)/(1024l * 1024l);
IOFunctions.println("Size: " + size + " needs " + Math.round( ram ) + " MB of RAM" );
if ( scale != 1 )
{
ram = (4l * size.x/scale * size.y/scale * size.z/scale)/(1024l * 1024l);
if ( views.get( 0 ).getViewStructure().getSPIMConfiguration().isDeconvolution )
IOFunctions.println("Scaled size("+scale+"): (" + Math.round(size.x/scale) + ", " + Math.round(size.y/scale) + ", " + Math.round(size.z/scale) + ") needs " + Math.round( ram ) + " MB of RAM x " + 2*views.size() + " = " + Math.round( ram )*2*views.size() + " MB" );
else
IOFunctions.println("Scaled size("+scale+"): (" + Math.round(size.x/scale) + ", " + Math.round(size.y/scale) + ", " + Math.round(size.z/scale) + ") needs " + Math.round( ram ) + " MB of RAM" );
}
if ( cropSizeX > 0 && cropSizeY > 0 && cropSizeZ > 0)
{
if (scale != 1 )
IOFunctions.println("Cropped & scaled("+scale+") image size: " + cropSizeX/scale + "x" + cropSizeY/scale + "x" + cropSizeZ/scale);
else
IOFunctions.println("Cropped image size: " + cropSizeX + "x" + cropSizeY + "x" + cropSizeZ);
ram = (4l * cropSizeX/scale * cropSizeY/scale * cropSizeZ/scale)/(1024l * 1024l);
if ( views.get( 0 ).getViewStructure().getSPIMConfiguration().isDeconvolution )
IOFunctions.println("Needs " + Math.round( ram ) + " MB of RAM x " + 2*views.size() + " = " + Math.round( ram )*2*views.size() + " MB" );
else
IOFunctions.println("Needs " + Math.round( ram ) + " MB of RAM");
}
}
}
}