diff --git a/src/main/java/StartingOSStats.java b/src/main/java/StartingOSStats.java index 97dcd05ad18109a56ba87db87ed532b66bd292f5..9c0c4c3fe8c0765ec3df25a9884e4b1fffe8879b 100644 --- a/src/main/java/StartingOSStats.java +++ b/src/main/java/StartingOSStats.java @@ -100,7 +100,7 @@ public class StartingOSStats ImageJFunctions.show( TestMIP.findMIP( stackImage, stackImage.factory() ), "MIP" ); - SurfacesExtraction.extract(stackImage,amplitude, otsu, sigmas, false, delta, false, + SurfacesExtraction.extract(stackImage, stackImage.factory(),amplitude, otsu, sigmas, false, delta, false, false, true, "MIP", k1, percent1, k2, percent2); } else diff --git a/src/main/java/SurfacesExtractionAnalyse.java b/src/main/java/SurfacesExtractionAnalyse.java index bdf8e2f20360acde36786b057d0385737ec189dc..ed2a32fd47b36d1083a6653e2e3c833d44c645b9 100644 --- a/src/main/java/SurfacesExtractionAnalyse.java +++ b/src/main/java/SurfacesExtractionAnalyse.java @@ -27,375 +27,349 @@ import java.util.ArrayList; public class SurfacesExtractionAnalyse { - public static < T extends RealType < T > & NativeType < T > > void main( String[] args ) - { - - /* User Parameters AKA arguments to run the program*/ - double amplitude = Double.parseDouble( args[ 0 ] ); - double otsu = Double.parseDouble( args[ 1 ] ); - int sigmas = Integer.parseInt( args[ 2 ] ); - int k1 = Integer.parseInt( args[ 3 ] ); - double percent1 = Double.parseDouble( args[ 4 ] ); - int k2 = Integer.parseInt( args[ 5 ] ); - double percent2 = Double.parseDouble( args[ 6 ] ); - int delta = Integer.parseInt( args[ 7 ] ); - - final String imagePath = args[8].trim(); +// public static < T extends RealType < T > & NativeType < T > > void main( String[] args ) +// { // - System.out.println(imagePath); - // Creation of the image : version with unsigned type. */ - /* JY version for opening files. */ - final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); - - final Img < T > stackImage = ( Img < T > ) imgPlus.getImg(); - /* End of parameters. */ - - - - } - - public static < T extends RealType< T > & NativeType< T > > void extract( - Img< T > input, double amplitude, double threshold, int sigmas, - boolean zMapDisplay, int delta, - boolean extractedStackDisplay, - boolean heightMapDisplay, - boolean projectionDisplay, - String projectionType , - int k1, double percent1, int k2, double percent2) - { - /* First step : Pixel selection */ - Pixels[][] maximums = SurfacePixelSelection.run( input, amplitude, threshold, sigmas ); - int width = ( int ) input.dimension( 0 ); - int height = ( int ) input.dimension( 1 ); - try - { - /* First round construction*/ - ArrayList< Surface > surfaces = firstRoundConstruction( maximums, width, height , percent1, k1); - System.out.println( "first round surfaces = " + surfaces.size() ); -// - for (Surface surface : surfaces) { - displaySurface(surface); - } - // Second round construction - ArrayList< Surface > finalSurfaces = secondRoundConstruction( surfaces, width, height, percent2, k2 ); -// if ( !finalSurfaces.isEmpty() ) -// { - int index = 0; - for ( Surface surface : finalSurfaces ) - { - System.out.println( "Surface number " + index ); +// /* User Parameters AKA arguments to run the program*/ +// double amplitude = Double.parseDouble( args[ 0 ] ); +// double otsu = Double.parseDouble( args[ 1 ] ); +// int sigmas = Integer.parseInt( args[ 2 ] ); +// int k1 = Integer.parseInt( args[ 3 ] ); +// double percent1 = Double.parseDouble( args[ 4 ] ); +// int k2 = Integer.parseInt( args[ 5 ] ); +// double percent2 = Double.parseDouble( args[ 6 ] ); +// int delta = Integer.parseInt( args[ 7 ] ); +// +// final String imagePath = args[8].trim(); +//// +// System.out.println(imagePath); +// // Creation of the image : version with unsigned type. */ +// /* JY version for opening files. */ +// final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); +// +// final Img < T > stackImage = ( Img < T > ) imgPlus.getImg(); +// /* End of parameters. */ +// +// +// +// } +// +// public static < T extends RealType< T > & NativeType< T > > void extract( +// Img< T > input, double amplitude, double threshold, int sigmas, +// boolean zMapDisplay, int delta, +// boolean extractedStackDisplay, +// boolean heightMapDisplay, +// boolean projectionDisplay, +// String projectionType , +// int k1, double percent1, int k2, double percent2) +// { +// /* First step : Pixel selection */ +// Pixels[][] maximums = SurfacePixelSelection.run( input, amplitude, threshold, sigmas ); +// int width = ( int ) input.dimension( 0 ); +// int height = ( int ) input.dimension( 1 ); +// try +// { +// /* First round construction*/ +// ArrayList< Surface > surfaces = firstRoundConstruction( maximums, width, height , percent1, k1); +// System.out.println( "first round surfaces = " + surfaces.size() ); +//// +// for (Surface surface : surfaces) { // displaySurface(surface); - Img< UnsignedShortType > zMap = surface.getZMap(); - projection( input, delta, zMap, zMapDisplay, - extractedStackDisplay, heightMapDisplay, - projectionDisplay, projectionType, index ); - index++; - } - -// // Display the projection - System.out.println( "The end" ); - } - catch ( Exception e ) - { - e.printStackTrace(); - } - } - - - //TODO Bad documentation - - /** - * This method is used to binarize a grey-level image using Otsu Thresholding method in order to classify its pixels - * into foreground/background and then apply a anisotropic gaussian blur. - */ - - private static < T extends RealType< T > & NativeType< T > > Img< T > maximumSearch( Img< T > input, double percent, int sigma ) - { - /* Local maximum detection using partial derivative */ - Img< T > maximums = LocalMaximumDetection.findMaximums( input, input.factory() ); - ImageJFunctions.show( maximums, "maximums" ); - - /* Grid of local maximums*/ - Img< BitType > threshold = LocalOtsuClassification.find( input, percent ); - //ImageJFunctions.show( threshold, "Local threshold " ); - - /* Maxima selection according to threshold*/ - maximums = Threshold.classification( maximums.copy(), maximums.factory(), threshold ); - - /* Dilatation of the resulting image*/ - Utils.gaussConvolution( maximums.copy(), maximums, new double[]{ sigma, sigma } ); -// ImageJFunctions.show( maximums, "blurred maximums" ); - - /* New local maximum detection due to previous smoothing*/ - maximums = LocalMaximumDetection.findMaximums( maximums.copy(), maximums.factory() ); - return maximums; - } - - - /** - * Setting of the unfiltered local maximums : first the original stack is filtered with an anisotropic - * gaussian convolution, the pixel values are normalized between 0 and 255 then the local maximums - * are detected. - * During these steps, the temporary local, sectional and global thresholds are computed. - */ - - - /** - * Creates the OS for each orthogonal section from the maximum coordinates found according to the dimension. - * - * @param maximums - the local maximums found with the {@link LocalMaximumDetection} class. - * @param osListsArray - the OS corresponding to a 1D surface. - */ - private static void set( Pixels[][] maximums, OSList[] osListsArray, int dimension, OSEStartingStatus startingStatus ) - { - - - for ( int i = 0; i <= maximums.length - 1; i++ ) - { - osListsArray[ i ] = OSConstruction.findOS( maximums[ i ], dimension, startingStatus ); - } - startingStatus.setStartingStatus(); - } - - /* ----- First and second round construction methods. ----- */ - private static ArrayList< Surface > firstRoundConstruction( Pixels[][] maximums, int width, int height, double percent, int k ) throws NoSurfaceFoundException - { - int dimension = 0; - // Setting and storage of the OS build in the X dimension according to the user thresholds settings. - OSEStartingStatus startingStatus = new OSEStartingStatus( dimension ); - OSList[] osListsArrayX = new OSList[ height ]; - set( maximums, osListsArrayX, dimension, startingStatus ); -// displayOS(osListsArrayX); - System.out.println( "OS.count = " + OSE.getCount() ); - /* Construction of the tempSurfaces*/ - ArrayList< Surface > surfaces = - SurfacesReconstruction.buildSurfaces( 0, osListsArrayX, width, height, percent, k ); - if ( ! surfaces.isEmpty() ) - { - mergeReferenceSurface( surfaces ); - } - else - { - System.out.println( "No Surface Exception" ); - throw new FirstRoundConstructionException(); - } - return surfaces; - } - - /** - * Rebuilds the double pixel array from the {@link SurfaceLine} array - * of a {@link Surface} for a construction in the height dimension. - * - * @param surface - the {@link SurfaceLine} array of a {@link Surface} - * @return a {@link Surface} specific double pixel array. - */ - private static Pixels[][] rebuildPixelsArray( Surface surface, int width, int height ) - { - Pixels[][] tempCoordinates = new Pixels[ width ][ height ]; // Transposed array - for ( int i = 0; i <= height - 1; i++ ) - { - for ( int j = 0; j <= width - 1; j++ ) - { - if ( surface.get( i ) != null ) - { - tempCoordinates[ j ][ i ] = surface.get( i ).get( j ); - } - } - } - displayMaximums( tempCoordinates ); - return tempCoordinates; - } - - /** - * Converts and transposes the specified Surface object into the specified OSList array. - * - * @param surface - the Surface object to process - * @param osListsArrayY -the output OSList array - */ - private static void transposeSurfaceLines( Surface surface, OSList[] osListsArrayY, int width, int height ) - { - int dimension = 1; - Pixels[][] pixels = rebuildPixelsArray( surface, width, height ); - /* OS construction in dimension height.*/ - OSEStartingStatus startingStatus = new OSEStartingStatus( dimension ); - set( pixels, osListsArrayY, dimension, startingStatus ); - } - - - /* ----- Other displaying methods -----*/ - - /** - * Reconstructs the TempSurface objects of the specified list in dimension width. - * - * @param surfaces - the list of TempSurface objects. - * @return a list of reconstructed Tempsurface objects - */ - private static ArrayList< Surface > secondRoundConstruction( ArrayList< Surface > surfaces, int width, int height, double percent, int k ) throws NoSurfaceFoundException - { - System.out.println( "========================================" ); - ArrayList< Surface > finalSurfaces = new ArrayList<>(); - - for ( Surface surface : surfaces ) - { - if ( surface.hasDuplicate() )// CoordinateList instead of Coordinate - { - OSList[] osListsArrayY = new OSList[ width ]; - - transposeSurfaceLines( surface, osListsArrayY, width, height ); - OSE.setStartingStatus( 1 ); - System.out.println( "OS.count = " + OSE.getCount() ); - -// displayOS( osListsArrayY ); - ArrayList< Surface > temps = SurfacesReconstruction.buildSurfaces( 1, osListsArrayY, width, height, percent, k ); - if ( temps.isEmpty() ) - { - System.out.println( "small surface in second round" ); -// throw new SecondRoundConstructionException(); - } - else - { - finalSurfaces.addAll( temps ); - } - } - else - { - finalSurfaces.add( surface.transpose() ); - } - } - mergeReferenceSurface( finalSurfaces ); - return finalSurfaces; - } - //TODO Handle the NOSurfaceFoundException dynamically : this particular surface has to be constructed with a - // smaller StartingOSMinimumSize but not necessary the other ones. - // May be add a static variable to store the maximum OS size per surfaces - // - - /** - * Merges the TempSurface objects of the specified list. - * - * @param surfaces - the list to merge - */ - private static void mergeReferenceSurface( ArrayList< Surface > surfaces ) - { - // Merging step. - ArrayList< Surface > toRemoved = new ArrayList<>(); - for ( int i = surfaces.size() - 1; i > 0; i-- ) - { - Surface one = surfaces.get( i ); - for ( int j = i - 1; j >= 0; j-- ) - { - Surface two = surfaces.get( j ); - if ( one.sameSurface( two ) ) - { - two.merge( one ); - toRemoved.add( one ); - } - } - } - surfaces.removeAll( toRemoved ); - } - - - private static < R extends RealType< R > & NativeType< R > > void projection( - Img< R > original, int delta, Img< UnsignedShortType > zMap, - boolean zMapDisplay, - boolean extractedStackDisplay, - boolean heightMapDisplay, - boolean projectionDisplay, - String projectionType, int index ) - { - - // First step : fill in the holes. */ - Img< UnsignedShortType > interpolated = Interpolation.execute( zMap ); - Img< UnsignedShortType > smoothed = interpolated.copy(); - Utils.gaussConvolution( interpolated, smoothed, new double[]{ 1.0, 1.0 } ); -// Second step smoothed the elevation map - if ( zMapDisplay ) - { - ImageJFunctions.show( smoothed, "Elevation map" + "RS n°" + index ); - } - - Img< R > extractedStack = StackProjection.getExtractedStack( original, smoothed, delta ); - if ( extractedStackDisplay ) - { - ImageJFunctions.show( extractedStack, - "Extracted stack (delta = " + delta + ")" + "RS n°" + index ); - } - if ( projectionDisplay ) - { - // A heightmap can be displayed - if ( projectionType.equals( "MIP" ) || - projectionType.equals( "Minimum Intensity" ) ) - { - Img< UnsignedShortType > heightMap = StackProjection.getHeightMap - ( original, smoothed, projectionType, delta ); - if ( heightMapDisplay ) - { - ImageJFunctions.show( heightMap, "Height Map with " + - projectionType + " RS n°" + index ); - } - - Img< R > projection = StackProjection.projection1( original, heightMap, projectionType ); - ImageJFunctions.show( projection, "Projection with " + projectionType + " RS n°" + index ); - } - else - { - - Img< R > projection = StackProjection.projection2( StackProjection.getHeightMapStack( original, zMap, delta ), projectionType ); - ImageJFunctions.show( projection, "Projection with " + projectionType + " RS n°" + index ); - - } -// ImageJFunctions.show(StackProjection.getHeightMapStack(original, zMap, delta), "HMS"); - } - - } - - /** - * Displays the local maximums found using jzy3D package. - */ - public static void displayMaximums( Pixels[][] maximums ) - { - try - { - LocalMaximumsDisplay localMaximumsDisplay = new LocalMaximumsDisplay( maximums ); - AnalysisLauncher.open( localMaximumsDisplay ); - } - catch ( Exception e ) - { - e.printStackTrace(); - } - } - - /** - * Displays the local maximums found using jzy3D package. - */ - public static void displayOS( OSList[] osLists ) - { - try - { - OSDisplay display = new OSDisplay( osLists ); - AnalysisLauncher.open( display ); - } - catch ( Exception e ) - { - e.printStackTrace(); - } - } - - - public static void displaySurface( Surface surface ) - { - try - { - SurfaceDisplay s = new SurfaceDisplay( surface ); - AnalysisLauncher.open( s ); - } - catch ( Exception e ) - { - e.printStackTrace(); - } - - } +// } +// // Second round construction +// ArrayList< Surface > finalSurfaces = secondRoundConstruction( surfaces, width, height, percent2, k2 ); +//// if ( !finalSurfaces.isEmpty() ) +//// { +// int index = 0; +// for ( Surface surface : finalSurfaces ) +// { +// System.out.println( "Surface number " + index ); +//// displaySurface(surface); +// Img< UnsignedShortType > zMap = surface.getZMap(); +// projection( input, delta, zMap, zMapDisplay, +// extractedStackDisplay, heightMapDisplay, +// projectionDisplay, projectionType, index ); +// index++; +// } +// +//// // Display the projection +// System.out.println( "The end" ); +// } +// catch ( Exception e ) +// { +// e.printStackTrace(); +// } +// } +// +// +// //TODO Bad documentation +// +// +// +// /** +// * Setting of the unfiltered local maximums : first the original stack is filtered with an anisotropic +// * gaussian convolution, the pixel values are normalized between 0 and 255 then the local maximums +// * are detected. +// * During these steps, the temporary local, sectional and global thresholds are computed. +// */ +// +// +// /** +// * Creates the OS for each orthogonal section from the maximum coordinates found according to the dimension. +// * +// * @param maximums - the local maximums found with the {@link LocalMaximumDetection} class. +// * @param osListsArray - the OS corresponding to a 1D surface. +// */ +// private static void set( Pixels[][] maximums, OSList[] osListsArray, int dimension, OSEStartingStatus startingStatus ) +// { +// +// +// for ( int i = 0; i <= maximums.length - 1; i++ ) +// { +// osListsArray[ i ] = OSConstruction.findOS( maximums[ i ], dimension, startingStatus ); +// } +// startingStatus.setStartingStatus(); +// } +// +// /* ----- First and second round construction methods. ----- */ +// private static ArrayList< Surface > firstRoundConstruction( Pixels[][] maximums, int width, int height, double percent, int k ) throws NoSurfaceFoundException +// { +// int dimension = 0; +// // Setting and storage of the OS build in the X dimension according to the user thresholds settings. +// OSEStartingStatus startingStatus = new OSEStartingStatus( dimension ); +// OSList[] osListsArrayX = new OSList[ height ]; +// set( maximums, osListsArrayX, dimension, startingStatus ); +//// displayOS(osListsArrayX); +// System.out.println( "OS.count = " + OSE.getCount() ); +// /* Construction of the tempSurfaces*/ +// ArrayList< Surface > surfaces = +// SurfacesReconstruction.buildSurfaces( 0, osListsArrayX, width, height, percent, k ); +// if ( ! surfaces.isEmpty() ) +// { +// mergeReferenceSurface( surfaces ); +// } +// else +// { +// System.out.println( "No Surface Exception" ); +// throw new FirstRoundConstructionException(); +// } +// return surfaces; +// } +// +// /** +// * Rebuilds the double pixel array from the {@link SurfaceLine} array +// * of a {@link Surface} for a construction in the height dimension. +// * +// * @param surface - the {@link SurfaceLine} array of a {@link Surface} +// * @return a {@link Surface} specific double pixel array. +// */ +// private static Pixels[][] rebuildPixelsArray( Surface surface, int width, int height ) +// { +// Pixels[][] tempCoordinates = new Pixels[ width ][ height ]; // Transposed array +// for ( int i = 0; i <= height - 1; i++ ) +// { +// for ( int j = 0; j <= width - 1; j++ ) +// { +// if ( surface.get( i ) != null ) +// { +// tempCoordinates[ j ][ i ] = surface.get( i ).get( j ); +// } +// } +// } +// displayMaximums( tempCoordinates ); +// return tempCoordinates; +// } +// +// /** +// * Converts and transposes the specified Surface object into the specified OSList array. +// * +// * @param surface - the Surface object to process +// * @param osListsArrayY -the output OSList array +// */ +// private static void transposeSurfaceLines( Surface surface, OSList[] osListsArrayY, int width, int height ) +// { +// int dimension = 1; +// Pixels[][] pixels = rebuildPixelsArray( surface, width, height ); +// /* OS construction in dimension height.*/ +// OSEStartingStatus startingStatus = new OSEStartingStatus( dimension ); +// set( pixels, osListsArrayY, dimension, startingStatus ); +// } +// +// +// /* ----- Other displaying methods -----*/ +// +// /** +// * Reconstructs the TempSurface objects of the specified list in dimension width. +// * +// * @param surfaces - the list of TempSurface objects. +// * @return a list of reconstructed Tempsurface objects +// */ +// private static ArrayList< Surface > secondRoundConstruction( ArrayList< Surface > surfaces, int width, int height, double percent, int k ) throws NoSurfaceFoundException +// { +// System.out.println( "========================================" ); +// ArrayList< Surface > finalSurfaces = new ArrayList<>(); +// +// for ( Surface surface : surfaces ) +// { +// if ( surface.hasDuplicate() )// CoordinateList instead of Coordinate +// { +// OSList[] osListsArrayY = new OSList[ width ]; +// +// transposeSurfaceLines( surface, osListsArrayY, width, height ); +// OSE.setStartingStatus( 1 ); +// System.out.println( "OS.count = " + OSE.getCount() ); +// +//// displayOS( osListsArrayY ); +// ArrayList< Surface > temps = SurfacesReconstruction.buildSurfaces( 1, osListsArrayY, width, height, percent, k ); +// if ( temps.isEmpty() ) +// { +// System.out.println( "small surface in second round" ); +//// throw new SecondRoundConstructionException(); +// } +// else +// { +// finalSurfaces.addAll( temps ); +// } +// } +// else +// { +// finalSurfaces.add( surface.transpose() ); +// } +// } +// mergeReferenceSurface( finalSurfaces ); +// return finalSurfaces; +// } +// //TODO Handle the NOSurfaceFoundException dynamically : this particular surface has to be constructed with a +// // smaller StartingOSMinimumSize but not necessary the other ones. +// // May be add a static variable to store the maximum OS size per surfaces +// // +// +// /** +// * Merges the TempSurface objects of the specified list. +// * +// * @param surfaces - the list to merge +// */ +// private static void mergeReferenceSurface( ArrayList< Surface > surfaces ) +// { +// // Merging step. +// ArrayList< Surface > toRemoved = new ArrayList<>(); +// for ( int i = surfaces.size() - 1; i > 0; i-- ) +// { +// Surface one = surfaces.get( i ); +// for ( int j = i - 1; j >= 0; j-- ) +// { +// Surface two = surfaces.get( j ); +// if ( one.sameSurface( two ) ) +// { +// two.merge( one ); +// toRemoved.add( one ); +// } +// } +// } +// surfaces.removeAll( toRemoved ); +// } +// +// +// private static < R extends RealType< R > & NativeType< R > > void projection( +// Img< R > original, int delta, Img< UnsignedShortType > zMap, +// boolean zMapDisplay, +// boolean extractedStackDisplay, +// boolean heightMapDisplay, +// boolean projectionDisplay, +// String projectionType, int index ) +// { +// +// // First step : fill in the holes. */ +// Img< UnsignedShortType > interpolated = Interpolation.execute( zMap ); +// Img< UnsignedShortType > smoothed = interpolated.copy(); +// Utils.gaussConvolution( interpolated, smoothed, new double[]{ 1.0, 1.0 } ); +//// Second step smoothed the elevation map +// if ( zMapDisplay ) +// { +// ImageJFunctions.show( smoothed, "Elevation map" + "RS n°" + index ); +// } +// +// Img< R > extractedStack = StackProjection.getExtractedStack( original, smoothed, delta ); +// if ( extractedStackDisplay ) +// { +// ImageJFunctions.show( extractedStack, +// "Extracted stack (delta = " + delta + ")" + "RS n°" + index ); +// } +// if ( projectionDisplay ) +// { +// // A heightmap can be displayed +// if ( projectionType.equals( "MIP" ) || +// projectionType.equals( "Minimum Intensity" ) ) +// { +// Img< UnsignedShortType > heightMap = StackProjection.getHeightMap +// ( original, smoothed, projectionType, delta ); +// if ( heightMapDisplay ) +// { +// ImageJFunctions.show( heightMap, "Height Map with " + +// projectionType + " RS n°" + index ); +// } +// +// Img< R > projection = StackProjection.projection1( original, heightMap, projectionType ); +// ImageJFunctions.show( projection, "Projection with " + projectionType + " RS n°" + index ); +// } +// else +// { +// +// Img< R > projection = StackProjection.projection2( StackProjection.getHeightMapStack( original, zMap, delta ), projectionType ); +// ImageJFunctions.show( projection, "Projection with " + projectionType + " RS n°" + index ); +// +// } +//// ImageJFunctions.show(StackProjection.getHeightMapStack(original, zMap, delta), "HMS"); +// } +// +// } +// +// /** +// * Displays the local maximums found using jzy3D package. +// */ +// public static void displayMaximums( Pixels[][] maximums ) +// { +// try +// { +// LocalMaximumsDisplay localMaximumsDisplay = new LocalMaximumsDisplay( maximums ); +// AnalysisLauncher.open( localMaximumsDisplay ); +// } +// catch ( Exception e ) +// { +// e.printStackTrace(); +// } +// } +// +// /** +// * Displays the local maximums found using jzy3D package. +// */ +// public static void displayOS( OSList[] osLists ) +// { +// try +// { +// OSDisplay display = new OSDisplay( osLists ); +// AnalysisLauncher.open( display ); +// } +// catch ( Exception e ) +// { +// e.printStackTrace(); +// } +// } +// +// +// public static void displaySurface( Surface surface ) +// { +// try +// { +// SurfaceDisplay s = new SurfaceDisplay( surface ); +// AnalysisLauncher.open( s ); +// } +// catch ( Exception e ) +// { +// e.printStackTrace(); +// } +// +// } } diff --git a/src/main/java/fr/pasteur/ida/zellige/main/Main.java b/src/main/java/fr/pasteur/ida/zellige/main/Main.java index 8f01c10bf21870099e8539099e878f5f7a1bc159..f014ae61912712e9f1ce429887bdc6982da94899 100644 --- a/src/main/java/fr/pasteur/ida/zellige/main/Main.java +++ b/src/main/java/fr/pasteur/ida/zellige/main/Main.java @@ -102,7 +102,7 @@ public class Main ImageJFunctions.show( stackImage, "original" ); ImageJFunctions.show( TestMIP.findMIP( stackImage, stackImage.factory() ), "MIP" ); - SurfacesExtraction.extract(stackImage,amplitude,otsu, sigmas, false, delta, false, + SurfacesExtraction.extract(stackImage, stackImage.factory(),amplitude,otsu, sigmas, false, delta, false, false, true, "MIP", k1, percent1, k2, percent2); } else diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesExtraction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesExtraction.java index 688c16de66d026e3537328702abcc8aada6e64c1..e62720161e0951a3b7ee6fe2f08fd8bd731dbf0b 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesExtraction.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesExtraction.java @@ -1,12 +1,5 @@ package fr.pasteur.ida.zellige.surfaceConstruction.construction; -import java.util.ArrayList; - -import fr.pasteur.ida.zellige.surfaceConstruction.element.os.OSE; -import fr.pasteur.ida.zellige.surfaceConstruction.element.os.OSEStartingStatus; -import fr.pasteur.ida.zellige.utils.*; -import org.jzy3d.analysis.AnalysisLauncher; - import fr.pasteur.ida.zellige.exception.FirstRoundConstructionException; import fr.pasteur.ida.zellige.exception.NoSurfaceFoundException; import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; @@ -14,14 +7,21 @@ import fr.pasteur.ida.zellige.jzy3D.OSDisplay; import fr.pasteur.ida.zellige.jzy3D.SurfaceDisplay; import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.os.OSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.os.OSEStartingStatus; import fr.pasteur.ida.zellige.surfaceConstruction.element.os.OSList; import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; +import fr.pasteur.ida.zellige.utils.*; +import net.imglib2.RandomAccessibleInterval; import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; import net.imglib2.img.display.imagej.ImageJFunctions; 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 org.jzy3d.analysis.AnalysisLauncher; + +import java.util.ArrayList; @@ -29,7 +29,7 @@ public class SurfacesExtraction { public static < T extends RealType< T > & NativeType< T > > void extract( - Img< T > input, double amplitude, double threshold, int sigmas, + RandomAccessibleInterval< T > input, ImgFactory<T> factory,double amplitude, double threshold, int sigmas, boolean zMapDisplay, int delta, boolean extractedStackDisplay, boolean heightMapDisplay, @@ -60,7 +60,7 @@ public class SurfacesExtraction System.out.println( "Surface number " + index ); // displaySurface(surface); Img< UnsignedShortType > zMap = surface.getZMap(); - projection( input, delta, zMap, zMapDisplay, + projection( input, factory,delta, zMap, zMapDisplay, extractedStackDisplay, heightMapDisplay, projectionDisplay, projectionType, index ); index++; @@ -76,44 +76,6 @@ public class SurfacesExtraction } - //TODO Bad documentation - - /** - * This method is used to binarize a grey-level image using Otsu Thresholding method in order to classify its pixels - * into foreground/background and then apply a anisotropic gaussian blur. - */ - - private static < T extends RealType< T > & NativeType< T > > Img< T > maximumSearch( Img< T > input, double percent, int sigma ) - { - /* Local maximum detection using partial derivative */ - Img< T > maximums = LocalMaximumDetection.findMaximums( input, input.factory() ); - ImageJFunctions.show( maximums, "maximums" ); - - /* Grid of local maximums*/ - Img< BitType > threshold = LocalOtsuClassification.find( input, percent ); - //ImageJFunctions.show( threshold, "Local threshold " ); - - /* Maxima selection according to threshold*/ - maximums = Threshold.classification( maximums.copy(), maximums.factory(), threshold ); - - /* Dilatation of the resulting image*/ - Utils.gaussConvolution( maximums.copy(), maximums, new double[]{ sigma, sigma } ); -// ImageJFunctions.show( maximums, "blurred maximums" ); - - /* New local maximum detection due to previous smoothing*/ - maximums = LocalMaximumDetection.findMaximums( maximums.copy(), maximums.factory() ); - return maximums; - } - - - /** - * Setting of the unfiltered local maximums : first the original stack is filtered with an anisotropic - * gaussian convolution, the pixel values are normalized between 0 and 255 then the local maximums - * are detected. - * During these steps, the temporary local, sectional and global thresholds are computed. - */ - - /** * Creates the OS for each orthogonal section from the maximum coordinates found according to the dimension. * @@ -271,7 +233,7 @@ public class SurfacesExtraction private static < R extends RealType< R > & NativeType< R > > void projection( - Img< R > original, int delta, Img< UnsignedShortType > zMap, + RandomAccessibleInterval< R > original, ImgFactory<R> factory, int delta, Img< UnsignedShortType > zMap, boolean zMapDisplay, boolean extractedStackDisplay, boolean heightMapDisplay, @@ -289,7 +251,7 @@ public class SurfacesExtraction ImageJFunctions.show( smoothed, "Elevation map" + "RS n°" + index ); } - Img< R > extractedStack = StackProjection.getExtractedStack( original, smoothed, delta ); + Img< R > extractedStack = StackProjection.getExtractedStack( original, factory, smoothed, delta ); if ( extractedStackDisplay ) { ImageJFunctions.show( extractedStack, @@ -309,13 +271,13 @@ public class SurfacesExtraction projectionType + " RS n°" + index ); } - Img< R > projection = StackProjection.projection1( original, heightMap, projectionType ); + Img< R > projection = StackProjection.projection1( original,factory, heightMap, projectionType ); ImageJFunctions.show( projection, "Projection with " + projectionType + " RS n°" + index ); } else { - Img< R > projection = StackProjection.projection2( StackProjection.getHeightMapStack( original, zMap, delta ), projectionType ); + Img< R > projection = StackProjection.projection2( StackProjection.getHeightMapStack( original, factory,zMap, delta ),factory, projectionType ); ImageJFunctions.show( projection, "Projection with " + projectionType + " RS n°" + index ); } diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/HistogramZ.java b/src/main/java/fr/pasteur/ida/zellige/utils/HistogramZ.java new file mode 100644 index 0000000000000000000000000000000000000000..a094d3e335acb47b2937acebe213b51165f6a863 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/utils/HistogramZ.java @@ -0,0 +1,206 @@ +package fr.pasteur.ida.zellige.utils; + + +import java.util.ArrayList; +import java.util.Arrays; + +import net.imglib2.IterableInterval; +import net.imglib2.RealCursor; +import net.imglib2.algorithm.Algorithm; +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.stats.HistogramBinMapper; +import net.imglib2.type.Type; + +/** + * Implements a Histogram over an Image. + * + * @author 2011 Larry Lindsey + * @author Larry Lindsey + */ +public class HistogramZ< T > implements Algorithm, Benchmark +{ + /** + * Hold the histogram itself. + */ + private final int[] histogram; + /** + * The Cursor from which the histogram is to be calculated. + */ + private final RealCursor< T > cursor; + /** + * The HistogramBinMapper, used to map Type values to histogram bin indices. + */ + private final HistogramBinMapper< T > binMapper; + /** + * Processing time, milliseconds. + */ + private long pTime = 0; + + /** + * Create a Histogram using the given mapper, calculating from the given + * Cursor. + * + * @param mapper the HistogramBinMapper used to map Type values to histogram + * bin indices. + * @param c a Cursor corresponding to the Image from which the Histogram + * will be calculated + */ + public HistogramZ( final HistogramBinMapper< T > mapper, + final RealCursor< T > c ) + { + cursor = c; + binMapper = mapper; + histogram = new int[ binMapper.getNumBins() ]; + } + + /** + * Create a Histogram using the given mapper, calculating from the given + * Image. + * + * @param mapper the HistogramBinMapper used to map Type values to histogram + * bin indices. + * @param image an Image from which the Histogram will be calculated + */ + public HistogramZ( final HistogramBinMapper< T > mapper, + final IterableInterval< T > image ) + { + this( mapper, image.cursor() ); + } + + /** + * Resets the histogram array and the Cursor. + */ + public void reset() + { + Arrays.fill( histogram, 0 ); + cursor.reset(); + } + + /** + * Returns the bin count corresponding to a given {@link Type}. + * + * @param t the Type corresponding to the requested + * @return The requested bin count. + */ + public int getBin( final T t ) + { + return getHistogram()[ binMapper.map( t ) ]; + } + + /** + * Returns the bin count given by the indicated bin index. + * + * @param i the index of the requested bin + * @return the bin count at the given index + */ + public int getBin( final int i ) + { + return getHistogram()[ i ]; + } + + /** + * Returns this Histogram's HistogramBinMapper. + * + * @return the HistogramBinMapper associated with this Histogram. + */ + public HistogramBinMapper< T > getBinMapper() + { + return binMapper; + } + + /** + * Returns the histogram array. + * + * @return the histogram array. + */ + public int[] getHistogram() + { + return histogram; + } + + /** + * Creates and returns the a Type whose value corresponds to the center of + * the bin indexed by i. + * + * @param i the requested bin index. + * @return a Type whose value corresponds to the requested bin center. + */ + public T getBinCenter( final int i ) + { + return getBinMapper().invMap( i ); + } + + /** + * Creates and returns a List containing Types that correspond to the + * centers of the histogram bins. + * + * @return a List containing Types that correspond to the centers of the + * histogram bins. + */ + public ArrayList< T > getBinCenters() + { + final ArrayList< T > binCenters = new ArrayList< T >( histogram.length ); + for ( int i = 0; i < histogram.length; ++ i ) + { + binCenters.add( i, getBinMapper().invMap( i ) ); + } + + return binCenters; + } + + /** + * Returns the number of bins in this Histogram. + * + * @return the number of bins in this Histogram + */ + public int getNumBins() + { + return getBinMapper().getNumBins(); + } + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public String getErrorMessage() + { + return null; + } + + @Override + public boolean process() + { + final long startTime = System.currentTimeMillis(); + int index; + + while ( cursor.hasNext() ) + { + cursor.fwd(); + index = binMapper.map( cursor.get() ); + /* + * The following check makes this run for IntegerTypes at 3 to 4 + * longer than the manual case on my machine. This is a necessary + * check, but if this takes too long, it might be worthwhile to + * separate out an UncheckedHistogram, which would instead throw an + * ArrayOutOfBoundsException. + */ + if ( index >= 0 && index < histogram.length ) + { + ++ histogram[ index ]; + } + } + + pTime = System.currentTimeMillis() - startTime; + return true; + } + + @Override + public long getProcessingTime() + { + return pTime; + } + +} \ No newline at end of file diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java b/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java index 8adad8ef99f8f7fc944bc1e98f35a7a30a1b723d..c7a4ecb329d9af6847222aaf45bad4fac5673e09 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java @@ -1,8 +1,6 @@ package fr.pasteur.ida.zellige.utils; -import net.imglib2.Cursor; -import net.imglib2.Interval; -import net.imglib2.RandomAccess; +import net.imglib2.*; import net.imglib2.algorithm.util.Grids; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; @@ -23,45 +21,43 @@ public class LocalOtsuClassification { /** - * - * @param input the input {@link Img} + * @param input the input {@link Img} * @param percent the percentage of global otsu value - * @param <T> the type on the input + * @param <T> the type on the input * @return a 3D binary {@link Img<BitType>} */ - public static < T extends RealType< T > & NativeType< T > > Img< BitType > find( final Img< T > input , double percent) + public static < T extends RealType< T > & NativeType< T > > Img< BitType > find( final RandomAccessibleInterval< T > input,ImgFactory<T> factory, double percent ) { // Prepare output. - final ImgFactory< BitType > factory = Util.getArrayOrCellImgFactory( input, new BitType() ); - Img< BitType > binary = factory.create( input ); - new OtsuThreshold<>( input, binary, percent); + final ImgFactory< BitType > bitTypeImgFactory = Util.getArrayOrCellImgFactory( input, new BitType() ); + Img< BitType > binary = bitTypeImgFactory.create( input ); + new OtsuThreshold<>( input,factory, binary, percent ); return binary; } /** - * * @param <T> */ private static final class OtsuThreshold< T extends RealType< T > & NativeType< T > > { - private final Img< T > source; - private final Img< T > grid; + private final RandomAccessibleInterval< T > source; + private final ImgFactory<T> factory; + private final RandomAccessibleInterval< T > grid; private final Img< BitType > binary; private final double percent; /** - * - * @param source the input {@link Img} - * @param binary the resulting binary image as a {@link Img<BitType> } + * @param source the input {@link Img} + * @param binary the resulting binary image as a {@link Img<BitType> } * @param percent the percentage of global otsu value */ - private OtsuThreshold( final Img< T > source, final Img< BitType > binary, double percent ) + private OtsuThreshold( final RandomAccessibleInterval< T > source, final ImgFactory<T> factory, final Img< BitType > binary, double percent ) { this.source = source; + this.factory = factory; this.binary = binary; this.percent = percent; - final ImgFactory< T > factory = Util.getArrayOrCellImgFactory( source, Util.getTypeFromInterval( source ) ); this.grid = factory.create( source ); run(); } @@ -71,27 +67,18 @@ public class LocalOtsuClassification */ public void run() { - computeLocalThreshold( source, grid); - applyLocalThreshold( source, grid, binary, percent ); + computeLocalThreshold( source, factory, grid ); + applyLocalThreshold( Views.iterable( source) ,Views.iterable( grid), binary, percent ); } - /** - * - * @return the image containing the grid of local thresholds - */ - public Img< BitType > getGrid() - { - return binary; - } /** - * * @param input the input {@link Img} - * @param grid the image containing the grid of local thresholds - * @param <T> the type on the input + * @param grid the image containing the grid of local thresholds + * @param <T> the type on the input */ public static < T extends RealType< T > & NativeType< T > > void - computeLocalThreshold( Img< T > input, Img< T > grid ) + computeLocalThreshold( RandomAccessibleInterval< T > input, ImgFactory<T> factory,RandomAccessibleInterval< T > grid ) { long width = input.dimension( 0 ); long height = input.dimension( 1 ); @@ -101,13 +88,13 @@ public class LocalOtsuClassification for ( Interval interval : intervals ) { - computeLocalThreshold( input, grid , interval ); + computeLocalThreshold( input, grid, interval ); } + grid = Utils.gaussConvolution( grid,factory, new double[]{ 5, 5, 1 } ); } - public static < T extends RealType< T > & NativeType< T > > void - computeLocalThreshold( Img< T > input, Img< T > grid , Interval interval ) + computeLocalThreshold( RandomAccessibleInterval< T > input, RandomAccessibleInterval< T > grid, Interval interval ) { IntervalView< T > viewSource = Views.offsetInterval( input, interval ); IntervalView< T > viewGrid = Views.offsetInterval( grid, interval ); @@ -116,30 +103,27 @@ public class LocalOtsuClassification } /** - * - * @param input the input {@link Img} - * @param grid the image containing the grid of local thresholds - * @param binary the resulting binary image as a {@link Img<BitType> } + * @param input the input image as an {@link IterableInterval} + * @param grid the image containing the grid of local thresholds as an {@link IterableInterval} + * @param binary the resulting binary image as a {@link RandomAccessibleInterval<BitType> } * @param percent the percentage of global otsu value - * @param <T> the input type + * @param <T> the input type */ public static < T extends RealType< T > & NativeType< T > > void - applyLocalThreshold( Img< T > input, Img< T > grid, Img<BitType> binary , double percent) + applyLocalThreshold( IterableInterval< T > input, IterableInterval< T > grid, RandomAccessibleInterval< BitType > binary, double percent ) { double threshold = Threshold.getThreshold( input, percent ).getRealDouble(); - Utils.gaussConvolution( grid.copy(), grid, new double[]{5, 5, 1} ); - ImageJFunctions.show( grid.copy(), "grid" ); - Cursor<T> sourceCursor = input.localizingCursor(); - Cursor<T> outputCursor = grid.cursor(); + Cursor< T > sourceCursor = input.localizingCursor(); + Cursor< T > outputCursor = grid.cursor(); RandomAccess< BitType > randomAccess = binary.randomAccess(); - while(sourceCursor.hasNext()) + while ( sourceCursor.hasNext() ) { sourceCursor.fwd(); outputCursor.fwd(); final double s = sourceCursor.get().getRealDouble(); final double o = outputCursor.get().getRealDouble(); { - if (s >= Math.max( o, threshold))// background subtraction + if ( s >= Math.max( o, threshold ) )// background subtraction { randomAccess.setPosition( sourceCursor ); randomAccess.get().set( true ); @@ -150,9 +134,17 @@ public class LocalOtsuClassification } + /** + * @return the image containing the grid of local thresholds + */ + public Img< BitType > getGrid() + { + return binary; + } } +} + -} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification.java b/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification.java index 74e9bc4941cf457a3435b4043d4b202b3686e0da..bb15d9423c986d0e0fd71cf42bd76a12adf4e5a5 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification.java @@ -30,54 +30,57 @@ public class MaximumAmplitudeClassification { /** - * - * @param input - the input image as a {@link Img} + * @param input - the input image as a {@link RandomAccessibleInterval} * @param amplitudeThreshold - the parameter user value to apply to the first mode of the image histogram. - * @param sizePercent the minimum percentage of pixels for isolated pixel elimination - * @param <T> - the type of the input + * @param sizePercent the minimum percentage of pixels for isolated pixel elimination + * @param <T> - the type of the input * @return a binary image of background foreground classification */ - public static < T extends RealType< T > & NativeType< T > > Img< FloatType > find( final Img< T > input, double amplitudeThreshold, double sizePercent ) + public static < T extends RealType< T > & NativeType< T > > Img< FloatType > find( final RandomAccessibleInterval< T > input, + final ImgFactory< T > factory, double amplitudeThreshold, double sizePercent ) { // Prepare output. Img< BitType > amplitude = new ArrayImgFactory<>( new BitType() ).create( input ); - MaximumAmplitude< T > maximumAmplitude = new MaximumAmplitude<>( input, amplitude, amplitudeThreshold ); - final ImgFactory< FloatType > factory = Util.getArrayOrCellImgFactory( input, new FloatType() ); - Img< FloatType > output = factory.create( input ); + MaximumAmplitude< T > maximumAmplitude = new MaximumAmplitude<>( input, factory, amplitude, amplitudeThreshold ); + final ImgFactory< FloatType > floatTypeImgFactory = Util.getArrayOrCellImgFactory( input, new FloatType() ); + Img< FloatType > output = floatTypeImgFactory.create( input ); new BackgroundForegroundGrid( maximumAmplitude.getAmplitude(), output, sizePercent ); + Utils.gaussConvolution( output.copy(), output, new double[]{ 0, 0, 0.5 } ); ImageJFunctions.show( output, "new class amplitude" ); return output; } /** * This class generate a binary image according to a percentage of the smallest mode value of the input image + * * @param <T> the type of the input {@link Img} */ private static final class MaximumAmplitude< T extends RealType< T > & NativeType< T > > { - private final Img< T > input; + private final RandomAccessibleInterval< T > input; + private final ImgFactory< T > factory; private final double amplitudeThreshold; - private Img< BitType > amplitude; + private RandomAccessibleInterval< BitType > amplitude; - public MaximumAmplitude( Img< T > input, Img< BitType > amplitude, double amplitudeThreshold ) + public MaximumAmplitude( RandomAccessibleInterval< T > input, ImgFactory< T > factory, RandomAccessibleInterval< BitType > amplitude, double amplitudeThreshold ) { this.input = input; + this.factory = factory; this.amplitude = amplitude; this.amplitudeThreshold = amplitudeThreshold; run(); } /** - * - * @param max the image containing only the values of local maximums + * @param max the image containing only the values of local maximums * @param min the image containing only the values of local minimums * @param <T> the type of both images {@link Img} * @return an image containing the value of the maximums amplitude */ private static < T extends RealType< T > & NativeType< T > > Img< T > getAmplitude( - Img< T > max, RandomAccessibleInterval< T > min ) + RandomAccessibleInterval< T > max, ImgFactory< T > factory, RandomAccessibleInterval< T > min ) { - Img< T > amp = max.factory().create( max ); + Img< T > amp = factory.create( max ); RandomAccess< T > maxAccess = max.randomAccess(); RandomAccess< T > minAccess = min.randomAccess(); RandomAccess< T > ampAccess = amp.randomAccess(); @@ -95,7 +98,7 @@ public class MaximumAmplitudeClassification if ( maxValue != 0 ) { minAccess.setPosition( maxAccess ); - double amplitude = getAmplitude( maxValue, minAccess, (int) max.dimension( 2 ) ); + double amplitude = getAmplitude( maxValue, minAccess, ( int ) max.dimension( 2 ) ); ampAccess.setPosition( maxAccess ); ampAccess.get().setReal( amplitude ); } @@ -106,16 +109,15 @@ public class MaximumAmplitudeClassification } /** - * - * @param maxValue the intensity value at local maximum positioned at minAccess location + * @param maxValue the intensity value at local maximum positioned at minAccess location * @param minAccess - the random access positioned at the same specific local maximum location - * @param <T> the type of the {@link RandomAccess} + * @param <T> the type of the {@link RandomAccess} * @return the chosen amplitude value of the local maximum positioned at minAccess location */ - private static < T extends RealType< T > & NativeType< T > > double getAmplitude( double maxValue, RandomAccess< T > minAccess , int depth) + private static < T extends RealType< T > & NativeType< T > > double getAmplitude( double maxValue, RandomAccess< T > minAccess, int depth ) { double up = findValueUp( minAccess, maxValue ); - double down = findValueDown( minAccess, maxValue , depth); + double down = findValueDown( minAccess, maxValue, depth ); double result = Math.max( Math.abs( maxValue - up ), Math.abs( maxValue - down ) ); if ( result == 0 ) { @@ -126,10 +128,9 @@ public class MaximumAmplitudeClassification } /** - * * @param minAccess the random access of the local minimum image - * @param maxValue the intensity value of the local maximum located at minAccess position - * @param <T> the type of the {@link RandomAccess} + * @param maxValue the intensity value of the local maximum located at minAccess position + * @param <T> the type of the {@link RandomAccess} * @return the amplitude value above the local maximum location in the Z dimension */ private static < T extends RealType< T > & NativeType< T > > double findValueUp( @@ -149,10 +150,9 @@ public class MaximumAmplitudeClassification } /** - * * @param minAccess the random access of the local minimum image - * @param maxValue the intensity value of the local maximum located at minAccess position - * @param <T> the type of the {@link RandomAccess} + * @param maxValue the intensity value of the local maximum located at minAccess position + * @param <T> the type of the {@link RandomAccess} * @return the amplitude value below the local maximum location in the Z dimension */ private static < T extends RealType< T > & NativeType< T > > double findValueDown( @@ -173,16 +173,16 @@ public class MaximumAmplitudeClassification public void run() { - Img< T > maximums = LocalMaximumDetection.findMaximums( input, input.factory() ); - Img< T > minimums = LocalMinimumDetection.findMinimums( input, input.factory() ); - Img< T > amp = getAmplitude( maximums, minimums ); + Img< T > maximums = LocalMaximumDetection.findMaximums( input, factory ); + Img< T > minimums = LocalMinimumDetection.findMinimums( input, factory ); + Img< T > amp = getAmplitude( maximums, factory, minimums ); T TMax = Threshold.getFirstMaxValue( maximums, false ); TMax.mul( amplitudeThreshold ); this.amplitude = Thresholder.threshold( amp.copy(), TMax, true, 2 ); } - public Img< BitType > getAmplitude() + public RandomAccessibleInterval< BitType > getAmplitude() { return amplitude; } @@ -194,17 +194,16 @@ public class MaximumAmplitudeClassification */ private static final class BackgroundForegroundGrid { - private final Img< BitType > source; - private final Img< FloatType > output; + private final RandomAccessibleInterval< BitType > source; + private final RandomAccessibleInterval< FloatType > output; private final double sizePercent; /** - * - * @param source - the input as a 2D {@link Img<BitType> } - * @param output - the output as a 2D {@link Img<FloatType> } + * @param source - the input as a 2D {@link Img<BitType> } + * @param output - the output as a 2D {@link Img<FloatType> } * @param sizePercent - the minimum percentage of positive pixels */ - private BackgroundForegroundGrid( final Img< BitType > source, Img< FloatType > output, double sizePercent ) + private BackgroundForegroundGrid( final RandomAccessibleInterval< BitType > source, Img< FloatType > output, double sizePercent ) { this.source = source; this.output = output; @@ -213,10 +212,9 @@ public class MaximumAmplitudeClassification } /** - * - * @param intervalView a sub 2D image - * @param sizePercent the minimum percentage of pixels for isolated pixel elimination - * @param <T> the type of the sub image + * @param intervalView a sub 2D image + * @param sizePercent the minimum percentage of pixels for isolated pixel elimination + * @param <T> the type of the sub image * @return true if the sub image does contain enough foreground pixels false otherwise */ public static < T extends RealType< T > & NativeType< T > > boolean isForeground( IntervalView< T > intervalView, double sizePercent ) @@ -253,14 +251,13 @@ public class MaximumAmplitudeClassification viewOutput.forEach( pixel -> pixel.setReal( foreground ) ); } // Slight dilatation in z dimension - Utils.gaussConvolution( output.copy(), output, new double[]{ 0, 0, 0.5 } ); + } /** - * * @return the smoothed amplitude classified image */ - public Img< FloatType > getOutput() + public RandomAccessibleInterval< FloatType > getOutput() { return output; } diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/StackProjection.java b/src/main/java/fr/pasteur/ida/zellige/utils/StackProjection.java index cd78b4543e1b0ca920aff92268a9a0c3ac891d04..de605624d3050b6c4a99ae92d9c36c1182b9a0ce 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/StackProjection.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/StackProjection.java @@ -79,7 +79,7 @@ public class StackProjection * @return */ public static < T extends RealType< T > & NativeType< T > > Img< UnsignedShortType > getHeightMap - ( Img< T > stack, RandomAccessibleInterval< UnsignedShortType > zMap, String method, int delta ) + ( RandomAccessibleInterval< T > stack, RandomAccessibleInterval< UnsignedShortType > zMap, String method, int delta ) { ImgFactory< UnsignedShortType > imgFactory = new ArrayImgFactory<>( new UnsignedShortType() ); Img< UnsignedShortType > heightMap = @@ -120,11 +120,11 @@ public class StackProjection * @return */ public static < T extends RealType< T > & NativeType< T > > Img< T > getHeightMapStack - ( Img< T > stack, RandomAccessibleInterval< UnsignedShortType > zMap, + ( RandomAccessibleInterval< T > stack, ImgFactory<T> factory, RandomAccessibleInterval< UnsignedShortType > zMap, int delta ) { //the stack slice number depends of the user "delta" parameter. - Img< T > heightMapStack = stack.factory().create( stack.dimension( 0 ), stack.dimension( 1 ), ( delta * 2 ) + 1 ); + Img< T > heightMapStack = factory.create( stack.dimension( 0 ), stack.dimension( 1 ), ( delta * 2 ) + 1 ); RandomAccess< T > stackAccess = stack.randomAccess(); RandomAccess< T > heightMapAccess = heightMapStack.randomAccess(); RandomAccess< UnsignedShortType > zMapAccess = zMap.randomAccess(); @@ -176,9 +176,9 @@ public class StackProjection public static < T extends RealType< T > & NativeType< T > > Img< T > getExtractedStack - ( Img< T > stack, RandomAccessibleInterval< UnsignedShortType > zMap, int delta ) + ( RandomAccessibleInterval< T > stack, ImgFactory<T> factory,RandomAccessibleInterval< UnsignedShortType > zMap, int delta ) { - Img< T > heightMapStack = stack.factory().create( stack.dimension( 0 ), stack.dimension( 1 ), stack.dimension( 2 ) ); + Img< T > heightMapStack = factory.create( stack.dimension( 0 ), stack.dimension( 1 ), stack.dimension( 2 ) ); RandomAccess< T > stackAccess = stack.randomAccess(); RandomAccess< T > heightmapAccess = heightMapStack.randomAccess(); RandomAccess< UnsignedShortType > zMapAccess = zMap.randomAccess(); @@ -222,12 +222,12 @@ public class StackProjection * @return the projection of the original stack image according to yhe height map. */ public static < T extends RealType< T > & NativeType< T > > Img< T > projection1( - Img< T > stack, Img< UnsignedShortType > heightMap, String projectionType ) + RandomAccessibleInterval< T > stack,ImgFactory<T> factory, RandomAccessibleInterval< UnsignedShortType > heightMap, String projectionType ) { //TODO replace stack by extracted stack if ( projectionType.equals( "MIP" ) ) { - Img< T > projection = stack.factory().create( stack.dimension( 0 ), stack.dimension( 1 ) ); + Img< T > projection = factory.create( stack.dimension( 0 ), stack.dimension( 1 ) ); RandomAccess< T > stackAccess = stack.randomAccess(); RandomAccess< T > projectionAccess = projection.randomAccess(); RandomAccess< UnsignedShortType > heightmapAccess = heightMap.randomAccess(); @@ -259,9 +259,9 @@ public class StackProjection } public static < T extends RealType< T > & NativeType< T > > Img< T > projection2( - Img< T > extractedStack, String projectionType ) + RandomAccessibleInterval< T > extractedStack, ImgFactory<T> factory,String projectionType ) { - Img< T > projection = extractedStack.factory().create( extractedStack.dimension( 0 ), extractedStack.dimension( 1 ) ); + Img< T > projection = factory.create( extractedStack.dimension( 0 ), extractedStack.dimension( 1 ) ); if ( projectionType.equals( "median" ) )// sumSlices, median, standardDeviation { medianProjection( extractedStack, projection ); @@ -306,7 +306,7 @@ public class StackProjection } public static < T extends RealType< T > & NativeType< T > > void medianProjection - ( Img< T > input, Img< T > output ) + ( RandomAccessibleInterval< T > input,RandomAccessibleInterval< T > output ) { RandomAccess< T > inputAccess = input.randomAccess(); RandomAccess< T > outputAccess = output.randomAccess(); diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/SurfacePixelSelection.java b/src/main/java/fr/pasteur/ida/zellige/utils/SurfacePixelSelection.java index c28fccf223904999e4a667db425bd5eb94e816d9..7691c3e86b9ba9d70b83c2bf3a844e38533da7aa 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/SurfacePixelSelection.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/SurfacePixelSelection.java @@ -5,12 +5,18 @@ import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; import net.imglib2.RandomAccess; import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.Converters; +import net.imglib2.converter.RealFloatConverter; +import net.imglib2.converter.readwrite.RealFloatSamplerConverter; 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.type.NativeType; import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.RealType; import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.ImgUtil; import org.jzy3d.analysis.AnalysisLauncher; public class SurfacePixelSelection @@ -26,14 +32,14 @@ public class SurfacePixelSelection * @param <T> * @return */ - public static < T extends RealType< T > & NativeType< T > > Pixels[][] run( Img< T > source, double amplitude, double threshold, int sigmas ) + public static < T extends RealType< T > & NativeType< T > > Pixels[][] run( RandomAccessibleInterval< T > source, double amplitude, double threshold, int sigmas ) { /* Pretreatment of the image.*/ Img< FloatType > normalized = pretreatment( source ); ImageJFunctions.show( normalized.copy(), "converted, blurred and normalized " ); - + ImgFactory<FloatType> factory = normalized.factory(); /* Local maximum search and selection */ - Pixels[][] maximums = maximumSearch( normalized, amplitude,threshold, sigmas ); + Pixels[][] maximums = maximumSearch( normalized, factory, amplitude,threshold, sigmas ); /* Output */ displayMaximums( maximums ); @@ -46,14 +52,17 @@ public class SurfacePixelSelection * @param <T> * @return */ - private static < T extends RealType< T > & NativeType< T > >Img<FloatType> pretreatment(Img< T > source) + private static < T extends RealType< T > & NativeType< T > >Img<FloatType> pretreatment( RandomAccessibleInterval< T > source ) { // Conversion into FloatType for the derivative computation (negative values) - Img< FloatType > converted = Utils.convertIntoFloatType( source.copy() ); + RandomAccessibleInterval< FloatType > converted = Converters.convert( source, new RealFloatSamplerConverter< T >() ); + //Image denoising with an anisotropic convolution - Utils.gaussConvolution( converted.copy(), converted, new double[]{1, 1, 0 } ); + ImgFactory<FloatType> factory = new ArrayImgFactory<>(new FloatType()); + Img <FloatType> c = Utils.gaussConvolution( converted,factory, new double[]{1, 1, 0 } ); + ImageJFunctions.show( c ); // Normalization - return Utils.normalizeImage( converted ); + return Utils.normalizeImage( c, c.factory()); } @@ -66,17 +75,17 @@ public class SurfacePixelSelection * @param <T> * @return */ - private static < T extends RealType< T > & NativeType< T > > Pixels[][] maximumSearch( Img< T > input, double amplitude, double threshold, int sigma ) + private static < T extends RealType< T > & NativeType< T > > Pixels[][] maximumSearch( RandomAccessibleInterval< T > input,ImgFactory<T> factory, double amplitude, double threshold, int sigma ) { /* Grid of amplitude thresholds */ - Img< FloatType > amplitudeImg = MaximumAmplitudeClassification.find(input,amplitude, 0.10); + Img< FloatType > amplitudeImg = MaximumAmplitudeClassification.find(input,factory, amplitude, 0.10); ImageJFunctions.show( amplitudeImg," amplitude" ); /* Grid of local thresholds*/ - Img< BitType > thresholds = LocalOtsuClassification.find( input, threshold ); + Img< BitType > thresholds = LocalOtsuClassification.find( input, factory, threshold ); /* Pixel selection */ - Img<FloatType> finalList = Threshold.classification( amplitudeImg, amplitudeImg.factory(), thresholds ); + Img<FloatType> finalList = Threshold.classification( amplitudeImg, amplitudeImg.factory(), thresholds ); /* Dilatation of the resulting image*/ Utils.gaussConvolution( finalList.copy(), finalList, new double[]{ sigma, sigma, 1 } ); diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/Threshold.java b/src/main/java/fr/pasteur/ida/zellige/utils/Threshold.java index 0a93f3086d70448c697471594eb18bd512f4fd49..81afcb9a2eed4c52b89aa0808503bdf528d2e2c4 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/Threshold.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/Threshold.java @@ -4,13 +4,11 @@ package fr.pasteur.ida.zellige.utils; import net.imglib2.Cursor; import net.imglib2.IterableInterval; import net.imglib2.RandomAccess; -import net.imglib2.algorithm.stats.Histogram; import net.imglib2.algorithm.stats.Max; import net.imglib2.algorithm.stats.RealBinMapper; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.script.slice.Slice2D; import net.imglib2.type.NativeType; import net.imglib2.type.logic.BitType; import net.imglib2.type.numeric.RealType; @@ -102,9 +100,9 @@ public class Threshold // TODO implements Otsu-2D ??? - public static < T extends RealType< T > & NativeType< T > > int[] getHistogram( Img< T > image, T min, T max, int numBins ) + public static < T extends RealType< T > & NativeType< T > > int[] getHistogram( IterableInterval< T > image, T min, T max, int numBins ) { - Histogram< T > H = new Histogram<>( new RealBinMapper<>( min, max, numBins ), image ); + HistogramZ< T > H = new HistogramZ<>( new RealBinMapper<>( min, max, numBins ), image ); H.process(); return H.getHistogram(); @@ -176,25 +174,33 @@ public class Threshold } - - public static < T extends RealType< T > & NativeType< T > > T getThreshold( Img< T > input, double percent ) + public static < T extends RealType< T > & NativeType< T > > T getThreshold( IterableInterval< T > input, double percent ) { - T max = Threshold.findMax( input ); T min = Threshold.findMin( input ); int[] histogram = getHistogram( input, min, max, 256 ); + T threshold = getThreshold( min, max, histogram ); + threshold.mul( percent ); + return threshold; + } + + public static < T extends RealType< T > & NativeType< T > > T getThreshold( T min, T max, int[] histogram ) + { histogram[ 0 ] = 0; int thresholdIndex = OtsuCelebiIndex( histogram ); + return getBinValueFromIndex( min, max, histogram, thresholdIndex ); + } + + public static < T extends RealType< T > & NativeType< T > > T getBinValueFromIndex( T min, T max, int[] histogram, int thresholdIndex ) + { double binWidth = ( max.getRealDouble() - min.getRealDouble() ) / ( double ) histogram.length; double result = ( ( double ) ( thresholdIndex + 1 ) * binWidth + min.getRealDouble() ); - T threshold = Utils.getTypeValue( result, input.firstElement() ); - threshold.mul( percent ); + T threshold = min.createVariable(); + threshold.setReal( result ); return threshold; } - - public static < T extends RealType< T > & NativeType< T > > Img< T > classification( IterableInterval< T > amplitude, ImgFactory< T > factory, Img< BitType > thresholds ) { @@ -202,13 +208,13 @@ public class Threshold Cursor< T > amplitudeCursor = amplitude.localizingCursor(); RandomAccess< BitType > thresholdAccess = thresholds.randomAccess(); RandomAccess< T > outputAccess = output.randomAccess(); - while( amplitudeCursor.hasNext()) + while ( amplitudeCursor.hasNext() ) { amplitudeCursor.fwd(); - if(amplitudeCursor.get().getRealDouble()!= 0) + if ( amplitudeCursor.get().getRealDouble() != 0 ) { thresholdAccess.setPosition( amplitudeCursor ); - if(thresholdAccess.get().get()) + if ( thresholdAccess.get().get() ) { outputAccess.setPosition( amplitudeCursor ); outputAccess.get().setOne(); @@ -216,18 +222,15 @@ public class Threshold } } ImageJFunctions.show( output.copy(), "amp+thresh" ); - return output; + return output; } - - - public static < T extends RealType< T > & NativeType< T > > T getFirstMaxValue( Img< T > input, boolean zero ) { T max = Threshold.findMax( input ); T min = Threshold.findMin( input ); - Histogram< T > H = new Histogram<>( new RealBinMapper<>( min, max, 256 ), input ); + HistogramZ< T > H = new HistogramZ<>( new RealBinMapper<>( min, max, 256 ), input ); H.process(); int[] histogram = H.getHistogram(); diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/Utils.java b/src/main/java/fr/pasteur/ida/zellige/utils/Utils.java index 9463814acbcc1aa72e9005d947df0fa7d8d7cf19..ec80bf79177cabde79c4a4363030b0eede17590e 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/Utils.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/Utils.java @@ -19,6 +19,7 @@ import net.imglib2.type.numeric.integer.IntType; import net.imglib2.type.numeric.integer.ShortType; import net.imglib2.type.numeric.real.DoubleType; import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.ImgUtil; import net.imglib2.view.Views; @@ -48,6 +49,15 @@ public class Utils Gauss3.gauss( sigma, infiniteInput, output ); } + public static < T extends RealType< T > & NativeType< T > > Img< T > + gaussConvolution( RandomAccessibleInterval< T > input, ImgFactory<T> factory, double[] sigma ) + { + Img<T> output = factory.create( input ); + RandomAccessible< T > infiniteInput = Views.extendValue( input, input.randomAccess().get() ); + Gauss3.gauss( sigma, infiniteInput, output ); + return output; + } + /** * Method to apply a centered partial derivative function to a {@link RandomAccessibleInterval} * in the second dimension of a {@link RandomAccessibleInterval}. @@ -75,12 +85,13 @@ public class Utils * @return a new Img with normalize pixel intensity values. */ public static < T extends RealType< T > & NativeType< T > > Img< T > normalizeImage( - Img< T > image ) + RandomAccessibleInterval< T > image, ImgFactory<T> factory ) { - Img< T > normalizedImage = image.copy(); - T min = image.firstElement().copy().createVariable(); + Img< T > normalizedImage = factory.create( image ); + ImgUtil.copy( image, normalizedImage ); + T min = normalizedImage.firstElement().createVariable(); min.setReal( 0 ); - T max = image.firstElement().copy().createVariable(); + T max = normalizedImage.firstElement().copy().createVariable(); max.setReal( 255 ); Normalize.normalize( normalizedImage, min, max ); return normalizedImage;