/*- * #%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.IJ; import java.util.ArrayList; import java.util.Date; import java.util.concurrent.atomic.AtomicInteger; import spim.vecmath.Point3d; import mpicbg.imglib.cursor.LocalizableCursor; import mpicbg.imglib.image.Image; import mpicbg.imglib.image.ImageFactory; import mpicbg.imglib.image.display.imagej.ImageJFunctions; import mpicbg.imglib.interpolation.Interpolator; import mpicbg.imglib.multithreading.SimpleMultiThreading; import mpicbg.imglib.type.numeric.real.FloatType; import mpicbg.imglib.util.Util; import mpicbg.models.AbstractAffineModel3D; import mpicbg.models.NoninvertibleModelException; import mpicbg.spim.io.IOFunctions; import mpicbg.spim.registration.ViewDataBeads; import mpicbg.spim.registration.ViewStructure; public class MappingFusionSequentialDifferentOutput extends SPIMImageFusion { final Image<FloatType> fusedImages[]; final int numViews; final int numParalellStacks; public static int[] angleIndiciesStatic = null; public int[] angleIndicies = null; public MappingFusionSequentialDifferentOutput( final ViewStructure viewStructure, final ViewStructure referenceViewStructure, final ArrayList<IsolatedPixelWeightenerFactory<?>> isolatedWeightenerFactories, final ArrayList<CombinedPixelWeightenerFactory<?>> combinedWeightenerFactories, final int numParalellStacks ) { super( viewStructure, referenceViewStructure, isolatedWeightenerFactories, combinedWeightenerFactories ); numViews = viewStructure.getNumViews(); if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Reserving memory for fused images."); if ( angleIndiciesStatic == null ) { angleIndicies = new int[ numViews ]; for ( int view = 0; view < numViews; view++ ) angleIndicies[ view ] = view; } else { if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): WARNING: Using statically defined angle-indices from class mpicbg.spim.fusion.MappingFusionSequentialDifferentOutput: " + Util.printCoordinates( angleIndicies ) ); angleIndicies = angleIndiciesStatic.clone(); } this.numParalellStacks = numParalellStacks; IJ.log( "nump = " + numParalellStacks ); fusedImages = new Image[ angleIndicies.length ]; final ImageFactory<FloatType> fusedImageFactory = new ImageFactory<FloatType>( new FloatType(), conf.processImageFactory ); final long size = (4l * imgW * imgH * imgD)/(1000l*1000l); for (int i = 0; i < angleIndicies.length; i++) { if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): Reserving " + size + " MiB for '" + viewStructure.getViews().get( angleIndicies[i] ).getName() + "'" ); fusedImages[ i ] = fusedImageFactory.createImage( new int[]{ imgW, imgH, imgD }, "Fused image" ); if ( fusedImages[i] == null && viewStructure.getDebugLevel() <= ViewStructure.DEBUG_ERRORONLY ) IOFunctions.printErr("MappingFusionSequentialDifferentOutput.constructor: Cannot create output image: " + conf.processImageFactory.getErrorMessage()); } } @Override public void fuseSPIMImages( final int channelIndex ) { // here we do all at once if ( channelIndex > 0 ) return; if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Unloading source images."); // // get all views of all channels // final ArrayList<ViewDataBeads> views = viewStructure.getViews(); final int numViews = angleIndicies.length; // unload images for ( final ViewDataBeads view : views ) view.closeImage(); if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Computing output image."); // iterate over input images in steps of numParalellStacks for ( int v = 0; v < angleIndicies.length; v += numParalellStacks ) { final int viewIndexStart = v; final int viewIndexEnd = Math.min( v + numParalellStacks, numViews ); // open input images //for ( int viewIndex = 0; viewIndex < angleIndicies.length; viewIndex++ ) for ( int viewIndex = viewIndexStart; viewIndex < viewIndexEnd; viewIndex++ ) { if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Loading view: " + views.get( angleIndicies[ viewIndex ] ).getName() ); views.get( angleIndicies[ viewIndex ] ).getImage( false ); } // compute output images in paralell final AtomicInteger ai = new AtomicInteger(0); final Thread[] threads = SimpleMultiThreading.newThreads(conf.numberOfThreads); final int numThreads = threads.length; for (int ithread = 0; ithread < threads.length; ++ithread) threads[ithread] = new Thread(new Runnable() { @Override public void run() { final int myNumber = ai.getAndIncrement(); //for ( int viewIndex = 0; viewIndex < angleIndicies.length; viewIndex++ ) for ( int viewIndex = viewIndexStart; viewIndex < viewIndexEnd; viewIndex++ ) if ( viewIndex % numThreads == myNumber) { final ViewDataBeads view = views.get( angleIndicies[ viewIndex ] ); IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): Computing individual registered image for '" + view.getName() + "'" ); if ( Math.max( view.getTile().getConnectedTiles().size(), view.getViewErrorStatistics().getNumConnectedViews() ) <= 0 && view.getViewStructure().getNumViews() > 1 ) { if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_ERRORONLY ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Cannot use view '" + view.getName() + ", view is not connected to any other view."); continue; } final Image<FloatType> img = view.getImage( conf.inputImageFactory, false ); final Interpolator<FloatType> interpolator = img.createInterpolator( conf.interpolatorFactorOutput ); final Point3d tmpCoordinates = new Point3d(); final int[] imageSize = view.getImageSize(); final int w = imageSize[ 0 ]; final int h = imageSize[ 1 ]; final int d = imageSize[ 2 ]; final AbstractAffineModel3D<?> model = (AbstractAffineModel3D<?>)view.getTile().getModel(); // temporary float array final double[] tmp = new double[ 3 ]; final CombinedPixelWeightener<?>[] combW = new CombinedPixelWeightener<?>[combinedWeightenerFactories.size()]; for (int i = 0; i < combW.length; i++) combW[i] = combinedWeightenerFactories.get(i).createInstance( views ); final double[][] loc = new double[ numViews ][ 3 ]; final boolean[] use = new boolean[ numViews ]; for ( int v = 0; v < numViews; ++v ) { use[ v ] = true; for ( int i = 0; i < 3; ++i ) loc[ v ][ i ] = viewStructure.getViews().get( v ).getImageSize()[ i ] / 2; } if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Starting fusion for: " + view.getName()); final LocalizableCursor<FloatType> iteratorFused = fusedImages[ viewIndex ].createLocalizableCursor(); try { while (iteratorFused.hasNext()) { iteratorFused.next(); // get the coordinates if cropped final int x = iteratorFused.getPosition(0) + cropOffsetX; final int y = iteratorFused.getPosition(1) + cropOffsetY; final int z = iteratorFused.getPosition(2) + cropOffsetZ; tmpCoordinates.x = x * scale + min.x; tmpCoordinates.y = y * scale + min.y; tmpCoordinates.z = z * scale + min.z; mpicbg.spim.mpicbg.Java3d.applyInverseInPlace( model, tmpCoordinates, tmp ); final int locX = (int)Util.round( tmpCoordinates.x ); final int locY = (int)Util.round( tmpCoordinates.y ); final int locZ = (int)Util.round( tmpCoordinates.z ); // do we hit the source image? if (locX >= 0 && locY >= 0 && locZ >= 0 && locX < w && locY < h && locZ < d ) { float weight = 1; // update combined weighteners if (combW.length > 0) { loc[ viewIndex ][ 0 ] = tmpCoordinates.x; loc[ viewIndex ][ 1 ] = tmpCoordinates.y; loc[ viewIndex ][ 2 ] = tmpCoordinates.z; for (final CombinedPixelWeightener<?> we : combW) we.updateWeights( loc, use ); for (final CombinedPixelWeightener<?> we : combW) weight *= we.getWeight( viewIndex ); } tmp[ 0 ] = tmpCoordinates.x; tmp[ 1 ] = tmpCoordinates.y; tmp[ 2 ] = tmpCoordinates.z; interpolator.setPosition( tmp ); final float intensity = interpolator.getType().get(); iteratorFused.getType().set( intensity * weight ); } } } catch (final NoninvertibleModelException e) { if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_ERRORONLY ) IOFunctions.println( "MappingFusionSequentialDifferentOutput(): Model not invertible for " + viewStructure ); } iteratorFused.close(); interpolator.close(); // unload input image view.closeImage(); } } }); SimpleMultiThreading.startAndJoin( threads ); } if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println("(" + new Date(System.currentTimeMillis()) + "): Done computing output image."); } @Override public Image<FloatType> getFusedImage() { return fusedImages[ 0 ]; } public Image<FloatType> getFusedImage( final int index ) { return fusedImages[ index ]; } @Override public boolean saveAsTiffs( final String dir, final String name, final int channelIndex ) { if ( channelIndex > 0 ) return true; boolean success = true; for ( int i = 0; i < fusedImages.length; i++ ) { final ViewDataBeads view = viewStructure.getViews().get( angleIndicies[ i ] ); if ( viewStructure.getDebugLevel() <= ViewStructure.DEBUG_MAIN ) IOFunctions.println( "(" + new Date(System.currentTimeMillis()) + "): Saving '" + name + "_ch" + view.getChannel() + "_angle" + view.getAcqusitionAngle() + "'" ); success &= ImageJFunctions.saveAsTiffs( fusedImages[ i ], dir, name + "_ch" + view.getChannel() + "_angle" + view.getAcqusitionAngle(), ImageJFunctions.GRAY32 ); } return success; } @Override public void closeImages() { for (int i = 0; i < fusedImages.length; i++) fusedImages[i].close(); } }