Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 10-multi-channel-image-handling
  • 25-multithreads-heavy-tasks
  • 9-gui-features
  • dev
  • dev-batch-mode
  • dev-display
  • master
  • median-3d
  • Cnam
  • zellige-core-1.0.0
  • zellige-core-1.1.0
  • zellige-core-1.1.1
  • zellige-core-1.1.3
13 results

Target

Select target project
  • ida-public/zellige-core
1 result
Select Git revision
  • 10-multi-channel-image-handling
  • 25-multithreads-heavy-tasks
  • 9-gui-features
  • dev
  • dev-batch-mode
  • dev-display
  • master
  • median-3d
  • Cnam
  • zellige-core-1.0.0
  • zellige-core-1.1.0
  • zellige-core-1.1.1
  • zellige-core-1.1.3
13 results
Show changes
Showing
with 725 additions and 613 deletions
......@@ -29,19 +29,19 @@
package fr.pasteur.ida.zellige.gui.task;
import fr.pasteur.ida.zellige.gui.ImageFXDisplay;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.image.ImageView;
import net.imglib2.img.Img;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.real.FloatType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ImageFXDisplayTask extends AbstractTask< ImageView[] >
public class SelectionDisplayTask extends AbstractTask< ImageView[] >
{
private final static Logger LOGGER = LoggerFactory.getLogger( ImageFXDisplayTask.class );
private final SimpleObjectProperty< Img< BitType > > input;
private final static Logger LOGGER = LoggerFactory.getLogger( SelectionDisplayTask.class );
private final Img< FloatType > input;
public ImageFXDisplayTask( SimpleObjectProperty< Img< BitType > > input )
public SelectionDisplayTask( Img< FloatType > input )
{
this.input = input;
}
......@@ -50,9 +50,8 @@ public class ImageFXDisplayTask extends AbstractTask< ImageView[] >
@Override
protected ImageView[] call()
{
ImageFXDisplay display = new ImageFXDisplay( input.getValue() );
display.set();
LOGGER.debug( "Classified image updated." );
return display.getImageViews();
ImageView[] imageViews = new ImageView[ ( int ) input.dimension(2)];
ImageFXDisplay.setSelectedPixels( input, imageViews );
return imageViews;
}
}
......@@ -28,24 +28,24 @@
*/
package fr.pasteur.ida.zellige.gui.task;
import fr.pasteur.ida.zellige.element.Pixels;
import fr.pasteur.ida.zellige.steps.selection.postTreatment.PostTreatment;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleObjectProperty;
import net.imglib2.img.Img;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.real.FloatType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SmoothingTask extends AbstractTask< Pixels[][] >
import static fr.pasteur.ida.zellige.gui.MainAppFrame.GUI_MARKER;
public class SmoothingTask extends AbstractTask<Img < FloatType >>
{
private final static Logger LOGGER = LoggerFactory.getLogger( SmoothingTask.class );
private final SimpleObjectProperty< Img< BitType > > islandSearchImage;
private final DoubleProperty sigmaXY;
private final DoubleProperty sigmaZ;
private final Img< BitType > islandSearchImage;
private final int sigmaXY;
private final int sigmaZ;
public SmoothingTask( SimpleObjectProperty< Img< BitType > > islandSearchImage, DoubleProperty sigmaXY, DoubleProperty sigmaZ )
public SmoothingTask( Img< BitType > islandSearchImage, int sigmaXY, int sigmaZ )
{
this.islandSearchImage = islandSearchImage;
this.sigmaXY = sigmaXY;
......@@ -53,9 +53,9 @@ public class SmoothingTask extends AbstractTask< Pixels[][] >
}
@Override
protected Pixels[][] call() throws Exception
protected Img<FloatType> call() throws Exception
{
LOGGER.info( "Computing first smoothing..." );
return PostTreatment.runSmoothing( islandSearchImage.getValue(), sigmaXY.intValue(), sigmaZ.intValue() );
LOGGER.info( GUI_MARKER,"Computing first smoothing..." );
return PostTreatment.runSmoothing( islandSearchImage, sigmaXY, sigmaZ );
}
}
package fr.pasteur.ida.zellige.gui.task;
import fr.pasteur.ida.zellige.element.ReferenceSurface;
import fr.pasteur.ida.zellige.steps.projection.ReferenceSurfaceProjection;
import net.imglib2.img.Img;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SubVolumeChannelTask< T extends RealType< T > & NativeType< T > > extends AbstractTask<Img<T>>
{
private final static Logger LOGGER = LoggerFactory.getLogger( SubVolumeChannelTask.class );
private final ReferenceSurface<T> referenceSurface;
private final int channel;
private final int delta;
private final int offset;
public SubVolumeChannelTask( ReferenceSurface< T > referenceSurface, int channel, int delta, int offset )
{
this.referenceSurface = referenceSurface;
this.channel = channel;
this.delta = delta;
this.offset = offset;
}
@Override
protected Img<T> call()
{
Img<T> subVolume = ReferenceSurfaceProjection.getSubStack( referenceSurface,channel, delta, offset );
LOGGER.debug( "SubVolume processed" );
return subVolume;
}
}
package fr.pasteur.ida.zellige.gui.task;
import fr.pasteur.ida.zellige.steps.projection.ReferenceSurfaceProjection;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SubVolumeTask< T extends RealType< T > & NativeType< T > > extends AbstractTask<Img<T>>
{
private final static Logger LOGGER = LoggerFactory.getLogger( SubVolumeTask.class );
private final RandomAccessibleInterval<T> input;
private final ImgFactory<T> factory;
private final RandomAccessibleInterval< UnsignedShortType > zMap;
private final int delta;
private final int offset;
public SubVolumeTask( RandomAccessibleInterval< T > input, ImgFactory< T > factory, Img< UnsignedShortType > zMap, int delta, int offset )
{
this.input = input;
this.factory = factory;
this.zMap = zMap;
this.delta = delta;
this.offset = offset;
}
@Override
protected Img<T> call()
{
Img<T> subVolume = ReferenceSurfaceProjection.getSubStack(
input,
factory,
zMap,
delta,
offset );
LOGGER.debug( "SubVolume processed" );
return subVolume;
}
}
......@@ -28,16 +28,14 @@
*/
package fr.pasteur.ida.zellige.steps;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessible;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.*;
import net.imglib2.algorithm.gauss3.Gauss3;
import net.imglib2.img.Img;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import net.imglib2.view.Views;
/**
* Some "utils" methods.
*/
......@@ -62,6 +60,14 @@ public class Utils
Gauss3.gauss( sigma, infiniteInput, output );
}
public static < T extends RealType< T > & NativeType< T > > void
gaussianConvolution_( RandomAccessibleInterval< T > input, Img< FloatType > output,
double[] sigma )
{
RandomAccessible< T > infiniteInput = Views.extendValue( input, input.randomAccess().get().getRealFloat() );
Gauss3.gauss( sigma, infiniteInput, output );
}
public static < T extends RealType< T > & NativeType< T > > void setPosition( RandomAccess< T > randomAccess, int u, int v )
{
......@@ -75,7 +81,6 @@ public class Utils
randomAccess.setPosition( z, 2 );
}
public static < T extends RealType< T > & NativeType< T > > T setPositionAndGet( RandomAccess< T > randomAccess, int u, int v )
{
setPosition( randomAccess, u, v );
......
package fr.pasteur.ida.zellige.steps.construction;
import fr.pasteur.ida.zellige.element.ReferenceSurface;
import fr.pasteur.ida.zellige.element.Surface;
import fr.pasteur.ida.zellige.steps.construction.exception.NoSurfaceFoundException;
import fr.pasteur.ida.zellige.steps.construction.rounds.ConstructionParameters;
import fr.pasteur.ida.zellige.steps.construction.rounds.FirstRoundConstruction;
import fr.pasteur.ida.zellige.steps.construction.rounds.SecondRoundConstruction;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
public class Construction< T extends RealType< T > & NativeType< T > >
{
private final static Logger LOGGER = LoggerFactory.getLogger( Construction.class );
private final ArrayList< ReferenceSurface< T > > referenceSurfaces = new ArrayList<>();
private int FS_startingOS_count;
private int FS_OS_count;
private int FS_smallSurfaces;
private int FS_goodSurfaces;
private int FS_finalizedSurfaces;
private int SS_startingOS_count;
private int SS_OS_count;
private int SS_smallSurfaces;
private int SS_goodSurfaces;
private long FS_OSConstructionProcessingTime;
private long FS_SurfaceConstructionProcessingTime;
private long SS_OSConstructionProcessingTime;
private long SS_SurfaceConstructionProcessingTime;
public static < T extends RealType< T > & NativeType< T > > ArrayList< ReferenceSurface< T > >
run( ConstructionParameters[] constructionParameters,
RandomAccessibleInterval< T > input,
ImgFactory< T > factory,
Img< FloatType > selectedPixels, int bin ) throws NoSurfaceFoundException
{
Construction< T > construction = new Construction<>();
construction.run( constructionParameters, selectedPixels, input, factory, bin );
return construction.referenceSurfaces;
}
private void run( ConstructionParameters[] constructionParameters, Img< FloatType > selectedPixels, RandomAccessibleInterval< T > input, ImgFactory< T > factory, int bin ) throws NoSurfaceFoundException
{
LOGGER.debug( "Running construction..." );
/* First round construction*/
FirstRoundConstruction step1 = new FirstRoundConstruction( selectedPixels, constructionParameters[ 0 ] );
step1.process();
ArrayList< Surface > tempSurfaces = step1.getSurfaces();
FS_startingOS_count = step1.getStartingOSCount();
FS_OS_count = step1.getOSCount();
FS_goodSurfaces = step1.getGoodSurfacesCount();
FS_smallSurfaces = step1.getSmallSurfacesCount();
FS_finalizedSurfaces = tempSurfaces.size();
FS_OSConstructionProcessingTime = step1.getOSConstructionProcessingTime();
FS_SurfaceConstructionProcessingTime = step1.getConstructionProcessingTime();
LOGGER.debug( "first round surfaces = {}", tempSurfaces.size() );
/* Second round construction */
SecondRoundConstruction step2 =
new SecondRoundConstruction( tempSurfaces, constructionParameters[ 1 ] );
step2.process();
ArrayList< Surface > finalSurfaces = step2.getSurfaces();
SS_startingOS_count = step2.getStartingOSCount();
SS_OS_count = step2.getOSCount();
SS_goodSurfaces = step2.getGoodSurfacesCount();
SS_smallSurfaces = step2.getSmallSurfacesCount();
SS_OSConstructionProcessingTime = step2.getOSConstructionProcessingTime();
SS_SurfaceConstructionProcessingTime = step2.getConstructionProcessingTime();
/* Building reference surfaces */
referenceSurfaces.addAll( ConstructionCompletion.run( input, factory, finalSurfaces, bin ) );
LOGGER.debug( " Constructions of {} surfaces.", referenceSurfaces.size() );
}
public ArrayList< ReferenceSurface< T > > getReferenceSurfaces()
{
return referenceSurfaces;
}
}
......@@ -28,10 +28,12 @@
*/
package fr.pasteur.ida.zellige.steps.construction;
import fr.pasteur.ida.zellige.Main;
import fr.pasteur.ida.zellige.element.Pixels;
import fr.pasteur.ida.zellige.element.ReferenceSurface;
import fr.pasteur.ida.zellige.element.Surface;
import fr.pasteur.ida.zellige.element.surfaceLine.SurfaceLine;
import fr.pasteur.ida.zellige.steps.selection.util.Unbinning;
import net.imglib2.Dimensions;
import net.imglib2.FinalDimensions;
import net.imglib2.RandomAccess;
......@@ -42,6 +44,8 @@ import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import net.imglib2.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
......@@ -54,19 +58,22 @@ import java.util.ArrayList;
*/
public class ConstructionCompletion< T extends RealType< T > & NativeType< T > >
{
private final ArrayList< ReferenceSurface< T > > referenceSurfaces;
private final static Logger LOGGER = LoggerFactory.getLogger( ConstructionCompletion.class );
private final ArrayList< ReferenceSurface< T > > referenceSurfaces;
private final int bin;
public ConstructionCompletion()
public ConstructionCompletion( int bin)
{
this.referenceSurfaces = new ArrayList<>();
this.bin = bin;
}
public static < T extends RealType< T > & NativeType< T > > ArrayList< ReferenceSurface< T > > run(
RandomAccessibleInterval< T > input, ImgFactory< T > factory, ArrayList< Surface > surfaces )
RandomAccessibleInterval< T > input, ImgFactory< T > factory, ArrayList< Surface > surfaces, int bin )
{
ConstructionCompletion< T > completion = new ConstructionCompletion<>();
ConstructionCompletion< T > completion = new ConstructionCompletion<>( bin );
completion.referenceSurfaceInstantiation( input, factory, surfaces );
return completion.getReferenceSurfaces();
}
......@@ -78,7 +85,15 @@ public class ConstructionCompletion< T extends RealType< T > & NativeType< T > >
for ( Surface surface : finalSurfaces )
{
Img< UnsignedShortType > processedZMap = processedZMap( surface );
if (bin > 1)
{
processedZMap = Unbinning.run( processedZMap, bin );
LOGGER.info("Height map unbinned");
}
else
{
LOGGER.debug ( "No bin.");
}
ReferenceSurface< T > referenceSurface = new ReferenceSurface<>( input, factory, processedZMap, index++ );
referenceSurfaces.add( referenceSurface );
}
......
......@@ -170,7 +170,7 @@ public class Interpolation
private Img< UnsignedShortType > execute( RandomAccessibleInterval< UnsignedShortType > input, int dimension )
{
ImgFactory< UnsignedShortType > factory = new ArrayImgFactory<>( new UnsignedShortType() );
Img< UnsignedShortType > interpolated = factory.create( input );
Img< UnsignedShortType > interpolated = factory.create( input.dimensionsAsLongArray() );
ImgUtil.copy( input, interpolated );
RandomAccess< UnsignedShortType > interpolatedAccess = interpolated.randomAccess();
RandomAccess< UnsignedShortType > zMapAccess = input.randomAccess();
......
......@@ -86,7 +86,7 @@ public abstract class ConstructionRound
}
else
{
LOGGER.debug( this.getClass().toGenericString() + " : No Surface Found in {}", this.getClass() );
LOGGER.debug( "{} : No Surface Found in {}", this.getClass().toGenericString(), this.getClass() );
throw new NoSurfaceFoundException();
}
}
......@@ -98,7 +98,7 @@ public abstract class ConstructionRound
*/
void mergeSurface( ArrayList< Surface > surfaces )
{
LOGGER.debug( this.getClass().toGenericString() + " : Merging step..." );
LOGGER.debug( "{} : Merging step...", this.getClass().toGenericString() );
// Merging step.
ArrayList< Surface > toRemoved = new ArrayList<>();
for ( int i = surfaces.size() - 1; i > 0; i-- )
......@@ -115,7 +115,7 @@ public abstract class ConstructionRound
}
else
{
LOGGER.debug( "Not the same : " + two.overlappingRate( one ) * 10 / 10.00 );
LOGGER.debug( "Not the same : {}", two.overlappingRate( one ) * 10 / 10.00 );
}
}
}
......@@ -146,10 +146,10 @@ public abstract class ConstructionRound
}
}
protected ArrayList< Surface > constructSurfaces( OSEListArray oseLists, int lineLength )
protected ArrayList< Surface > constructSurfaces( OSEListArray oseLists, int lineLength, int surfaceMaxSize )
{
SurfacesConstruction construction =
new SurfacesConstruction( oseLists, lineLength, overlap, connexityRate, surfaceMinSizeFactor );
new SurfacesConstruction( oseLists, lineLength, overlap, connexityRate, surfaceMinSizeFactor, surfaceMaxSize );
construction.buildAllSurfaces();
int tempSmallSurfaceCount = construction.getSmallSurfaceCount();
smallSurfacesCount += tempSmallSurfaceCount;
......
......@@ -28,9 +28,17 @@
*/
package fr.pasteur.ida.zellige.steps.construction.rounds;
import fr.pasteur.ida.zellige.element.Coordinate;
import fr.pasteur.ida.zellige.element.Pixels;
import fr.pasteur.ida.zellige.element.ose.OSEListArray;
import fr.pasteur.ida.zellige.steps.Utils;
import fr.pasteur.ida.zellige.steps.construction.exception.NoSurfaceFoundException;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.img.Img;
import net.imglib2.type.NativeType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.real.FloatType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
......@@ -41,23 +49,24 @@ public class FirstRoundConstruction extends ConstructionRound
private final Pixels[][] maximums;
public FirstRoundConstruction( Pixels[][] maximums, ConstructionParameters constructionParameters )
public FirstRoundConstruction( Img< FloatType > maximums, ConstructionParameters constructionParameters )
{
super( constructionParameters );
this.maximums = maximums;
this.maximums = buildPixelArray( maximums );
}
public FirstRoundConstruction( Pixels[][] maximums, final double startingSizeThreshold,
public FirstRoundConstruction( Img< FloatType > maximums, final double startingSizeThreshold,
final int overlap,
final double connexityRate,
final double surfaceMinSizeFactor )
{
super( startingSizeThreshold, overlap, connexityRate, surfaceMinSizeFactor );
this.maximums = maximums;
this.maximums = buildPixelArray( maximums );
}
public void process() throws NoSurfaceFoundException
{
int surfaceMaxSize = maximums.length * maximums[0].length;
LOGGER.debug( "Processing..." );
/* OSE construction according to the dimension considered */
long startOseConstructionTime = System.currentTimeMillis();
......@@ -68,11 +77,47 @@ public class FirstRoundConstruction extends ConstructionRound
LOGGER.debug( "Starting surface construction..." );
int lineLength = maximums[ 0 ].length;
long startSurfacesConstructionTime = System.currentTimeMillis();
this.getSurfaces().addAll( constructSurfaces( oseLists, lineLength ) );
this.getSurfaces().addAll( constructSurfaces( oseLists, lineLength, surfaceMaxSize) );
long stopSurfacesConstructionTime = System.currentTimeMillis();
this.setConstructionProcessingTime( stopSurfacesConstructionTime - startSurfacesConstructionTime );
LOGGER.debug( "Finalizing : {} surfaces", this.getSurfaces().size() );
this.finalizeSurface( this.getSurfaces() );
LOGGER.debug( "Round completed : {} surfaces found", this.getSurfaces().size() );
}
/**
* @param stack an image as a {@link RandomAccessibleInterval}
* @param <T> the image type
* @return the image as a 2D {@link Pixels} array.
*/
public static < T extends RealType< T > & NativeType< T > > Pixels[][] buildPixelArray(
final RandomAccessibleInterval< T > stack )
{
RandomAccess< T > access = stack.randomAccess();
Pixels[][] pixels = new Pixels[ ( int ) stack.dimension( 1 ) ][ ( int ) stack.dimension( 0 ) ];
for ( int x = 0; x <= stack.dimension( 0 ) - 1; x++ )
{
for ( int y = 0; y <= stack.dimension( 1 ) - 1; y++ )
{
for ( int z = 0; z <= stack.dimension( 2 ) - 1; z++ )
{
double value = Utils.setPositionAndGet( access, x, y, z ).getRealDouble();
if ( value > 0 )
{
if ( pixels[ y ][ x ] == null )
{ // nothing yet
pixels[ y ][ x ] = new Pixels( new Coordinate( x, y, z ) );
}
else // already at least two coordinates
{
pixels[ y ][ x ].add( new Coordinate( x, y, z ) );
}
}
}
}
}
return pixels;
}
}
......@@ -98,7 +98,9 @@ public class SecondRoundConstruction extends ConstructionRound
if ( surface.hasDuplicate() )// CoordinateList instead of Coordinate
{
LOGGER.debug( "Surface with duplicates to process." );
Pixels[][] maximums = rebuildMaximums( surface );
int surfaceMaxSize = maximums.length * maximums[0].length;
/* OSE construction according to the dimension considered */
long startOseConstructionTime = System.currentTimeMillis();
OSEListArray oseLists = constructOSE( maximums );
......@@ -107,7 +109,7 @@ public class SecondRoundConstruction extends ConstructionRound
int lineLength = maximums[ 0 ].length;
long startSurfacesConstructionTime = System.currentTimeMillis();
ArrayList< Surface > temps = constructSurfaces( oseLists, lineLength );
ArrayList< Surface > temps = constructSurfaces( oseLists, lineLength, surfaceMaxSize);
long stopSurfacesConstructionTime = System.currentTimeMillis();
tempSurfaceTime += stopSurfacesConstructionTime - startSurfacesConstructionTime;
if ( temps.isEmpty() )
......
......@@ -65,7 +65,7 @@ public abstract class OseConstruction
oseListArray.set(i, findOSE( maximums[ i ] ));
}
startingStatus.setStartingStatus();
LOGGER.debug( "Starting size = " + startingStatus.getStartingSize() );
LOGGER.debug( "Starting size = {}", startingStatus.getStartingSize() );
LOGGER.debug( "Ose construction complete. All sections processed." );
}
......
......@@ -53,19 +53,21 @@ public class SurfacesConstruction
private final int overlap;
private int smallSurfaceCount;
private final double surfaceMinSizeFactor;
private final int surfaceMaxSize;
public SurfacesConstruction( OSEListArray oseLists, int width, int overlap, double connexity, double surfaceMinSizeFactor )
public SurfacesConstruction( OSEListArray oseLists, int width, int overlap, double connexity, double surfaceMinSizeFactor, int surfaceMaxSize )
{
this.oseListArray = oseLists;
this.width = width;
this.connexity = connexity;
this.overlap = overlap;
this.surfaceMinSizeFactor = surfaceMinSizeFactor;
this.surfaceMaxSize = surfaceMaxSize;
}
public static ArrayList< Surface > run( OSEListArray oseListArray, int surfaceLineLength, int overlap, double connexity, double surfaceMinSizeFactor )
public static ArrayList< Surface > run( OSEListArray oseListArray, int width, int overlap, double connexity, double surfaceMinSizeFactor, int surfaceMaxSize )
{
SurfacesConstruction reconstruction = new SurfacesConstruction( oseListArray, surfaceLineLength, overlap, connexity, surfaceMinSizeFactor );
SurfacesConstruction reconstruction = new SurfacesConstruction( oseListArray, width, overlap, connexity, surfaceMinSizeFactor, surfaceMaxSize );
reconstruction.buildAllSurfaces();
return reconstruction.constructedSurfaces;
}
......@@ -73,6 +75,7 @@ public class SurfacesConstruction
public void buildAllSurfaces()
{
LOGGER.debug("Surface min size= {}", surfaceMaxSize * surfaceMinSizeFactor );
while ( true )
{
// All the OSLists are set to "not visited".
......@@ -97,9 +100,10 @@ public class SurfacesConstruction
* @param surface the surface to check.
*/
private void checkSurface( Surface surface )
{LOGGER.debug("****************Surface size = {}", surface.getSize());
if ( surface.getSize() >= surfaceMaxSize * surfaceMinSizeFactor )
{
if ( surface.getSize() >= width * oseListArray.getLength() * surfaceMinSizeFactor )
{
constructedSurfaces.add( surface );
}
else
......
package fr.pasteur.ida.zellige.steps.projection;
public class ChannelProjectionParameter
{
private final int delta;
private final int offset;
private final String method;
public ChannelProjectionParameter( int deltaZ, int offset, String method )
{
this.delta = deltaZ;
this.offset = offset;
this.method = method;
}
public int getDelta()
{
return delta;
}
public int getOffset()
{
return offset;
}
public String getMethod()
{
return method;
}
}
......@@ -30,66 +30,61 @@ package fr.pasteur.ida.zellige.steps.projection;
import fr.pasteur.ida.zellige.steps.selection.exception.DataValidationException;
import java.util.ArrayList;
public class ProjectionParameters
{
private final int delta;
private final String projectionMethod;
public ProjectionParameters( int delta, String projectionMethod ) throws DataValidationException
{
projectionParametersValidationCheck( delta, projectionMethod );
this.delta = delta;
this.projectionMethod = projectionMethod;
}
private final ArrayList<ChannelProjectionParameter> channelProjectionParameters;
/**
* Checks if the delta value is superior or equal to zero and throws an {@link DataValidationException} otherwise.
* @param value the delta value choose by the user. //TODO javadoc : extracted volume defined as delta x 2 + 1
* @throws DataValidationException - if the value is under zero.
*/
private void deltaValidationCheck( int value ) throws DataValidationException
{
if ( value < 0 )
public ProjectionParameters( ArrayList< ChannelProjectionParameter > channelProjectionParameters )
{
throw new DataValidationException( "The value of parameter Delta has to be superior to zero !" );
}
this.channelProjectionParameters = channelProjectionParameters;
}
/**
*
* @param projectionMethod the method choose to projection the extracted surface.
* @throws DataValidationException if the method is not implemented (implemented methods are MIP, Mean, Median and MinIP).
*/
private void projectionMethodValidationCheck( String projectionMethod ) throws DataValidationException
{
if ( ! projectionMethod.equals( "MIP" ) && ! projectionMethod.equals( "Mean" ) && ! projectionMethod.equals( "MinIP" ) && ! projectionMethod.equals( "Median" ) )
{
throw new DataValidationException( String.format( "The method %s is not implemented in Zellige !", projectionMethod ) );
}
}
/**
* Checks the validity of both parameters.
*
* @param delta the extracted stack parameters
* @param projectionMethod the method choose to projection the extracted surface.
* @throws DataValidationException if at least one parameter is not valid.
*/
private void projectionParametersValidationCheck( int delta, String projectionMethod ) throws DataValidationException
{
deltaValidationCheck( delta );
projectionMethodValidationCheck( projectionMethod );
}
// /**
// * Checks if the delta value is superior or equal to zero and throws an {@link DataValidationException} otherwise.
// * @param value the delta value choose by the user. //TODO javadoc : extracted volume defined as delta x 2 + 1
// * @throws DataValidationException - if the value is under zero.
// */
// private void deltaValidationCheck( int value ) throws DataValidationException
// {
// if ( value < 0 )
// {
// throw new DataValidationException( "The value of parameter Delta has to be superior to zero !" );
// }
// }
//
// /**
// *
// * @param projectionMethod the method choose to projection the extracted surface.
// * @throws DataValidationException if the method is not implemented (implemented methods are MIP, Mean, Median and MinIP).
// */
// private void projectionMethodValidationCheck( String projectionMethod ) throws DataValidationException
// {
// if ( ! projectionMethod.equals( "MIP" ) && ! projectionMethod.equals( "Mean" ) && ! projectionMethod.equals( "MinIP" ) && ! projectionMethod.equals( "Median" ) )
// {
// throw new DataValidationException( String.format( "The method %s is not implemented in Zellige !", projectionMethod ) );
// }
// }
//
// /**
// * Checks the validity of both parameters.
// *
// * @param delta the extracted stack parameters
// * @param projectionMethod the method choose to projection the extracted surface.
// * @throws DataValidationException if at least one parameter is not valid.
// */
// private void projectionParametersValidationCheck( int delta, String projectionMethod ) throws DataValidationException
// {
// deltaValidationCheck( delta );
// projectionMethodValidationCheck( projectionMethod );
// }
public int getDelta()
{
return delta;
}
public String getProjectionType()
public ArrayList< ChannelProjectionParameter > getChannelProjectionParameters()
{
return projectionMethod;
return channelProjectionParameters;
}
}
......@@ -28,20 +28,28 @@
*/
package fr.pasteur.ida.zellige.steps.projection;
import fr.pasteur.ida.zellige.element.Projection;
import fr.pasteur.ida.zellige.element.ReferenceSurface;
import net.imglib2.Cursor;
import net.imglib2.IterableInterval;
import io.scif.img.IO;
import net.imagej.ImageJ;
import net.imagej.ImgPlus;
import net.imagej.axis.Axes;
import net.imagej.axis.AxisType;
import net.imglib2.RandomAccess;
import net.imglib2.RandomAccessibleInterval;
import net.imglib2.display.ColorTable;
import net.imglib2.img.Img;
import net.imglib2.img.ImgFactory;
import net.imglib2.img.array.ArrayImgFactory;
import net.imglib2.img.display.imagej.ImageJFunctions;
import net.imglib2.loops.LoopBuilder;
import net.imglib2.type.NativeType;
import net.imglib2.type.logic.BitType;
import net.imglib2.type.numeric.RealType;
import net.imglib2.type.numeric.integer.UnsignedShortType;
import net.imglib2.view.Views;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import static fr.pasteur.ida.zellige.steps.Utils.setPosition;
......@@ -50,302 +58,285 @@ import static fr.pasteur.ida.zellige.steps.Utils.setPosition;
* @author ctrebeau
* Methods to extract the reference surface from a stack with the height map obtained with the program.
*/
@SuppressWarnings( "Duplicates" )
public class ReferenceSurfaceProjection< T extends RealType< T > & NativeType< T > >
{
public static < T extends RealType< T > & NativeType< T > > void run( ReferenceSurface< T > referenceSurface,
ProjectionParameters projectionParameters,
DisplayParameters displayParameters ) throws NoPossibleDisplayException
private final static Logger LOGGER = LoggerFactory.getLogger( ReferenceSurfaceProjection.class );
public static < T extends RealType< T > & NativeType< T > > Projection< T > run( ReferenceSurface< T > referenceSurface, int delta, int offset, String method )
{
Projection< T > projection = new Projection<>( offset, delta, method, referenceSurface );
projection.setSubVolume( getSubStack( referenceSurface, delta, offset ) );
if ( method.equals( "Mean" ) )
{
ReferenceSurfaceProjection< T > referenceSurfaceProjection = new ReferenceSurfaceProjection<>();
referenceSurfaceProjection.set( referenceSurface, projectionParameters );
referenceSurfaceProjection.display( referenceSurface, projectionParameters, displayParameters );
projection.setProjection( runMean( projection.getSubVolume(), projection.getSubVolume().factory() ) );
}
public static < T extends RealType< T > & NativeType< T > > void run( ReferenceSurface< T > referenceSurface,
int delta, String projectionType,
boolean projectionDisplay,
boolean rawHeightMapDisplay,
boolean extractedHeightMapDisplay,
boolean reduced3DSpaceDisplay,
boolean segmentedSurfaceDisplay,
boolean segmentedSurfaceMaskDisplay,
int delta2 ) throws NoPossibleDisplayException
{
ReferenceSurfaceProjection< T > referenceSurfaceProjection = new ReferenceSurfaceProjection<>();
referenceSurfaceProjection.set( referenceSurface, delta, projectionType, delta2 );
referenceSurfaceProjection.display( referenceSurface, delta, projectionType,
projectionDisplay,
rawHeightMapDisplay,
extractedHeightMapDisplay,
reduced3DSpaceDisplay,
segmentedSurfaceDisplay,
segmentedSurfaceMaskDisplay, delta2 );
else if ( method.equals( "MIP" ) )
{
projection.setProjection( runMIP( projection.getSubVolume(), projection.getSubVolume().factory() ) );
}
/**
* @param referenceSurface the considered referenceSurface
* @param method the desired user projection method
* @param delta the desired user volume value
* @param <T> the type of the input image
* @return the height map projection
*/
public static < T extends RealType< T > & NativeType< T > > Img< UnsignedShortType > getProjectionHeightMap
( ReferenceSurface< T > referenceSurface, String method, int delta )
else
{
RandomAccessibleInterval< T > input = referenceSurface.getInput();
RandomAccessibleInterval< UnsignedShortType > zMap = referenceSurface.getzMap();
ImgFactory< UnsignedShortType > imgFactory = new ArrayImgFactory<>( new UnsignedShortType() );
Img< UnsignedShortType > heightMap =
imgFactory.create( input.dimension( 0 ), input.dimension( 1 ) );
RandomAccess< T > stackAccess = input.randomAccess();
RandomAccess< UnsignedShortType > heightmapAccess = heightMap.randomAccess();
RandomAccess< UnsignedShortType > zMapAccess = zMap.randomAccess();
return null;
}
return projection;
}
if ( method.equals( "MIP" ) )
public static < T extends RealType< T > & NativeType< T > > Projection< T > run( ReferenceSurface< T > referenceSurface, int channel, int delta, int offset, String method )
{
for ( int x = 0; x <= input.dimension( 0 ) - 1; x++ )
Projection< T > projection = new Projection<>( offset, delta, method, referenceSurface );
projection.setSubVolume( getSubStack( referenceSurface, channel, delta, offset ) );
if ( method.equals( "Mean" ) )
{
for ( int y = 0; y <= input.dimension( 1 ) - 1; y++ )
projection.setProjection( runMean( projection.getSubVolume(), projection.getSubVolume().factory() ) );
}
else if ( method.equals( "MIP" ) )
{
projection.setProjection( runMIP( projection.getSubVolume(), projection.getSubVolume().factory() ) );
}
else
{
return null;
}
return projection;
}
setPosition( zMapAccess, x, y );
int z = zMapAccess.get().getInteger() - 1;
if ( z != - 1 )
public static < T extends RealType< T > & NativeType< T > > Img< T > run( Img< T > subVolume, String method )
{
setPosition( stackAccess, x, y, z );
int finalZ = findZ( stackAccess, ( int ) input.dimension( 2 ) - 1, delta );
setPosition( heightmapAccess, x, y );
heightmapAccess.get().set( finalZ + 1 );
if (subVolume.dimension( 2 ) == 1)
{
return subVolume;
}
Img< T > projection;
if ( method.equals( "Mean" ) )
{
projection = runMean( subVolume, subVolume.factory() );
}
else if ( method.equals( "MIP" ) )
{
projection = runMIP( subVolume, subVolume.factory() );
}
else
{
return null;
}
//TODO implement other projection method (average intensity, min intensity, sum slices,
// standard deviation, and median)
return heightMap;
return projection;
}
/**
* @param <T> the type of the reference surface
* @param referenceSurface the considered reference surface
* @param channel the desired channel
* @param delta the desired user volume value
* @param <T> the type of the reference surface
* @return the extracted height map subStack
*/
public static < T extends RealType< T > & NativeType< T > > Img< T > getExtractedHeightMapSubStack
( ReferenceSurface< T > referenceSurface,
int delta )
public static < T extends RealType< T > & NativeType< T > > Img< T > getSubStack
( ReferenceSurface< T > referenceSurface, int channel, int delta, int offset )
{
RandomAccessibleInterval< T > input = Views.hyperSlice( referenceSurface.getInput(), 2, channel );
ImgFactory< T > factory = referenceSurface.getFactory();
RandomAccessibleInterval< UnsignedShortType > zMap = referenceSurface.getzMap();
return getSubStack( input, factory, zMap, delta, offset );
}
public static < T extends RealType< T > & NativeType< T > > Img< T > getSubStack
( ReferenceSurface< T > referenceSurface, int delta, int offset )
{
RandomAccessibleInterval< T > input = referenceSurface.getInput();
ImgFactory< T > factory = referenceSurface.getFactory();
RandomAccessibleInterval< UnsignedShortType > zMap = referenceSurface.getzMap();
//the stack slice number depends on the user "delta" parameter.
Img< T > heightMapStack = factory.create( input.dimension( 0 ), input.dimension( 1 ), ( delta * 2L ) + 1 );
return getSubStack( input, factory, zMap, delta, offset );
}
/**
* Generates a sub-volume from a specified height map and original image.
*
* @param input the input stack
* @param factory the input factory
* @param zMap the surface corresponding zmap
* @param delta the desired user volume value
* @param offset the desired offset value
* @param <T> the type of the reference surface
* @return the extracted height map subStack
*/
public static < T extends RealType< T > & NativeType< T > > Img< T > getSubStack
( RandomAccessibleInterval< T > input, ImgFactory< T > factory, RandomAccessibleInterval< UnsignedShortType > zMap, int delta, int offset )
{
//the stack slice number depends on the user "delta" and offset parameters.
Img< T > subStack = factory.create( input.dimension( 0 ), input.dimension( 1 ), ( ( delta * 2L ) + 1 ) );
RandomAccess< T > stackAccess = input.randomAccess();
RandomAccess< T > heightMapAccess = heightMapStack.randomAccess();
LOGGER.debug("Input dimensions:{}", input.dimensionsAsLongArray());
LOGGER.debug("zMap dimensions:{}", zMap.dimensionsAsLongArray());
LOGGER.debug("subVolume dimensions:{}", subStack.dimensionsAsLongArray());
//TODO the unbinning is not right sizes don't match
//TODO change the factory (best factory for the input!)
RandomAccess< T > subStackAccess = subStack.randomAccess();
RandomAccess< UnsignedShortType > zMapAccess = zMap.randomAccess();
for ( int x = 0; x <= input.dimension( 0 ) - 1; x++ )
for ( int x = 0; x < zMap.dimension( 0 ) ; x++ )
{
zMapAccess.setPosition( x, 0 );
for ( int y = 0; y <= input.dimension( 1 ) - 1; y++ )
for ( int y = 0; y < zMap.dimension( 1 ) ; y++ )
{
zMapAccess.setPosition( y, 1 );
int zMapValue = zMapAccess.get().getInteger() - 1;// ImgLib2 can not display negative values.
if ( zMapValue != - 1 )
{
int startIndex = Math.max( zMapValue - delta, 0 );
int stopIndex = Math.min( ( zMapValue + delta ), ( int ) input.dimension( 2 ) - 1 );
int startIndex = Math.max( zMapValue - delta + offset, 0 );
int stopIndex = Math.min( ( zMapValue + delta + offset ), ( int ) input.dimension( 2 ) - 1 );
//first loop for minus delta
int zHeight = delta;
for ( int z = zMapValue; z >= startIndex; z-- )
for ( int z = zMapValue + offset; z >= startIndex; z-- )
{
setPosition( heightMapAccess, x, y, zHeight );
setPosition( subStackAccess, x, y, zHeight );
setPosition( stackAccess, x, y, z );
heightMapAccess.get().set( stackAccess.get() );
subStackAccess.get().set( stackAccess.get() );
zHeight--;
}
//second loop for plus delta
zHeight = delta + 1;
for ( int z = zMapValue + 1; z <= stopIndex; z++ )
zHeight = delta + offset + 1;
for ( int z = zMapValue + 1 + offset; z <= stopIndex; z++ )
{
setPosition( heightMapAccess, x, y, zHeight );
setPosition( subStackAccess, x, y, zHeight );
setPosition( stackAccess, x, y, z );
heightMapAccess.get().set( stackAccess.get() );
subStackAccess.get().set( stackAccess.get() );
zHeight++;
}
}
else
{
for ( int z = 0; z <= heightMapStack.dimension( 2 ) - 1; z++ )
for ( int z = 0; z <= subStack.dimension( 2 ) - 1; z++ )
{
setPosition( heightMapAccess, x, y, z );
heightMapAccess.get().setReal( 0 );
}
setPosition( subStackAccess, x, y, z );
subStackAccess.get().setReal( 0 );
}
}
}
return heightMapStack;
}
return subStack;
}
public static < T extends RealType< T > & NativeType< T > > Img< BitType > getSegmentedSurfaceMask
( IterableInterval< T > segmentedSurface, int delta )
public static < T extends RealType< T > & NativeType< T > > Img< T > getSubStack
( RandomAccessibleInterval< T > input, ImgFactory< T > factory, RandomAccessibleInterval< UnsignedShortType > zMap, int channel, int delta, int offset )
{
Img< BitType > segmentedSurfaceMask = new ArrayImgFactory<>( new BitType() ).create( segmentedSurface );
Cursor< T > surfaceAccess = segmentedSurface.cursor();
RandomAccess< BitType > mapAccess = segmentedSurfaceMask.randomAccess();
while ( surfaceAccess.hasNext() )
RandomAccess< T > stackAccess;
if ( input.numDimensions() < 4 ) // only one channel
{
surfaceAccess.fwd();
if ( surfaceAccess.get().getRealFloat() > 0 )// there is a surface pixel
{
int x = surfaceAccess.getIntPosition( 0 );
int y = surfaceAccess.getIntPosition( 1 );
int z = surfaceAccess.getIntPosition( 2 );
int startIndex = Math.max( z - delta, 0 );
int stopIndex = Math.min( ( int ) segmentedSurface.dimension( 2 ) - 1, ( z + delta ) );
for ( int Z = startIndex; Z <= stopIndex; Z++ )
stackAccess = input.randomAccess();
}
else
{
mapAccess.setPosition( surfaceAccess );
setPosition( mapAccess, x, y, Z );
mapAccess.get().set( true );
stackAccess = input.randomAccess( Views.hyperSlice( input, 2, channel ) );
}
//the stack slice number depends on the user "delta" and offset parameters.
Img< T > heightMapStack = factory.create( input.dimension( 0 ), input.dimension( 1 ), ( ( delta * 2L ) + 1 ) );
RandomAccess< T > heightMapAccess = heightMapStack.randomAccess();
RandomAccess< UnsignedShortType > zMapAccess = zMap.randomAccess();
for ( int x = 0; x <= input.dimension( 0 ) - 1; x++ )
{
zMapAccess.setPosition( x, 0 );
for ( int y = 0; y <= input.dimension( 1 ) - 1; y++ )
{
zMapAccess.setPosition( y, 1 );
int zMapValue = zMapAccess.get().getInteger() - 1;// ImgLib2 can not display negative values.
if ( zMapValue != - 1 )
{
int startIndex = Math.max( zMapValue - delta + offset, 0 );
int stopIndex = Math.min( ( zMapValue + delta + offset ), ( int ) input.dimension( 2 ) - 1 );
//first loop for minus delta
int zHeight = delta;
for ( int z = zMapValue + offset; z >= startIndex; z-- )
{
setPosition( heightMapAccess, x, y, zHeight );
setPosition( stackAccess, x, y, z );
heightMapAccess.get().set( stackAccess.get() );
zHeight--;
}
//second loop for plus delta
zHeight = delta + offset + 1;
for ( int z = zMapValue + 1 + offset; z <= stopIndex; z++ )
{
setPosition( heightMapAccess, x, y, zHeight );
setPosition( stackAccess, x, y, z );
heightMapAccess.get().set( stackAccess.get() );
zHeight++;
}
// ImageJFunctions.show( segmentedSurfaceMask, "Mask with " + delta );
return segmentedSurfaceMask;
}
public static < T extends RealType< T > & NativeType< T > > Img< T > getSegmentedSurface
( Img< UnsignedShortType > extractedHeightMap, RandomAccessibleInterval< T > originalInput, ImgFactory< T > factory )
{
Img< T > segmentedSurface = factory.create( originalInput );
RandomAccess< T > surfaceAccess = segmentedSurface.randomAccess();
RandomAccess< T > inputAccess = originalInput.randomAccess();
Cursor< UnsignedShortType > heightMapCursor = extractedHeightMap.cursor();
while ( heightMapCursor.hasNext() )
else
{
heightMapCursor.fwd();
if ( heightMapCursor.get().getInteger() > 0 )
for ( int z = 0; z <= heightMapStack.dimension( 2 ) - 1; z++ )
{
int x = heightMapCursor.getIntPosition( 0 );
int y = heightMapCursor.getIntPosition( 1 );
setPosition( surfaceAccess, x, y, heightMapCursor.get().getInteger() - 1 );
setPosition( inputAccess, x, y, heightMapCursor.get().getInteger() - 1 );
surfaceAccess.get().set( inputAccess.get() );
setPosition( heightMapAccess, x, y, z );
heightMapAccess.get().setReal( 0 );
}
}
return segmentedSurface;
}
}
ImageJFunctions.show(heightMapStack, "heightmapstack");
return heightMapStack;
}
/**
* * Generates the projection from a specified height map and original image.
*
* @param referenceSurface the considered reference surface
* @param projectionType the type of the projection
* @param <T> the original stack image type.
* @return the projection of the original stack image according to yhe height map.
* @param stack the stack to project
* @param factory the stack factory
* @param <T> the stack type
* @return the Maximum Intensity Projection of the input stack.
*/
public static < T extends RealType< T > & NativeType< T > > Img< T > projection1(
ReferenceSurface< T > referenceSurface, String projectionType )
{
RandomAccessibleInterval< T > input = referenceSurface.getInput();
ImgFactory< T > factory = referenceSurface.getFactory();
RandomAccessibleInterval< UnsignedShortType > projectionHeightMap = referenceSurface.getProjection().getExtractedHeightMap();
if ( projectionType.equals( "MIP" ) )
public static < T extends RealType< T > & NativeType< T > > Img< T > runMIP( RandomAccessibleInterval< T > stack, ImgFactory< T > factory )
{
Img< T > projection = factory.create( input.dimension( 0 ), input.dimension( 1 ) );
RandomAccess< T > stackAccess = input.randomAccess();
Img< T > projection = factory.create( stack.dimension( 0 ), stack.dimension( 1 ) );
RandomAccess< T > stackAccess = stack.randomAccess();
RandomAccess< T > projectionAccess = projection.randomAccess();
RandomAccess< UnsignedShortType > heightmapAccess = projectionHeightMap.randomAccess();
UnsignedShortType minus = new UnsignedShortType();
minus.setZero();
for ( int x = 0; x <= input.dimension( 0 ) - 1; x++ )
for ( int x = 0; x <= stack.dimension( 0 ) - 1; x++ )
{
for ( int y = 0; y <= input.dimension( 1 ) - 1; y++ )
for ( int y = 0; y <= stack.dimension( 1 ) - 1; y++ )
{
setPosition( heightmapAccess, x, y );
final int z = heightmapAccess.get().getInteger();
if ( z == 0 )
setPosition( projectionAccess, x, y );
setPosition( stackAccess, x, y );
T max = stackAccess.get().createVariable();
max.setZero();
for ( int z = 0; z < stack.dimension( 2 ); z++ )
{
projectionAccess.get().setReal( 0 );
}
else
setPosition( stackAccess, x, y, z );
if ( stackAccess.get().compareTo( max ) >= 0 )
{
setPosition( stackAccess, x, y, z - 1 );
// " - 1" because by convention the z values start at 1, 0 is for unassigned values.
setPosition( projectionAccess, x, y );
projectionAccess.get().set( stackAccess.get() );
max.set( stackAccess.get() );
}
}
projectionAccess.get().set( max );
}
return projection;
}
return null;
}
/**
* @param referenceSurface the considered reference surface
* @param projectionType the user projection method
* @param <T> the reference surface type
* @return a projection according to the specified parameters.
*/
public static < T extends RealType< T > & NativeType< T > > Img< T > projection2(
ReferenceSurface< T > referenceSurface, String projectionType )
{
ImgFactory< T > factory = referenceSurface.getFactory();
RandomAccessibleInterval< T > extractedHeightMapSubStack = referenceSurface.getProjection().getReduced3DSpace();
Img< T > projection = factory.create( extractedHeightMapSubStack.dimension( 0 ), extractedHeightMapSubStack.dimension( 1 ) );
if ( projectionType.equals( "median" ) )// sumSlices, median, standardDeviation
{
medianProjection( extractedHeightMapSubStack, projection );
return projection;
}
return null;
}
/**
* Find the z value of the pixel with the maximum intensity at
* the specified RandomAccess position plus or minus delta
*
* @param stackAccess - the {@link RandomAccess} of original stack
* @param stop - the maximum value that z can take.
* @param delta - the delta to which the search will occur.
* @param <T> - the type of the {@link RandomAccess}
* @return - the z value for which the pixel intensity is maximal.
*/
private static < T extends RealType< T > & NativeType< T > > int findZ(
RandomAccess< T > stackAccess, int stop, int delta )
public static < T extends RealType< T > & NativeType< T > > Img< T > runMean( RandomAccessibleInterval< T > stack, ImgFactory< T > factory )
{
T max = stackAccess.get().createVariable();
max.setZero();
int Z = 0;
int xValue = stackAccess.getIntPosition( 0 );
int yValue = stackAccess.getIntPosition( 1 );
int zValue = stackAccess.getIntPosition( 2 );
int startIndex = Math.max( zValue - delta, 0 );
int stopIndex = Math.min( stop, ( zValue + delta ) );
for ( int z = startIndex; z <= stopIndex; z++ )
Img< T > projection = factory.create( stack.dimension( 0 ), stack.dimension( 1 ) );
RandomAccess< T > stackAccess = stack.randomAccess();
RandomAccess< T > projectionAccess = projection.randomAccess();
for ( int x = 0; x <= stack.dimension( 0 ) - 1; x++ )
{
setPosition( stackAccess, xValue, yValue, z );
if ( stackAccess.get().compareTo( max ) >= 0 )
for ( int y = 0; y <= stack.dimension( 1 ) - 1; y++ )
{
setPosition( projectionAccess, x, y );
setPosition( stackAccess, x, y );
T sum = stackAccess.get().createVariable();
sum.setZero();
for ( int z = 0; z < stack.dimension( 2 ); z++ )
{
max = stackAccess.get().copy();
Z = z;
setPosition( stackAccess, x, y, z );
sum.setReal( sum.getRealFloat() + stackAccess.get().getRealFloat() );
}
projectionAccess.get().setReal( sum.getRealFloat() / stack.dimension( 2 ) );
}
return Z;
}
return projection;
}
public static < T extends RealType< T > & NativeType< T > > void medianProjection
( RandomAccessibleInterval< T > input, RandomAccessibleInterval< T > output )
{
......@@ -369,265 +360,70 @@ public class ReferenceSurfaceProjection< T extends RealType< T > & NativeType< T
}
}
static double median( double[] a )
{
double[] sorted = a.clone();
Arrays.sort( sorted );
int middle = a.length / 2;
if ( ( a.length & 1 ) == 0 ) //even
{
return ( sorted[ middle - 1 ] + sorted[ middle ] ) / 2f;
}
else
{
return sorted[ middle ];
}
}
public void set( ReferenceSurface< T > referenceSurface, ProjectionParameters projectionParameters )
{
int delta = projectionParameters.getDelta();
String projectionType = projectionParameters.getProjectionType();
if ( projectionType.equals( "MIP" ) ||
projectionType.equals( "Minimum Intensity" ) )
{
referenceSurface.getProjection().setExtractedHeightMap( getProjectionHeightMap( referenceSurface, projectionType, delta ) );
referenceSurface.getProjection().setReduced3DSpace( getExtractedHeightMapSubStack( referenceSurface, delta ) );
referenceSurface.getProjection().setProjection( projection1( referenceSurface, projectionType ) );
referenceSurface.getProjection().setSegmentedSurface( getSegmentedSurface( referenceSurface.getProjection().getExtractedHeightMap(),
referenceSurface.getInput(),
referenceSurface.getFactory() ) );
}
else
{
referenceSurface.getProjection().setReduced3DSpace( getExtractedHeightMapSubStack( referenceSurface, delta ) );
referenceSurface.getProjection().setProjection( projection2( referenceSurface, projectionType ) );
}
}
public void set( ReferenceSurface< T > referenceSurface, int delta, String projectionType, int delta2 )
{
if ( projectionType.equals( "MIP" ) ||
projectionType.equals( "Minimum Intensity" ) )
{
referenceSurface.getProjection().setExtractedHeightMap( getProjectionHeightMap( referenceSurface, projectionType, delta ) );
referenceSurface.getProjection().setReduced3DSpace( getExtractedHeightMapSubStack( referenceSurface, delta ) );
referenceSurface.getProjection().setProjection( projection1( referenceSurface, projectionType ) );
referenceSurface.getProjection().setSegmentedSurface( getSegmentedSurface( referenceSurface.getProjection().getExtractedHeightMap(),
referenceSurface.getInput(),
referenceSurface.getFactory() ) );
referenceSurface.getProjection().setSegmentedSurfaceMask( getSegmentedSurfaceMask( referenceSurface.getProjection().getSegmentedSurface(), delta2 ) );
}
else
{
referenceSurface.getProjection().setReduced3DSpace( getExtractedHeightMapSubStack( referenceSurface, delta ) );
referenceSurface.getProjection().setProjection( projection2( referenceSurface, projectionType ) );
}
}
public void display( ReferenceSurface< T > referenceSurface, ProjectionParameters projectionParameters, DisplayParameters displayParameters ) throws NoPossibleDisplayException
public static < T extends RealType< T > & NativeType< T > > Img<T> regroupChannels( ArrayList<Img<T>> channels )
{
int delta = projectionParameters.getDelta();
String projectionType = projectionParameters.getProjectionType();
if ( referenceSurface.getProjection() != null )
if( channels.size() > 1)
{
if ( displayParameters.isProjectionDisplay() )
{
ImageJFunctions.show( referenceSurface.getProjection().get(),
"Projection with " + projectionType + " RS n°" + delta );
int x = ( int ) channels.get( 0 ).dimension( 0 );
int y = ( int ) channels.get( 0 ).dimension( 1 );
Img< T > output = channels.get( 0 ).factory().create( x, y, channels.size() );
RandomAccessibleInterval< T > output2 = Views.stack( channels );
LOGGER.debug( "output dimensions = {}", output );
LOGGER.debug( "stack dimensions = {}", Views.stack( channels ) );
ImageJFunctions.show(Views.stack( channels ), "output");
LoopBuilder.setImages( output2, output ).forEachPixel(
( a, b ) -> b.setReal( a.getRealDouble() ) );
ImageJFunctions.show(output, "output");
// return ( Img< T > ) Views.stack( channels );
return output;
}
if ( displayParameters.iszMapDisplay() )
{
ImageJFunctions.show( referenceSurface.getzMap(), "Raw height map" + "RS n°" + delta );
return channels.get( 0 );
}
if ( displayParameters.isExtractedHeightMapSubStackDisplay() )
public static < T extends RealType< T > & NativeType< T > > ImgPlus<T> setColorsAndName( Img<T> projection, ArrayList< ColorTable > colorTables, String name )
{
if ( delta >= 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getReduced3DSpace(),
"Extracted surface subStack (delta = " + delta + ")" + "RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "The value of parameter delta equals 0 !" );
}
}
if ( displayParameters.isSurfaceSubVolumeDisplay() )
{
if ( delta > 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getSegmentedSurface(),
"RS n°" + delta + ": segmented surface (delta = " + delta + ")" + "RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "The value of parameter delta equals 0 !" );
}
}
if ( displayParameters.isHeightMapSubVolumeMask() )
{
ImageJFunctions.show( referenceSurface.getProjection().getSegmentedSurfaceMask(),
"RS n° " + delta + " : segmented surface mask mask (delta = " + delta );
}
if ( ( projectionType.equals( "MIP" ) || projectionType.equals( "Min" ) ) && displayParameters.isProjectionHeightMapDisplay() )
{
if ( delta >= 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getExtractedHeightMap(), "Extracted height Map with " +
projectionType + " RS n°" + delta );
}
else
int channelsNb = colorTables.size();;
ImgPlus<T> imgPlus = new ImgPlus<>( projection, name,new AxisType[] { Axes.X, Axes.Y, Axes.CHANNEL} );
imgPlus.setCompositeChannelCount(channelsNb);
imgPlus.initializeColorTables( channelsNb );
for( int i = 0 ; i < channelsNb ; i ++)
{
throw new NoPossibleDisplayException( "This method does not produce a projection height map !" );
imgPlus.setColorTable(colorTables.get( i ), i );
}
return imgPlus;
}
}
}
public void display( ReferenceSurface< T > referenceSurface, int delta, String projectionType,
boolean projectionDisplay,
boolean rawHeightMapDisplay,
boolean extractedHeightMapDisplay,
boolean reduced3DSpaceDisplay,
boolean segmentedSurfaceDisplay,
boolean segmentedSurfaceMaskDisplay ) throws NoPossibleDisplayException
{
if ( referenceSurface.getProjection() != null )
{
if ( projectionDisplay )// the projection
{
ImageJFunctions.show( referenceSurface.getProjection().get(),
"Projection with " + projectionType + " RS n°" + delta );
}
if ( rawHeightMapDisplay )// the raw height map
{
ImageJFunctions.show( referenceSurface.getzMap(), "Zellige generated height map" + "RS n°" + delta );
}
if ( reduced3DSpaceDisplay )
{
if ( delta != 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getReduced3DSpace(),
"Reduced 3D space (delta = " + delta + ")" + "RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "The value of parameter delta equals 0 !" );
}
}
if ( ( projectionType.equals( "MIP" ) || projectionType.equals( "Min" ) ) && extractedHeightMapDisplay ) // the extracted height map
{
if ( delta != 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getExtractedHeightMap(), "Extracted height Map with " +
projectionType + " RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "This method does not produce a extracted height map !" );
}
}
if ( segmentedSurfaceDisplay )
{
if ( delta != 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getSegmentedSurface(),
"Height map subVolume mask (delta = " + delta + ")" + "RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "The value of parameter delta equals 0 !" );
}
}
if ( segmentedSurfaceMaskDisplay ) // the segmented surface mask
{
ImageJFunctions.show( referenceSurface.getProjection().getSegmentedSurfaceMask(),
"RS n° " + delta + " : SubVolume mask (delta = " + delta );
}
}
}
public void display( ReferenceSurface< T > referenceSurface, int delta, String projectionType,
boolean projectionDisplay,
boolean rawHeightMapDisplay,
boolean extractedHeightMapDisplay,
boolean reduced3DSpaceDisplay,
boolean segmentedSurfaceDisplay,
boolean segmentedSurfaceMaskDisplay,
int delta2 ) throws NoPossibleDisplayException
{
if ( referenceSurface.getProjection() != null )
{
if ( projectionDisplay )// the projection
{
ImageJFunctions.show( referenceSurface.getProjection().get(),
"Projection with " + projectionType + " RS n°" + delta );
}
if ( rawHeightMapDisplay )// the raw height map
static double median( double[] a )
{
ImageJFunctions.show( referenceSurface.getzMap(), "Raw Height Map" + "RS n°" + delta );
}
double[] sorted = a.clone();
Arrays.sort( sorted );
int middle = a.length / 2;
if ( reduced3DSpaceDisplay )// the reduced 3D stack of size (X, Y, delta*2 +1)
{
if ( delta != 0 )
if ( ( a.length & 1 ) == 0 ) //even
{
ImageJFunctions.show( referenceSurface.getProjection().getReduced3DSpace(),
"Reduced 3D space (delta = " + delta + ")" + "RS n°" + delta );
return ( sorted[ middle - 1 ] + sorted[ middle ] ) / 2f;
}
else
{
throw new NoPossibleDisplayException( "The value of parameter delta equals 0 !" );
return sorted[ middle ];
}
}
if ( ( projectionType.equals( "MIP" ) || projectionType.equals( "Min" ) ) && extractedHeightMapDisplay ) // the extracted height map
public static < T extends RealType< T > & NativeType< T >, R extends RealType< T > & NativeType< T > > void main( String[] args ) throws InterruptedException
{
if ( delta != 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getExtractedHeightMap(), "Extracted height Map with " +
projectionType + " RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "This method does not produce a extracted height map !" );
}
}
if ( segmentedSurfaceDisplay )
{
if ( delta > 0 )
{
ImageJFunctions.show( referenceSurface.getProjection().getSegmentedSurface(),
"Segmented surface (delta = " + delta + ")" + "RS n°" + delta );
}
else
{
throw new NoPossibleDisplayException( "The value of parameter delta equals 0 !" );
}
}
if ( segmentedSurfaceMaskDisplay ) // the segmented surface mask
{
ImageJFunctions.show( referenceSurface.getProjection().getSegmentedSurfaceMask(),
"RS n° " + delta + " : Segmented surface mask (delta = " + delta2 );
}
}
ImageJ ij = new ImageJ();
ij.launch( args );
final Img< T > stackImage = ( Img< T > ) IO.openAll( "doc/Cochlée1.tif" ).get( 0 ).getImg(); // TODO Handling larger images
ImageJFunctions.show( stackImage, "original" );
final Img< UnsignedShortType > zMap = ( Img< UnsignedShortType > ) IO.openAll( "H:\\Projet\\Zellige\\Zellige analysis\\files\\STK\\STK_HM.tif" ).get( 0 ).getImg();
ImageJFunctions.show( zMap, "HM" );
Img< T > output = ReferenceSurfaceProjection.getSubStack( stackImage, stackImage.factory(), zMap, 1, 0 );
ImageJFunctions.show( output, "substack" );
assert output != null;
ImageJFunctions.show( ReferenceSurfaceProjection.runMIP( output, output.factory() ), "MIP" );
ImageJFunctions.show( ReferenceSurfaceProjection.runMean( output, output.factory() ), "Mean" );
}
}
......
......@@ -28,15 +28,13 @@
*/
package fr.pasteur.ida.zellige.steps.selection;
import fr.pasteur.ida.zellige.element.Pixels;
import fr.pasteur.ida.zellige.steps.selection.classification.AmplitudeClassification;
import fr.pasteur.ida.zellige.steps.selection.classification.Classification;
import fr.pasteur.ida.zellige.steps.selection.classification.ClassificationParameters;
import fr.pasteur.ida.zellige.steps.selection.classification.OtsuClassification;
import fr.pasteur.ida.zellige.steps.selection.exception.DataValidationException;
import fr.pasteur.ida.zellige.steps.selection.exception.ImageTooLargeException;
import fr.pasteur.ida.zellige.steps.selection.exception.NoClassificationException;
import fr.pasteur.ida.zellige.steps.selection.postTreatment.PostTreatment;
import fr.pasteur.ida.zellige.steps.selection.postTreatment.PostTreatmentParameters;
import fr.pasteur.ida.zellige.steps.selection.postTreatment.islandSearch.IslandSearch;
import fr.pasteur.ida.zellige.steps.selection.pretreatment.Pretreatment;
import fr.pasteur.ida.zellige.steps.selection.pretreatment.PretreatmentParameters;
......@@ -56,35 +54,63 @@ public class Selection< T extends RealType< T > & NativeType< T > >
{
private final static Logger LOGGER = LoggerFactory.getLogger( Selection.class );
private final double radius;
private final ClassificationParameters classificationParameters;
private final PostTreatmentParameters postTreatmentParameters;
private final PretreatmentParameters pretreatmentParameters;
private final SelectionParameters selectionParameters;
// private final ClassificationParameters classificationParameters;
// private final PostTreatmentParameters postTreatmentParameters;
private final RandomAccessibleInterval< T > input;
private Pixels[][] output;
private Img<FloatType> output;
private static long amplitudePT;
private static long otsuPT;
private static long islandSearchPT;
public Selection( PretreatmentParameters pretreatmentParameters, ClassificationParameters classificationParameters, PostTreatmentParameters postTreatmentParameters,
RandomAccessibleInterval< T > input )
public Selection (PretreatmentParameters pretreatmentParameters,SelectionParameters selectionParameters, RandomAccessibleInterval< T > input )
{
this.radius = pretreatmentParameters.getParameter();
this.classificationParameters = classificationParameters;
this.postTreatmentParameters = postTreatmentParameters;
this.pretreatmentParameters = pretreatmentParameters;
this.selectionParameters = selectionParameters;
this.input = input;
}
public static < T extends RealType< T > & NativeType< T > > Pixels[][]
run( RandomAccessibleInterval< T > input, PretreatmentParameters pretreatmentParameters, ClassificationParameters classificationParameters, PostTreatmentParameters postTreatmentParameters ) throws NoClassificationException, DataValidationException
public static < T extends RealType< T > & NativeType< T > > Img<FloatType>
run( RandomAccessibleInterval< T > input, PretreatmentParameters pretreatmentParameters, SelectionParameters selectionParameters ) throws NoClassificationException, DataValidationException, ImageTooLargeException
{
LOGGER.debug( "Starting process..." );
Selection< T > selection = new Selection<>( pretreatmentParameters, classificationParameters, postTreatmentParameters, input );
Selection< T > selection = new Selection<>( pretreatmentParameters, selectionParameters, input );
selection.run();
LOGGER.debug( "Process complete." );
return selection.output;
}
/**
* @throws NoClassificationException if the no classification was performed
* @throws DataValidationException if one of the input parameters is not valid
*/
public void run() throws NoClassificationException, DataValidationException, ImageTooLargeException
{
/* Pretreatment of the image.*/
Img< FloatType > pretreatedImage = Pretreatment.run( input, pretreatmentParameters.getRadius() , pretreatmentParameters.getBin() );
if (pretreatedImage != null)
{
/* Classification. */
Img< BitType > classifiedPixel = Classification.run( pretreatedImage, pretreatedImage.factory(), selectionParameters.getAmplitude(), selectionParameters.getOtsu() );
amplitudePT = AmplitudeClassification.getProcessingTime();
otsuPT = OtsuClassification.getProcessingTime();
/* PostTreatment and output*/
output = PostTreatment.run( classifiedPixel, selectionParameters.getXyBlur(), selectionParameters.getzBlur(), selectionParameters.getIsland() );
islandSearchPT = IslandSearch.getProcessingTime();
}
else
{
throw new ImageTooLargeException();
}
}
public Img<FloatType> getOutput()
{
return output;
}
public static Logger getLOGGER()
{
......@@ -105,26 +131,4 @@ public class Selection< T extends RealType< T > & NativeType< T > >
{
return islandSearchPT;
}
/**
* @throws NoClassificationException if the no classification was performed
* @throws DataValidationException if one of the input parameters is not valid
*/
public void run() throws NoClassificationException, DataValidationException
{
/* Pretreatment of the image.*/
Img< FloatType > pretreatedImage = Pretreatment.run( input, radius );
/* Classification. */
Img< BitType > classifiedPixel = Classification.run( pretreatedImage, pretreatedImage.factory(), classificationParameters );
amplitudePT = AmplitudeClassification.getProcessingTime();
otsuPT = OtsuClassification.getProcessingTime();
/* PostTreatment and output*/
output = PostTreatment.run( classifiedPixel, postTreatmentParameters );
islandSearchPT = IslandSearch.getProcessingTime();
}
public Pixels[][] getOutput()
{
return output;
}
}
package fr.pasteur.ida.zellige.steps.selection;
public class SelectionParameters
{
private final int amplitude;
private final int otsu;
private final int island;
private final double xyBlur;
private final double zBlur;
public SelectionParameters( int amplitude, int otsu, double xyBlur, double zBlur, int island )
{
this.amplitude = amplitude;
this.otsu = otsu;
this.island = island;
this.xyBlur = xyBlur;
this.zBlur = zBlur;
}
public int getAmplitude()
{
return amplitude;
}
public int getOtsu()
{
return otsu;
}
public int getIsland()
{
return island;
}
public double getXyBlur()
{
return xyBlur;
}
public double getzBlur()
{
return zBlur;
}
}
......@@ -82,7 +82,7 @@ public class AmplitudeClassification< T extends RealType< T > & NativeType< T >
final ImgFactory< T > factory, double amplitudeThreshold ) throws DataValidationException
{
long start = System.currentTimeMillis();
Img< BitType > amplitude = new ArrayImgFactory<>( new BitType() ).create( input );
Img< BitType > amplitude = new ArrayImgFactory<>( new BitType() ).create( input.dimensionsAsLongArray() );
AmplitudeClassification< T > classification =
new AmplitudeClassification<>( input, factory, amplitude, amplitudeThreshold );
classification.run();
......@@ -122,7 +122,7 @@ public class AmplitudeClassification< T extends RealType< T > & NativeType< T >
{
AmplitudeClassification< T > classification =
new AmplitudeClassification<>( input, factory );
return classification.getAmplitudeImg();
return classification.computeAmplitudeImg();
}
/**
* @param max the image containing only the values of local maximums
......@@ -132,7 +132,7 @@ public class AmplitudeClassification< T extends RealType< T > & NativeType< T >
private Img< T > getAmplitude(
RandomAccessibleInterval< T > max, ImgFactory< T > factory, RandomAccessibleInterval< T > min )
{
Img< T > amp = factory.create( max );
Img< T > amp = factory.create( max.dimensionsAsLongArray() );
RandomAccess< T > maxAccess = max.randomAccess();
RandomAccess< T > minAccess = min.randomAccess();
RandomAccess< T > ampAccess = amp.randomAccess();
......@@ -238,7 +238,7 @@ public class AmplitudeClassification< T extends RealType< T > & NativeType< T >
return output;
}
public Img< T > getAmplitudeImg() throws DataValidationException
public Img< T > computeAmplitudeImg() throws DataValidationException
{
Img< T > maximums = ExtremaDetection.findMaxima( input, factory );
Img< T > minimums = ExtremaDetection.findMinima( input, factory );
......
......@@ -68,35 +68,38 @@ public class Classification< T extends RealType< T > & NativeType< T > >
this.factory = factory;
}
/**
* Static method to provide a 2-part classification on a 3D image
*
* @param <T> the image type
* @param input the 3D image
* @param factory the input's {@link ImgFactory}.
* @param parameters - the parameters for the 2-parts classification
* @return a binary image
* @throws NoClassificationException if the binary image is empty.
* @throws DataValidationException if the value of parameter amplitudeThreshold is not valid.
* @param amplitude the threshold value for the amplitude classification
* @param otsu the threshold value for the otsu classification
* @return a binary classified image
* @param <T> the image type
* @throws NoClassificationException if both value are set to ZERO
* @throws DataValidationException if one of the user thresholds is incorrect
*/
public static < T extends RealType< T > & NativeType< T > > Img< BitType >
run( RandomAccessibleInterval< T > input, ImgFactory< T > factory, ClassificationParameters parameters ) throws NoClassificationException, DataValidationException
run( RandomAccessibleInterval< T > input, ImgFactory< T > factory, int amplitude, int otsu ) throws NoClassificationException, DataValidationException
{
Classification< T > classification = new Classification<>( input, factory );
classification.process( parameters );
classification.process( amplitude, otsu );
return classification.output;
}
/**
* @param parameters - the user parameters to set the classifications
* @throws NoClassificationException if both classifications are set to Zero
*
* @param amplitudeThreshold the threshold value for the amplitude classification
* @param otsuThreshold the threshold value for the otsu classification
* @throws NoClassificationException if both value are set to ZERO
* @throws DataValidationException if one of the user thresholds is incorrect
*/
void process( ClassificationParameters parameters ) throws NoClassificationException, DataValidationException
void process( int amplitudeThreshold, int otsuThreshold ) throws NoClassificationException, DataValidationException
{
LOGGER.debug( "Starting classification..." );
int amplitudeThreshold = parameters.getAmplitudeThreshold();
int otsuThreshold = parameters.getOtsuThreshold();
if ( amplitudeThreshold != ZERO && otsuThreshold != ZERO )
{
processBothClassification( factory, amplitudeThreshold, otsuThreshold );
......@@ -134,11 +137,11 @@ public class Classification< T extends RealType< T > & NativeType< T > >
/* Classification according to maximum amplitude */
Img< BitType > amplitudeImg = AmplitudeClassification.run( input, factory, amplitudeThreshold );
ImageJFunctions.show( amplitudeImg );
ImageJFunctions.show( amplitudeImg, "amplitude image" );
/* Classification according to local intensity*/
Img< BitType > otsuImg = OtsuClassification.run( input, factory, otsuThreshold );
ImageJFunctions.show( Objects.requireNonNull( otsuImg ) );
ImageJFunctions.show( Objects.requireNonNull( otsuImg ), "otsu output" );
/* Intersection of both classification */
this.output = interClassification( amplitudeImg, amplitudeImg.factory(), otsuImg );
}
......@@ -153,7 +156,7 @@ public class Classification< T extends RealType< T > & NativeType< T > >
ImgFactory< BitType > factory,
RandomAccessibleInterval< BitType > thresholds )
{
Img< BitType > output = factory.create( thresholds );
Img< BitType > output = factory.create( thresholds.dimensionsAsLongArray() );
Cursor< BitType > amplitudeCursor = amplitude.localizingCursor();
RandomAccess< BitType > thresholdAccess
= thresholds.randomAccess();
......