diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d84e78d4f4fe852336269ef1821f7b2ce529d38d..29de07cd84970214b33910a4809f0b144633ebda 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,7 +8,17 @@ maven: stage: build when: always script: - - mvn clean install -DskipTests + - mvn clean install artifacts: paths: - target/*.jar + +paramaterSweep: + image: maven:3.6.3-jdk-11 + stage: test + when: manual + script: + - mvn -Dtest=PipelineTest test + artifacts: + paths: + - target/*.csv \ No newline at end of file diff --git a/doc/fakeHeightMap.tif b/doc/fakeHeightMap.tif new file mode 100644 index 0000000000000000000000000000000000000000..33abd675186f4878a814da63ee812c3141326028 Binary files /dev/null and b/doc/fakeHeightMap.tif differ diff --git a/doc/surf_phantom2_voronoiMesh.tif b/doc/surf_phantom2_voronoiMesh.tif new file mode 100644 index 0000000000000000000000000000000000000000..cf6389950725a55ccf916daf472c3e29bf96cbac Binary files /dev/null and b/doc/surf_phantom2_voronoiMesh.tif differ diff --git a/doc/surf_phantom2_voronoiMesh_HM.tif b/doc/surf_phantom2_voronoiMesh_HM.tif new file mode 100644 index 0000000000000000000000000000000000000000..1251acca76ba4004309afdfe860ee245afbfeac5 Binary files /dev/null and b/doc/surf_phantom2_voronoiMesh_HM.tif differ diff --git a/pom.xml b/pom.xml index 37d3376a897d343ab3d61f90e8a9ed9e524cb111..dfe4113b216ddd32fb1aee9059c5b3941fcbe444 100644 --- a/pom.xml +++ b/pom.xml @@ -60,6 +60,19 @@ <release>11</release> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>3.0.0-M5</version> + <configuration> + <includes> + <include>*Test.java</include> + </includes> + <excludes> + <exclude>**/behavior/*</exclude> + </excludes> + </configuration> + </plugin> </plugins> </build> @@ -139,16 +152,67 @@ <groupId>io.scif</groupId> <artifactId>scifio</artifactId> </dependency> + + + <dependency> + <groupId>ch.qos.logback</groupId> + <artifactId>logback-classic</artifactId> + <version>1.2.3</version> + </dependency> + + <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-api</artifactId> - <version>5.6.2</version> + <version>5.7.0</version> </dependency> + <dependency> + <groupId>org.junit.jupiter</groupId> + <artifactId>junit-jupiter-params</artifactId> + <version>5.7.0</version> + </dependency> + + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <scope>compile</scope> </dependency> + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <version>3.13.2</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-all</artifactId> + <version>1.10.19</version> + <scope>test</scope> + </dependency> + <!-- <dependency>--> + <!-- <groupId>org.powermock</groupId>--> + <!-- <artifactId>powermock-api-mockito2</artifactId>--> + <!-- <version>2.0.9</version>--> + <!-- </dependency>--> + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-api-mockito</artifactId> + <version>1.7.4</version> + </dependency> + + <dependency> + <groupId>org.powermock</groupId> + <artifactId>powermock-module-junit4</artifactId> + <version>2.0.9</version> + </dependency> + + <!-- <dependency>--> + <!-- <groupId>org.jacoco</groupId>--> + <!-- <artifactId>jacoco-maven-plugin</artifactId>--> + <!-- <version>0.8.6</version>--> + <!-- </dependency>--> + </dependencies> diff --git a/src/main/java/StartingOSStats.java b/src/main/java/StartingOSStats.java deleted file mode 100644 index 11bf1a4fc718480b2f0b2de5c3708fd9d1bc9dda..0000000000000000000000000000000000000000 --- a/src/main/java/StartingOSStats.java +++ /dev/null @@ -1,114 +0,0 @@ -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.AdvancedUserParameters; -import fr.pasteur.ida.zellige.surfaceConstruction.construction.ReferenceSurfaceExtraction; -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.UserParameters; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; -import fr.pasteur.ida.zellige.utils.TestMIP; -import ij.IJ; -import ij.ImageJ; -import io.scif.img.IO; -import io.scif.img.SCIFIOImgPlus; -import net.imglib2.img.Img; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.type.NativeType; -import net.imglib2.type.numeric.RealType; - -import java.io.BufferedWriter; -import java.io.FileWriter; -import java.io.IOException; -import java.util.TreeMap; - -public class StartingOSStats -{ - - BufferedWriter writer; - FileWriter fileWriter; - private final String path = "C:\\Users\\ctrebeau\\Desktop\\stats\\"; - - public StartingOSStats( String fileName ) throws IOException - { - fileName.replace( "tif", "txt" ); - writer = new BufferedWriter(new FileWriter(fileName)); - writer.write(path + fileName ); - } - - - public void writeParameter(String amplitude, String otsu, String sigmas, String delta) throws IOException - { - StringBuilder s = new StringBuilder( - "Amplitude percent : " + amplitude + - "Otsu percent : " +otsu + System.lineSeparator() + - "Sigmas : " + sigmas + System.lineSeparator()+ - "Delta : " + delta + System.lineSeparator()); - - writer.write( s.toString() ); - } - - public void close() throws IOException - { - writer.close(); - } - - public void writeHistogram( TreeMap<Integer, Integer> map ) throws IOException - { - writer.write(map.toString()); - } - - public void writeInfo( String info, double value) throws IOException - { - writer.write( info + " : " + value ); - } - public static < T extends RealType< T > & NativeType< T > > void main( String[] args ) throws IOException - { - String path = "C:\\Users\\ctrebeau\\Desktop\\MoucheAile\\STK\\"; - String fileName = "STK_Mouche_c01_f0001_p005.tif"; - - // Input of the image. - final String imagePath = path + fileName; -// "doc/BG2.tif"; - - - - - System.out.println("File : " + fileName ); - - - ImageJ ij = new ImageJ(); - /* JY version for opening files. */ - final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); - - final Img< T > stackImage = ( Img < T > ) imgPlus.getImg(); - - - /* 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 ] ); - /* End of parameters. */ - - UserParameters userParameters = new UserParameters( amplitude, otsu, sigmas, delta, "MIP" ); - AdvancedUserParameters advancedUserParameters = new AdvancedUserParameters( k1, percent1, k2, percent2 ); - - IJ.log("Type: "+ imgPlus.firstElement().getClass().toGenericString()); - - if ( stackImage.numDimensions() == 3 )// Is it a stack ? - { -// ImageJFunctions.show( stackImage, "original" ); -// ImageJFunctions.show( TestMIP.findMIP( stackImage, stackImage.factory() ), "MIP" ); - new ReferenceSurfaceExtraction<>( stackImage, stackImage.factory(),userParameters, advancedUserParameters ); - } - else - { - System.out.println( " This image has to be a z-stack ! " ); - } - StartingOSStats startingOSStats = new StartingOSStats( fileName ); - startingOSStats.writeParameter( args[ 0 ] , args[ 1 ] , args[ 2 ], args[ 3 ] ); - startingOSStats.writeInfo( "OS count ", OSE.getCount() ); -// startingOSStats.writeHistogram( OSE.getOcc() ); - startingOSStats.close(); - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/ReferenceSurfaceExtraction.java b/src/main/java/fr/pasteur/ida/zellige/ReferenceSurfaceExtraction.java new file mode 100644 index 0000000000000000000000000000000000000000..86216b5187d03cd3f5413e804ba433c00da207b7 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/ReferenceSurfaceExtraction.java @@ -0,0 +1,176 @@ +package fr.pasteur.ida.zellige; + +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; +import fr.pasteur.ida.zellige.jzy3D.SurfaceDisplay; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.FirstRoundConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.SecondRoundConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.Pretreatment; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.SurfacePixelSelection; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ReferenceSurface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.DisplayParameter; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ProjectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.utils.Interpolation; +import fr.pasteur.ida.zellige.utils.Utils; +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.jzy3d.analysis.AnalysisLauncher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + + + +public class ReferenceSurfaceExtraction< T extends RealType< T > & NativeType< T > > +{ + + private final static Logger LOGGER = LoggerFactory.getLogger( ReferenceSurfaceExtraction.class ); + + private final ArrayList< ReferenceSurface <T>> referenceSurfaces = new ArrayList<>(); + private final RandomAccessibleInterval< T > input; + private final ImgFactory<T> factory; + private final PixelSelectionParameters selectionParameters; + private final ProjectionParameters projectionParameters; + private final ConstructionParameters [] constructionParameters; + private Pixels [][] maximums; + public ReferenceSurfaceExtraction( RandomAccessibleInterval< T > input, ImgFactory< T > factory, + PixelSelectionParameters parameters , ProjectionParameters projectionParameters, + ConstructionParameters [] constructionParameters) + { + this.input = input; + this.factory = factory; + this.selectionParameters = parameters; + this.projectionParameters = projectionParameters; + this.constructionParameters = constructionParameters; + } + + + public void select() throws NoClassificationException, EmptyOutputException + { + /* First step : Pixel selection */ + LOGGER.info("Running selection..."); + maximums = SurfacePixelSelection.run( input, selectionParameters ); + } + + + public void construct() + { + LOGGER.info("Running construction..."); + try + { + /* First round construction*/ + ArrayList< Surface > tempSurfaces = FirstRoundConstruction.run( maximums, constructionParameters[0] ); + System.out.println( "first round surfaces = " + tempSurfaces.size() ); +// for(Surface s : tempSurfaces) +// { +// displaySurface( s ); +// } + /* Second round construction */ + ArrayList<Surface> finalSurfaces = SecondRoundConstruction.run( tempSurfaces, constructionParameters[1] ); + + /* Building reference surfaces */ + referenceSurfaceInstantiation( finalSurfaces ); + } + catch ( Exception e ) + { + e.printStackTrace(); + } + } + public void extract( ) throws EmptyOutputException, NoClassificationException + { +// select(); + construct(); +// /* Second step : Surface construction in 2 rounds */ +// try +// { +// /* First round construction*/ +// ArrayList< Surface > tempSurfaces = FirstRoundConstruction.run( maximums, constructionParameters[0] ); +// System.out.println( "first round surfaces = " + tempSurfaces.size() ); +//// for(Surface s : tempSurfaces) +//// { +//// displaySurface( s ); +//// } +// /* Second round construction */ +// ArrayList<Surface> finalSurfaces = SecondRoundConstruction.run( tempSurfaces, constructionParameters[1] ); +// +// /* Building reference surfaces */ +// referenceSurfaceInstantiation( finalSurfaces ); +// } +// catch ( Exception e ) +// { +// e.printStackTrace(); +// } + } + + public Img<UnsignedShortType> processedZMap(Surface surface) + { + // First step : fill in the holes with linear interpolation. */ + Img< UnsignedShortType > interpolated = Interpolation.run( surface.getZMap() ); + Img< UnsignedShortType > smoothed = interpolated.copy(); +// ImageJFunctions.show( smoothed, "smoothed" ); + // Second step: smooth the elevation map with gaussian blur + Utils.gaussConvolution( interpolated, smoothed, new double[]{ 1.0, 1.0 } ); + return smoothed; + } + + private void referenceSurfaceInstantiation(ArrayList<Surface> finalSurfaces) + { + int index = 0; + for( Surface surface : finalSurfaces ) + { +// displaySurface( surface ); + Img< UnsignedShortType > processedZMap = processedZMap( surface ); + ReferenceSurface< T > referenceSurface = new ReferenceSurface<>( input, factory, processedZMap, index++ ); + referenceSurfaces.add( referenceSurface ); + } + } + + public void project() + { + LOGGER.info("Running projection..."); + for(ReferenceSurface<T> referenceSurface : referenceSurfaces) + { + referenceSurface.setProjection( projectionParameters.getProjectionType(), projectionParameters.getDelta() ); + DisplayParameter displayParameter = new DisplayParameter( projectionParameters.getDelta(), false, + false, false, true ); + referenceSurface.display( displayParameter ); + } + } + + + + public static void displaySurface( Surface surface ) + { + try + { + SurfaceDisplay s = new SurfaceDisplay( surface ); + AnalysisLauncher.open( s ); + } + catch ( Exception e ) + { + e.printStackTrace(); + } + } + + public ArrayList< ReferenceSurface< T > > getReferenceSurfaces() + { + return referenceSurfaces; + } + + public Pixels[][] getMaximums() + { + return maximums; + } +} + + diff --git a/src/main/java/SurfacesExtractionAnalyse.java b/src/main/java/fr/pasteur/ida/zellige/SurfacesExtractionAnalyse.java similarity index 70% rename from src/main/java/SurfacesExtractionAnalyse.java rename to src/main/java/fr/pasteur/ida/zellige/SurfacesExtractionAnalyse.java index e37a8e35963a6dfec8110fc16d49c1b07b779020..7fc10e6e8d4abbd1c397401d79503708021e8043 100644 --- a/src/main/java/SurfacesExtractionAnalyse.java +++ b/src/main/java/fr/pasteur/ida/zellige/SurfacesExtractionAnalyse.java @@ -1,3 +1,26 @@ +package fr.pasteur.ida.zellige; + +import fr.pasteur.ida.zellige.ReferenceSurfaceExtraction; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ProjectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import ij.IJ; +import ij.ImageJ; +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.img.Img; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.TreeMap; + public class SurfacesExtractionAnalyse { // public static < T extends RealType < T > & NativeType < T > > void main( String[] args ) @@ -344,6 +367,108 @@ public class SurfacesExtractionAnalyse // // } + public static class StartingOSStats + { + + BufferedWriter writer; + FileWriter fileWriter; + private final String path = "C:\\Users\\ctrebeau\\Desktop\\stats\\"; + + public StartingOSStats( String fileName ) throws IOException + { + fileName.replace( "tif", "txt" ); + writer = new BufferedWriter(new FileWriter(fileName)); + writer.write(path + fileName ); + } + + + public void writeParameter(String amplitude, String otsu, String sigmas, String delta) throws IOException + { + StringBuilder s = new StringBuilder( + "Amplitude percent : " + amplitude + + "Otsu percent : " +otsu + System.lineSeparator() + + "Sigmas : " + sigmas + System.lineSeparator()+ + "Delta : " + delta + System.lineSeparator()); + + writer.write( s.toString() ); + } + + public void close() throws IOException + { + writer.close(); + } + + public void writeHistogram( TreeMap<Integer, Integer> map ) throws IOException + { + writer.write(map.toString()); + } + + public void writeInfo( String info, double value) throws IOException + { + writer.write( info + " : " + value ); + } + public static < T extends RealType< T > & NativeType< T > > void main( String[] args ) throws Exception + { + String path = "C:\\Users\\ctrebeau\\Desktop\\MoucheAile\\STK\\"; + String fileName = "STK_Mouche_c01_f0001_p005.tif"; + + // Input of the image. + final String imagePath = path + fileName; + // "doc/BG2.tif"; + + + + + System.out.println("File : " + fileName ); + + + ImageJ ij = new ImageJ(); + /* JY version for opening files. */ + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + + final Img< T > stackImage = ( Img < T > ) imgPlus.getImg(); + + + /* User Parameters AKA arguments to run the program*/ + double amplitude = Double.parseDouble( args[ 0 ] ); + double otsu = Double.parseDouble( args[ 1 ] ); + double sigmaXY = Double.parseDouble( args[ 2 ] ); + double sigmaZ = Double.parseDouble( args[ 3 ] ); + int k1 = Integer.parseInt( args[ 4 ] ); + double percent1 = Double.parseDouble( args[ 5 ] ); + int k2 = Integer.parseInt( args[ 6 ] ); + double percent2 = Double.parseDouble( args[ 7 ] ); + int delta = Integer.parseInt( args[ 8 ] ); + /* End of parameters. */ + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "Median", new double[]{2} ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 5, 8 ); + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + ProjectionParameters projectionParameters = new ProjectionParameters( delta, "MIP" ); + ConstructionParameters[] constructionParameters = new ConstructionParameters[]{ + new ConstructionParameters( 0.75, k1, percent1 ), + new ConstructionParameters( 0.1, k2, percent2 )}; + + IJ.log("Type: "+ imgPlus.firstElement().getClass().toGenericString()); + + if ( stackImage.numDimensions() == 3 )// Is it a stack ? + { + // ImageJFunctions.show( stackImage, "original" ); + // ImageJFunctions.show( TestMIP.findMIP( stackImage, stackImage.factory() ), "MIP" ); + new ReferenceSurfaceExtraction<> + ( stackImage, stackImage.factory(), pixelSelectionParameters, projectionParameters, constructionParameters); + } + else + { + System.out.println( " This image has to be a z-stack ! " ); + } + StartingOSStats startingOSStats = new StartingOSStats( fileName ); + startingOSStats.writeParameter( args[ 0 ] , args[ 1 ] , args[ 2 ], args[ 3 ] ); + startingOSStats.writeInfo( "OS count ", AbstractOSE.getCount() ); + // startingOSStats.writeHistogram( OSE.getOcc() ); + startingOSStats.close(); + } + } } diff --git a/src/main/java/fr/pasteur/ida/zellige/exception/DataValidationException.java b/src/main/java/fr/pasteur/ida/zellige/exception/DataValidationException.java new file mode 100644 index 0000000000000000000000000000000000000000..90a7f76fd089b9334d7cc963f0bdc5867e8a7e44 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/exception/DataValidationException.java @@ -0,0 +1,10 @@ +package fr.pasteur.ida.zellige.exception; + +public class DataValidationException extends Exception +{ + + public DataValidationException( String message ) + { + super( message ); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/exception/DifferentReferenceTestedSizeException.java b/src/main/java/fr/pasteur/ida/zellige/exception/DifferentReferenceTestedSizeException.java new file mode 100644 index 0000000000000000000000000000000000000000..795e67782dba0ce05b5e660a85ac5418f6eb383a --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/exception/DifferentReferenceTestedSizeException.java @@ -0,0 +1,5 @@ +package fr.pasteur.ida.zellige.exception; + +public class DifferentReferenceTestedSizeException extends Exception +{ +} diff --git a/src/main/java/fr/pasteur/ida/zellige/exception/EmptyOutputException.java b/src/main/java/fr/pasteur/ida/zellige/exception/EmptyOutputException.java new file mode 100644 index 0000000000000000000000000000000000000000..f29b4de815707c97d02a23c7af4f41a6e2323f4a --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/exception/EmptyOutputException.java @@ -0,0 +1,9 @@ +package fr.pasteur.ida.zellige.exception; + +public class EmptyOutputException extends Exception +{ + public EmptyOutputException( String message ) + { + super( message ); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/exception/NoClassificationException.java b/src/main/java/fr/pasteur/ida/zellige/exception/NoClassificationException.java new file mode 100644 index 0000000000000000000000000000000000000000..944f69d20e1d531fddbcb3193c1a12a8a3028d32 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/exception/NoClassificationException.java @@ -0,0 +1,5 @@ +package fr.pasteur.ida.zellige.exception; + +public class NoClassificationException extends Exception +{ +} diff --git a/src/main/java/fr/pasteur/ida/zellige/exception/NotAnHeightMapException.java b/src/main/java/fr/pasteur/ida/zellige/exception/NotAnHeightMapException.java new file mode 100644 index 0000000000000000000000000000000000000000..1c7778a2dea82cade92c039ac2cbf9fef81e265b --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/exception/NotAnHeightMapException.java @@ -0,0 +1,5 @@ +package fr.pasteur.ida.zellige.exception; + +public class NotAnHeightMapException extends Exception +{ +} diff --git a/src/main/java/fr/pasteur/ida/zellige/jzy3D/LocalMaximumsDisplay.java b/src/main/java/fr/pasteur/ida/zellige/jzy3D/LocalMaximumsDisplay.java index 1eb743fece7ba90e2f3ce7ee77ce37458ea52c3d..1acfcc368aef77fa52b25d867e57d7eb86cc0e22 100644 --- a/src/main/java/fr/pasteur/ida/zellige/jzy3D/LocalMaximumsDisplay.java +++ b/src/main/java/fr/pasteur/ida/zellige/jzy3D/LocalMaximumsDisplay.java @@ -4,15 +4,21 @@ package fr.pasteur.ida.zellige.jzy3D; import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; import org.jzy3d.analysis.AbstractAnalysis; +import org.jzy3d.analysis.AnalysisLauncher; import org.jzy3d.chart.factories.AWTChartComponentFactory; import org.jzy3d.colors.Color; import org.jzy3d.maths.Coord3d; import org.jzy3d.plot3d.primitives.Scatter; import org.jzy3d.plot3d.rendering.canvas.Quality; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.awt.*; public class LocalMaximumsDisplay extends AbstractAnalysis { + private final static Logger LOGGER = LoggerFactory.getLogger( LocalMaximumsDisplay.class ); private final Pixels[][] maximumCoordinates; public LocalMaximumsDisplay( Pixels[][] localMaximums ) @@ -21,8 +27,32 @@ public class LocalMaximumsDisplay extends AbstractAnalysis init(); } + /** + * Displays the local maximums found using jzy3D package. + */ + public static void displayMaximums( Pixels[][] maximums ) + { + LOGGER.info( "3D maximums display" ); +// if ( ! GraphicsEnvironment.isHeadless() ) + { + try + { + LocalMaximumsDisplay localMaximumsDisplay = new LocalMaximumsDisplay( maximums ); + AnalysisLauncher.open( localMaximumsDisplay ); + } + catch ( Exception e ) + { + e.printStackTrace(); + } + } +// else +// { +// LOGGER.warn( "No Graphics Environment detected" ); +// } + } + @Override - public void init( ) + public void init() { if ( maximumCoordinates != null ) { @@ -44,8 +74,9 @@ public class LocalMaximumsDisplay extends AbstractAnalysis { if ( pixels.size() == 1 ) - { Coordinate pixel = pixels.get(0); - x = pixel.getX(); + { + Coordinate pixel = pixels.get( 0 ); + x = pixel.getX(); y = pixel.getY(); z = pixel.getZ(); points[ index ] = new Coord3d( x, y, z ); @@ -54,9 +85,9 @@ public class LocalMaximumsDisplay extends AbstractAnalysis } else { - for ( Coordinate coordinate : pixels .get() ) + for ( Coordinate coordinate : pixels.get() ) { - z = coordinate.getZ(); + z = coordinate.getZ(); points[ index ] = new Coord3d( coordinate.getX(), coordinate.getY(), z ); colors[ index ] = Color.RED; // System.out.println( points[index]); @@ -78,13 +109,13 @@ public class LocalMaximumsDisplay extends AbstractAnalysis private int getNumberOfCoordinates() { int n = 0; - for ( int i = 0; i <= maximumCoordinates[0].length - 1; i++ ) + for ( int i = 0; i <= maximumCoordinates[ 0 ].length - 1; i++ ) { - for ( int j = 0; j <= maximumCoordinates.length - 1 ; j++ ) + for ( int j = 0; j <= maximumCoordinates.length - 1; j++ ) { - if( maximumCoordinates[j][i] != null) + if ( maximumCoordinates[ j ][ i ] != null ) { - n += maximumCoordinates[j][i].size(); + n += maximumCoordinates[ j ][ i ].size(); } } } diff --git a/src/main/java/fr/pasteur/ida/zellige/jzy3D/OSDisplay.java b/src/main/java/fr/pasteur/ida/zellige/jzy3D/OSDisplay.java index b1d8a7ba26d9de079fa5d2a922bb9fcc26bd26e7..553fc6ba0913934212f31ba0b795cb0ce7b8e240 100644 --- a/src/main/java/fr/pasteur/ida/zellige/jzy3D/OSDisplay.java +++ b/src/main/java/fr/pasteur/ida/zellige/jzy3D/OSDisplay.java @@ -1,7 +1,7 @@ package fr.pasteur.ida.zellige.jzy3D; import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; import org.jzy3d.analysis.AbstractAnalysis; import org.jzy3d.chart.factories.AWTChartComponentFactory; @@ -26,7 +26,7 @@ public class OSDisplay extends AbstractAnalysis { public void init() { int count = 0; for ( OSEList oseList : oseLists ) { - for ( OSE os : oseList ) { + for ( AbstractOSE os : oseList ) { count = count+os.size(); } } @@ -42,7 +42,7 @@ public class OSDisplay extends AbstractAnalysis { int index = 0; for ( OSEList oseList : oseLists ) { - for ( OSE os : oseList ) { + for ( AbstractOSE os : oseList ) { // one color per OS float G = r.nextFloat() ; float R = r.nextFloat() ; 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 d2530578c92e44d0b65907218db57465f519b44c..bcac6578fdc6745fce67c700da500839c2f8afcd 100644 --- a/src/main/java/fr/pasteur/ida/zellige/main/Main.java +++ b/src/main/java/fr/pasteur/ida/zellige/main/Main.java @@ -1,9 +1,13 @@ package fr.pasteur.ida.zellige.main; -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.AdvancedUserParameters; -import fr.pasteur.ida.zellige.surfaceConstruction.construction.ReferenceSurfaceExtraction; -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.UserParameters; -import fr.pasteur.ida.zellige.utils.TestMIP; + +import fr.pasteur.ida.zellige.ReferenceSurfaceExtraction; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ProjectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; import ij.IJ; import ij.ImageJ; import io.scif.img.IO; @@ -13,6 +17,8 @@ import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.NativeType; import net.imglib2.type.numeric.RealType; +import java.awt.*; + /** * @@ -57,12 +63,14 @@ public class Main // Input of the image. final String imagePath = -// "doc/C0T0.tif"; -// "C:\\Users\\ctrebeau\\Desktop\\MoucheAile\\STK\\STK_Mouche_c01_f0001_p005.tif"; -// "C:\\Users\\ctrebeau\\Downloads\\phantom3b_combined.tif"; + "doc/STK.tif"; +// "doc/phantoms_crossingsurf_crop.tif"; +// "doc/phantom_small4test.tif"; +// "C:\\Users\\ctrebeau\\Desktop\\MoucheAile\\STK\\STK_Mouche_c01_f0001_p003.tif"; +// "C:\\Users\\ctrebeau\\Downloads\\phantoms_curv100.tif"; // "C:\\Users\\ctrebeau\\Desktop\\HighRes\\STK_170706_Vangl2-Lp-wt_E14.5_Phall_cochlea_01bHighRes_c01_f0001_p005.tif"; // "C:\\Users\\ctrebeau\\Desktop\\HighRes\\STK_170706_Vangl2-Lp-wt_E14.5_Phall_cochlea_01bHighRes_c01_f0001_p002.tif"; - "C:\\Users\\ctrebeau\\Desktop\\Acq060720\\C2-MucilairB spike zo1 J2 Mock-2.tif"; +// "C:\\Users\\ctrebeau\\Desktop\\phantom_tests_snr_1surf\\phantoms_1surf_snr0.mat.tif"; System.out.println(imagePath); // Creation of the image : version with unsigned type. */ /* JY version for opening files. */ @@ -74,34 +82,48 @@ public class Main /* User Parameters AKA arguments to run the program*/ double amplitude = Double.parseDouble( args[ 0 ] ); double otsu = Double.parseDouble( args[ 1 ] ); - double sigmas = Double.parseDouble( 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 ] ); + double sigmaXY = Double.parseDouble( args[ 2 ] ); + double sigmaZ = Double.parseDouble( args[ 3 ] ); + int k1 = Integer.parseInt( args[ 4 ] ); + double percent1 = Double.parseDouble( args[ 5 ] ); + int k2 = Integer.parseInt( args[ 6 ] ); + double percent2 = Double.parseDouble( args[ 7 ] ); + int delta = Integer.parseInt( args[ 8 ] ); /* End of parameters. */ /* Print parameters.*/ System.out.println( " amplitude : " + amplitude ); System.out.println( " threshold : " + otsu ); - System.out.println( " Blur size : " + sigmas ); + System.out.println( " Blur size : " + sigmaXY ); System.out.println( " Delta : " + delta ); System.out.println( System.lineSeparator() ); /* End of Print parameters.*/ - UserParameters userParameters = new UserParameters( amplitude, otsu, sigmas, delta, "MIP" ); - AdvancedUserParameters advancedUserParameters = new AdvancedUserParameters( k1, percent1, k2, percent2 ); + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "GaussianBlur", new double[]{2, 2, 1} ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 10, 8 ); + + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + ProjectionParameters projectionParameters = new ProjectionParameters( delta, "MIP" ); + ConstructionParameters[] constructionParameters = new ConstructionParameters[]{ + new ConstructionParameters( 0.75, k1, percent1 ), + new ConstructionParameters( 0.1, k2, percent2 )}; IJ.log("Type: "+ imgPlus.firstElement().getClass().toGenericString()); if ( stackImage.numDimensions() == 3 )// Is it a stack ? { - ImageJFunctions.show( stackImage, "original" ); +// ImageJFunctions.show( stackImage, "original" ); // ImageJFunctions.show( TestMIP.findMIP( stackImage, stackImage.factory() ), "MIP" ); - ReferenceSurfaceExtraction rse = new ReferenceSurfaceExtraction<>( stackImage, stackImage.factory(),userParameters, advancedUserParameters ); - rse.extract(); - rse.project(); + ReferenceSurfaceExtraction<T> rse = new ReferenceSurfaceExtraction<> + ( stackImage, stackImage.factory(), pixelSelectionParameters, projectionParameters,constructionParameters ); + rse.select(); + rse.construct(); + + if ( !GraphicsEnvironment.isHeadless()) + { + rse.project(); + } } else { diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/OSEListConstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/OSEListConstruction.java deleted file mode 100644 index 2b573922e769630c45f3880564aa02fce662197a..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/OSEListConstruction.java +++ /dev/null @@ -1,272 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.construction; - -import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEStartingStatus; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.Queue; - - -/** - * This class contains a main method for the construction of OSE : {@code findOS}. - */ -public class OSEListConstruction -{ - private final Pixels[][] maximums; - private final OSEList[] oseLists; - private final OSEStartingStatus startingStatus; - private final int dimension; - - public OSEListConstruction( Pixels[][] maximums , int dimension) - { - this.maximums = maximums; - this.oseLists = new OSEList[maximums.length]; - this.dimension = dimension; - this.startingStatus = new OSEStartingStatus( dimension ); - } - - public void run() - { - for ( int i = 0; i <= maximums.length - 1; i++ ) - { - oseLists[ i ] = findOSE( maximums[ i ] ); - } - startingStatus.setStartingStatus(); - System.out.println( "Starting size = " + startingStatus.getMinimumSize()); - } - - /** - * This method finds all the possible 2D surfaces that can be created from the coordinates detected as - * local maximums on a array of {@link }. - * - * @param rawCoordinates - the raw Coordinates from the projection. - * @return all the orthogonal surfaces for the projection as an {@link ArrayList<OSE>}. - */ - private OSEList findOSE( Pixels[] rawCoordinates ) - { - if ( dimension == 1 ) - { - reset( rawCoordinates );// the number of right and left coordinates are reset to 0. - } - ArrayList< Coordinate > startingCoordinates = checkForSideCoordinates( rawCoordinates ); - OSEList paths = findSimplePaths( startingCoordinates, rawCoordinates ); - OSEList finalPaths = findComplexPaths( paths ); - if ( dimension == 0 ) - { - finalPaths.removeIf( ose -> ose.size() < 3 );// shortest ones are removed - } - for ( OSE ose : finalPaths ) - { - ose.set(); - } - finalPaths.reset(); - return finalPaths; - } - - private OSEList findSimplePaths( ArrayList< Coordinate > startingCoordinates, Pixels[] rawCoordinates ) - { - Queue< Coordinate > firstQueue = new LinkedList<>( startingCoordinates );// Add the coordinates with no left coordinates in - // the queue - OSEList smallPath = new OSEList(); - while ( ! firstQueue.isEmpty() ) - { - Coordinate current = firstQueue.remove(); - OSE ose = new OSE( startingStatus ); - ose.add( current ); - findSimplePaths( rawCoordinates, smallPath, ose, current, firstQueue ); - } - return smallPath; - } - - /** - * @param rawCoordinates - the raw Coordinates from the projection. - * @param smallPath - the one-Z-OSE - * @param ose - the first OSE considered. - * @param current - the starting {@link Coordinate} considered. - */ - private void findSimplePaths - ( Pixels[] rawCoordinates, - ArrayList< OSE > smallPath, OSE ose, Coordinate current, Queue< Coordinate > queue ) - { - int i; - if ( dimension == 0 ) - { - i = current.getX(); - } - else - { - i = current.getY(); - } - while ( i <= rawCoordinates.length - 2 // x must be < to the length of the image minus 1 because [X + 1] - && rawCoordinates[ i ] != null // the contents in the array at index x must not be null - && rawCoordinates[ i + 1 ] != null // the contents in the array at index (x +1) must not be null - && current != null ) - { - Coordinate next = current.getNext( rawCoordinates[ i + 1 ], 0 ); - if ( next != null )// Conditions : |x1 - x2 |= 1 => only 1D surface - { - ose.add( next ); - } - else - { - if ( ( i ) < rawCoordinates.length - 3 - && rawCoordinates[ i + 2 ] != null// the contents in the array at index (x +1) must not - // be null - && current.getRightNumber() == 0 ) - { - next = current.getNext( rawCoordinates[ i + 2 ], 0 ); - if ( next != null )//fake coordinate - { - if ( dimension == 0 ) - { - ose.add( new Coordinate( current.getX() + 1, current.getY(), current.getZ() ) ); - } - else - { - ose.add( new Coordinate( current.getX(), current.getY() + 1, current.getZ() ) ); - } - ose.add( next ); - queue.remove( next ); - i++; - } - } - } - current = next; - i++; //increment of the index - } - smallPath.add( ose ); - } - - /** - * @param shortPaths - the list of one-z-OSE. - * @return - the list of "big paths" OSE. - */ - private OSEList findComplexPaths( OSEList shortPaths ) - { - OSEList paths = new OSEList(); - Queue< OSE > queue = new LinkedList<>( shortPaths ); - while ( ! queue.isEmpty() ) - { - OSE first = queue.remove(); - shortPaths.remove( first ); - findComplexPaths( first, shortPaths, queue, paths ); - } - return paths; - } - - /** - * @param first - the starting OSE. - * @param smallPaths - the list of all small path OSE. - * @param queue - the remaining OSE. - * @param longPaths - the list of long path OSE. - */ - private void findComplexPaths( OSE first, - ArrayList< OSE > smallPaths, - Queue< OSE > queue, ArrayList< OSE > longPaths ) - { - for ( OSE o : smallPaths ) - { - if ( first.isNextTo( o, dimension ) ) - { - first.addAll( o ); - o.setVisited( true ); - queue.remove( o ); - } - } - smallPaths.removeIf( OSE::isVisited ); - longPaths.add( first ); - } - - /** - * Stores the coordinates that don't have a coordinate to the left. - * - * @param slice - a one-dimensional array containing the local maximums. - * @return - a list of coordinates that don't have a coordinate to the left. - */ - private ArrayList< Coordinate > checkForSideCoordinates( Pixels[] slice ) - { - /* Stores the coordinates that have no coordinates to the left.*/ - ArrayList< Coordinate > noLeftCoordinates = new ArrayList<>(); - /* Starting point. */ - int start = 0; - while ( start <= slice.length - 3 && slice[ start ] == null ) - { - start++;//the index starts where a List of Coordinates is found in the array - } - if ( slice[ start ] != null && slice[ start + 1 ] != null ) //some Coordinates were found - { - /* For the first list of coordinates. */ - for ( int i = 0; i <= slice[ start ].size() - 1; i++ )// the first coordinates of the array have obviously no left coordinates - { - Coordinate coordinate = slice[ start ].get( i ); - noLeftCoordinates.add( coordinate ); - coordinate.setRightCoordinates( slice[ start + 1 ] ); - } - } - /* For the rest. */ - for ( int i = start + 1; i <= slice.length - 2; i++ ) - { - if ( slice[ i ] != null ) - { - for ( Coordinate coordinate : slice[ i ].get() ) - { - coordinate.setRightCoordinates( slice[ i + 1 ] ); - coordinate.setLeftCoordinates( slice[ i - 1 ] ); - boolean hasNoLeft = coordinate.numberOfNeighbours( slice[ i - 1 ], 0 ) == 0; - if ( hasNoLeft ) - { - noLeftCoordinates.add( coordinate ); - } - } - } - } - /* the last one */ //Avoid ArrayOutOfBoundException - int end = slice.length - 1; - if ( slice[ end ] != null && slice[ end - 1 ] != null ) - { - for ( Coordinate coordinate : slice[ end ].get() ) - { - coordinate.setLeftCoordinates( slice[ end - 1 ] ); - boolean hasNoLeft = coordinate.numberOfNeighbours( slice[ end - 1 ], 0 ) == 0; - if ( hasNoLeft ) - { - noLeftCoordinates.add( coordinate ); - } - } - } - return noLeftCoordinates; - } - - /** - * Resets the parameters of each {@link Coordinate } for a given {@link Pixels} array - * before the Y dimension reconstruction ( rightsNumber , leftsNumber, noLeft, queue ). - * - * @param slice - the {@link Pixels} array considered. - */ - private void reset( Pixels[] slice ) - { - for ( Pixels pixels : slice ) - { - if ( pixels != null ) - { - pixels.resetSideCoordinate(); - } - } - } - - - public OSEList[] getOseLists() - { - return oseLists; - } - - public OSEStartingStatus getStartingStatus() - { - return startingStatus; - } -} - diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ReferenceSurfaceExtraction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ReferenceSurfaceExtraction.java deleted file mode 100644 index 5d72072a75e65829122371a2c646dbef2f908fc4..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ReferenceSurfaceExtraction.java +++ /dev/null @@ -1,294 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.construction; - -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.AdvancedUserParameters; -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.DisplayParameter; -import fr.pasteur.ida.zellige.exception.FirstRoundConstructionException; -import fr.pasteur.ida.zellige.exception.NoSurfaceFoundException; -import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; -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.ReferenceSurface; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; -import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.UserParameters; -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.numeric.RealType; -import net.imglib2.type.numeric.integer.UnsignedShortType; -import org.jzy3d.analysis.AnalysisLauncher; - -import java.util.ArrayList; - - - -public class ReferenceSurfaceExtraction< T extends RealType< T > & NativeType< T > > -{ - - private final ArrayList< ReferenceSurface <T>> referenceSurfaces = new ArrayList<>(); - private final RandomAccessibleInterval< T > input; - private final ImgFactory<T> factory; - private final UserParameters userParameters; - private final AdvancedUserParameters advancedUserParameters; - public ReferenceSurfaceExtraction( RandomAccessibleInterval< T > input, ImgFactory<T> factory, - UserParameters userParameters, AdvancedUserParameters advancedUserParameters) - { - this.input = input; - this.factory = factory; - this.userParameters = userParameters; - this.advancedUserParameters = advancedUserParameters; - } - - - public void extract( ) - { - /* First step : Pixel selection */ - SurfacePixelSelection selection = new SurfacePixelSelection( userParameters ); - selection.run( input ); - Pixels[][] maximums = selection.getMaximums(); - /* Second step : Surface construction in 2 rounds */ - try - { - /* First round construction*/ - double percent1 = advancedUserParameters.getPercent1(); - int k1 = advancedUserParameters.getK1(); - ArrayList< Surface > tempSurfaces = firstRoundConstruction( maximums, percent1, k1); - System.out.println( "first round surfaces = " + tempSurfaces.size() ); - - /* Second round construction */ - double percent2 = advancedUserParameters.getPercent2(); - int k2 = advancedUserParameters.getK2(); - ArrayList< Surface > finalSurfaces = secondRoundConstruction( tempSurfaces, percent2, k2 ); - System.out.println( "second round surfaces = " + finalSurfaces.size() ); - - /* Process of zMaps */ - processZMap( finalSurfaces ); - - System.out.println( "The end" ); - } - catch ( Exception e ) - { - e.printStackTrace(); - } - } - - /* ----- First and second round construction methods. ----- */ - private ArrayList< Surface > firstRoundConstruction( Pixels[][] maximums, double percent, int k ) throws NoSurfaceFoundException - { - int dimension = 0; - ArrayList<Surface> surfaces = constructSurfaces( maximums, dimension, 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 Pixels[][] rebuildPixelsArray( Surface surface ) - { - int width = surface.getHeight(); - int height = surface.getWidth(); - Pixels[][] tempCoordinates = new Pixels[ height ][ width ]; // Transposed array - for ( int i = 0; i <= width - 1; i++ ) - { - for ( int j = 0; j <= height - 1; j++ ) - { - if ( surface.get( i ) != null ) - { - tempCoordinates[ j ][ i ] = surface.get( i ).get( j ); - } - } - } - displayMaximums( tempCoordinates ); - return tempCoordinates; - } - - /** - * Reconstructs the TempSurface objects of the specified list in dimension width. - * - * @param surfaces - the list of TempSurface objects. - */ - private ArrayList< Surface > secondRoundConstruction( ArrayList< Surface > surfaces, double percent, int k ) throws NoSurfaceFoundException - { - int dimension = 1; - ArrayList< Surface > finalSurfaces = new ArrayList<>(); - System.out.println( "========================================" ); - for ( Surface surface : surfaces ) - { - int width = surface.getHeight(); - if ( surface.hasDuplicate() )// CoordinateList instead of Coordinate - { - Pixels[][] maximums = rebuildPixelsArray( surface ); - ArrayList< Surface > temps = constructSurfaces( maximums,dimension, 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(width) ); - } - } - mergeReferenceSurface( surfaces); - return finalSurfaces; - - } - - private Img<UnsignedShortType> processedZMap(Surface surface) - { - // First step : fill in the holes with linear interpolation. */ - Img< UnsignedShortType > interpolated = Interpolation.run( surface.getZMap() ); - Img< UnsignedShortType > smoothed = interpolated.copy(); -// ImageJFunctions.show( smoothed, "smoothed" ); - // Second step: smooth the elevation map with gaussian blur - Utils.gaussConvolution( interpolated, smoothed, new double[]{ 1.0, 1.0 } ); - return smoothed; - } - - //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 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 OSEList[] constructOSLists( Pixels[][] maximums, int dimension) - { - OSEListConstruction construction = new OSEListConstruction( maximums, dimension ); - construction.run(); - return construction.getOseLists(); - } - - - private ArrayList<Surface> constructSurfaces(Pixels[][] maximums, int dimension, double percent, int k) - { - int surfaceLineLength = maximums[0].length; - OSEList[] oseLists = constructOSLists( maximums, dimension ); - SurfacesReconstruction surfacesReconstruction = new SurfacesReconstruction(dimension, oseLists,surfaceLineLength, percent, k); - surfacesReconstruction.buildSurfaces( ); - System.out.println(" Small Surface count : " + surfacesReconstruction.getSmallSurfaceCount()); - return surfacesReconstruction.getSurfaces(); - } - - private void processZMap(ArrayList<Surface> surfaces) - { - int index = 0; - for( Surface surface : surfaces ) - { - displaySurface( surface ); - Img< UnsignedShortType > processedZMap = processedZMap( surface ); - ReferenceSurface< T > referenceSurface = new ReferenceSurface<>( input, factory, processedZMap, index++ ); - referenceSurfaces.add( referenceSurface ); - } - } - - public void project() - { - for(ReferenceSurface<T> referenceSurface : referenceSurfaces) - { - referenceSurface.setProjection( userParameters.getProjectionType(), userParameters.getDelta() ); - DisplayParameter displayParameter = new DisplayParameter( userParameters.getDelta(), false, - false, false, true ); - referenceSurface.display( displayParameter ); - } - } - - - /* ----- Displaying methods -----*/ - /** - * 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( OSEList[] oseLists ) - { - try - { - OSDisplay display = new OSDisplay( oseLists ); - 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/surfaceConstruction/construction/SurfacePixelSelection.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacePixelSelection.java deleted file mode 100644 index cc73b76159bcfb66bbd81f0d5cfd01dc403cfe1b..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacePixelSelection.java +++ /dev/null @@ -1,222 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.construction; - -import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; -import fr.pasteur.ida.zellige.surfaceConstruction.parameters.UserParameters; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; -import fr.pasteur.ida.zellige.utils.*; -import fr.pasteur.ida.zellige.utils.islandSearch.IslandSearch; -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.converter.Converters; -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 org.jzy3d.analysis.AnalysisLauncher; - -public class SurfacePixelSelection -{ - - private final UserParameters userParameters; - private Pixels[][] maximums; - - public SurfacePixelSelection( UserParameters userParameters ) - { - this.userParameters = userParameters; - } - - /** - * - * @param source - * @param <T> - * @return - */ - public < T extends RealType< T > & NativeType< T > > void run( RandomAccessibleInterval< T > source ) - { - /* 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 */ - maximums = maximumSearch2( normalized, factory ); - /* Output */ - displayMaximums( maximums ); - } - - /** - * - * @param source - * @param <T> - * @return - */ - private < T extends RealType< T > & NativeType< T > >Img<FloatType> pretreatment( RandomAccessibleInterval< T > source ) - { - // Conversion into FloatType for the derivative computation (negative values) - RandomAccessibleInterval< FloatType > converted = Converters.convert( source, new RealFloatSamplerConverter<>() ); - - //Image denoising with an anisotropic convolution or median filter !! - ImgFactory<FloatType> factory = new ArrayImgFactory<>(new FloatType()); -// Img <FloatType> c = Utils.gaussConvolution( converted,factory, new double[]{1, 1, 0 } ); - Img <FloatType> c = Filter2D.median( converted, 2 ); - // Normalization - return Utils.normalizeImage( c, c.factory()); - } - - /** - * - * @param input - * @param factory - * @param <T> - * @return - */ - private < T extends RealType< T > & NativeType< T > > Pixels[][] maximumSearch( RandomAccessibleInterval< T > input,ImgFactory<T> factory ) - { - /* Grid of amplitude thresholds */ - Img< FloatType > amplitudeImg = MaximumAmplitudeClassification.find(input,factory,userParameters.getAmplitude() , 0.10); - - ImageJFunctions.show( amplitudeImg," amplitude1" ); - - /* Grid of local thresholds*/ - Img< BitType > thresholds = LocalOtsuClassification.find( input, factory, userParameters.getThreshold()); - - /* Pixel selection */ - Img<FloatType> finalList = Threshold.classification( amplitudeImg, amplitudeImg.factory(), thresholds ); - - /* Isolated Pixel removal */ - ImageJFunctions.show( finalList.copy(), "isolated pixel sel" ); - /* Dilatation of the resulting image*/ - - double sigma = userParameters.getSigmas(); - - Utils.gaussConvolution( finalList.copy(), finalList, new double[]{ sigma, sigma, 1 } ); - ImageJFunctions.show(finalList, "blurred maximums1" ); - - /* Local maximum detection due to previous smoothing*/ - finalList = LocalExtremaDetection.findMaxima( finalList.copy(), finalList.factory() ); - return buildPixelArray( finalList ); - } - - /** - * - * @param input - * @param factory - * @param <T> - * @return - */ - private < T extends RealType< T > & NativeType< T > > Pixels[][] maximumSearch3( RandomAccessibleInterval< T > input,ImgFactory<T> factory ) - { - /* Grid of amplitude thresholds */ - Img< FloatType > amplitudeImg = MaximumAmplitudeClassification3.find(input,factory,userParameters.getAmplitude() , 0.10); - -// ImageJFunctions.show( amplitudeImg," amplitude1" ); - - /* Grid of local thresholds*/ - Img< BitType > thresholds = LocalOtsuClassification.find( input, factory, userParameters.getThreshold()); - - /* Pixel selection */ - Img<FloatType> finalList = Threshold.classification( amplitudeImg, amplitudeImg.factory(), thresholds ); - - /* Isolated Pixel removal */ - -// ImageJFunctions.show( finalList.copy(), "isolated pixel sel" );//new ImgLabeling< FloatType, IntType >( labeling ), - /* Dilatation of the resulting image*/ - - double sigma = userParameters.getSigmas(); - - Utils.gaussConvolution( finalList.copy(), finalList, new double[]{ sigma, sigma, 1 } ); -// ImageJFunctions.show(finalList, "blurred maximums1" ); - - /* Local maximum detection due to previous smoothing*/ - finalList = LocalExtremaDetection.findMaxima( finalList.copy(), finalList.factory() ); - return buildPixelArray( finalList ); - } - - private < T extends RealType< T > & NativeType< T > > Pixels[][] maximumSearch2( RandomAccessibleInterval< T > input,ImgFactory<T> factory ) - { - /* Grid of amplitude thresholds */ - Img< BitType > amplitudeImg = MaximumAmplitudeClassification2.find(input,factory,userParameters.getAmplitude() ); -// ImageJFunctions.show( amplitudeImg2," amplitude2" ); - - /* Grid of local thresholds*/ - Img< BitType > thresholds = LocalOtsuClassification.find( input, factory, userParameters.getThreshold()); - - /* Pixel selection */ - Img<BitType> finalList = Threshold.classification( amplitudeImg, amplitudeImg.factory(), thresholds ); - - /* Isolated Pixel removal */ - finalList = IslandSearch.run( finalList, 5, 8 ); - ImageJFunctions.show( finalList.copy(), "isolated pixel sel" ); - Img< FloatType > converted = Utils.convertBitTypeIntoFloatType( finalList ); - - /* Dilatation of the resulting image*/ - double sigma = userParameters.getSigmas(); - - Utils.gaussConvolution( converted.copy(), converted, new double[]{ sigma, sigma, 0 } ); -// ImageJFunctions.show(converted, "blurred maximums2" ); - - /* Local maximum detection due to previous smoothing*/ - converted = LocalExtremaDetection.findMaxima( converted.copy(), converted.factory() ); - return buildPixelArray( converted ); - } - /** - * - * @param stack - * @param <T> - * @return - */ - public < 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; - } - - /** - * 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(); - } - } - - public Pixels[][] getMaximums() - { - return maximums; - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesReconstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesReconstruction.java deleted file mode 100644 index 35b57ad6fbf2238ebbd6f80d07cfad71f7474cbd..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesReconstruction.java +++ /dev/null @@ -1,322 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.construction; - - -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; -import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; -import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLineX; -import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLineY; - -import java.util.ArrayList; - - -/** - * Static methods to build Surface instances from OS. - */ -public class SurfacesReconstruction -{ - - private final ArrayList< Surface > surfaces = new ArrayList<>(); - private final int dimension; - private final OSEList[] oseLists; - private final int surfaceLineLength; - private final double percent; - private final int k; - - private int smallSurfaceCount; - - public SurfacesReconstruction( int dimension, OSEList[] oseLists, int surfaceLineLength, double percent, int k ) - { - this.dimension = dimension; - this.oseLists = oseLists; - this.surfaceLineLength = surfaceLineLength; - this.percent = percent; - this.k = k; - } - - /** - * Returns a list of TempSurface constructed from the specified OSList array. - * - */ - public void buildSurfaces() - { - // Construction of the list of output TempSurface. - smallSurfaceCount = 0; - int finalIndex = findIndexValue( 0 ); - do - { - // All the OSLists are set to "not visited". - reset(); - int startingIndex = finalIndex; - OSE firstOSE = getFirstOs( finalIndex ); -// osLists[finalIndex].remove( firstOS );//TODO remove or not - if ( firstOSE != null ) - { - Surface surface = createSurface( firstOSE, surfaceLineLength ); - buildSurface( surface, finalIndex ); - //it is really a reference surface ? - if ( surface.getSize() >= surfaceLineLength * oseLists.length * 0.01 ) - { - surfaces.add( surface ); - } - else - { - smallSurfaceCount++; - System.out.println( "searching.....from " ); - } - finalIndex = findIndexValue( startingIndex ); - } - } - while ( finalIndex <= oseLists.length - 2 - && ( oseLists[ finalIndex ] != null - && ! oseLists[ finalIndex ].isEmpty() - && oseLists[ finalIndex ].containsAStart() ) ); - System.out.println( "smallSurfaceCount = " + smallSurfaceCount ); - } - - /** - * Adds new SurfaceLine to a specified TempSurface. - * - * @param surface - the TempSurface to construct - * @param index - the position in the TempSurface - */ - private void buildSurface( Surface surface, int index ) - { - /* First round of the construction -> top to bottom in the dimension.*/ - SurfaceLine current = surface.get( index ); - SurfaceLine next; - while ( current != null ) - { - next = searchForward( current ); - if ( next != null ) - { - surface.set( next.getLine(), next ); - } - current = next; - } - - /* Second round of the construction -> top to bottom to top until every OS has been visited.*/ - int size = 0; - while ( size != surface.getSize() ) - { - size = surface.getSize(); - for ( int i = surface.getHeight() - 1; i > 0; i-- ) - { - if ( surface.get( i ) != null ) - { - SurfaceLine previous = searchBackward( surface.get( i ) ); - if ( previous != null ) - { - if ( surface.get( i - 1 ) != null ) - { - surface.get( i - 1 ).merge( previous ); - } - else - { - surface.set( i - 1, previous ); - } - } - } - } - for ( int i = 0; i <= surface.getHeight() - 2; i++ ) - { - if ( surface.get( i ) != null ) - { - next = searchForward( surface.get( i ) ); - if ( next != null ) - { - if ( surface.get( i + 1 ) != null ) - { - surface.get( i + 1 ).merge( next ); - } - else - { - surface.set( i + 1, next ); - } - } - } - } - } - } - - - /** - * @param firstOSE - the first surface element. - * @return a surface as a {@link Surface} - */ - private Surface createSurface( OSE firstOSE, int surfaceLineLength ) - { - Surface surface = new Surface( dimension, oseLists.length ); - - int i; - if ( dimension == 0 ) - { - i = firstOSE.get( 0 ).getY(); - surface.set( i, new SurfaceLineX( surfaceLineLength, firstOSE ) ); - } - else - { - i = firstOSE.get( 0 ).getX(); - surface.set( i, new SurfaceLineY( surfaceLineLength, firstOSE ) ); - } - return surface; - } - - /** - * Returns the index value of the first OSList containing a starting OS. - * - * @param index - the previous index value - * @return the value of the first OSList containing a starting OS - */ - private int findIndexValue( int index ) - { - int i = index; - int limitValue; - limitValue = oseLists.length - 1; - while ( i <= limitValue - 2 ) - { - if ( oseLists[ i ] != null - && ! oseLists[ i ].isEmpty() - && oseLists[ i ].containsAStart() ) - { - return i; - } - i++; - } - index = limitValue; - return index; - } - - /** - * Returns the first OS with a start status in the OSList array. - * - * @param index the previous index value - * @return the first OS with a true Start status - */ - private OSE getFirstOs( int index ) - { - for ( OSE os : oseLists[ index ] ) - { - if ( os.isAStart() ) - { - os.setVisited(); - return os; - } - } - return null; - } - - /** - * Returns the SurfaceLine matching the current in a specific direction - * - * @param next - the OSList to search into - * @param currentLine - the {@link SurfaceLine } to match. - * @param j - the direction of the search (1 or -1) - * @return the OSList matching SurfaceLine - */ - private SurfaceLine search( OSEList next, SurfaceLine currentLine, int j ) - { - ArrayList< SurfaceLine > list = new ArrayList<>(); - for ( OSE os : next ) - { - - if ( ! os.isVisited() ) // The OS is already added to the referenceSurface - { - SurfaceLine line = currentLine.match( os, j, percent, k ); - if ( line != null ) - { - list.add( line ); - } - } - } - if ( list.size() == 0 ) - { - return null; - } - else - { - merge( list ); - return list.get( 0 ); - } - } - - /** - * Returns the SurfaceLine generated from the specified OSList array matching the current SurfaceLine - * - * @param current - the {@link SurfaceLine } to match - * @return the SurfaceLine generated from the specified OSList array matching the current SurfaceLine - */ - private SurfaceLine searchForward( SurfaceLine current ) - { - - if ( current.getLine() + 1 <= oseLists.length - 1 ) - { - return search( oseLists[ current.getLine() + 1 ], current, 1 ); - } - else - { - return null; - } - } - - /** - * @param current - the current SurfaceLine to match with. - * @return - the matching SurfaceLine. - */ - private SurfaceLine searchBackward( SurfaceLine current ) - { - if ( current.getCoordinatesNumber() != 0 && current.getLine() > 0 ) - { - return search( oseLists[ current.getLine() - 1 ], current, - 1 ); - } - else - { - return null; - } - } - - /** - * Merges the SurfaceLine objects in the specified list. - * - * @param list - the list to merge. - */ - private void merge( ArrayList< SurfaceLine > list ) - { - if ( list.size() >= 2 ) //more than one SurfaceLine - { - int index = list.size() - 1; - while ( list.size() != 1 ) - { - list.get( index - 1 ).merge( list.remove( index ) ); - index--; - } - } - } - - - /** - * Resets the "visited" value of each OS of each OSList to FALSE. - * - */ - private void reset() - { - for ( OSEList oseList : oseLists ) - { - if ( oseList != null ) - { - oseList.reset(); - } - } - } - - public int getSmallSurfaceCount() - { - return smallSurfaceCount; - } - - public ArrayList< Surface > getSurfaces() - { - return surfaces; - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/ConstructionPipeline.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/ConstructionPipeline.java new file mode 100644 index 0000000000000000000000000000000000000000..9b1a1f8ebba05dd21919016ced867fba49539835 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/ConstructionPipeline.java @@ -0,0 +1,73 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound; + +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.Util; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.ose.OseConstructionXZ; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.ose.OseConstructionYZ; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.surface.AllSurfaceConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +public class ConstructionPipeline +{ + private final static Logger LOGGER = LoggerFactory.getLogger( ConstructionPipeline.class ); + + public static final int DIMENSION = 0; + private final Pixels[][] maximums; + private final double startingSizeThreshold; + private final int overlap; + private final double connexityRate; + private final ArrayList< Surface > surfaces; + private final int dimension; + + + + + public static ArrayList<Surface> run( Pixels[][] maximums, int dimension, ConstructionParameters constructionParameters ) + { + ConstructionPipeline pipeline = new ConstructionPipeline( maximums, dimension, constructionParameters ); + pipeline.run(); + return pipeline.surfaces; + } + + + public ConstructionPipeline( Pixels[][] maximums, int dimension, ConstructionParameters constructionParameters ) + { + this.maximums = maximums; + this.dimension = dimension; + this.startingSizeThreshold = constructionParameters.getStartingSizeThreshold(); + this.overlap = constructionParameters.getOverlap(); + this.connexityRate = constructionParameters.getConnexityRate(); + this.surfaces = new ArrayList<>(); + } + + public void run() + { + LOGGER.info("Processing..."); + /* OSE construction according to the dimension considered */ + OSEListArray oseLists = constructOSE( ); + + /* Construction of surfaces*/ + LOGGER.info("Starting surface construction..."); + int lineLength = maximums[0].length; + this.surfaces.addAll( AllSurfaceConstruction.run( oseLists, lineLength, overlap, connexityRate )); + LOGGER.info("Completed"); + } + + private OSEListArray constructOSE( ) + { + if ( dimension == DIMENSION ) + { + return OseConstructionXZ.runConstruction( maximums, startingSizeThreshold ); + } + else + { + return OseConstructionYZ.runConstruction( maximums, startingSizeThreshold ); + } + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/ConstructionRound.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/ConstructionRound.java new file mode 100644 index 0000000000000000000000000000000000000000..6c73495b7398fbdedbf972e896946f7e0b5d8f37 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/ConstructionRound.java @@ -0,0 +1,56 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound; + +import fr.pasteur.ida.zellige.exception.NoSurfaceFoundException; +import fr.pasteur.ida.zellige.jzy3D.OSDisplay; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; +import org.jzy3d.analysis.AnalysisLauncher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +public class ConstructionRound +{ + private final static Logger LOGGER = LoggerFactory.getLogger( ConstructionRound.class ); + + void finalizeSurface(ArrayList<Surface> surfaces) throws NoSurfaceFoundException + { + if ( ! surfaces.isEmpty() ) + { + mergeReferenceSurface( surfaces ); + } + else + { + System.out.println( "No Surface Exception" ); + throw new NoSurfaceFoundException(); + } + } + + /** + * Merges the TempSurface objects of the specified list. + * + * @param surfaces - the list to merge + */ + void mergeReferenceSurface( ArrayList< Surface > surfaces ) + { + LOGGER.info("Merging 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 ); + LOGGER.info("Merging completed"); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/FirstRoundConstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/FirstRoundConstruction.java new file mode 100644 index 0000000000000000000000000000000000000000..55b16d837621986c8d90e7042780768b99cd4303 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/FirstRoundConstruction.java @@ -0,0 +1,42 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound; + +import fr.pasteur.ida.zellige.exception.NoSurfaceFoundException; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +public class FirstRoundConstruction extends ConstructionRound +{ + private final static Logger LOGGER = LoggerFactory.getLogger( FirstRoundConstruction.class ); + + private static final int DIMENSION = 0; + private final Pixels[][] maximums; + private final ConstructionParameters constructionParameters; + private final ArrayList< Surface > surfaces; + + public static ArrayList< Surface> run(Pixels[][] maximums, ConstructionParameters constructionParameters) throws NoSurfaceFoundException + { + FirstRoundConstruction round = new FirstRoundConstruction( maximums, constructionParameters ); + round.process(); + return round.surfaces; + } + + public FirstRoundConstruction( Pixels[][] maximums, ConstructionParameters constructionParameters ) + { + this.maximums = maximums; + this.constructionParameters= constructionParameters; + this.surfaces = new ArrayList<>(); + } + + public void process() throws NoSurfaceFoundException + { + LOGGER.info("Processing..."); + surfaces.addAll( ConstructionPipeline.run(maximums, DIMENSION, constructionParameters )); + this.finalizeSurface( surfaces ); + LOGGER.info("Round completed"); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/OSEStartingStatus.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/OSEStartingStatus.java new file mode 100644 index 0000000000000000000000000000000000000000..d3d86816e9ed36245626e60037b54ce377b02b7c --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/OSEStartingStatus.java @@ -0,0 +1,39 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.TreeMap; + +public class OSEStartingStatus extends TreeMap<Integer, Integer> +{ + private final static Logger LOGGER = LoggerFactory.getLogger( OSEStartingStatus.class ); + + private int startingSize; + private final double threshold; + + public OSEStartingStatus( double threshold ) + { + this.threshold = threshold; + LOGGER.info( "OSE starting threshold = {}", threshold); + } + + public void setStartingStatus() + { + if (this.size() != 0) + { + { + for ( int i = 0; this.size() > 20 && i <= this.size()* threshold ;i++ ) + { + this.remove( this.lastKey() ); + } + } + this.startingSize = this.lastKey() ; + LOGGER.info("Starting OSE size = {}", startingSize); + }} + + public int getStartingSize() + { + return startingSize; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/SecondRoundConstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/SecondRoundConstruction.java new file mode 100644 index 0000000000000000000000000000000000000000..df991c90c062cd1dc0037e1e22510c77e3ba2613 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/SecondRoundConstruction.java @@ -0,0 +1,91 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound; + +import fr.pasteur.ida.zellige.exception.NoSurfaceFoundException; +import fr.pasteur.ida.zellige.exception.SecondRoundConstructionException; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +public class SecondRoundConstruction extends ConstructionRound +{ + private final static Logger LOGGER = LoggerFactory.getLogger( SecondRoundConstruction.class ); + + private final ConstructionParameters constructionParameters; + private final ArrayList< Surface > tempSurfaces; + private final ArrayList< Surface > finalSurfaces; + private static final int DIMENSION = 1; + + public SecondRoundConstruction( ArrayList< Surface > tempSurfaces, ConstructionParameters constructionParameters ) + { + this.constructionParameters = constructionParameters; + this.tempSurfaces = tempSurfaces; + this.finalSurfaces = new ArrayList<>(); + + } + + public static ArrayList< Surface> run( ArrayList< Surface > tempSurfaces, ConstructionParameters constructionParameters) throws NoSurfaceFoundException + { + SecondRoundConstruction round = new SecondRoundConstruction( tempSurfaces, constructionParameters ); + round.process(); + return round.finalSurfaces; + } + + public void process() throws NoSurfaceFoundException + { + LOGGER.info("Starting process..."); + for ( Surface surface : tempSurfaces ) + { + if ( surface.hasDuplicate() )// CoordinateList instead of Coordinate + { + Pixels[][] maximums = rebuildMaximums( surface ); + ArrayList< Surface > temps = ConstructionPipeline.run( maximums, DIMENSION, constructionParameters ); + if ( temps.isEmpty() ) + { + System.out.println( "small surface in second round" ); + throw new SecondRoundConstructionException(); + } + else + { + finalSurfaces.addAll( temps ); + } + } + else + { + finalSurfaces.add( surface.transpose() ); + } + } + LOGGER.info("Process completed."); + } + + /** + * 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 Pixels[][] rebuildMaximums( Surface surface ) + { + LOGGER.info("Rebuilding surface..."); + int width = surface.getHeight(); + int height = surface.getWidth(); + Pixels[][] tempCoordinates = new Pixels[ height ][ width ]; // Transposed array + for ( int i = 0; i <= width - 1; i++ ) + { + for ( int j = 0; j <= height - 1; j++ ) + { + if ( surface.get( i ) != null ) + { + tempCoordinates[ j ][ i ] = surface.get( i ).get( j ); + } + } + } + LOGGER.info("Surface rebuild."); + return tempCoordinates; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/constructSurface/ConstructionRounds.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/constructSurface/ConstructionRounds.java new file mode 100644 index 0000000000000000000000000000000000000000..3d72d2f37350c2b22599b11ff1f813af85ba4b45 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/constructionRound/constructSurface/ConstructionRounds.java @@ -0,0 +1,48 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.constructSurface; + +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.FirstRoundConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.SecondRoundConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; + +public class ConstructionRounds +{ + private final static Logger LOGGER = LoggerFactory.getLogger( ConstructionRounds.class ); + + public static final double adequacy = 0.02; + private final ArrayList< Surface > referenceSurfaces = new ArrayList<>(); + + + public void process( Pixels[][] maximums, ConstructionParameters[] constructionParameters) + { + LOGGER.info("Starting rounds..."); + LOGGER.info( "Adequacy value = {}", adequacy); + try + { + /* First round construction*/ + ArrayList< Surface > tempSurfaces = FirstRoundConstruction.run( maximums, constructionParameters[0] ); + System.out.println( "first round surfaces = " + tempSurfaces.size() ); +// for(Surface s : tempSurfaces) +// { +// displaySurface( s ); +// } + /* Second round construction */ + referenceSurfaces.addAll( SecondRoundConstruction.run( tempSurfaces, constructionParameters[1] )); + } + catch ( Exception e ) + { + e.printStackTrace(); + } + LOGGER.info("Rounds completed."); + } + + public ArrayList< Surface > getReferenceSurfaces() + { + return referenceSurfaces; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/LocalExtremaDetection.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/LocalExtremaDetection.java similarity index 68% rename from src/main/java/fr/pasteur/ida/zellige/utils/LocalExtremaDetection.java rename to src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/LocalExtremaDetection.java index 0127fe3e74116e9db6fec4278af12c3e2b5b280b..06f4f991ca48f0e2547fc99fc7a98838277b8cbe 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/LocalExtremaDetection.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/LocalExtremaDetection.java @@ -1,53 +1,37 @@ -package fr.pasteur.ida.zellige.utils; +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.utils.Utils; import net.imglib2.RandomAccess; 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.numeric.RealType; import net.imglib2.view.IntervalView; import net.imglib2.view.Views; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; -public class LocalExtremaDetection +class LocalExtremaDetection < T extends RealType< T > & NativeType< T > > { + //TODO review implementation (multithreading) - public static < T extends RealType< T > & NativeType< T > > Img< T > findMinima( RandomAccessibleInterval< T > input, - ImgFactory< T > factory ) - { - Img< T > minima = factory.create( input.dimension( 0 ), input.dimension( 1 ), input.dimension( 2 ) ); - new LocalExtremaDetectionOperator<>( input, factory, minima, "min" ); - return minima; - } - - - public static < T extends RealType< T > & NativeType< T > > Img< T > findMaxima( RandomAccessibleInterval< T > input, - ImgFactory< T > factory ) - { - Img< T > maxima = factory.create( input.dimension( 0 ), input.dimension( 1 ), input.dimension( 2 ) ); - new LocalExtremaDetectionOperator<>( input, factory, maxima, "max" ); - return maxima; - } - - private static class LocalExtremaDetectionOperator< T extends RealType< T > & NativeType< T > > - { - + public static final String MAX = "max"; private final RandomAccessibleInterval< T > input; private final ImgFactory< T > factory; - private final RandomAccessibleInterval< T > output; + private final Img< T > extrema; private final String type; - public LocalExtremaDetectionOperator( RandomAccessibleInterval< T > input, ImgFactory< T > factory, - RandomAccessibleInterval< T > output, String type ) + private final static Logger LOGGER = LoggerFactory.getLogger( LocalExtremaDetection.class ); + + LocalExtremaDetection( RandomAccessibleInterval< T > input, ImgFactory< T > factory, String type ) { this.input = input; this.factory = factory; - this.output = output; + this.extrema = factory.create( input ); this.type = type; - findExtrema(); } /** @@ -55,9 +39,10 @@ public class LocalExtremaDetection * according to given local and global thresholds by first using a gaussian blur. * The local maximums are stored in a 2D {@link Pixels} array. */ - private void + public void findExtrema() { + LOGGER.info("Starting detection..."); // For each individual orthogonal XZ sections.*/ for ( int x = 0; x <= input.dimension( 0 ) - 1; x++ )// iteration on dimension X { @@ -66,16 +51,16 @@ public class LocalExtremaDetection // The YZ section. IntervalView< T > intervalView = Views.hyperSlice( this.input, 0, x ); // Each maximum found is stored in double Pixel array . - getExtrema( intervalView, x ); + setExtrema( intervalView, x ); } catch ( Exception e ) { e.printStackTrace(); } } + LOGGER.info("Extrema detected."); } - /** * Finds all the local maximums of an 2D image, * according to given local and global thresholds by first using a gaussian blur and then a partial derivative @@ -85,14 +70,14 @@ public class LocalExtremaDetection * @param x - the index of the orthogonal section AKA the stack index in dimension X. */ private void - getExtrema( RandomAccessibleInterval< T > YZProjection, int x ) + setExtrema( RandomAccessibleInterval< T > YZProjection, int x ) { // Output for partial derivative. // Addition of one supplementary line, in case the last slice contains some local maximums - RandomAccessibleInterval< T > partialDerivative = factory.create(YZProjection); + RandomAccessibleInterval< T > partialDerivative = factory.create( YZProjection ); // Partial derivative computation */ Utils.derivative( YZProjection, partialDerivative ); - getExtrema( partialDerivative, YZProjection, x ); + setExtrema( partialDerivative, YZProjection, x ); } @@ -101,17 +86,17 @@ public class LocalExtremaDetection * @param x the index in dimension X */ private void - getExtrema + setExtrema ( RandomAccessibleInterval< T > partialDerivative, RandomAccessibleInterval< T > original, int x ) { RandomAccess< T > derivativeAccess = Views.extendMirrorSingle( partialDerivative ).randomAccess(); - RandomAccess< T > maxAccess = output.randomAccess(); + RandomAccess< T > maxAccess = extrema.randomAccess(); RandomAccess< T > oriAccess = original.randomAccess(); - for ( int y = 0; y <= output.dimension( 1 ) - 1; y++ ) + for ( int y = 0; y <= extrema.dimension( 1 ) - 1; y++ ) { derivativeAccess.setPosition( y, 0 ); - for ( int z = 0; z <= output.dimension( 2 ) - 1; z++ ) + for ( int z = 0; z <= extrema.dimension( 2 ) - 1; z++ ) { if ( isALocalExtrema( derivativeAccess, z ) ) { @@ -140,7 +125,7 @@ public class LocalExtremaDetection final double tZero = derivative.get().getRealDouble(); derivative.setPosition( ( z + 1 ), 1 ); final double tOne = derivative.get().getRealDouble(); - if ( type.equals( "max" ) ) + if ( type.equals( MAX ) ) { return ( tZero >= 0 && tOne < 0 ); } @@ -149,6 +134,10 @@ public class LocalExtremaDetection return ( tZero < 0 && tOne >= 0 ); } } - + public Img< T > getExtrema() + { + return extrema; + } } -} + + diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/LocalOtsuClassification.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/LocalOtsuClassification.java new file mode 100644 index 0000000000000000000000000000000000000000..bbec02a7be814420d65d78d8efb4c25bbc297b3c --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/LocalOtsuClassification.java @@ -0,0 +1,130 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.utils.Otsu; +import fr.pasteur.ida.zellige.utils.Threshold; +import fr.pasteur.ida.zellige.utils.Utils; +import net.imglib2.*; +import net.imglib2.algorithm.util.Grids; +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.view.IntervalView; +import net.imglib2.view.Views; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +public class LocalOtsuClassification< T extends RealType< T > & NativeType< T > > +{ + + private final RandomAccessibleInterval< T > source; + private final ImgFactory< T > factory; + private final RandomAccessibleInterval< T > grid; + private final Img< BitType > binary; + private final double percent; + + private final static Logger LOGGER = LoggerFactory.getLogger( LocalOtsuClassification.class ); + + /** + * @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 + */ + LocalOtsuClassification( 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; + this.grid = factory.create( source ); + } + + /** + * @param input the input {@link Img} + * @param grid the image containing the grid of local thresholds + */ + public void + computeLocalThreshold( RandomAccessibleInterval< T > input, ImgFactory< T > factory, RandomAccessibleInterval< T > grid ) + { + long width = input.dimension( 0 ); + long height = input.dimension( 1 ); + long depth = input.dimension( 2 ); + List< Interval > intervals = Grids.collectAllContainedIntervals( new long[]{ width, height, depth }, + new int[]{ 50, 50, 3 } );//TODO Size of a grid cube ? + + for ( Interval interval : intervals ) + { + computeLocalThreshold( input, grid, interval ); + } + grid = Utils.gaussConvolution( grid, factory, new double[]{ 5, 5, 1 } ); + LOGGER.info("Local thresholds computed with user value = {}.", this.percent); + } + + public void + computeLocalThreshold( RandomAccessibleInterval< T > input, RandomAccessibleInterval< T > grid, Interval interval ) + { + IntervalView< T > viewSource = Views.offsetInterval( input, interval ); + IntervalView< T > viewGrid = Views.offsetInterval( grid, interval ); + T threshold = Otsu.getThreshold( viewSource ); + viewGrid.forEach( pixel -> pixel.set( threshold ) ); + + } + + /** + * @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 + */ + public static < T extends RealType< T > & NativeType< T > > void + applyLocalThreshold( IterableInterval< T > input, IterableInterval< T > grid, RandomAccessibleInterval< BitType > binary, double percent ) + { + double threshold = Threshold.getThreshold( input, percent ).getRealDouble(); + Cursor< T > sourceCursor = input.localizingCursor(); + Cursor< T > outputCursor = grid.cursor(); + RandomAccess< BitType > randomAccess = binary.randomAccess(); + 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 + { + randomAccess.setPosition( sourceCursor ); + randomAccess.get().set( true ); + } + } + } + LOGGER.info("Local thresholds applied."); + } + + /** + * + */ + void run() + { + 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; + } +} + + + + + + diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/MaximumAmplitudeClassification.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/MaximumAmplitudeClassification.java new file mode 100644 index 0000000000000000000000000000000000000000..957672b429dbf0a6b8776ceaa537fdf4738c6f5e --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/MaximumAmplitudeClassification.java @@ -0,0 +1,168 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; +import fr.pasteur.ida.zellige.utils.Threshold; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.binary.Thresholder; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static fr.pasteur.ida.zellige.utils.Threshold.findMin; + +/** + * This class makes it possible to get a binary classification of the pixels (background/ foreground) according to their amplitude and their amplitude's neighborhood. + * First, the amplitude of all local maximums is computed and stored as an {@link Img}.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} + */ +public class MaximumAmplitudeClassification< T extends RealType< T > & NativeType< T > > +{ + + private final RandomAccessibleInterval< T > input; + private final ImgFactory< T > factory; + private final double userAmplitudeThreshold; + private Img< BitType > amplitude; + + private final static Logger LOGGER = LoggerFactory.getLogger( MaximumAmplitudeClassification.class ); + public MaximumAmplitudeClassification( RandomAccessibleInterval< T > input, + ImgFactory< T > factory, Img< BitType > amplitude, + double userAmplitudeThreshold ) throws EmptyOutputException + { + this.input = input; + this.factory = factory; + this.amplitude = amplitude; + this.userAmplitudeThreshold = userAmplitudeThreshold; + LOGGER.info("User input = {}", userAmplitudeThreshold); + } + + /** + * @param max the image containing only the values of local maximums + * @param min the image containing only the values of local minimums + * @return an image containing the value of the maximums amplitude + */ + private Img< T > getAmplitude( + RandomAccessibleInterval< T > max, ImgFactory< T > factory, RandomAccessibleInterval< T > min ) + { + Img< T > amp = factory.create( max ); + RandomAccess< T > maxAccess = max.randomAccess(); + RandomAccess< T > minAccess = min.randomAccess(); + RandomAccess< T > ampAccess = amp.randomAccess(); + for ( int x = 0; x <= max.dimension( 0 ) - 1; x++ ) + { + maxAccess.setPosition( x, 0 ); + for ( int y = 0; y <= max.dimension( 1 ) - 1; y++ ) + { + maxAccess.setPosition( y, 1 ); + for ( int z = 0; z <= max.dimension( 2 ) - 1; z++ ) + { + maxAccess.setPosition( z, 2 ); + double maxValue = maxAccess.get().getRealDouble(); + if ( maxValue != 0 ) + { + minAccess.setPosition( maxAccess ); + double amplitude = getAmplitude( maxValue, minAccess, ( int ) max.dimension( 2 ) ); + ampAccess.setPosition( maxAccess ); + ampAccess.get().setReal( amplitude ); + } + } + } + } +// ImageJFunctions.show( amp, "amplitude" ); + return amp; + } + + /** + * @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 + * @return the chosen amplitude value of the local maximum positioned at minAccess location + */ + private double getAmplitude( double maxValue, RandomAccess< T > minAccess, int depth ) + { + double up = findValueUp( minAccess, maxValue ); + double down = findValueDown( minAccess, maxValue, depth ); + double result = Math.max( Math.abs( maxValue - up ), Math.abs( maxValue - down ) ); + if ( result == 0 ) + { + return maxValue; + + } + return ( result ); + } + + /** + * @param minAccess the random access of the local minimum image + * @param maxValue the intensity value of the local maximum located at minAccess position + * @return the amplitude value above the local maximum location in the Z dimension + */ + private double findValueUp( + RandomAccess< T > minAccess, double maxValue ) + { + int start = minAccess.getIntPosition( 2 ); + for ( int z = start - 1; z >= 0; z-- ) + { + minAccess.setPosition( z, 2 ); + double value = minAccess.get().getRealDouble(); + if ( value != 0 ) + { + return value; + } + } + return maxValue; + } + + /** + * @param minAccess the random access of the local minimum image + * @param maxValue the intensity value of the local maximum located at minAccess position + * @return the amplitude value below the local maximum location in the Z dimension + */ + private double findValueDown( + RandomAccess< T > minAccess, double maxValue, int depth ) + { + int start = minAccess.getIntPosition( 2 ); + for ( int z = start + 1; z <= depth - 1; z++ ) + { + minAccess.setPosition( z, 2 ); + double value = minAccess.get().getRealDouble(); + if ( value != 0 ) + { + return value; + } + } + return maxValue; + } + + public void runClassification() throws EmptyOutputException + { + Img< T > maximums = Util.findMaxima( input, factory ); + Img< T > minimums = Util.findMinima( input, factory ); + Img< T > amp = getAmplitude( maximums, factory, minimums ); + T TMax = Threshold.getFirstMaxValue( maximums, false ); + LOGGER.info( "threshold value before user = {} ", TMax ); + TMax.mul( userAmplitudeThreshold ); + LOGGER.info( "Threshold value after user = {}", TMax ); +// checkOutput( amp, TMax ); + amplitude = Thresholder.threshold( amp.copy(), TMax, true, 2 ); + } + + private void checkOutput( Img< T > amp, T finalThreshold ) throws EmptyOutputException + { + double min = findMin( amp, true ).getRealDouble(); + if ( min > finalThreshold.getRealDouble() ) + { + throw new EmptyOutputException( "The amplitude image is empty" ); + } + } + + public Img< BitType > getAmplitude() + { + return amplitude; + } +} + diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PixelClassification.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PixelClassification.java new file mode 100644 index 0000000000000000000000000000000000000000..d48f067caab643eb00dba995652542dbe38c7992 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PixelClassification.java @@ -0,0 +1,121 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * This class allows the double classification of a 3D image using in parallel a classification based on the amplitude + * of the image local maxima and a classification based on the local intensity. The final classification is the + * intersection of both. Each classification is parametrized by a value provided by the user. + * + * @param <T> + */ +public class PixelClassification< T extends RealType< T > & NativeType< T > > +{ + private final static Logger LOGGER = LoggerFactory.getLogger( PixelClassification.class ); + + + public static final int ZERO = 0; + Img< BitType > output; + + + + PixelClassification() {} + + /** + * @param input the 3D image to classified + * @param factory - the input {@link ImgFactory} + * @param parameters - the user parameters to set the classifications + */ + public void process( RandomAccessibleInterval< T > input, ImgFactory< T > factory, PixelClassificationParameters parameters ) throws EmptyOutputException, NoClassificationException + { + LOGGER.info("Starting classification..."); + double amplitudeThreshold = parameters.getAmplitudeThreshold(); + double otsuThreshold = parameters.getOtsuThreshold(); + if ( amplitudeThreshold != ZERO && otsuThreshold != ZERO ) + { + processBothClassification( input, factory, amplitudeThreshold, otsuThreshold ); + } + else if ( amplitudeThreshold != ZERO ) + { + processAmplitudeClassification( input, factory, amplitudeThreshold ); + } + else if ( otsuThreshold != ZERO ) + { + processOtsuClassification( input, factory, otsuThreshold ); + } + else + { + throw new NoClassificationException(); + } + LOGGER.info("Classification complete"); + } + + void processAmplitudeClassification( RandomAccessibleInterval< T > input, ImgFactory< T > factory, double amplitudeThreshold ) throws EmptyOutputException + { + /* Classification according to maximum amplitude */ + output = Util.runAmplitudeClassification( input, factory, amplitudeThreshold ); + } + + void processOtsuClassification( RandomAccessibleInterval< T > input, ImgFactory< T > factory, double otsuThreshold ) + { + /* Classification according to local intensity*/ + output = Util.runOtsuClassification( input, factory, otsuThreshold ); + } + + void processBothClassification (RandomAccessibleInterval< T > input, ImgFactory< T > factory, double amplitudeThreshold , double otsuThreshold ) throws EmptyOutputException + { + /* Classification according to maximum amplitude */ + + Img< BitType > amplitudeImg = Util.runAmplitudeClassification( input, factory, amplitudeThreshold ); + /* Classification according to local intensity*/ + + Img< BitType > otsuImg = Util.runOtsuClassification( input, factory, otsuThreshold ); + /* Intersection of both classification */ + this.output = classification( amplitudeImg, amplitudeImg.factory(), otsuImg ); + } + + + /** + * @param amplitude an {@link BitType}{@link Img} + * @param factory the same {@link BitType} {@link ImgFactory} as amplitude + * @param thresholds another {@link BitType}{@link Img} + * @return the intersection of both image + */ + private Img< BitType > classification( IterableInterval< BitType > amplitude, + ImgFactory< BitType > factory, + RandomAccessibleInterval< BitType > thresholds ) + { + Img< BitType > output = factory.create( thresholds ); + Cursor< BitType > amplitudeCursor = amplitude.localizingCursor(); + RandomAccess< BitType > thresholdAccess = thresholds.randomAccess(); + RandomAccess< BitType > outputAccess = output.randomAccess(); + while ( amplitudeCursor.hasNext() ) + { + amplitudeCursor.fwd(); + if ( amplitudeCursor.get().getRealDouble() != 0 ) + { + thresholdAccess.setPosition( amplitudeCursor ); + if ( thresholdAccess.get().get() ) + { + outputAccess.setPosition( amplitudeCursor ); + outputAccess.get().setOne(); + } + } + } + return output; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PostTreatment.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PostTreatment.java new file mode 100644 index 0000000000000000000000000000000000000000..7069ba6ebc0c4ef5965057b6c9bac19370f5419a --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PostTreatment.java @@ -0,0 +1,107 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.utils.Utils; +import fr.pasteur.ida.zellige.utils.islandSearch.IslandSearch; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PostTreatment +{ + private final static Logger LOGGER = LoggerFactory.getLogger( PostTreatment.class ); + + private Pixels[][] output; + + public PostTreatment() + { + } + + public void process( Img< BitType > classifiedPixel , PostTreatmentParameters parameters ) + { + LOGGER.info("Starting post treatment."); + /* Isolated Pixel removal */ + int islandSize = parameters.getIslandSize(); + if (islandSize != 0) + { + int connexity = parameters.getConnexity(); + classifiedPixel = runIslandSearch( classifiedPixel, islandSize, connexity ); + } + + /* Conversion into FloatType before dilatation */ + Img< FloatType > converted = Utils.convertBitTypeIntoFloatType( classifiedPixel ); + // Use of Converter.convert() not possible because classifiedPixel type do not extends RealType + + /* Dilatation of the resulting image*/ + runDilatation( converted, parameters ); +// double sigmaXY = parameters.getSigmaXY(); +// double sigmaZ = parameters.getSigmaZ(); +// Utils.gaussConvolution( converted.copy(), converted, new double[]{ sigmaXY, sigmaXY, sigmaZ } ); + + /* Final local maximum detection */ + converted = Util.findMaxima( converted.copy(), converted.factory() ); + output = buildPixelArray( converted ); + LOGGER.info("Post treatment complete"); + } + + Img< BitType > runIslandSearch(Img< BitType > input , int islandSize, int connexity) + { + return IslandSearch.run( input, islandSize, connexity ); + } + + void runDilatation( Img<FloatType> input, PostTreatmentParameters parameters ) + { + double sigmaXY = parameters.getSigmaXY(); + double sigmaZ = parameters.getSigmaZ(); + LOGGER.info( "Running dilatation with sigma XY = {} and sigma Z = {}", sigmaXY, sigmaZ); + Utils.gaussConvolution( input.copy(), input, new double[]{ sigmaXY, sigmaXY, sigmaZ } ); + } + + /** + * + * @param stack an image as a {@link RandomAccessibleInterval} + * @param <T> the image type + * @return the image as a 2D {@link Pixels} array. + */ + public < 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; + } + + public Pixels[][] getOutput() + { + return output; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/Pretreatment.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/Pretreatment.java new file mode 100644 index 0000000000000000000000000000000000000000..0539d4ace105ac5ea052d54773b2efac47aff357 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/Pretreatment.java @@ -0,0 +1,110 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import fr.pasteur.ida.zellige.utils.Filter2D; +import fr.pasteur.ida.zellige.utils.Utils; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.Converters; +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.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Pretreatment< T extends RealType< T > & NativeType< T > > +{ + private final static Logger LOGGER = LoggerFactory.getLogger( Pretreatment.class ); + + public static final String MEDIAN = "Median"; + public static final String GAUSSIAN_BLUR = "GaussianBlur"; + private final RandomAccessibleInterval< T > input; + private final PretreatmentParameters pretreatmentParameters; + private Img< FloatType > pretreatedImg; + + + Pretreatment( RandomAccessibleInterval< T > input, PretreatmentParameters pretreatmentParameters ) + { + this.input = input; + this.pretreatmentParameters = pretreatmentParameters; + } + + + void run() + { + LOGGER.info("Starting process..."); + // The input is converted into FloatType + RandomAccessibleInterval< FloatType > converted = Converters.convert( input, new RealFloatSamplerConverter<>() ); + LOGGER.info("Input converted."); + //The choose method of denoising is applied + Img< FloatType > denoised = denoisingStep( converted, this.pretreatmentParameters.getMethod() ); + // The denoised image is normalized between 0 and 255 + this.pretreatedImg = Utils.normalizeImage( denoised, denoised.factory() ); + LOGGER.info("Input normalized."); + LOGGER.info("Process complete."); + } + + /** + * Applies the denoising step on the specified input according to the specified method + * + * @param input the noisy image as a {@link RandomAccessibleInterval} + * @param denoisingMethod the denoising method + * @return a denoised image as a {@link Img} + */ + Img< FloatType > denoisingStep( RandomAccessibleInterval< FloatType > input, String denoisingMethod ) + { + LOGGER.info("Starting denoising step..."); + RandomAccessibleInterval< FloatType > converted = Converters.convert( input, new RealFloatSamplerConverter<>() ); + if ( denoisingMethod.equals( MEDIAN ) ) + { + LOGGER.info("Denoising step complete, with median filter"); + return medianFilterDenoising( converted ); + } + else// if ( denoisingMethod.equals( GAUSSIAN_BLUR ) ) + { + LOGGER.info("Denoising step complete, with gaussian filter"); + return gaussianBlurFilterDenoising( converted ); + } + + } + + /** + * Applies a 2D median filter to a noisy image + * + * @param input the noisy image as a {@link RandomAccessibleInterval} + * @return a denoised image as a {@link Img} + */ + Img< FloatType > medianFilterDenoising( RandomAccessibleInterval< FloatType > input ) + { + int radius = ( int ) pretreatmentParameters.getParameters()[ 0 ]; + return Filter2D.median( input, radius ); + } + + /** + * Applies a gaussian filter to a noisy image + * + * @param input the noisy image as a {@link RandomAccessibleInterval} + * @return a denoised image as a {@link Img} + */ + Img< FloatType > gaussianBlurFilterDenoising( RandomAccessibleInterval< FloatType > input ) + { + ImgFactory< FloatType > factory = new ArrayImgFactory<>( new FloatType() ); + double[] parameters = pretreatmentParameters.getParameters(); + //TODO (3 parameters or only 2 (x == y)) + return Utils.gaussConvolution( input, factory, parameters ); + } + + public RandomAccessibleInterval< T > getInput() + { + return input; + } + + + public Img< FloatType > getPretreatedImg() + { + return pretreatedImg; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/SurfacePixelSelection.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/SurfacePixelSelection.java new file mode 100644 index 0000000000000000000000000000000000000000..983eac5964964ffb5a6777a21f976b4c6c7d768b --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/SurfacePixelSelection.java @@ -0,0 +1,74 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +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 org.jzy3d.analysis.AnalysisLauncher; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class allows to get a set of classified and localised Pixels objects as a double array. + */ +public class SurfacePixelSelection +{ + private final static Logger LOGGER = LoggerFactory.getLogger( SurfacePixelSelection.class ); + + private final PretreatmentParameters pretreatmentParameters; + private final PixelClassificationParameters classificationParameters; + private final PostTreatmentParameters postTreatmentParameters; + private Pixels[][] maximums; + + public SurfacePixelSelection( PixelSelectionParameters parameters ) + { + this.pretreatmentParameters = parameters.getPretreatmentParameters(); + this.classificationParameters = parameters.getClassificationParameters(); + this.postTreatmentParameters = parameters.getPostTreatmentParameters(); + } + + public static < T extends RealType< T > & NativeType< T > >Pixels[][] + run(RandomAccessibleInterval< T > source,PixelSelectionParameters parameters ) throws EmptyOutputException, NoClassificationException + { + LOGGER.info("Starting selection..."); + SurfacePixelSelection selection = new SurfacePixelSelection ( parameters ); + selection.run( source ); + LOGGER.info("Selection complete"); + return selection.maximums; + } + + /** + * @param source - the pretreated image + */ + public < T extends RealType< T > & NativeType< T > >void run( RandomAccessibleInterval< T > source ) throws EmptyOutputException, NoClassificationException + { + /* Pretreatment of the image.*/ + LOGGER.info("Running Pretreatment..."); + Img< FloatType > pretreatedImage = Util.runPretreatment( source, pretreatmentParameters ); + + /* Classification. */ + LOGGER.info("Running Classification..."); + Img< BitType > classifiedPixel = Util.runClassification( pretreatedImage, pretreatedImage.factory(), classificationParameters ); +// ImageJFunctions.show( classifiedPixel ); + /* PostTreatment and output*/ + LOGGER.info("Running Post treatment..."); + maximums = Util.runPostTreatment( classifiedPixel, postTreatmentParameters ); +// displayMaximums( maximums ); + } + + public Pixels[][] getMaximums() + { + return maximums; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/Util.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/Util.java new file mode 100644 index 0000000000000000000000000000000000000000..b5a998c2ccf6ab770d04c60653ddff3a394d4ee7 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/Util.java @@ -0,0 +1,143 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +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 org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Util +{ + + private final static Logger LOGGER = LoggerFactory.getLogger( Util.class ); + /** + * + * @param input the image containing the extrema + * @param factory the input factory + * @param type the type of extrema to search (min for minimums or max for maximums) + * @param <T> the input type + * @return an image with extrema with original value and other pixels at zero value + */ + private static < T extends RealType< T > & NativeType< T > > Img< T > findExtrema( RandomAccessibleInterval< T > input, + ImgFactory< T > factory, String type ) + { + LocalExtremaDetection <T> detector = new LocalExtremaDetection<>( input, factory, type ); + detector.findExtrema(); + return detector.getExtrema(); + } + /** + * + * @param input the image containing the extrema + * @param factory the input factory + * @param <T> the input type + * @return an image with extrema with original value and other pixels at zero value + */ + public static < T extends RealType< T > & NativeType< T > > Img< T > findMinima( RandomAccessibleInterval< T > input, + ImgFactory< T > factory ) + { + LOGGER.info("Finding minima..."); + return findExtrema( input, factory, "min" ); + } + + /** + * + * @param input the image containing the extrema + * @param factory the input factory + * @param <T> the input type + * @return an image with extrema with original value and other pixels at zero value + */ + public static < T extends RealType< T > & NativeType< T > > Img< T > findMaxima( RandomAccessibleInterval< T > input, + ImgFactory< T > factory ) + { + LOGGER.info("Finding maxima..."); + return findExtrema( input, factory, "max" ); + } + + + /** + * @param <T> - the type of the input + * @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. + * @return a binary image of background foreground classification + */ + public static < T extends RealType< T > & NativeType< T > > Img< BitType > runAmplitudeClassification( final RandomAccessibleInterval< T > input, + final ImgFactory< T > factory, double amplitudeThreshold ) throws EmptyOutputException + { + // Prepare output. + Img< BitType > amplitude = new ArrayImgFactory<>( new BitType() ).create( input ); + MaximumAmplitudeClassification <T>classification = + new MaximumAmplitudeClassification<>( input, factory,amplitude, amplitudeThreshold ); + classification.runClassification(); + return classification.getAmplitude(); + } + + + /** + * 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 EmptyOutputException if the binary image is empty. + */ + public static < T extends RealType< T > & NativeType< T > > Img< BitType > + runClassification( RandomAccessibleInterval< T > input, ImgFactory< T > factory, PixelClassificationParameters parameters ) throws EmptyOutputException, NoClassificationException + { + PixelClassification< T > classification = new PixelClassification<>( ); + classification.process( input, factory, parameters ); + return classification.output; + } + + /** + * @param input the input {@link Img} + * @param percent the percentage of global otsu value + * @param <T> the type on the input + * @return a 3D binary {@link Img<BitType>} + */ + public static < T extends RealType< T > & NativeType< T > > Img< BitType > runOtsuClassification( final RandomAccessibleInterval< T > input, ImgFactory< T > factory, double percent ) + { + if (percent > 0) + { + // Prepare output. + final ImgFactory< BitType > bitTypeImgFactory = net.imglib2.util.Util.getArrayOrCellImgFactory( input, new BitType() ); + Img< BitType > binary = bitTypeImgFactory.create( input ); + LocalOtsuClassification <T> classification = new LocalOtsuClassification<>(input, factory, binary,percent); + classification.run(); + + return classification.getGrid(); + } + else + { + return null; + } + } + + public static < T extends RealType< T > & NativeType< T > > Img< FloatType > + runPretreatment( RandomAccessibleInterval< T > input, PretreatmentParameters pretreatmentParameters) + { + Pretreatment<T> pretreatment = new Pretreatment<>( input, pretreatmentParameters ); + pretreatment.run(); + return pretreatment.getPretreatedImg(); + } + + public static Pixels[][] runPostTreatment( Img< BitType > input, PostTreatmentParameters parameters) + { + PostTreatment postTreatment = new PostTreatment(); + postTreatment.process( input, parameters ); + return postTreatment.getOutput(); + } + +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstruction.java new file mode 100644 index 0000000000000000000000000000000000000000..54e11df77e22a9e22ff20d3d6115b6e73d48626a --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstruction.java @@ -0,0 +1,243 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.ose; + +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.OSEStartingStatus; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; + +public abstract class OseConstruction +{ + + private final Pixels[][] maximums; + private final OSEListArray oseListArray; + private final OSEStartingStatus startingStatus; + + private final static Logger LOGGER = LoggerFactory.getLogger( OseConstruction.class ); + + + public OseConstruction( Pixels[][] maximums , double threshold) + { + this.maximums = maximums; + this.startingStatus = new OSEStartingStatus( threshold); + this.oseListArray = new OSEListArray( maximums.length ); + } + + public void processAllSection() + { + LOGGER.info( "Starting Ose construction..." ); + for ( int i = 0; i <= maximums.length - 1; i++ ) + { + oseListArray.set(i, findOSE( maximums[ i ] )); + } + startingStatus.setStartingStatus(); + System.out.println( "Starting size = " + startingStatus.getStartingSize()); + LOGGER.info( "Ose construction complete. All sections processed." ); + } + + /** + * This method finds all the possible 2D surfaces that can be created from the coordinates detected as + * local maximums on a array of {@link }. + * + * @param rawCoordinates - the raw Coordinates from the projection. + * @return all the orthogonal surfaces for the projection as an {@link ArrayList <AbstractOSE>}. + */ + private OSEList findOSE( Pixels[] rawCoordinates ) + { + // beware reset dimension if dimension 2 + ArrayList< Coordinate > startingCoordinates = checkForSideCoordinates( rawCoordinates ); + OSEList paths = findSimplePaths( startingCoordinates, rawCoordinates ); + OSEList finalPaths = findComplexPaths( paths ); + for ( AbstractOSE ose : finalPaths ) + { + ose.set(); + } + finalPaths.reset(); + return finalPaths; + } + + public abstract OSEList findSimplePaths( ArrayList< Coordinate > startingCoordinates, Pixels[] rawCoordinates ); + + /** + * @param rawCoordinates - the raw Coordinates from the projection. + * @param smallPath - the one-Z-AbstractOSE + * @param ose - the first AbstractOSE considered. + * @param current - the starting {@link Coordinate} considered. + */ + void findSimplePaths + ( Pixels[] rawCoordinates, + ArrayList< AbstractOSE > smallPath, AbstractOSE ose, Coordinate current, Queue< Coordinate > queue ) + { + int i = ose.startingIndex( current ); + while ( i <= rawCoordinates.length - 2 // x must be < to the length of the image minus 1 because [X + 1] + && rawCoordinates[ i ] != null // the contents in the array at index x must not be null + && rawCoordinates[ i + 1 ] != null // the contents in the array at index (x +1) must not be null + && current != null ) + { + Coordinate next = current.getNext( rawCoordinates[ i + 1 ], 0 ); + if ( next != null )// Conditions : |x1 - x2 |= 1 => only 1D surface + { + ose.add( next ); + } + else + { + if ( ( i ) < rawCoordinates.length - 3 + && rawCoordinates[ i + 2 ] != null// the contents in the array at index (x +1) must not + // be null + && current.getRightNumber() == 0 ) + { + next = current.getNext( rawCoordinates[ i + 2 ], 0 ); + if ( next != null )//fake coordinate + { + ose.createCoordinate(current); + + ose.add( next ); + queue.remove( next ); + i++; + } + } + } + current = next; + i++; //increment of the index + } + smallPath.add( ose ); + } + + /** + * @param shortPaths - the list of one-z-AbstractOSE. + * @return - the list of "big paths" AbstractOSE. + */ + private OSEList findComplexPaths( OSEList shortPaths ) + { + OSEList paths = new OSEList(); + Queue< AbstractOSE > queue = new LinkedList<>( shortPaths ); + while ( ! queue.isEmpty() ) + { + AbstractOSE first = queue.remove(); + shortPaths.remove( first ); + findComplexPaths( first, shortPaths, queue, paths ); + } + return paths; + } + + /** + * @param first - the starting AbstractOSE. + * @param smallPaths - the list of all small path AbstractOSE. + * @param queue - the remaining AbstractOSE. + * @param longPaths - the list of long path AbstractOSE. + */ + private void findComplexPaths( AbstractOSE first, + ArrayList< AbstractOSE > smallPaths, + Queue< AbstractOSE > queue, ArrayList< AbstractOSE > longPaths ) + { + for ( AbstractOSE o : smallPaths ) + { + if ( first.isNextTo( o )) + { + first.addAll( o ); + o.setVisited( true ); + queue.remove( o ); + } + } + smallPaths.removeIf( AbstractOSE::isVisited ); + longPaths.add( first ); + } + + /** + * Stores the coordinates that don't have a coordinate to the left. + * + * @param slice - a one-dimensional array containing the local maximums. + * @return - a list of coordinates that don't have a coordinate to the left. + */ + ArrayList< Coordinate > checkForSideCoordinates( Pixels[] slice ) + { + /* Stores the coordinates that have no coordinates to the left.*/ + ArrayList< Coordinate > noLeftCoordinates = new ArrayList<>(); + /* Starting point. */ + int start = 0; + while ( start <= slice.length - 3 && slice[ start ] == null ) + { + start++;//the index starts where a List of Coordinates is found in the array + } + if ( slice[ start ] != null && slice[ start + 1 ] != null ) //some Coordinates were found + { + /* For the first list of coordinates. */ + for ( int i = 0; i <= slice[ start ].size() - 1; i++ )// the first coordinates of the array have obviously no left coordinates + { + Coordinate coordinate = slice[ start ].get( i ); + noLeftCoordinates.add( coordinate ); + coordinate.setRightCoordinates( slice[ start + 1 ] ); + } + } + /* For the rest. */ + for ( int i = start + 1; i <= slice.length - 2; i++ ) + { + if ( slice[ i ] != null ) + { + for ( Coordinate coordinate : slice[ i ].get() ) + { + coordinate.setRightCoordinates( slice[ i + 1 ] ); + coordinate.setLeftCoordinates( slice[ i - 1 ] ); + boolean hasNoLeft = coordinate.numberOfNeighbours( slice[ i - 1 ], 0 ) == 0; + if ( hasNoLeft ) + { + noLeftCoordinates.add( coordinate ); + } + } + } + } + /* the last one */ //Avoid ArrayOutOfBoundException + int end = slice.length - 1; + if ( slice[ end ] != null && slice[ end - 1 ] != null ) + { + for ( Coordinate coordinate : slice[ end ].get() ) + { + coordinate.setLeftCoordinates( slice[ end - 1 ] ); + boolean hasNoLeft = coordinate.numberOfNeighbours( slice[ end - 1 ], 0 ) == 0; + if ( hasNoLeft ) + { + noLeftCoordinates.add( coordinate ); + } + } + } + return noLeftCoordinates; + } + +// /** +// * Resets the parameters of each {@link Coordinate } for a given {@link Pixels} array +// * before the Y dimension reconstruction ( rightsNumber , leftsNumber, noLeft, queue ). +// * +// * @param slice - the {@link Pixels} array considered. +// */ +// private void reset( Pixels[] slice ) +// { +// for ( Pixels pixels : slice ) +// { +// if ( pixels != null ) +// { +// pixels.resetSideCoordinate(); +// } +// } +// } + + public Pixels[][] getMaximums() + { + return maximums; + } + public OSEListArray getOseListArray() + { + return oseListArray; + } + + public OSEStartingStatus getStartingStatus() + { + return startingStatus; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstructionXZ.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstructionXZ.java new file mode 100644 index 0000000000000000000000000000000000000000..586310c1f0ef1fa06239daa4d5bf4c90d76519c6 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstructionXZ.java @@ -0,0 +1,49 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.ose; + +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.MaximumAmplitudeClassification; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OseXZ; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; + +public class OseConstructionXZ extends OseConstruction +{ + + private final static Logger LOGGER = LoggerFactory.getLogger( OseConstructionXZ.class ); + public static OSEListArray runConstruction( Pixels[][] maximums, double threshold) + { + LOGGER.info("Starting XZ construction"); + OseConstructionXZ constructionXZ = new OseConstructionXZ( maximums, threshold ); + constructionXZ.processAllSection(); + return constructionXZ.getOseListArray(); + } + + public OseConstructionXZ( Pixels[][] maximums, double threshold ) + { + super( maximums, threshold ); + } + + @Override + public OSEList findSimplePaths( ArrayList< Coordinate > startingCoordinates, Pixels[] rawCoordinates ) + { + Queue< Coordinate > firstQueue = new LinkedList<>( startingCoordinates );// Add the coordinates with no left coordinates in + // the queue + OSEList smallPath = new OSEList(); + while ( ! firstQueue.isEmpty() ) + { + Coordinate current = firstQueue.remove(); + AbstractOSE ose = new OseXZ( this.getStartingStatus() ); + ose.add( current ); + findSimplePaths( rawCoordinates, smallPath, ose, current, firstQueue ); + } + return smallPath; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstructionYZ.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstructionYZ.java new file mode 100644 index 0000000000000000000000000000000000000000..dfc1aaa0739a470d77d64f3417a62b5d83625336 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/ose/OseConstructionYZ.java @@ -0,0 +1,69 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.ose; + +import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OseYZ; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.Queue; + +public class OseConstructionYZ extends OseConstruction +{ + private final static Logger LOGGER = LoggerFactory.getLogger( OseConstructionYZ.class ); + public OseConstructionYZ( Pixels[][] maximums, double threshold ) + { + super( maximums, threshold ); + } + + + public static OSEListArray runConstruction( Pixels[][] maximums, double threshold) + { + LOGGER.info("Starting YZ construction"); + OseConstructionYZ constructionYZ = new OseConstructionYZ( maximums, threshold ); + constructionYZ.reset(); + constructionYZ.processAllSection(); + + return constructionYZ.getOseListArray(); + } + + @Override + public OSEList findSimplePaths( ArrayList< Coordinate > startingCoordinates, Pixels[] rawCoordinates ) + { + Queue< Coordinate > firstQueue = new LinkedList<>( startingCoordinates );// Add the coordinates with no left coordinates in + // the queue + OSEList smallPath = new OSEList(); + while ( ! firstQueue.isEmpty() ) + { + Coordinate current = firstQueue.remove(); + AbstractOSE ose = new OseYZ( this.getStartingStatus() ); + ose.add( current ); + findSimplePaths( rawCoordinates, smallPath, ose, current, firstQueue ); + } + return smallPath; + } + + /** + * Resets the parameters of each {@link Coordinate } for a given {@link Pixels} array + * before the Y dimension reconstruction ( rightsNumber , leftsNumber, noLeft, queue ). + * + */ + private void reset() + { + + for ( Pixels [] pixelLine : this.getMaximums() ) + { + for (Pixels pixels : pixelLine) + if ( pixels != null ) + { + pixels.resetSideCoordinate(); + } + } + LOGGER.info( "Ose reset" ) ; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/surface/AllSurfaceConstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/surface/AllSurfaceConstruction.java new file mode 100644 index 0000000000000000000000000000000000000000..2478f9c9f2f2d222b01bd5846fc85b52506a48b3 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/surface/AllSurfaceConstruction.java @@ -0,0 +1,164 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.surface; + + +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.*; + +import java.util.ArrayList; + + +/** + * + */ +public class AllSurfaceConstruction +{ + + private final ArrayList< Surface > surfaces = new ArrayList<>(); + private final OSEListArray oseListArray; + private final int width; + private final double connexity; + private final int overlap; + + private int smallSurfaceCount; + + public AllSurfaceConstruction( OSEListArray oseLists, int width, int overlap, double connexity ) + { + this.oseListArray = oseLists; + this.width = width; + this.connexity = connexity; + this.overlap = overlap; + } + + public static ArrayList< Surface > run( OSEListArray oseListArray, int surfaceLineLength, int overlap, double connexity ) + { + AllSurfaceConstruction reconstruction = new AllSurfaceConstruction( oseListArray, surfaceLineLength, overlap, connexity ); + reconstruction.buildAllSurfaces2(); + return reconstruction.surfaces; + } + + + /** + * Returns a list of TempSurface constructed from the specified OSList array. + */ +// public void buildAllSurfaces() +// { +// // Construction of the list of output TempSurface. +// smallSurfaceCount = 0; +// int finalIndex = findIndexValue( 0 ); +// do +// { +// // All the OSLists are set to "not visited". +// reset(); +// int startingIndex = finalIndex; +// AbstractOSE firstOSE = getFirstOs( finalIndex ); +// if ( firstOSE != null ) +// { +// Surface surface = OneSurfaceConstruction.run( oseLists, firstOSE, width, overlap, connexity ); +// checkSurface( surface ); +// finalIndex = findIndexValue( startingIndex ); +// } +// } +// while ( finalIndex <= oseLists.length - 2 +// && ( oseLists[ finalIndex ] != null +// && ! oseLists[ finalIndex ].isEmpty() +// && oseLists[ finalIndex ].containsAStart() ) ); +// System.out.println( "smallSurfaceCount = " + smallSurfaceCount ); +// } + + public void buildAllSurfaces2() + { + while ( true ) + { + // All the OSLists are set to "not visited". + AbstractOSE startingOse = oseListArray.getAStartingOse(); + if ( startingOse != null ) + { + Surface surface = OneSurfaceConstruction.run( oseListArray, startingOse, width, overlap, connexity ); + checkSurface( surface ); + } + else + { + break; + } + } + System.out.println( "smallSurfaceCount = " + smallSurfaceCount ); + } + +// /** +// * Returns the index value of the first OSList containing a starting OS. +// * +// * @param index - the previous index value +// * @return the value of the first OSList containing a starting OS +// */ +// private int findIndexValue( int index ) +// { +// int i = index; +// int limitValue; +// limitValue = oseLists.length - 1; +// while ( i <= limitValue - 2 ) +// { +// if ( oseLists[ i ] != null +// && ! oseLists[ i ].isEmpty() +// && oseLists[ i ].containsAStart() ) +// { +// return i; +// } +// i++; +// } +// index = limitValue; +// return index; +// } + +// /** +// * Returns the first OS with a start status in the OSList array. +// * +// * @param index the previous index value +// * @return the first OS with a true Start status +// */ +// private AbstractOSE getFirstOs( int index ) +// { +// for ( AbstractOSE os : oseLists[ index ] ) +// { +// if ( os.isAStart() ) +// { +// os.setVisited(); +// return os; +// } +// } +// return null; +// } + + /** + * Surface validation check. if the current surface is valid it will be added to the surfaces list. + * + * @param surface the surface to check. + */ + private void checkSurface( Surface surface ) + { + if ( surface.getSize() >= width * oseListArray.getLength() * 0.01 ) + { + surfaces.add( surface ); + } + else + { + smallSurfaceCount++; + System.out.println( "searching.....from " ); + } + } + +// /** +// * Resets the "visited" value of each OS contained in each OSList to FALSE. +// */ +// private void reset() +// { +// for ( OSEList oseList : oseLists ) +// { +// oseList.reset(); // empty list instead of null +// } +// } + + public int getSmallSurfaceCount() + { + return smallSurfaceCount; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/surface/OneSurfaceConstruction.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/surface/OneSurfaceConstruction.java new file mode 100644 index 0000000000000000000000000000000000000000..65630020f436d06a5567524dc2e63eeb590a679a --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/surface/OneSurfaceConstruction.java @@ -0,0 +1,200 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.surface; + +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; + +import java.util.ArrayList; + +public class OneSurfaceConstruction +{ + + private final OSEListArray oseLists; + private final AbstractOSE firstOSE; + private final Surface surface; + private final int overlap; + private final double connexity; + + + + public static Surface run(OSEListArray oseListArray, AbstractOSE firstOSE, int width, int overlap, double connexity ) + { + OneSurfaceConstruction construction = new OneSurfaceConstruction( oseListArray, firstOSE, width, overlap, connexity ); + construction.set(); + construction.build(); + return construction.surface; + } + + + public OneSurfaceConstruction( OSEListArray oseListArray, AbstractOSE firstOSE, int width, int overlap, double connexity ) + { + this.oseLists = oseListArray; + this.firstOSE = firstOSE; + int height = oseListArray.getLength(); + this.surface = new Surface( width,height ); + this.overlap = overlap; + this.connexity = connexity; + } + + + + private void set() + { + firstOSE.setFirstOSE( surface ); + } + + private void build() + { + SurfaceLine next; + /* Sweep until no more OSE can be added to the current surface.*/ + int size = 0; + while ( size != surface.getSize() ) + { + size = surface.getSize(); + for ( int i = surface.getHeight() - 1; i > 0; i-- ) + { + if ( surface.get( i ) != null ) + { + SurfaceLine previous = searchBackward( surface.get( i ) ); + if ( previous != null ) + { + surface.set( i - 1, previous ); + } + } + } + for ( int i = 0; i <= surface.getHeight() - 2; i++ ) + { + if ( surface.get( i ) != null ) + { + next = searchForward( surface.get( i ) ); + if ( next != null ) + { + surface.set( i + 1, next ); + } + } + } + } + } + + + /** + * Returns the SurfaceLine matching the current in a specific direction + * + * @param next - the OSList to search into + * @param currentLine - the {@link SurfaceLine } to match. + * @param j - the direction of the search (1 or -1) + * @return the OSList matching SurfaceLine + */ + private SurfaceLine search( OSEList next, SurfaceLine currentLine, int j ) + { + ArrayList< SurfaceLine > list = new ArrayList<>(); + for ( AbstractOSE os : next ) + { + if ( ! os.isVisited() ) // The OS is already added to the referenceSurface + { + SurfaceLine line = currentLine.match( os, j, overlap, connexity ); + if ( line != null ) + { + list.add( line ); + } + } + } + if ( list.isEmpty() ) + { + return null; + } + merge( list ); + return list.get( 0 ); + } + + private SurfaceLine search2( OSEList next, SurfaceLine currentLine, int j ) + { + ArrayList< SurfaceLine > list = new ArrayList<>(); + for ( AbstractOSE os : next ) + { + if ( ! os.isVisited() ) // The OS is already added to the referenceSurface + { + SurfaceLine line = currentLine.match( os, j, overlap, connexity ); + if ( line != null && line.isInAdequacyWith( surface.get(currentLine.getLine() + j))) + { + os.setVisited(); + surface.set( currentLine.getLine() + j, line );// the line is directly added to the surface + } + } + } + if ( list.isEmpty() ) + { + return null; + } + merge( list ); + return list.get( 0 ); + } + + + + /** + * Returns the SurfaceLine generated from the specified OSList array matching the current SurfaceLine + * + * @param current - the {@link SurfaceLine } to match + * @return the SurfaceLine generated from the specified OSList array matching the current SurfaceLine + */ +// private SurfaceLine searchForward( SurfaceLine current ) +// { +// if ( current.getLine() + 1 <= oseLists.length - 1 ) +// { +// return search( oseLists[ current.getLine() + 1 ], current, 1 ); +// } +// return null; +// } + + private SurfaceLine searchForward( SurfaceLine current ) + { + if ( current.getLine() + 1 <= oseLists.getLength() - 1 ) + { + return search2( oseLists.get( current.getLine() + 1 ), current, 1 ); + } + return null; + } + + /** + * @param current - the current SurfaceLine to match with. + * @return - the matching SurfaceLine. + */ +// private SurfaceLine searchBackward( SurfaceLine current ) +// { +// if ( current.getCoordinatesNumber() != 0 && current.getLine() > 0 ) +// { +// return search( oseLists[ current.getLine() - 1 ], current, - 1 ); +// } +// return null; +// } + + private SurfaceLine searchBackward( SurfaceLine current ) + { + if ( current.getCoordinatesNumber() != 0 && current.getLine() > 0 ) + { + return search2( oseLists.get( current.getLine() - 1 ), current, - 1 ); + } + return null; + } + + /** + * Merges the SurfaceLine objects in the specified list. + * + * @param list - the list to merge. + */ + private void merge( ArrayList< SurfaceLine > list ) + { + if ( list.size() >= 2 ) //more than one SurfaceLines + { + int index = list.size() - 1; + while ( list.size() != 1 ) + { + list.get( index - 1 ).merge( list.remove( index ) ); + index--; + } + } + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ReferenceSurface.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ReferenceSurface.java index d2462d8b0646e05210f2e09c3e742db314a27bf8..6d49c8ff212694babbd9c38d76c5d98655d5aafb 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ReferenceSurface.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ReferenceSurface.java @@ -44,6 +44,7 @@ public class ReferenceSurface< T extends RealType< T > & NativeType< T > > projectionType.equals( "Minimum Intensity" ) ) { elevationMap = StackProjection.getElevationMap ( input, zMap, projectionType, delta ); +// ImageJFunctions.show( elevationMap, "elevationMap" ); projection = StackProjection.projection1( input,factory, elevationMap, projectionType ); } else @@ -83,6 +84,10 @@ public class ReferenceSurface< T extends RealType< T > & NativeType< T > > } } + public Img< UnsignedShortType > getZMap() + { + return zMap; + } } diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/Surface.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/Surface.java index 5dee004e442256035a7d4feff2aa47c10d4b2bd1..813b46c361090f8358a27d0cce7fa2df4a98adb6 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/Surface.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/Surface.java @@ -1,6 +1,5 @@ package fr.pasteur.ida.zellige.surfaceConstruction.element; -import fr.pasteur.ida.zellige.surfaceConstruction.construction.ReferenceSurfaceExtraction; import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLineX; import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLineY; @@ -9,7 +8,6 @@ import net.imglib2.FinalDimensions; import net.imglib2.RandomAccess; import net.imglib2.img.Img; import net.imglib2.img.ImgFactory; -import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.numeric.integer.UnsignedShortType; import net.imglib2.util.Util; @@ -19,15 +17,16 @@ public class Surface { private final SurfaceLine[] surfaceLines; - private final int dimension; + private final int width, height; - - public Surface( int dimension, int length ) + public Surface( int width, int height ) { - this.dimension = dimension; - this.surfaceLines = new SurfaceLine[ length ]; + this.width = width; + this.height = height; + this.surfaceLines = new SurfaceLine[ height ]; } +// /** * Returns true if the instance has a {@link Pixels }which the size is superior to 1 (at least 2{@link Coordinate}) @@ -99,6 +98,37 @@ public class Surface return ( r ) > 0.70; } + public double overlappingRate( Surface other ) + { + int inCommon = 0; + int count = 0; + for ( int i = 0; i <= this.getHeight() - 1; i++ ) + { + SurfaceLine refLine = this.get( i ); + if ( refLine != null ) + { + for ( int j = 0; j < refLine.getLength(); j++ ) + { + Pixels refPixels = refLine.get( j ); + if ( refPixels != null ) + { + count++; + SurfaceLine toTest = other.get(i); + + if ( toTest != null && refPixels.equals( other.get( i ).get( j ) )) + { + inCommon++; + } + } + } + + } + + } + return inCommon / (double) count; + } + + /** * Checks if this object shares a majority of {@link Pixels} with an other {@link Surface} object. @@ -147,20 +177,12 @@ public class Surface * * @return the identical transposed {@link Surface} */ - public Surface transpose( int length ) + public Surface transpose() { - Surface surface = new Surface( Math.abs( this.dimension - 1 ), length ); - Class< ? > theClass; - - if ( this.dimension == 0 ) - { - theClass = SurfaceLineY.class; - } - else - { - theClass = SurfaceLineX.class; - } - + int height = this.getWidth(); + int width = this.getHeight(); + Surface surface = new Surface( width, height ); + Class< ? > theClass = getTransposedClass(); for ( int i = 0; i <= this.getHeight() - 1; i++ ) { SurfaceLine surfaceLine = this.get( i ); @@ -176,7 +198,7 @@ public class Surface { try { - newSurfaceLine = ( SurfaceLine ) theClass.getDeclaredConstructors()[ 1 ].newInstance( length, j ); + newSurfaceLine = ( SurfaceLine ) theClass.getDeclaredConstructors()[ 1 ].newInstance( width, j ); } catch ( InstantiationException | IllegalAccessException | InvocationTargetException e ) { @@ -215,21 +237,27 @@ public class Surface } /** - * Sets the specified {@link SurfaceLine} at the specified index position into the {@link SurfaceLine} array of - * the instance. + * Sets the specified {@link SurfaceLine} at the specified index position. If the Surfaline at the specified index + * not null, the both are merged. * * @param index the position of the SurfaceLine * @param surfaceLine the SurfaceLine object to set */ public void set( int index, SurfaceLine surfaceLine ) { - this.surfaceLines[ index ] = surfaceLine; + if ( this.surfaceLines[ index ] != null ) + { + surfaceLines[ index ].merge( surfaceLine ); + } + else + { + this.surfaceLines[ index ] = surfaceLine; + } } public Img< UnsignedShortType > getZMap() { -// ReferenceSurfaceExtraction.displaySurface( this ); int count = 0; Dimensions dim = FinalDimensions.wrap( new long[]{ getHeight(), getWidth() } ); ImgFactory< UnsignedShortType > factory = Util.getArrayOrCellImgFactory( dim, new UnsignedShortType() ); @@ -306,21 +334,45 @@ public class Surface } - public int getWidth() + public Class< ? > getTransposedClass() { - for ( SurfaceLine surfaceLine : surfaceLines - ) + + for ( SurfaceLine surfaceLine : surfaceLines ) { if ( surfaceLine != null ) { - return surfaceLine.getLength(); + return surfaceLine.getClass() == SurfaceLineX.class ? SurfaceLineY.class : SurfaceLineX.class; } } - return 0; + return null; + } + + + public int getWidth() + { + return width; } public int getHeight() { - return this.surfaceLines.length; + return height; + } + + @Override + public boolean equals( Object obj ) + { + if ( this == obj ) + { + return true; + } + if ( obj == null || getClass() != obj.getClass() ) + { + return false; + } + Surface other = (Surface ) obj; + { + System.out.println(overlappingRate( other )); + return overlappingRate( other ) == 1; + } } } diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSE.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/AbstractOSE.java similarity index 60% rename from src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSE.java rename to src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/AbstractOSE.java index eeddbe41f8c6d65723c4691953535537e58d2670..8dca542e75d440a0419c34dbe5fefc492344aeb3 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSE.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/AbstractOSE.java @@ -1,15 +1,14 @@ package fr.pasteur.ida.zellige.surfaceConstruction.element.ose; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.OSEStartingStatus; import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; -import java.util.*; +import java.util.ArrayList; - -/** - * OS stands for Orthogonal Surface Element which is list of ordered Coordinates being part of the same 1D surface. - */ -public class OSE extends ArrayList< Coordinate > +public abstract class AbstractOSE extends ArrayList< Coordinate > { + public static int sizeSum = 0; /* Not necessary for the program.*/ private static int count = 0; @@ -26,7 +25,7 @@ public class OSE extends ArrayList< Coordinate > */ private boolean start = true; - public OSE( OSEStartingStatus startingStatus ) + public AbstractOSE( OSEStartingStatus startingStatus ) { this.startingStatus = startingStatus; } @@ -56,27 +55,32 @@ public class OSE extends ArrayList< Coordinate > * Test if the current instance is adjacent to the next OS instance. * * @param next - the other OS instance. - * @param dimension - the dimension in witch the OS has been constructed. * @return true if the two OS are next to each other, false otherwise. */ - public boolean isNextTo( OSE next, int dimension ) + public abstract boolean isNextTo( AbstractOSE next ); { - if ( dimension == 0 ) - { - return ( next.get( 0 ).getX() - this.get( this.size() - 1 ).getX() == 1 // the two OS are next to each other - && this.get( this.size() - 1 ).getRightNumber() == 1 // this OS has only one possible right coordinate - && next.get( 0 ).getLeftNumber() == 1 // the next OS has only one possible left coordinate - && this.get( this.size() - 1 ).isNext( next.get( 0 ), 1 ) );// the difference between the z values equals 1 - } - else - { - return ( next.get( 0 ).getY() - this.get( this.size() - 1 ).getY() == 1 // the two OS are next to each other - && this.get( this.size() - 1 ).getRightNumber() == 1 // this OS has only one possible right coordinate - && next.get( 0 ).getLeftNumber() == 1 // the next OS has only one possible left coordinate - && this.get( this.size() - 1 ).isNext( next.get( 0 ), 1 ) );// the difference between the z values equals 1 - } +// if ( dimension == 0 ) +// { +// return ( next.get( 0 ).getX() - this.get( this.size() - 1 ).getX() == 1 // the two OS are next to each other +// && this.get( this.size() - 1 ).getRightNumber() == 1 // this OS has only one possible right coordinate +// && next.get( 0 ).getLeftNumber() == 1 // the next OS has only one possible left coordinate +// && this.get( this.size() - 1 ).isNext( next.get( 0 ), 1 ) );// the difference between the z values equals 1 +// } +// else +// { +// return ( next.get( 0 ).getY() - this.get( this.size() - 1 ).getY() == 1 // the two OS are next to each other +// && this.get( this.size() - 1 ).getRightNumber() == 1 // this OS has only one possible right coordinate +// && next.get( 0 ).getLeftNumber() == 1 // the next OS has only one possible left coordinate +// && this.get( this.size() - 1 ).isNext( next.get( 0 ), 1 ) );// the difference between the z values equals 1 +// } } + public abstract void createCoordinate( Coordinate coordinate ); + + public abstract void setFirstOSE( Surface surface); + + public abstract int startingIndex(Coordinate current); + /** * Returns true if the OS has been visited. * @@ -132,13 +136,13 @@ public class OSE extends ArrayList< Coordinate > */ public boolean isAStart() { - return ( this.size() >= this.startingStatus.getMinimumSize() && start ); + return ( this.size() >= this.startingStatus.getStartingSize() && start ); } + public abstract int getCoordinate(int index); /* Not necessary for the program.*/ public String toString() { return ( " " + name ); } - } diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEList.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEList.java index c1c69856fc4ffbaff319ea3cb47e10b3cf8edf3f..7cd25e829bd9cca6e2e41641bd0523e4bcd5f72e 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEList.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEList.java @@ -6,16 +6,16 @@ import java.util.Collection; /** * An OSList object is simply an ArrayList of {@link OSE} */ -public class OSEList extends ArrayList < OSE > +public class OSEList extends ArrayList< AbstractOSE > { - public OSEList( ) + public OSEList() { } @Override - public boolean add( OSE os ) + public boolean add( AbstractOSE os ) { if ( this.contains( os ) ) { @@ -28,52 +28,37 @@ public class OSEList extends ArrayList < OSE > } @Override - public boolean addAll( Collection < ? extends OSE > c ) + public boolean addAll( Collection< ? extends AbstractOSE > c ) { boolean add = false; - for ( OSE os : c ) + for ( AbstractOSE os : c ) { add = add( os ); } return add; } - - public int getSize( ) + public int getSize() { int size = 0; - for ( OSE os : this ) + for ( AbstractOSE os : this ) { size = size + os.size(); } return size; } -// public boolean isVisited() -// { -// boolean visited = true; -// for ( OS os : this -// ) -// { -// if ( !os.isVisited() ) -// { -// visited = false; -// } -// } -// return visited; -// } - - public void reset( ) + public void reset() { - for ( OSE os : this ) + for ( AbstractOSE os : this ) { os.setVisited( false ); } } - public boolean containsAStart( ) + public boolean containsAStart() { - for ( OSE os : this ) + for ( AbstractOSE os : this ) { if ( os.isAStart() ) { diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEListArray.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEListArray.java new file mode 100644 index 0000000000000000000000000000000000000000..ccb6eb85d207f54b49e429b89403a8884c216be0 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEListArray.java @@ -0,0 +1,89 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.element.ose; + +public class OSEListArray +{ + + private final OSEList [] oseLists; + private int index = 0; + + public OSEListArray( int length ) + { + this.oseLists = new OSEList[length]; + } + + /** + * Returns the index value of the first OSList containing a starting OS. + * + * @param index - the previous index value + * @return the value of the first OSList containing a starting OS + */ + private int findIndexValue( int index ) + { + int i = index; + int limitValue; + limitValue = oseLists.length - 1; + while ( i <= limitValue - 2 ) + { + if ( oseLists[ i ] != null + && ! oseLists[ i ].isEmpty() + && oseLists[ i ].containsAStart() ) + { + return i; + } + i++; + } + index = limitValue; + return index; + } + + /** + * Returns the first OS with a start status in the OSList array. + * + * @return the first OS with a true Start status + */ + public AbstractOSE getAStartingOse() + { + reset(); + index = findIndexValue( index ); + for ( AbstractOSE os : oseLists[ index ] ) + { + if ( os.isAStart() ) + { + os.setVisited(); + return os; + } + } + return null; + } + + /** + * Resets the "visited" value of each OS contained in each OSList to FALSE. + */ + public void reset() + { + for ( OSEList oseList : oseLists ) + { + oseList.reset(); // empty list instead of null + } + } + + public void set(int i, OSEList oseList) + { + this.oseLists[i] = oseList; + } + + public OSEList get(int index) + { + return oseLists[index]; + } + + public int getLength() + { + return oseLists.length; + } + + public OSEList[] getOseLists() + { + return oseLists; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEStartingStatus.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEStartingStatus.java deleted file mode 100644 index be3f4c2906574bb0fac1b2df050b34369c97338a..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OSEStartingStatus.java +++ /dev/null @@ -1,44 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.element.ose; - -import java.util.TreeMap; - -public class OSEStartingStatus extends TreeMap<Integer, Integer> -{ - private final int dimension; - private int minimumSize; - - public OSEStartingStatus( int dimension ) - { - this.dimension = dimension; - } - - public void setStartingStatus() - { - //TODO user parameter ? - - if (this.size() != 0) - { - - if ( this.dimension == 0 ) - { - for ( int i = 0; this.size() > 20 && i <= this.size()* 0.75 ;i++ ) - { - this.remove( this.lastKey() ); - } - } - else - { - for ( int i = 0; this.size() > 2 && i <= this.size() / 10; i++ ) - { - this.remove( this.lastKey() ); - } - } - this.minimumSize = this.lastKey() ; - - }} - - public int getMinimumSize() - { - return minimumSize; - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OseXZ.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OseXZ.java new file mode 100644 index 0000000000000000000000000000000000000000..4e78dee2eb4c9ae346642f19096cbd14909b7e76 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OseXZ.java @@ -0,0 +1,48 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.element.ose; + +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.OSEStartingStatus; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLineX; + +public class OseXZ extends AbstractOSE +{ + public OseXZ( OSEStartingStatus startingStatus ) + { + super( startingStatus ); + } + + @Override + public boolean isNextTo( AbstractOSE next ) + { + return ( next.get( 0 ).getX() - this.get( this.size() - 1 ).getX() == 1 // the two OS are next to each other + && this.get( this.size() - 1 ).getRightNumber() == 1 // this OS has only one possible right coordinate + && next.get( 0 ).getLeftNumber() == 1 // the next OS has only one possible left coordinate + && this.get( this.size() - 1 ).isNext( next.get( 0 ), 1 ) );// the difference between the z values equals 1 + } + + @Override + public void createCoordinate( Coordinate current ) + { + this.add( new Coordinate( current.getX() + 1, current.getY(), current.getZ() ) ); + } + + @Override + public void setFirstOSE( Surface surface ) + { + int i = this.get(0). getY(); + int lineLength = surface.getWidth(); + surface.set( i, new SurfaceLineX( lineLength, this ) ); + } + + + public int getCoordinate(int index) + { + return this.get(index).getX(); + } + @Override + public int startingIndex( Coordinate current ) + { + return current.getX(); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OseYZ.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OseYZ.java new file mode 100644 index 0000000000000000000000000000000000000000..1b9717bc8829590c290fcd12bc73764c96886160 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/ose/OseYZ.java @@ -0,0 +1,49 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.element.ose; + +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.OSEStartingStatus; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLineY; + +public class OseYZ extends AbstractOSE +{ + public OseYZ( OSEStartingStatus startingStatus ) + { + super( startingStatus ); + } + + + @Override + public boolean isNextTo( AbstractOSE next ) + { + return ( next.get( 0 ).getY() - this.get( this.size() - 1 ).getY() == 1 // the two OS are next to each other + && this.get( this.size() - 1 ).getRightNumber() == 1 // this OS has only one possible right coordinate + && next.get( 0 ).getLeftNumber() == 1 // the next OS has only one possible left coordinate + && this.get( this.size() - 1 ).isNext( next.get( 0 ), 1 ) );// the difference between the z values equals 1 + } + + @Override + public void createCoordinate( Coordinate current ) + { + this.add( new Coordinate( current.getX(), current.getY() + 1, current.getZ() ) ); + } + + @Override + public void setFirstOSE( Surface surface ) + { + int i = this.get( 0 ).getX(); + int lineLength = surface.getWidth(); + surface.set( i, new SurfaceLineY( lineLength, this ) ); + } + + @Override + public int getCoordinate(int index) + { + return this.get(index).getY(); + } + @Override + public int startingIndex( Coordinate current ) + { + return current.getY(); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLine.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLine.java index 54321fb750330488b0fa68f5a2f988cd11a30b23..de7d167c4a674d8ac6e10c2fa358ff2dfa6453f6 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLine.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLine.java @@ -2,7 +2,9 @@ package fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine; import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; + +import static fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.constructSurface.ConstructionRounds.adequacy; public abstract class SurfaceLine { @@ -37,19 +39,58 @@ public abstract class SurfaceLine * * @param os - a list of Coordinates to fill the pixel array. */ - public SurfaceLine(int length , OSE os) + public SurfaceLine(int length , AbstractOSE os) { this.dimension = new Pixels[ length ] ; set( os ); } - - public SurfaceLine( OSE os, int size ) + + + public SurfaceLine( AbstractOSE os, int size ) { this.dimension = new Pixels[ size] ; set( os ); } - public void set( OSE os ) + public SurfaceLine match2( AbstractOSE os, int overlap, double connexity, SurfaceLine surfaceLine ) + { + int match = 0; + int k = 0; + for ( int i = 0; i <= os.size() - 1; i++ ) + { + int j = os.getCoordinate( i ); + Coordinate coordinate = os.get( i ); + if ( this.get( j ) != null ) + { + k++; + if ( isAMatch( this.get( j ), coordinate ) ) + { + match++; + } + } + surfaceLine.set( j, coordinate ); + } + if ( k == 0 || match == 0 ) + { + return null; + } + else if ( ( double ) match / ( double ) k >= connexity && k >= overlap ) + { +// System.out.println("percent = " + ( double ) match / ( double ) k); +// System.out.println("k = " + k); +// os.setVisited(); + return surfaceLine; + } + else + { +// System.out.println("* percent = " + ( double ) match / ( double ) k); +// System.out.println("* k = " + k); + return null; + } + } + + + public void set( AbstractOSE os ) { for ( Coordinate coordinate : os ) { @@ -66,16 +107,28 @@ public abstract class SurfaceLine } } + public boolean isInAdequacyWith( SurfaceLine surfaceLine) + { +// if ( surfaceLine != null ) +// { +// System.out.println("is in adequacy ? " + overlappingRate( surfaceLine )); +// } + return ( surfaceLine == null ) || overlappingRate( surfaceLine ) < adequacy; + + } + + + /** * Tests if the OS matches the SurfaceLine instance and creates a new SurfaceLine object if so. * * @param os - the OS to test against the SurfaceLine instance. * @param j - an integer witch indicates the line of the resulting SurfaceLine. - * @param percent - the minimum percentage of match between the OS and the current instance. - * @param matched - the minimum number of matching coordinates. + * @param overlap - the minimum number of matching coordinates. + * @param connexity - the minimum percentage of match between the OS and the current instance. * @return - a new SurfaceLine if there is a match, null otherwise. */ - public abstract SurfaceLine match( OSE os, int j, double percent, int matched ); + public abstract SurfaceLine match( AbstractOSE os, int j, int overlap, double connexity ); /** @@ -129,6 +182,9 @@ public abstract class SurfaceLine */ public void merge( SurfaceLine other ) { + double o = overlappingRate( other ); +// System.out.println("overlap = " + o); +// if (o < 0.30) for ( int i = 0; i <= this.dimension.length - 1; i++ ) { // only two situations where we have to do something @@ -143,6 +199,20 @@ public abstract class SurfaceLine } } + public double overlappingRate(SurfaceLine other) + { + int overlappedCoordinates = 0; + for ( int i = 0; i <= this.dimension.length - 1; i++ ) + { + // only two situations where we have to do something + if ( this.get( i ) != null && other.get( i ) != null ) + { + overlappedCoordinates ++; + } + } + return overlappedCoordinates/ (double) this.getCoordinatesNumber(); + } + /** * Combines two Pixels * @@ -150,8 +220,7 @@ public abstract class SurfaceLine * @param list2 - the second pixel list. * @return a pixel list containing the specified Pixels. */ - public Pixels merge( Pixels list1, Pixels list2 - ) + public Pixels merge( Pixels list1, Pixels list2 ) { if (! list1.equals( list2 )) { @@ -171,8 +240,8 @@ public abstract class SurfaceLine */ public boolean isAMatch( Pixels pixels, Coordinate c ) { - if (pixels.size() <= 3)//TODO how many Coordinates authorized ? - { +// if (pixels.size() <= 3)//TODO how many Coordinates authorized ? +// { for ( Coordinate coordinate : pixels.get() ) { if ( ( Math.abs( c.getZ() - coordinate.getZ() ) ) <= 1 ) @@ -180,7 +249,11 @@ public abstract class SurfaceLine return true; } } - } +// } +// else +// { +// System.out.println(" sup to 3"); +// } return false; } diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineX.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineX.java index 9fe02a4d25843339ccdf4aaa82439701bdfaed4d..432befddea8cee98390b6849ceb97b3072260f95 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineX.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineX.java @@ -1,7 +1,6 @@ package fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; public class SurfaceLineX extends SurfaceLine { @@ -12,12 +11,67 @@ public class SurfaceLineX extends SurfaceLine * * @param os - a list of Coordinates to fill the pixel array. */ - public SurfaceLineX( int length, OSE os ) +// public SurfaceLineX( int length, OSE os ) +// { +// super( length, os ); +// this.setLine( os.get( 0 ).getY() ); +// } + + public SurfaceLineX( int length, AbstractOSE os ) { super( length, os ); this.setLine( os.get( 0 ).getY() ); } + public SurfaceLine match( AbstractOSE os, int direction, int overlap, double connexity ) + { + SurfaceLineX surfaceLineX = new SurfaceLineX( this.getLength(), this.getLine() + direction ); + return this.match2( os, overlap, connexity, surfaceLineX); + } + /** + * Tests if the OS matches the SurfaceLine instance and creates a new SurfaceLine object if so. + * + * @param os - the OS to test against the SurfaceLine instance. + * @param j - an integer witch indicates the line of the resulting SurfaceLine. + * @param percent - the minimum percentage of match between the OS and the current instance. + * @param matched - the minimum number of matching coordinates. + * @return - a new SurfaceLine if there is a match, null otherwise. + */ +// @Override +// public SurfaceLine match( AbstractOSE os, int j, double percent, int matched ) +// { +// SurfaceLineX surfaceLine = new SurfaceLineX(this.getLength(), this.getLine() + j ); +// int match = 0; +// int k = 0; +// for ( int i = 0; i <= os.size() - 1; i++ ) +// { +// int x = os.get( i ).getX(); +// Coordinate coordinate = os.get( i ); +// if ( this.get( x ) != null ) +// { +// k++; +// if ( isAMatch( this.get( x ), coordinate ) ) +// { +// match++; +// } +// } +// surfaceLine.set( x, coordinate ); +// } +// if ( k == 0 || match == 0 ) +// { +// return null; +// } +// else if ( ( double ) match / ( double ) k >= percent && k >= matched ) +// { +// os.setVisited(); +// return surfaceLine; +// } +// else +// { +// return null; +// } +// } + /** * Constructor. * @@ -28,52 +82,18 @@ public class SurfaceLineX extends SurfaceLine super(length, line ); } - public SurfaceLineX( OSE os, int size ) +// public SurfaceLineX( OSE os, int size ) +// { +// super( os , size); +// this.setLine( os.get( 0 ).getY() ); +// } + + public SurfaceLineX( AbstractOSE os, int size ) { super( os , size); this.setLine( os.get( 0 ).getY() ); } - /** - * Tests if the OS matches the SurfaceLine instance and creates a new SurfaceLine object if so. - * - * @param os - the OS to test against the SurfaceLine instance. - * @param j - an integer witch indicates the line of the resulting SurfaceLine. - * @param percent - the minimum percentage of match between the OS and the current instance. - * @param matched - the minimum number of matching coordinates. - * @return - a new SurfaceLine if there is a match, null otherwise. - */ - public SurfaceLine match( OSE os, int j, double percent, int matched ) - { - SurfaceLineX surfaceLine = new SurfaceLineX(this.getLength(), this.getLine() + j ); - int match = 0; - int k = 0; - for ( int i = 0; i <= os.size() - 1; i++ ) - { - int x = os.get( i ).getX(); - Coordinate coordinate = os.get( i ); - if ( this.get( x ) != null ) - { - k++; - if ( isAMatch( this.get( x ), coordinate ) ) - { - match++; - } - } - surfaceLine.set( x, coordinate ); - } - if ( k == 0 || match == 0 ) - { - return null; - } - else if ( ( double ) match / ( double ) k >= percent && k >= matched ) - { - os.setVisited(); - return surfaceLine; - } - else - { - return null; - } - } + + } diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineY.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineY.java index 9204b511cf3cdbfd54b4bc70a0eeda5065000278..66071ea9e79e12dd7210fadfedb4f5f04a0f647f 100644 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineY.java +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/element/surfaceLine/SurfaceLineY.java @@ -1,7 +1,6 @@ package fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine; -import fr.pasteur.ida.zellige.surfaceConstruction.element.Coordinate; -import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; public class SurfaceLineY extends SurfaceLine { @@ -11,7 +10,7 @@ public class SurfaceLineY extends SurfaceLine * * @param os - a list of Coordinates to fill the pixel array. */ - public SurfaceLineY( int length, OSE os ) + public SurfaceLineY( int length, AbstractOSE os ) { super(length, os ); this.setLine( os.get( 0 ).getX() ); @@ -32,46 +31,55 @@ public class SurfaceLineY extends SurfaceLine * Tests if the OS matches the SurfaceLine instance and creates a new SurfaceLine object if so. * * @param os - the OS to test against the SurfaceLine instance. - * @param j - an integer witch indicates the line of the resulting SurfaceLine. - * @param percent - the minimum percentage of match between the OS and the current instance. - * @param matched - the minimum number of matching coordinates. + * @param direction - an integer witch indicates the line of the resulting SurfaceLine. + * @param overlap - the minimum number of matching coordinates. + * @param connexity - the minimum percentage of match between the OS and the current instance. * @return - a new SurfaceLine if there is a match, null otherwise. */ - public SurfaceLine match( OSE os, int j, double percent, int matched ) +// public SurfaceLine match( AbstractOSE os, int j, double percent, int matched ) +// { +// SurfaceLineY surfaceLine = new SurfaceLineY(this.getLength(), this.getLine() + j ); +// int match = 0; +// int k = 0; +// for ( int i = 0; i <= os.size() - 1; i++ ) +// { +// int y = os.get( i ).getY(); +// Coordinate coordinate = os.get( i ); +// if ( this.get( y ) != null ) +// { +// k++; +// if ( isAMatch( this.get( y ), coordinate ) ) +// { +// match++; +// } +// } +// surfaceLine.set( y, coordinate ); +// } +// if ( k == 0 || match == 0 ) +// { +// return null; +// } +// else if ( ( double ) match / ( double ) k >= percent && k >= matched ) +// { +//// System.out.println("percent = " + ( double ) match / ( double ) k); +//// System.out.println("k = " + k); +// os.setVisited(); +// return surfaceLine; +// } +// else +// { +//// System.out.println("* percent = " + ( double ) match / ( double ) k); +//// System.out.println("* k = " + k); +// return null; +// } +// } + + + + public SurfaceLine match( AbstractOSE os, int direction, int overlap, double connexity ) { - SurfaceLineY surfaceLine = new SurfaceLineY(this.getLength(), this.getLine() + j ); - int match = 0; - int k = 0; - for ( int i = 0; i <= os.size() - 1; i++ ) - { - int y = os.get( i ).getY(); - Coordinate coordinate = os.get( i ); - if ( this.get( y ) != null ) - { - k++; - if ( isAMatch( this.get( y ), coordinate ) ) - { - match++; - } - } - surfaceLine.set( y, coordinate ); - } - if ( k == 0 || match == 0 ) - { - return null; - } - else if ( ( double ) match / ( double ) k >= percent && k >= matched ) - { -// System.out.println("percent = " + ( double ) match / ( double ) k); -// System.out.println("k = " + k); - os.setVisited(); - return surfaceLine; - } - else - { -// System.out.println("* percent = " + ( double ) match / ( double ) k); -// System.out.println("* k = " + k); - return null; - } + SurfaceLineY surfaceLineY = new SurfaceLineY( this.getLength(), this.getLine() + direction ); + return this.match2( os, overlap, connexity, surfaceLineY); } + } diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/AdvancedUserParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/AdvancedUserParameters.java deleted file mode 100644 index 1f91632024362c725cdfba3dca8391f57afff9c2..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/AdvancedUserParameters.java +++ /dev/null @@ -1,57 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.parameters; - -public class AdvancedUserParameters -{ - private int k1; - private double percent1; - private int k2; - private double percent2; - - public AdvancedUserParameters( int k1, double percent1, int k2, double percent2 ) - { - this.k1 = k1; - this.percent1 = percent1; - this.k2 = k2; - this.percent2 = percent2; - } - - public int getK1() - { - return k1; - } - - public void setK1( int k1 ) - { - this.k1 = k1; - } - - public double getPercent1() - { - return percent1; - } - - public void setPercent1( double percent1 ) - { - this.percent1 = percent1; - } - - public int getK2() - { - return k2; - } - - public void setK2( int k2 ) - { - this.k2 = k2; - } - - public double getPercent2() - { - return percent2; - } - - public void setPercent2( double percent2 ) - { - this.percent2 = percent2; - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ConstructionParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ConstructionParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..a344dbf66671d221e09c02992c64293eb30c1667 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ConstructionParameters.java @@ -0,0 +1,32 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +public class ConstructionParameters +{ + + private final double startingSizeThreshold; + private final int overlap; + private final double connexityRate; + + public ConstructionParameters( double startingSizeThreshold, int overlap, double connexityRate ) + { + this.startingSizeThreshold = startingSizeThreshold; + this.overlap = overlap; + this.connexityRate = connexityRate; + } + + + public double getStartingSizeThreshold() + { + return startingSizeThreshold; + } + + public int getOverlap() + { + return overlap; + } + + public double getConnexityRate() + { + return connexityRate; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/Parameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/Parameters.java new file mode 100644 index 0000000000000000000000000000000000000000..a4aecc425b50e04a5a4f522b30ac7172d53005df --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/Parameters.java @@ -0,0 +1,5 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +public class Parameters +{ +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ProjectionParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ProjectionParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..7f7c0b46542622c1c11176fe2920b7808a76a75e --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ProjectionParameters.java @@ -0,0 +1,66 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +import fr.pasteur.ida.zellige.exception.DataValidationException; + +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; + } + + /** + * 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 //TODO javadoc : extracted volume defined as delta x 2 + 1 + * @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() + { + return projectionMethod; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/UserParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/UserParameters.java deleted file mode 100644 index 61352462a9825e5692f6934516bbfc7f680164d2..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/UserParameters.java +++ /dev/null @@ -1,72 +0,0 @@ -package fr.pasteur.ida.zellige.surfaceConstruction.parameters; - -public class UserParameters -{ - - private double amplitude; - private double threshold; - private double sigmas; - private int delta; - private String projectionType; - - - public UserParameters( double amplitude, double threshold, double sigmas, int delta , String projectionType) - { - this.amplitude = amplitude; - this.threshold = threshold; - this.sigmas = sigmas; - this.delta = delta; - this.projectionType = projectionType; - - } - - public double getAmplitude() - { - return amplitude; - } - - public void setAmplitude( double amplitude ) - { - this.amplitude = amplitude; - } - - public double getThreshold() - { - return threshold; - } - - public void setThreshold( double threshold ) - { - this.threshold = threshold; - } - - public double getSigmas() - { - return sigmas; - } - - public void setSigmas( int sigmas ) - { - this.sigmas = sigmas; - } - - public int getDelta() - { - return delta; - } - - public void setDelta( int delta ) - { - this.delta = delta; - } - - public String getProjectionType() - { - return projectionType; - } - - public void setProjectionType( String projectionType ) - { - this.projectionType = projectionType; - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PixelClassificationParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PixelClassificationParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..2ee37bffc13129d0127ee4621c939a75069ce7ea --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PixelClassificationParameters.java @@ -0,0 +1,59 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.Util; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.Parameters; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PixelClassificationParameters extends Parameters +{ + private final static Logger LOGGER = LoggerFactory.getLogger( PixelClassificationParameters.class ); + + private final double amplitudeThreshold, otsuThreshold; + public PixelClassificationParameters( double amplitudeThreshold, double otsuThreshold ) throws DataValidationException + { + classificationParametersValidationCheck( amplitudeThreshold, otsuThreshold ); + this.amplitudeThreshold = amplitudeThreshold; + this.otsuThreshold = otsuThreshold; + } + + /** + * + * @param name the parameter name to display in the error message. + * @param value the value of the parameter tested. + * @throws DataValidationException if the value is inferior to zero. + */ + private void valueValidationCheck( String name, double value ) throws DataValidationException + { + if ( value < 0 ) + { + throw new DataValidationException( "The value of parameter " + name + " has to be superior to zero !" ); + } + + } + + + /** + * Checks the four parameters validity. + * @param amplitudeThreshold - the value choose as amplitude threshold. + * @param otsuThreshold - the value choose as otsu threshold. + * @throws DataValidationException if at least one parameter is not valid. + */ + public void classificationParametersValidationCheck( double amplitudeThreshold, double otsuThreshold) throws DataValidationException + { + valueValidationCheck( "Amplitude Threshold", amplitudeThreshold ); + valueValidationCheck( "Otsu Threshold", otsuThreshold ); + } + + + public double getAmplitudeThreshold() + { + return amplitudeThreshold; + } + + public double getOtsuThreshold() + { + return otsuThreshold; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PixelSelectionParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PixelSelectionParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..7326264ab854235eb16f555c7f8bcd203943b786 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PixelSelectionParameters.java @@ -0,0 +1,32 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection; + +import fr.pasteur.ida.zellige.exception.DataValidationException; + +public class PixelSelectionParameters +{ + PretreatmentParameters pretreatmentParameters; + PixelClassificationParameters classificationParameters; + PostTreatmentParameters postTreatmentParameters; + + public PixelSelectionParameters( PretreatmentParameters pretreatmentParameters, PixelClassificationParameters classificationParameters, PostTreatmentParameters postTreatmentParameters ) + { + this.pretreatmentParameters = pretreatmentParameters; + this.classificationParameters = classificationParameters; + this.postTreatmentParameters = postTreatmentParameters; + } + + public PretreatmentParameters getPretreatmentParameters() + { + return pretreatmentParameters; + } + + public PixelClassificationParameters getClassificationParameters() + { + return classificationParameters; + } + + public PostTreatmentParameters getPostTreatmentParameters() + { + return postTreatmentParameters; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PostTreatmentParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PostTreatmentParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..0786164b02de60cdbd9faee854277cba3949bdd2 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PostTreatmentParameters.java @@ -0,0 +1,85 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.Parameters; + +public class PostTreatmentParameters extends Parameters +{ + + public static final String SIGMA_XY = "Sigma XY"; + public static final String SIGMA_Z = "Sigma Z"; + public static final String ISLAND_SIZE = "Island size"; + public static final String CONNEXITY = "connexity"; + private final double sigmaXY, sigmaZ; + private final int islandSize, connexity; + + + public PostTreatmentParameters( double sigmaXY, double sigmaZ, int islandSize, int connexity ) throws DataValidationException + { + postTreatmentParametersValidationCheck( sigmaXY, sigmaZ , islandSize); + this.sigmaXY = sigmaXY; + this.sigmaZ = sigmaZ; + this.islandSize = islandSize; + this.connexity = connexity; + + } + + + /** + * Checks the four parameters validity. + * @param sigmaXY the value choose for the XY image dilatation. + * @param sigmaZ the value choose for the Z image dilatation. + * @throws DataValidationException if at least one parameter is not valid. + */ + public void postTreatmentParametersValidationCheck( double sigmaXY, double sigmaZ, int islandSize) throws DataValidationException + { + valueValidationCheck( SIGMA_XY, sigmaXY ); + valueValidationCheck( SIGMA_Z, sigmaZ); + valueValidationCheck( ISLAND_SIZE , islandSize); + } + + /** + * + * @param name the parameter name to display in the error message. + * @param value the value of the parameter tested. + * @throws DataValidationException if the value is inferior to zero. + */ + private void valueValidationCheck( String name, double value ) throws DataValidationException + { + if (name.equals( CONNEXITY )) + { + if (value!= 4 && value!= 8) + { + throw new DataValidationException( "The value of parameter " + name + " has to be 4 or 8 !" ); + } + } + else + { + if ( value < 0 ) + { + throw new DataValidationException( "The value of parameter " + name + " has to be superior to zero !" ); + } + } + + } + + public double getSigmaXY() + { + return sigmaXY; + } + + public double getSigmaZ() + { + return sigmaZ; + } + + public int getIslandSize() + { + return islandSize; + } + + public int getConnexity() + { + return connexity; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PretreatmentParameters.java b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PretreatmentParameters.java new file mode 100644 index 0000000000000000000000000000000000000000..b9bc13adee1926837370884028f46aac2b1b8a97 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/maximumSelection/PretreatmentParameters.java @@ -0,0 +1,76 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.Parameters; + +public class PretreatmentParameters extends Parameters +{ + public static final String MEDIAN = "Median"; + public static final String GAUSSIAN_BLUR = "GaussianBlur"; + public static final String NOT_IMPLEMENTED = "not implemented"; + private final String method; + private final double[] parameters; + + + public PretreatmentParameters( String method, double[] parameters ) throws DataValidationException + { + this.checkParameters(method, parameters); + this.method = method; + this.parameters = parameters; + } + + /** + * Checks if the method end the arguments methods are valid + * @param method the method choose + * @param parameters the method arguments + * @throws DataValidationException if at least one is not valid + */ + private void checkParameters( String method, double[] parameters ) throws DataValidationException + { + checkDenoisingMethod( method ); + checkParametersLength(method, parameters); + } + + /** + * Checks the length of the parameters according to the method choose + * @param method the method choose + * @param parameters the method arguments + * @throws DataValidationException if at the pramaters legnth (the number of arguments) is not valid according to the method choose + */ + private void checkParametersLength( String method, double[] parameters ) throws DataValidationException + { + if (method.equals( GAUSSIAN_BLUR ) && parameters.length != 3) + { + throw new DataValidationException( "The parameters for the " + method + " requires 3 arguments " + + "(one for each image dimensions)"); + } + if (method.equals( MEDIAN ) && parameters.length != 1) + { + throw new DataValidationException( "The " + method + "method requires only one arguments " + + "(radius)" ); + } + } + + /** + * Check if the denoising method is valid. + * @param denoisingMethod the input denoising method + * @throws DataValidationException if the method is not valid + */ + public void checkDenoisingMethod( String denoisingMethod ) throws DataValidationException + { + if ( ! denoisingMethod.equals( GAUSSIAN_BLUR ) && ! denoisingMethod.equals( MEDIAN ) ) + { + throw new DataValidationException( "The denoising method " + denoisingMethod + " is " + NOT_IMPLEMENTED + " in the program" ); + } + } + + public String getMethod() + { + return method; + } + + public double[] getParameters() + { + return parameters; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/CSVWriter.java b/src/main/java/fr/pasteur/ida/zellige/utils/CSVWriter.java new file mode 100644 index 0000000000000000000000000000000000000000..8d6492be1cf7b1b867231562e6882396751a841f --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/utils/CSVWriter.java @@ -0,0 +1,78 @@ +package fr.pasteur.ida.zellige.utils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; + +public class CSVWriter +{ + + private BufferedWriter bw; + private final static String EXTENSION = ".csv"; + + private final static Logger LOGGER = LoggerFactory.getLogger( CSVWriter.class ); + + public CSVWriter( String testedFileName ) + { + String path = "target/" +testedFileName + EXTENSION; + + LOGGER.info( "CSV file name : {}", path ); + File csvOutputFile = new File( path ); + try + { + bw = new BufferedWriter( new FileWriter( csvOutputFile ) ); + LOGGER.info("CSV location : " + "target/" + testedFileName + EXTENSION); + } + catch ( IOException ioException ) + { + LOGGER.debug(" The csv file has not be created"); + } + } + + public void writeToFile( String line ) throws IOException + { + bw.write(line); + bw.newLine(); + LOGGER.info( " New line added in CSV file" ); + } + + public void writeToFile( String testedParameter, String testedParameterValue, int pixelNumber, double Rmse, double coverage ) throws IOException + { + String line = convertToCSV( testedParameter,testedParameterValue, pixelNumber, Rmse, coverage ); + writeToFile( line ); + } + + public String convertToCSV( String testedParameter, String testedParameterValue, int pixelNumber, double Rmse, double coverage ) { + String [] data = buildStringLine(testedParameter,testedParameterValue, pixelNumber, Rmse, coverage ); + return String.join( ";", data ); + } + + + public String [] buildStringLine( String testedParameter, String testedParameterValue, int pixelNumber, double Rmse, double coverage, double weightedRMSE ) + { + return new String[]{testedParameter, testedParameterValue, String.valueOf( pixelNumber ), String.valueOf( Rmse ), String.valueOf( coverage ), String.valueOf( weightedRMSE )}; + } + + public void writeToFile( String testedParameter, String testedParameterValue, int pixelNumber, double Rmse, double coverage, double weightedRMSE ) throws IOException + { + String line = convertToCSV( testedParameter,testedParameterValue, pixelNumber, Rmse, coverage , weightedRMSE); + writeToFile( line ); + } + + public String convertToCSV( String testedParameter, String testedParameterValue, int pixelNumber, double Rmse, double coverage, double weightedRMSE ) { + String [] data = buildStringLine(testedParameter,testedParameterValue, pixelNumber, Rmse, coverage, weightedRMSE ); + return String.join( ";", data ); + } + + + public String [] buildStringLine( String testedParameter, String testedParameterValue, int pixelNumber, double Rmse, double coverage ) + { + return new String[]{testedParameter, testedParameterValue, String.valueOf( pixelNumber ), String.valueOf( Rmse ), String.valueOf( coverage )}; + } + + public void close() throws IOException + { + bw.close(); + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java b/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java deleted file mode 100644 index c7a4ecb329d9af6847222aaf45bad4fac5673e09..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/utils/LocalOtsuClassification.java +++ /dev/null @@ -1,150 +0,0 @@ -package fr.pasteur.ida.zellige.utils; - -import net.imglib2.*; -import net.imglib2.algorithm.util.Grids; -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.util.Util; -import net.imglib2.view.IntervalView; -import net.imglib2.view.Views; - -import java.util.List; - -/** - * - */ -public class LocalOtsuClassification -{ - - /** - * @param input the input {@link Img} - * @param percent the percentage of global otsu value - * @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 RandomAccessibleInterval< T > input,ImgFactory<T> factory, double percent ) - { - // Prepare output. - 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 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 percent the percentage of global otsu value - */ - 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; - this.grid = factory.create( source ); - run(); - } - - /** - * - */ - public void run() - { - computeLocalThreshold( source, factory, grid ); - applyLocalThreshold( Views.iterable( source) ,Views.iterable( grid), binary, percent ); - } - - - /** - * @param input the input {@link Img} - * @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( RandomAccessibleInterval< T > input, ImgFactory<T> factory,RandomAccessibleInterval< T > grid ) - { - long width = input.dimension( 0 ); - long height = input.dimension( 1 ); - long depth = input.dimension( 2 ); - List< Interval > intervals = Grids.collectAllContainedIntervals( new long[]{ width, height, depth }, - new int[]{ 50, 50, 3 } );//TODO Size of a grid cube ? - - for ( Interval interval : intervals ) - { - computeLocalThreshold( input, grid, interval ); - } - grid = Utils.gaussConvolution( grid,factory, new double[]{ 5, 5, 1 } ); - } - - public static < T extends RealType< T > & NativeType< T > > void - computeLocalThreshold( RandomAccessibleInterval< T > input, RandomAccessibleInterval< T > grid, Interval interval ) - { - IntervalView< T > viewSource = Views.offsetInterval( input, interval ); - IntervalView< T > viewGrid = Views.offsetInterval( grid, interval ); - T threshold = Otsu.getThreshold( viewSource ); - viewGrid.forEach( pixel -> pixel.set( threshold ) ); - } - - /** - * @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 - */ - public static < T extends RealType< T > & NativeType< T > > void - applyLocalThreshold( IterableInterval< T > input, IterableInterval< T > grid, RandomAccessibleInterval< BitType > binary, double percent ) - { - double threshold = Threshold.getThreshold( input, percent ).getRealDouble(); - Cursor< T > sourceCursor = input.localizingCursor(); - Cursor< T > outputCursor = grid.cursor(); - RandomAccess< BitType > randomAccess = binary.randomAccess(); - 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 - { - randomAccess.setPosition( sourceCursor ); - randomAccess.get().set( true ); - } - } - } - ImageJFunctions.show( binary, "binary" ); - } - - - /** - * @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 deleted file mode 100644 index 178d6d6633c4fd30cd8a6641830f89419aa2ae3c..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification.java +++ /dev/null @@ -1,269 +0,0 @@ -package fr.pasteur.ida.zellige.utils; - -import net.imglib2.Cursor; -import net.imglib2.Interval; -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.algorithm.binary.Thresholder; -import net.imglib2.algorithm.util.Grids; -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.Util; -import net.imglib2.view.IntervalView; -import net.imglib2.view.Views; - -import java.util.List; - -/** - * This class makes it possible to get a binary classification of the pixels (background/ foreground) according to their amplitude and their amplitude's neighborhood. - * First, the amplitude of all local maximums is computed and stored as an {@link Img}. This {@link Img} is cut into squares (15x15) then depending on the number of pixels contained in - * each square, the latter will be classified as background or foreground. - */ -public class MaximumAmplitudeClassification -{ - - /** - * @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 - * @return a binary image of background foreground classification - */ - 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, 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 RandomAccessibleInterval< T > input; - private final ImgFactory< T > factory; - private final double amplitudeThreshold; - private RandomAccessibleInterval< BitType > amplitude; - - 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 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( - RandomAccessibleInterval< T > max, ImgFactory< T > factory, RandomAccessibleInterval< T > min ) - { - Img< T > amp = factory.create( max ); - RandomAccess< T > maxAccess = max.randomAccess(); - RandomAccess< T > minAccess = min.randomAccess(); - RandomAccess< T > ampAccess = amp.randomAccess(); - for ( int x = 0; x <= max.dimension( 0 ) - 1; x++ ) - { - maxAccess.setPosition( x, 0 ); - for ( int y = 0; y <= max.dimension( 1 ) - 1; y++ ) - { - maxAccess.setPosition( y, 1 ); - for ( int z = 0; z <= max.dimension( 2 ) - 1; z++ ) - - { - maxAccess.setPosition( z, 2 ); - double maxValue = maxAccess.get().getRealDouble(); - if ( maxValue != 0 ) - { - minAccess.setPosition( maxAccess ); - double amplitude = getAmplitude( maxValue, minAccess, ( int ) max.dimension( 2 ) ); - ampAccess.setPosition( maxAccess ); - ampAccess.get().setReal( amplitude ); - } - } - } - } - ImageJFunctions.show (amp," neutral amp"); - return amp; - } - - /** - * @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} - * @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 ) - { - double up = findValueUp( minAccess, maxValue ); - double down = findValueDown( minAccess, maxValue, depth ); - double result = Math.max( Math.abs( maxValue - up ), Math.abs( maxValue - down ) ); - if ( result == 0 ) - { - return maxValue; - - } - return ( result ); - } - - /** - * @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} - * @return the amplitude value above the local maximum location in the Z dimension - */ - private static < T extends RealType< T > & NativeType< T > > double findValueUp( - RandomAccess< T > minAccess, double maxValue ) - { - int start = minAccess.getIntPosition( 2 ); - for ( int z = start - 1; z >= 0; z-- ) - { - minAccess.setPosition( z, 2 ); - double value = minAccess.get().getRealDouble(); - if ( value != 0 ) - { - return value; - } - } - return maxValue; - } - - /** - * @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} - * @return the amplitude value below the local maximum location in the Z dimension - */ - private static < T extends RealType< T > & NativeType< T > > double findValueDown( - RandomAccess< T > minAccess, double maxValue, int depth ) - { - int start = minAccess.getIntPosition( 2 ); - for ( int z = start + 1; z <= depth - 1; z++ ) - { - minAccess.setPosition( z, 2 ); - double value = minAccess.get().getRealDouble(); - if ( value != 0 ) - { - return value; - } - } - return maxValue; - } - - public void run() - { - Img< T > maximums = LocalExtremaDetection.findMaxima( input, factory ); - Img< T > minimums = LocalExtremaDetection.findMinima( input, factory ); - Img< T > amp = getAmplitude( maximums, factory, minimums ); - ImageJFunctions.show(amp, "neutral amp 2"); - T TMax = Threshold.getFirstMaxValue( amp, false ); - System.out.println("Amplitude threshold value before user = " + TMax); - TMax.mul( amplitudeThreshold ); - System.out.println("Amplitude threshold value after user = " + TMax); - this.amplitude = Thresholder.threshold( amp.copy(), TMax, true, 2 ); - - } - - public RandomAccessibleInterval< BitType > getAmplitude() - { - return amplitude; - } - } - - /** - * This class allows the elimination of isolated pixels. the image is containing the amplitude values of each local maximums is split into {15 x 15 x 1} sections, - * and the sections containing less than the size percent value are set to zero. - */ - private static final class BackgroundForegroundGrid - { - 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 sizePercent - the minimum percentage of positive pixels - */ - private BackgroundForegroundGrid( final RandomAccessibleInterval< BitType > source, Img< FloatType > output, double sizePercent ) - { - this.source = source; - this.output = output; - this.sizePercent = sizePercent; - run(); - } - - /** - * @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 ) - { - double sum = 0; - Cursor< T > cursor = intervalView.cursor(); - while ( cursor.hasNext() ) - { - cursor.fwd(); - if ( cursor.get().getRealDouble() != 0 ) - { - sum++; - } - } - return ( sum ) > ( intervalView.dimension( 0 ) * intervalView.dimension( 1 ) * sizePercent ); - } - - /** - * - */ - public void run() - { - long width = source.dimension( 0 ); - long height = source.dimension( 1 ); - long depth = source.dimension( 2 ); - List< Interval > intervals = Grids.collectAllContainedIntervals( new long[]{ width, height, depth }, - new int[]{ 15, 15, 1 } ); - - for ( Interval interval : intervals ) - { - IntervalView< BitType > viewSource = Views.offsetInterval( source, interval ); - IntervalView< FloatType > viewOutput = Views.offsetInterval( output, interval ); - int foreground = isForeground( viewSource, sizePercent ) ? 1 : 0; - viewOutput.forEach( pixel -> pixel.setReal( foreground ) ); - } - // Slight dilatation in z dimension - - } - - /** - * @return the smoothed amplitude classified image - */ - public RandomAccessibleInterval< FloatType > getOutput() - { - return output; - } - } - -} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification2.java b/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification2.java deleted file mode 100644 index b8168a6c938f359b7fa1d4e3356632c3c0b0732a..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification2.java +++ /dev/null @@ -1,177 +0,0 @@ -package fr.pasteur.ida.zellige.utils; - -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.algorithm.binary.Thresholder; -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; - -/** - * This class makes it possible to get a binary classification of the pixels (background/ foreground) according to their amplitude and their amplitude's neighborhood. - * First, the amplitude of all local maximums is computed and stored as an {@link Img}. This {@link Img} is cut into squares (15x15) then depending on the number of pixels contained in - * each square, the latter will be classified as background or foreground. - */ -public class MaximumAmplitudeClassification2 -{ - - /** - * @param <T> - the type of the input - * @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. - * @return a binary image of background foreground classification - */ - public static < T extends RealType< T > & NativeType< T > > Img< BitType > find( final RandomAccessibleInterval< T > input, - final ImgFactory< T > factory, double amplitudeThreshold ) - { - // Prepare output. - Img< BitType > amplitude = new ArrayImgFactory<>( new BitType() ).create( input ); - MaximumAmplitude< T > maximumAmplitude = new MaximumAmplitude<>( input, factory, amplitude, amplitudeThreshold ); -// Utils.gaussConvolution( maximumAmplitude.getAmplitude().copy(), maximumAmplitude.getAmplitude(), new double[]{ 0, 0, 0.5 } ); - return maximumAmplitude.getAmplitude(); - } - - /** - * 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 RandomAccessibleInterval< T > input; - private final ImgFactory< T > factory; - private final double amplitudeThreshold; - private Img< BitType > amplitude; - - public MaximumAmplitude( RandomAccessibleInterval< T > input, ImgFactory< T > factory, Img< BitType > amplitude, double amplitudeThreshold ) - { - this.input = input; - this.factory = factory; - this.amplitude = amplitude; - this.amplitudeThreshold = amplitudeThreshold; - run(); - } - - public void run() - { - Img< T > maximums = LocalExtremaDetection.findMaxima( input, factory ); - Img< T > minimums = LocalExtremaDetection.findMinima( input, factory ); - Img< T > amp = getAmplitude( maximums, factory, minimums ); - T TMax = Threshold.getFirstMaxValue( maximums, false ); - System.out.println("Amplitude threshold value before user = " + TMax); - TMax.mul( amplitudeThreshold ); - System.out.println("Amplitude threshold value after user = " + TMax); - this.amplitude = Thresholder.threshold( amp.copy(), TMax, true, 2 ); - } - - /** - * @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( - RandomAccessibleInterval< T > max, ImgFactory< T > factory, RandomAccessibleInterval< T > min ) - { - Img< T > amp = factory.create( max ); - RandomAccess< T > maxAccess = max.randomAccess(); - RandomAccess< T > minAccess = min.randomAccess(); - RandomAccess< T > ampAccess = amp.randomAccess(); - for ( int x = 0; x <= max.dimension( 0 ) - 1; x++ ) - { - maxAccess.setPosition( x, 0 ); - for ( int y = 0; y <= max.dimension( 1 ) - 1; y++ ) - { - maxAccess.setPosition( y, 1 ); - for ( int z = 0; z <= max.dimension( 2 ) - 1; z++ ) - - { - maxAccess.setPosition( z, 2 ); - double maxValue = maxAccess.get().getRealDouble(); - if ( maxValue != 0 ) - { - minAccess.setPosition( maxAccess ); - double amplitude = getAmplitude( maxValue, minAccess, ( int ) max.dimension( 2 ) ); - ampAccess.setPosition( maxAccess ); - ampAccess.get().setReal( amplitude ); - } - } - } - } - ImageJFunctions.show (amp," neutral amp"); - return amp; - } - - /** - * @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} - * @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 ) - { - double up = findValueUp( minAccess, maxValue ); - double down = findValueDown( minAccess, maxValue, depth ); - double result = Math.max( Math.abs( maxValue - up ), Math.abs( maxValue - down ) ); - if ( result == 0 ) - { - return maxValue; - - } - return ( result ); - } - - /** - * @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} - * @return the amplitude value above the local maximum location in the Z dimension - */ - private static < T extends RealType< T > & NativeType< T > > double findValueUp( - RandomAccess< T > minAccess, double maxValue ) - { - int start = minAccess.getIntPosition( 2 ); - for ( int z = start - 1; z >= 0; z-- ) - { - minAccess.setPosition( z, 2 ); - double value = minAccess.get().getRealDouble(); - if ( value != 0 ) - { - return value; - } - } - return maxValue; - } - - /** - * @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} - * @return the amplitude value below the local maximum location in the Z dimension - */ - private static < T extends RealType< T > & NativeType< T > > double findValueDown( - RandomAccess< T > minAccess, double maxValue, int depth ) - { - int start = minAccess.getIntPosition( 2 ); - for ( int z = start + 1; z <= depth - 1; z++ ) - { - minAccess.setPosition( z, 2 ); - double value = minAccess.get().getRealDouble(); - if ( value != 0 ) - { - return value; - } - } - return maxValue; - } - - public Img< BitType > getAmplitude() - { - return amplitude; - } - } -} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification3.java b/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification3.java deleted file mode 100644 index 07626f2b40bb2e588951228be74ce6822800ea05..0000000000000000000000000000000000000000 --- a/src/main/java/fr/pasteur/ida/zellige/utils/MaximumAmplitudeClassification3.java +++ /dev/null @@ -1,271 +0,0 @@ -package fr.pasteur.ida.zellige.utils; - -import net.imglib2.Cursor; -import net.imglib2.Interval; -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.algorithm.binary.Thresholder; -import net.imglib2.algorithm.util.Grids; -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.Util; -import net.imglib2.view.IntervalView; -import net.imglib2.view.Views; - -import java.util.List; - -/** - * This class makes it possible to get a binary classification of the pixels (background/ foreground) according to their amplitude and their amplitude's neighborhood. - * First, the amplitude of all local maximums is computed and stored as an {@link Img}. This {@link Img} is cut into squares (15x15) then depending on the number of pixels contained in - * each square, the latter will be classified as background or foreground. - */ -public class MaximumAmplitudeClassification3 -{ - - /** - * @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 - * @return a binary image of background foreground classification - */ - 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, 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 RandomAccessibleInterval< T > input; - private final ImgFactory< T > factory; - private final double amplitudeThreshold; - private RandomAccessibleInterval< BitType > amplitude; - - 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 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( - RandomAccessibleInterval< T > max, ImgFactory< T > factory, RandomAccessibleInterval< T > min ) - { - Img< T > amp = factory.create( max ); - RandomAccess< T > maxAccess = max.randomAccess(); - RandomAccess< T > minAccess = min.randomAccess(); - RandomAccess< T > ampAccess = amp.randomAccess(); - for ( int x = 0; x <= max.dimension( 0 ) - 1; x++ ) - { - maxAccess.setPosition( x, 0 ); - for ( int y = 0; y <= max.dimension( 1 ) - 1; y++ ) - { - maxAccess.setPosition( y, 1 ); - for ( int z = 0; z <= max.dimension( 2 ) - 1; z++ ) - - { - maxAccess.setPosition( z, 2 ); - double maxValue = maxAccess.get().getRealDouble(); - if ( maxValue != 0 ) - { - minAccess.setPosition( maxAccess ); - double amplitude = getAmplitude( maxValue, minAccess, ( int ) max.dimension( 2 ) ); - ampAccess.setPosition( maxAccess ); - ampAccess.get().setReal( amplitude ); - } - } - } - }ImageJFunctions.show (amp," neutral amp"); - return amp; - } - - /** - * @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} - * @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 ) - { - double up = findValueUp( minAccess, maxValue ); - double down = findValueDown( minAccess, maxValue, depth ); - double result = Math.max( Math.abs( maxValue - up ), Math.abs( maxValue - down ) ); - if ( result == 0 ) - { - return maxValue; - - } - return ( result ); - } - - /** - * @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} - * @return the amplitude value above the local maximum location in the Z dimension - */ - private static < T extends RealType< T > & NativeType< T > > double findValueUp( - RandomAccess< T > minAccess, double maxValue ) - { - int start = minAccess.getIntPosition( 2 ); - for ( int z = start - 1; z >= 0; z-- ) - { - minAccess.setPosition( z, 2 ); - double value = minAccess.get().getRealDouble(); - if ( value != 0 ) - { - return value; - } - } - return maxValue; - } - - /** - * @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} - * @return the amplitude value below the local maximum location in the Z dimension - */ - private static < T extends RealType< T > & NativeType< T > > double findValueDown( - RandomAccess< T > minAccess, double maxValue, int depth ) - { - int start = minAccess.getIntPosition( 2 ); - for ( int z = start + 1; z <= depth - 1; z++ ) - { - minAccess.setPosition( z, 2 ); - double value = minAccess.get().getRealDouble(); - if ( value != 0 ) - { - return value; - } - } - return maxValue; - } - - public void run() - { - Img< T > maximums = LocalExtremaDetection.findMaxima( input, factory ); - Img< T > minimums = LocalExtremaDetection.findMinima( input, factory ); - Img< T > amp = getAmplitude( maximums, factory, minimums ); - T TMax = Threshold.getFirstMaxValue( maximums, false ); - System.out.println("Amplitude threshold value before user = " + TMax); - TMax.mul( amplitudeThreshold ); - System.out.println("Amplitude threshold value after user = " + TMax); - this.amplitude = Thresholder.threshold( amp.copy(), TMax, true, 2 ); - - } - - public RandomAccessibleInterval< BitType > getAmplitude() - { - return amplitude; - } - } - - /** - * This class allows the elimination of isolated pixels. the image is containing the amplitude values of each local maximums is split into {15 x 15 x 1} sections, - * and the sections containing less than the size percent value are set to zero. - */ - private static final class BackgroundForegroundGrid - { - 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 sizePercent - the minimum percentage of positive pixels - */ - private BackgroundForegroundGrid( final RandomAccessibleInterval< BitType > source, Img< FloatType > output, double sizePercent ) - { - this.source = source; - this.output = output; - this.sizePercent = sizePercent; - run(); - } - - /** - * @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 ) - { - double sum = 0; - Cursor< T > cursor = intervalView.cursor(); - while ( cursor.hasNext() ) - { - cursor.fwd(); - if ( cursor.get().getRealDouble() != 0 ) - { - sum++; - } - } - return ( sum ) > ( intervalView.dimension( 0 ) * intervalView.dimension( 1 ) * sizePercent ); - } - - /** - * - */ - public void run() - { - long width = source.dimension( 0 ); - long height = source.dimension( 1 ); - long depth = source.dimension( 2 ); - List< Interval > intervals = Grids.collectAllContainedIntervals( new long[]{ width, height, depth }, - new int[]{ 15, 15, 1 } ); - - for ( Interval interval : intervals ) - { - IntervalView< BitType > viewSource = Views.offsetInterval( source, interval ); - IntervalView< FloatType > viewOutput = Views.offsetInterval( output, interval ); - int foreground = isForeground( viewSource, sizePercent ) ? 1 : 0; - if (foreground == 0) - viewOutput.forEach( pixel -> pixel.setReal( foreground ) ); - else{ - Utils.convertBitTypeIntoFloatType( viewSource, viewOutput ); - } - } - // Slight dilatation in z dimension - - } - - /** - * @return the smoothed amplitude classified image - */ - public RandomAccessibleInterval< FloatType > getOutput() - { - return output; - } - } - -} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/ParameterSweepFiles.java b/src/main/java/fr/pasteur/ida/zellige/utils/ParameterSweepFiles.java new file mode 100644 index 0000000000000000000000000000000000000000..93906b35797ec70133d2f660b2fbf2ddeaae2de3 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/utils/ParameterSweepFiles.java @@ -0,0 +1,48 @@ +package fr.pasteur.ida.zellige.utils; + +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.img.Img; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; + +public class ParameterSweepFiles +{ + private final Img<IntType> ref; + private final Img<FloatType> tested; + private final String path; + + public ParameterSweepFiles() + { + /* The reference height map*/ + final String refImagePath = "doc/Ground truth Height map.tif"; +// final String refImagePath = "C:\\Users\\ctrebeau\\Desktop\\phantom_tests_snr_1surf\\phantoms_1surf_snrtest_zmap1_gt.tif"; +// String p = "C:\\Users\\ctrebeau\\Desktop\\phantom_tests_snr_1surf\\phantoms_1surf_snrtest_zmap1_gt.tif"; + final SCIFIOImgPlus< ? > refImgPlus = IO.openImgs( refImagePath ).get( 0 ); + ref = ( Img< IntType > ) refImgPlus.getImg(); + + /* The raw image to test*/ + final String imagePath = "doc/STK.tif"; +// final String imagePath = "C:\\Users\\ctrebeau\\Desktop\\phantom_tests_snr_1surf\\phantoms_1surf_snr0.mat.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + tested = ( Img< FloatType > ) imgPlus.getImg(); + + path ="STK"; +// ( imagePath.substring( 4 ) ); + } + + public Img< IntType > getRef() + { + return ref; + } + + public Img< FloatType > getTested() + { + return tested; + } + + public String getPath() + { + return path; + } +} diff --git a/src/main/java/fr/pasteur/ida/zellige/utils/ReconstructionErrors.java b/src/main/java/fr/pasteur/ida/zellige/utils/ReconstructionErrors.java new file mode 100644 index 0000000000000000000000000000000000000000..b1fe82b267763a80731fc3269b0e223c09bd5780 --- /dev/null +++ b/src/main/java/fr/pasteur/ida/zellige/utils/ReconstructionErrors.java @@ -0,0 +1,112 @@ +package fr.pasteur.ida.zellige.utils; + +import fr.pasteur.ida.zellige.exception.DifferentReferenceTestedSizeException; +import fr.pasteur.ida.zellige.exception.NotAnHeightMapException; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ReconstructionErrors< T extends RealType< T > & NativeType< T >, R extends RealType< R > & NativeType< R > > +{ + + private final static Logger LOGGER = LoggerFactory.getLogger( ReconstructionErrors.class ); + private final RandomAccessibleInterval< T > ref; + private final RandomAccessibleInterval< R > tested; + private double reconstructedPixelsPercentage = 0; + private double RMSEValue; + private double RMSEWithPenalty; + + public ReconstructionErrors( RandomAccessibleInterval< T > ref, + RandomAccessibleInterval< R > tested ) throws NotAnHeightMapException, DifferentReferenceTestedSizeException + { + inputCheck( ref, tested ); + this.ref = ref; + this.tested = tested; + } + + + public void compute() + { + RandomAccess< T > refAccess = ref.randomAccess(); + RandomAccess< R > testedAccess = tested.randomAccess(); + int totalPixel = 0; + int testedPixelNumber = 0; + double meanSquareError = 0; + for ( int x = 0; x < ref.dimension( 0 ); x++ ) + { + for ( int y = 0; y < ref.dimension( 1 ); y++ ) + { + double refValue = Utils.setPositionAndGet( refAccess, x, y ).getRealDouble(); + if ( refValue != 0 ) // there is a pixel in the ref height map + { + totalPixel++; + double testedValue = Utils.setPositionAndGet( testedAccess, x, y ).getRealDouble(); + if ( testedValue != 0 ) + { + testedPixelNumber++; + meanSquareError = + Math.pow( ( refValue - testedValue ), 2 ); + } + } + } + } + this.RMSEValue = Math.sqrt( meanSquareError / testedPixelNumber ); + double penalty = totalPixel - testedPixelNumber; + this.RMSEWithPenalty = Math.sqrt( ( meanSquareError + ( penalty * 0.5 ) ) / totalPixel ); + this.reconstructedPixelsPercentage = ( double ) testedPixelNumber / totalPixel; + LOGGER.info( "RMSE = {} ", this.RMSEValue ); + LOGGER.info( "Reconstructed pixel percentage = {} ", this.reconstructedPixelsPercentage ); + LOGGER.info( " Reconstruction Error Computation done." ); + } + + void inputCheck( RandomAccessibleInterval< T > ref, + RandomAccessibleInterval< R > tested ) throws NotAnHeightMapException, DifferentReferenceTestedSizeException + { + if ( ref != null && tested != null ) + { + if ( ref.numDimensions() > 2 ) + { + LOGGER.error( "The reference height map has more than one dimension ! (It's not an height map)" ); + throw new NotAnHeightMapException(); + } + if ( tested.numDimensions() > 2 ) + { + LOGGER.error( "The tested height map has more than one dimension ! (It's not an height map)" ); + throw new NotAnHeightMapException(); + } + if ( ref.dimension( 0 ) != tested.dimension( 0 ) || ref.dimension( 1 ) != tested.dimension( 1 ) ) + { + LOGGER.error( "The reference and tested height maps have different size !" ); + throw new DifferentReferenceTestedSizeException(); + } + LOGGER.info( "Starting Reconstruction Error Computation." ); + } + else if ( ref == null ) + { + LOGGER.error( "The reference height map is null" ); + throw new NullPointerException(); + } + else + { + LOGGER.error( "The tested height map is null" ); + throw new NullPointerException(); + } + } + + public double getReconstructedPixelsPercentage() + { + return reconstructedPixelsPercentage; + } + + public double getRMSEValue() + { + return RMSEValue; + } + + public double getRMSEWithPenalty() + { + return RMSEWithPenalty; + } +} 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 1b6538b11dc502b9d8776375ea30e37e9767a3a6..d7bddeffd834acbfbf0fa425c6f1e8047bff34ca 100644 --- a/src/main/java/fr/pasteur/ida/zellige/utils/Threshold.java +++ b/src/main/java/fr/pasteur/ida/zellige/utils/Threshold.java @@ -201,34 +201,8 @@ public class Threshold return threshold; } - public static < T extends RealType< T > & NativeType< T > > Img< T > classification( - IterableInterval< T > amplitude, ImgFactory< T > factory, Img< BitType > thresholds ) - { - Img< T > output = factory.create( thresholds ); - Cursor< T > amplitudeCursor = amplitude.localizingCursor(); - RandomAccess< BitType > thresholdAccess = thresholds.randomAccess(); - RandomAccess< T > outputAccess = output.randomAccess(); - while ( amplitudeCursor.hasNext() ) - { - amplitudeCursor.fwd(); - if ( amplitudeCursor.get().getRealDouble() != 0 ) - { - thresholdAccess.setPosition( amplitudeCursor ); - if ( thresholdAccess.get().get() ) - { - outputAccess.setPosition( amplitudeCursor ); - outputAccess.get().setOne(); - } - } - } - ImageJFunctions.show( output.copy(), "amp+thresh" ); - return output; - } - - public static < T extends RealType< T > & NativeType< T > > T getFirstMaxValue( Img< T > input, boolean zero ) { - ImageJFunctions.show(input.copy()); T max = Threshold.findMax( input ); T min = Threshold.findMin( input ); int numBins = (int)max.getRealDouble(); diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/CrossSurfaceTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/CrossSurfaceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..ea95d30b5839c8a9c8fd6ed4e4628169745abd0c --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/CrossSurfaceTest.java @@ -0,0 +1,67 @@ +package fr.pasteur.ida.zellige.behavior; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.SurfacePixelSelection; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import ij.ImageJ; +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.img.Img; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import org.junit.jupiter.api.Test; + + +public class CrossSurfaceTest +{ + + + @Test + < T extends RealType< T > & NativeType< T > > void someTest() throws DataValidationException, NoClassificationException, EmptyOutputException + { + /* Parameters*/ + double amplitude = 0.2; + double otsu = 0.2; + double sigmaXY = 2; + double sigmaZ = 0; + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "Median", new double[]{ 2 } ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 5, 8 ); + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + + final String imagePath = "doc/STK_08.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + new ImageJ(); + final Img< T > source = ( Img< T > ) imgPlus.getImg(); + + Pixels[][] maximums = SurfacePixelSelection.run( source, pixelSelectionParameters ); +// displayMaximums(maximums); + } + + public static< T extends RealType< T > & NativeType< T > > void main( String[] args ) throws DataValidationException, NoClassificationException, EmptyOutputException + { + /* Parameters*/ + double amplitude = 0; + double otsu = 0.2; + double sigmaXY = 2; + double sigmaZ = 1; + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "Median", new double[]{ 2 } ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 5, 8 ); + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + + final String imagePath = "doc/phantoms_crossingsurf.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + new ImageJ(); + final Img< T > source = ( Img< T > ) imgPlus.getImg(); + + Pixels[][] maximums = SurfacePixelSelection.run( source, pixelSelectionParameters ); +// displayMaximums(maximums); + } +} diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/GetFirstMaxValueBehaviorTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/GetFirstMaxValueBehaviorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..002086abf9be1da414b2305ea2da7ea77cf812d8 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/GetFirstMaxValueBehaviorTest.java @@ -0,0 +1,53 @@ +package fr.pasteur.ida.zellige.behavior; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.jzy3D.LocalMaximumsDisplay; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.SurfacePixelSelection; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.ose.OseConstructionXZ; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.img.Img; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import org.junit.Ignore; +import org.junit.jupiter.api.Test; + + +class GetFirstMaxValueBehaviorTest < T extends RealType< T > & NativeType< T > > +{ + @Ignore + @Test + void GetFirstMaxValueBehaviorTest() throws DataValidationException, NoClassificationException, EmptyOutputException + { + /* + Test to show a specific behavior for few images when determining the First max value in + MaximumAmplitudeClassification class + */ + // Parameters + double amplitude = 0.2; + double otsu = 0.2; + double sigmaXY = 2; + double sigmaZ = 0; + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "Median", new double[]{ 2 } ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 5, 8 ); + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + + final String imagePath = "doc/STK_08.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + + final Img< T > source = ( Img< T > ) imgPlus.getImg(); + + Pixels[][] maximums = SurfacePixelSelection.run( source, pixelSelectionParameters ); +// displayMaximums(maximums); + + } +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/OneSurfaceConstructionTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/OneSurfaceConstructionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3c4037e623215c6eea8bcc5c004f00306c028615 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/OneSurfaceConstructionTest.java @@ -0,0 +1,19 @@ +package fr.pasteur.ida.zellige.behavior; + +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ProjectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class OneSurfaceConstructionTest +{ + + + +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/PipelineTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/PipelineTest.java new file mode 100644 index 0000000000000000000000000000000000000000..7f852d41db2f1c8092aa1663b233c838ad948308 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/PipelineTest.java @@ -0,0 +1,146 @@ +package fr.pasteur.ida.zellige.behavior; + +import fr.pasteur.ida.zellige.ReferenceSurfaceExtraction; +import fr.pasteur.ida.zellige.exception.*; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ReferenceSurface; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ProjectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import fr.pasteur.ida.zellige.utils.CSVWriter; +import fr.pasteur.ida.zellige.utils.ParameterSweepFiles; +import fr.pasteur.ida.zellige.utils.ReconstructionErrors; +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.IntType; +import net.imglib2.type.numeric.integer.UnsignedShortType; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvFileSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +import java.io.IOException; +import java.util.ArrayList; + + +public class PipelineTest< T extends RealType< T > & NativeType< T > > +{ + + private final static Logger LOGGER = LoggerFactory.getLogger( CSVWriter.class ); + static Img< IntType > ref; + static Img< FloatType > tested; + private static CSVWriter writer; + + @BeforeAll + static void init() + { + ParameterSweepFiles files = new ParameterSweepFiles(); + ref = files.getRef(); + tested = files.getTested(); + writer = new CSVWriter( files.getPath() ); + } + + @AfterAll + static void closeBw() throws IOException + { + writer.close(); + } + + @ParameterizedTest + @CsvFileSource( files = "src/test/resources/phantoms_1surf_snr0.mat_PS.csv", numLinesToSkip = 2, delimiterString = ";" ) + void parameterSweep( String testedParameter, String testedParameterValue, String filter, int filterParameter, + double amplitudeThreshold, double otsuThreshold, + int ISConnexity, int ISSize, + double XYSmoothing, double ZSmoothing, + double startingThreshold1, int overlap1, double connexityRate1, + double startingThreshold2, int overlap2, double connexityRate2 ) throws DataValidationException, NoClassificationException, EmptyOutputException, IOException + { + LOGGER.info( "Parameter sweep " ); + ImgFactory< FloatType > factory = tested.factory(); + + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( filter, new double[]{ filterParameter } ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitudeThreshold, otsuThreshold ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( XYSmoothing, ZSmoothing, ISSize, ISConnexity ); + PixelSelectionParameters selectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + + ProjectionParameters projectionParameters = new ProjectionParameters( 1, "MIP" ); + ConstructionParameters[] constructionParameters = new ConstructionParameters[]{ + new ConstructionParameters( startingThreshold1, overlap1, connexityRate1 ), + new ConstructionParameters( startingThreshold2, overlap2, connexityRate2 ) }; + + ReferenceSurfaceExtraction< FloatType > extraction = new ReferenceSurfaceExtraction<>( tested, factory, + selectionParameters, projectionParameters, constructionParameters ); + extraction.select(); + int pixelNumber = getPixelNumber( extraction.getMaximums() ); + try + { + extraction.construct(); + ArrayList< ReferenceSurface< FloatType > > surfaces = extraction.getReferenceSurfaces(); + ArrayList<ReconstructionErrors<T, UnsignedShortType>> errorsList = new ArrayList<>(); + for ( ReferenceSurface rf : surfaces ) + { + ReconstructionErrors< T, UnsignedShortType > errors = new ReconstructionErrors<>( ref, rf.getZMap() ); + errors.compute(); + errorsList.add(errors); + + } + ReconstructionErrors< T, UnsignedShortType > finalError = findRightSurface( errorsList ); + writer.writeToFile( testedParameter, testedParameterValue, pixelNumber, + finalError.getRMSEValue(), + finalError.getReconstructedPixelsPercentage(), + finalError.getRMSEWithPenalty() ); + } + catch ( Exception n ) + { + writer.writeToFile( testedParameter, testedParameterValue, pixelNumber, + 100, + 0, + 100 ); + } + } + + private int getPixelNumber( Pixels[][] maximumCoordinates ) + { + int n = 0; + for ( int i = 0; i <= maximumCoordinates[ 0 ].length - 1; i++ ) + { + for ( int j = 0; j <= maximumCoordinates.length - 1; j++ ) + { + if ( maximumCoordinates[ j ][ i ] != null ) + { + n += maximumCoordinates[ j ][ i ].size(); + } + } + } + return n; + } + + + ReconstructionErrors <T, UnsignedShortType> findRightSurface(ArrayList<ReconstructionErrors<T, UnsignedShortType>> list) + { + if(list.size() > 1) + { + ReconstructionErrors< T, UnsignedShortType > errors = null; + double max = 0; + for ( ReconstructionErrors< T, UnsignedShortType > temp : list ) + { + if ( temp.getReconstructedPixelsPercentage() > max ) + { + errors = temp; + } + } + return errors; + } + return list.get(0); + } + +} diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/StartingOSE_SameSurfaceTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/StartingOSE_SameSurfaceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9a1ddc0ee97466147afa9f71d258b1ccc7d31091 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/StartingOSE_SameSurfaceTest.java @@ -0,0 +1,127 @@ +package fr.pasteur.ida.zellige.behavior; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.SurfacePixelSelection; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.ose.OseConstructionXZ; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.surface.OneSurfaceConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEList; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.element.surfaceLine.SurfaceLine; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.img.Img; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import org.junit.Ignore; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.*; + +import static org.assertj.core.api.Assertions.assertThat; + +public class StartingOSE_SameSurfaceTest +{ + @Ignore + @DisplayName( "Two starting OSE from the same surface don't necessarily produce the exact same surface" ) + @Test + < T extends RealType< T > & NativeType< T > > void TwoStartingOSEFromTheSameSurface_whenUseForStartASurface_DonTProduceTheExactSameSurface() throws DataValidationException, EmptyOutputException, NoClassificationException + { + /* Parameters*/ + double amplitude = 0.2; + double otsu = 0.2; + double sigmaXY = 2; + double sigmaZ = 0; + double startingSizeThreshold = 0.9; + int overlap = 10; + double connexity = 0.5; + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "Median", new double[]{ 2 } ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 5, 8 ); + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + + final String imagePath = "doc/STK_08.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + + final Img< T > source = ( Img< T > ) imgPlus.getImg(); + + Pixels[][] maximums = SurfacePixelSelection.run( source, pixelSelectionParameters ); + OSEListArray oseLists = OseConstructionXZ.runConstruction( maximums, startingSizeThreshold ); + int width = maximums[ 0 ].length; + + ArrayList< AbstractOSE > list1 = findAllStartingOSE( oseLists ); + AbstractOSE ose1 = oseLists.getAStartingOse(); + Surface reference = OneSurfaceConstruction.run( oseLists, ose1, width, overlap, connexity ); + + ArrayList< AbstractOSE > list2 = findAllStartingOSE( oseLists ); + + ArrayList< AbstractOSE > osePartOfSurface = list1; + osePartOfSurface.removeAll( list2 ); + System.out.println( "surface starting ose : " + osePartOfSurface.size() ); + TreeMap< Double, Integer > uniqueSurface = new TreeMap<>(); + for ( AbstractOSE ose : osePartOfSurface ) + { + oseLists.reset(); + Surface s = OneSurfaceConstruction.run( oseLists, ose, width, overlap, connexity ); + double overlappingRate = overlappingRate( reference, s ); + System.out.println( " This surface has an overlap rate of " + overlappingRate + " with the reference surface" ); + Integer j = uniqueSurface.get(overlappingRate); + uniqueSurface.put( overlappingRate, ( j == null ) ? 1 : j + 1 ); + } + System.out.println(uniqueSurface ); + assertThat( uniqueSurface.size()> 1 ); + } + + + public ArrayList<AbstractOSE> findAllStartingOSE (OSEListArray oseListArray) + { + ArrayList<AbstractOSE> list = new ArrayList<>(); + for ( OSEList oseList : oseListArray.getOseLists() ) + { + for ( AbstractOSE ose :oseList ) + { + if(ose.isAStart()) + { + list.add(ose); + } + } + } + return list; + } + + public double overlappingRate( Surface reference, Surface other ) + { + int inCommon = 0; + int count = 0; + for ( int i = 0; i <= reference.getHeight() - 1; i++ ) + { + SurfaceLine refLine = reference.get( i ); + if ( refLine != null ) + { + for ( int j = 0; j < refLine.getLength(); j++ ) + { + Pixels refPixels = refLine.get( j ); + if ( refPixels != null ) + { + count++; + if ( refPixels.equals (other.get( i ).get( j ) )) + { + inCommon++; + } + } + } + + } + } + return inCommon / (double) count * 100.0; + } +} diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/SurfaceConstructionBehaviorTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/SurfaceConstructionBehaviorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..542ac8c56ee54ad2740a0da3ec05667e19747f5d --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/SurfaceConstructionBehaviorTest.java @@ -0,0 +1,76 @@ +package fr.pasteur.ida.zellige.behavior; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.constructionRound.constructSurface.ConstructionRounds; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.SurfacePixelSelection; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.ose.OseConstructionXZ; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.surface.OneSurfaceConstruction; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Pixels; +import fr.pasteur.ida.zellige.surfaceConstruction.element.Surface; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.AbstractOSE; +import fr.pasteur.ida.zellige.surfaceConstruction.element.ose.OSEListArray; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.ConstructionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PostTreatmentParameters; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import ij.ImageJ; +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.img.Img; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +import static fr.pasteur.ida.zellige.ReferenceSurfaceExtraction.displaySurface; + +public class SurfaceConstructionBehaviorTest< T extends RealType< T > & NativeType< T > > +{ + + @Test + void testIt() throws DataValidationException, NoClassificationException, EmptyOutputException + { + /* Parameters*/ + double amplitude = 0; + double otsu = 1; + double sigmaXY = 2; + double sigmaZ = 0; + double startingSizeThreshold = 0.9; + int overlap = 10; + double connexity = 0.7; + int overlap1 = 30; + double connexity1 = 0.9; + + PretreatmentParameters pretreatmentParameters = new PretreatmentParameters( "Median", new double[]{ 2 } ); + PixelClassificationParameters classificationParameters = new PixelClassificationParameters( amplitude, otsu ); + PostTreatmentParameters postTreatmentParameters = new PostTreatmentParameters( sigmaXY, sigmaZ, 5, 8 ); + PixelSelectionParameters pixelSelectionParameters = new PixelSelectionParameters( pretreatmentParameters, classificationParameters, postTreatmentParameters ); + ConstructionParameters[] constructionParameters = new ConstructionParameters[]{ + new ConstructionParameters( 0.75, overlap, connexity ), + new ConstructionParameters( 0.1, overlap1, connexity1 )}; + ij.ImageJ ij = new ImageJ(); + final String imagePath = "doc/STK_08.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + + final Img< T > source = ( Img< T > ) imgPlus.getImg(); + + Pixels[][] maximums = SurfacePixelSelection.run( source, pixelSelectionParameters ); + OSEListArray oseLists = OseConstructionXZ.runConstruction( maximums, startingSizeThreshold ); + +// displayMaximums( maximums ); + + ConstructionRounds rounds = new ConstructionRounds(); + rounds.process( maximums, constructionParameters ); + ArrayList<Surface> surfaces = rounds.getReferenceSurfaces(); + for(Surface surface : surfaces) + { +// displaySurface( surface ); + } + + while(true); + } +} diff --git a/src/test/java/fr/pasteur/ida/zellige/behavior/SurfaceLineConstructionBehaviorTest.java b/src/test/java/fr/pasteur/ida/zellige/behavior/SurfaceLineConstructionBehaviorTest.java new file mode 100644 index 0000000000000000000000000000000000000000..fb40f4f00b0ac454ce033294094ff3b5c79c323e --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/behavior/SurfaceLineConstructionBehaviorTest.java @@ -0,0 +1,6 @@ +package fr.pasteur.ida.zellige.behavior; + +public class SurfaceLineConstructionBehaviorTest +{ + +} diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacePixelSelectionTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacePixelSelectionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e9de32a78a1313ea2d127faa3d79a5c98414edbe --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacePixelSelectionTest.java @@ -0,0 +1,9 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction; + +import static org.junit.jupiter.api.Assertions.*; + +class SurfacePixelSelectionTest +{ + + +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesReconstructionTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesReconstructionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a9c7e7fe998e0432c96d5e87e69b2ff2a24a8759 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/SurfacesReconstructionTest.java @@ -0,0 +1,9 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction; + + +class SurfacesReconstructionTest +{ + + + +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PixelClassificationTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PixelClassificationTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c0c3dda421771bbcf0ed04000ecfa6fb669b7ef1 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PixelClassificationTest.java @@ -0,0 +1,85 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.exception.EmptyOutputException; +import fr.pasteur.ida.zellige.exception.NoClassificationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.spy; + + +class PixelClassificationTest +{ + + @DisplayName( "When neither of the two thresholds is equals to Zero both classifications are done" ) + @Test + void NoneZeroThresholdValue_usesBothClassifications() throws DataValidationException, NoClassificationException, EmptyOutputException + { + // Given + PixelClassificationParameters parameters = new PixelClassificationParameters( 1, 1 ); + ImgFactory< FloatType > factory = new ArrayImgFactory<>( new FloatType() ); + RandomAccessibleInterval< FloatType > rai = factory.create( 5, 5, 5 ); + PixelClassification< FloatType > classificationSpy = spy( new PixelClassification<>() ); + double amplitudeThreshold = parameters.getAmplitudeThreshold(); + double otsuThreshold = parameters.getOtsuThreshold(); + // When + classificationSpy.process( rai, factory, parameters ); + // Then + Mockito.verify( classificationSpy, Mockito.times( 1 ) ).processBothClassification( rai, factory, amplitudeThreshold,otsuThreshold ); + } + + @DisplayName( "When both thresholds equal Zero a DataValidationException is raised" ) + @Test + void ZeroThresholdValues_raisesAnException() throws DataValidationException + { + // Given + PixelClassificationParameters parameters = new PixelClassificationParameters( 0, 0 ); + ImgFactory< FloatType > factory = new ArrayImgFactory<>( new FloatType() ); + RandomAccessibleInterval< FloatType > rai = factory.create( 5, 5, 5 ); + PixelClassification< FloatType > classification = new PixelClassification<>(); + + // When + assertThrows(NoClassificationException.class, () -> classification.process( rai, factory, parameters )); + } + + @DisplayName( "When Amplitude Threshold equals Zero only the Otsu classification is done" ) + @Test + void AnAmplitudeThresholdSetToZero_usesOnlyOtsuClassification() throws DataValidationException, NoClassificationException, EmptyOutputException + { + // Given + PixelClassificationParameters parameters = new PixelClassificationParameters( 0, 0.1 ); + ImgFactory< FloatType > factory = new ArrayImgFactory<>( new FloatType() ); + RandomAccessibleInterval< FloatType > rai = factory.create( 5, 5, 5 ); + PixelClassification< FloatType > classificationSpy = spy( new PixelClassification<>() ); + double otsuThreshold = parameters.getOtsuThreshold(); + // When + classificationSpy.process( rai, factory, parameters ); + // Then + Mockito.verify( classificationSpy, Mockito.times( 1 ) ).processOtsuClassification( rai, factory, otsuThreshold ); + } + + @DisplayName( "When Otsu Threshold equals Zero only the Amplitude classification is done" ) + @Test + void AnOtsuThresholdSetToZero_usesOnlyAmplitudeClassification() throws DataValidationException, NoClassificationException, EmptyOutputException + { + // Given + PixelClassificationParameters parameters = new PixelClassificationParameters( 1, 0 ); + ImgFactory< FloatType > factory = new ArrayImgFactory<>( new FloatType() ); + RandomAccessibleInterval< FloatType > rai = factory.create( 5, 5, 5 ); + PixelClassification< FloatType > classificationSpy = spy( new PixelClassification<>() ); + double amplitudeThreshold = parameters.getAmplitudeThreshold(); + // When + classificationSpy.process( rai, factory, parameters ); + // Then + Mockito.verify( classificationSpy, Mockito.times( 1 ) ).processAmplitudeClassification( rai, factory, amplitudeThreshold ); + } + +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PostTreatmentTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PostTreatmentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..d3c9e75ad13c20dc8a9d4d1768047e4c92b5730e --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PostTreatmentTest.java @@ -0,0 +1,8 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import static org.junit.jupiter.api.Assertions.*; + +class PostTreatmentTest +{ + +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PreTreatmentTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PreTreatmentTest.java new file mode 100644 index 0000000000000000000000000000000000000000..3be9939d3401aeaf35cc2eff61253bf4fd545f12 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/construction/maximumSelection/PreTreatmentTest.java @@ -0,0 +1,47 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.construction.maximumSelection.Pretreatment; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.numeric.real.FloatType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.mockito.Matchers.any; + +public class PreTreatmentTest +{ + + public static final String GAUSSIAN_BLUR = "GaussianBlur"; + public static final String MEDIAN = "Median"; + + +@DisplayName("When MEDIAN is selected as method in PretreatmentParameters the medianFilterDenoising method is used" ) + @Test + void aMedianFilterAsParameter_whenTreated_usesTheMedianFilterDenoisingMethod() throws DataValidationException + { + + PretreatmentParameters parameters = new PretreatmentParameters( MEDIAN, new double []{2} ); + RandomAccessibleInterval< FloatType > rai = new ArrayImgFactory<>(new FloatType()).create( 5, 5, 5 ); + Pretreatment<FloatType> pretreatment = new Pretreatment<>(rai, parameters); + Pretreatment<FloatType> pretreatmentSpy = Mockito.spy( pretreatment ); + pretreatmentSpy.run(); + Mockito.verify(pretreatmentSpy, Mockito.times( 1 )).medianFilterDenoising(any(RandomAccessibleInterval.class)); + } + + + @DisplayName("When GAUSSIAN_BLUR is selected as method in PretreatmentParameters the gaussianBlurFilterDenoising method is used" ) + @Test + void aGaussianBlurFilterAsParameter_whenTreated_usesTheMedianFilterDenoisingMethod() throws DataValidationException + { + PretreatmentParameters parameters = new PretreatmentParameters( GAUSSIAN_BLUR, new double []{1, 1, 0} ); + RandomAccessibleInterval<FloatType> rai = new ArrayImgFactory<>(new FloatType()).create( 5, 5, 5 ); + Pretreatment<FloatType> pretreatment = new Pretreatment<>(rai, parameters); + Pretreatment<FloatType> pretreatmentSpy = Mockito.spy( pretreatment ); + pretreatmentSpy.run(); + Mockito.verify(pretreatmentSpy, Mockito.times( 1 )).gaussianBlurFilterDenoising(any(RandomAccessibleInterval.class)); + } +} diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/element/PixelsTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/element/PixelsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..e582ebf6897b82cce08e8f57fa36cec1b98efd79 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/element/PixelsTest.java @@ -0,0 +1,79 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.element; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +class PixelsTest +{ + + @DisplayName(" The check of a Coordinate already in a Pixel returns TRUE" ) + @Test + void APixel_whenCheckingIfItContainsACoordinateAlreadyThere_ReturnsTrue() + { + Coordinate c1 = new Coordinate(0, 0, 0); + Coordinate c2 = new Coordinate( 0, 0, 4); + + Pixels pixels = new Pixels( c1 ); + pixels.add(c2); + assertThat(pixels.contains( c2 )).isTrue(); + + + } + + @Test + void add() + { + } + + @Test + void addAll() + { + } + + @Test + void get() + { + } + + @Test + void resetSideCoordinate() + { + } + + @Test + void size() + { + } + + @Test + void testGet() + { + } + + @Test + void containsAll() + { + } + + @Test + void testEquals() + { + Coordinate c1 = new Coordinate(0, 0, 0); + Coordinate c2 = new Coordinate( 0, 0, 4); + + Pixels pixels = new Pixels( c1 ); + pixels.add(c2); + + Pixels pixels2 = new Pixels( c1 ); + pixels2.add( c2 ); + assertThat( pixels ).isEqualTo( pixels2 ); + } + + @Test + void testToString() + { + } +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/element/SurfaceTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/element/SurfaceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..dc7a896d21df98b52ed1c5913e1822308984ee37 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/element/SurfaceTest.java @@ -0,0 +1,70 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.element; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class SurfaceTest +{ + + @Test + void hasDuplicate() + { + } + + @Test + void sameSurface() + { + } + + @Test + void merge() + { + } + + @Test + void transpose() + { + } + + @Test + void get() + { + } + + @Test + void testGet() + { + } + + @Test + void set() + { + } + + @Test + void set1() + { + + } + + @Test + void getZMap() + { + } + + @Test + void getSize() + { + } + + @Test + void getWidth() + { + } + + @Test + void getHeight() + { + } +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PixelClassificationParametersTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PixelClassificationParametersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8da9a58322093217520ff43f4a6ddd5bf5ae1413 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PixelClassificationParametersTest.java @@ -0,0 +1,47 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelClassificationParameters; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class PixelClassificationParametersTest +{ + @DisplayName( "A good set of parameters don't raise any exception" ) + @Test + void rightPixelClassificationParameters_whenCreatingANewInstance_DonTRaiseAValidationDataException() + { + double amplitudeThreshold = 1.0; + double otsuThreshold = 1.5; + assertDoesNotThrow( () -> new PixelClassificationParameters(amplitudeThreshold, otsuThreshold) ); + } + + + @DisplayName( " An amplitude threshold inferior to zero raises a ValidationDataException with a specific message" ) + @Test + void amplitudeThresholdUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() + { + double amplitudeThreshold = -1; + double otsuThreshold = 1.5; + DataValidationException exception = assertThrows(DataValidationException.class, () -> + new PixelClassificationParameters(amplitudeThreshold, otsuThreshold) ); + assertThat(exception).hasMessageContaining( "Amplitude" ); + } + + @DisplayName( " An otsu threshold inferior to zero raises a ValidationDataException with a specific message " ) + @Test + void otsuThresholdUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() + { + double amplitudeThreshold = 1; + double otsuThreshold = -1; + DataValidationException exception = assertThrows(DataValidationException.class, () -> + new PixelClassificationParameters(amplitudeThreshold, otsuThreshold) ); + assertThat(exception).hasMessageContaining( "Otsu" ); + } + + +} diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PixelSelectionParametersTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PixelSelectionParametersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9fe6c848ee7ffbb11294030179f6007e10ac34d3 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PixelSelectionParametersTest.java @@ -0,0 +1,68 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PixelSelectionParameters; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.*; + +public class PixelSelectionParametersTest +{ + +// @Test +// void rightPixelSelectionParameters_whenCreatingANewInstance_DonTRaiseAValidationDataException() +// { +// double amplitudeThreshold = 1.0; +// double otsuThreshold = 1.5; +// double sigmaXY = 2.0; +// double sigmaZ = 3.0; +// assertDoesNotThrow( () -> new PixelSelectionParameters(amplitudeThreshold, otsuThreshold, sigmaXY, sigmaZ) ); +// } +// +// +// @Test +// void amplitudeThresholdUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() +// { +// double amplitudeThreshold = -1; +// double otsuThreshold = 1.5; +// double sigmaXY = 2.0; +// double sigmaZ = 3.0; +// DataValidationException exception = assertThrows(DataValidationException.class, () -> new PixelSelectionParameters(amplitudeThreshold, otsuThreshold, sigmaXY, sigmaZ) ); +// assertThat(exception).hasMessageContaining( "Amplitude" ); +// } +// +// @Test +// void otsuThresholdUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() +// { +// double amplitudeThreshold = 1; +// double otsuThreshold = -1; +// double sigmaXY = 2.0; +// double sigmaZ = 3.0; +// DataValidationException exception = assertThrows(DataValidationException.class, () -> new PixelSelectionParameters(amplitudeThreshold, otsuThreshold, sigmaXY, sigmaZ) ); +// assertThat(exception).hasMessageContaining( "Otsu" ); +// } +// +// @Test +// void sigmaXYThresholdUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() +// { +// double amplitudeThreshold = 1; +// double otsuThreshold = 1; +// double sigmaXY = -1; +// double sigmaZ = 3.0; +// DataValidationException exception = assertThrows(DataValidationException.class, () -> new PixelSelectionParameters(amplitudeThreshold, otsuThreshold, sigmaXY, sigmaZ) ); +// assertThat(exception).hasMessageContaining( "XY" ); +// } +// +// @Test +// void sigmaZThresholdUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() +// { +// double amplitudeThreshold = 1; +// double otsuThreshold = 1; +// double sigmaXY = 1; +// double sigmaZ = -1; +// DataValidationException exception = assertThrows(DataValidationException.class, () -> new PixelSelectionParameters(amplitudeThreshold, otsuThreshold, sigmaXY, sigmaZ) ); +// assertThat(exception).hasMessageContaining( "Z" ); +// } + +} diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PretreatmentParametersTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PretreatmentParametersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..c59e65261e891ffd6ad93dd15b6d93383f2f7357 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/PretreatmentParametersTest.java @@ -0,0 +1,49 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvFileSource; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters.GAUSSIAN_BLUR; +import static fr.pasteur.ida.zellige.surfaceConstruction.parameters.maximumSelection.PretreatmentParameters.MEDIAN; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class PretreatmentParametersTest +{ + @DisplayName( "Any method associated with the right number of parameters should not raise any exception" ) + @ParameterizedTest + @CsvSource( {GAUSSIAN_BLUR +", 1, 1, 0", GAUSSIAN_BLUR+", 2, 0, 3"} ) + void gaussianBlurMethodWithThreeParameter_whenChecked_doesnTRaiseAnyException(String method, double a , double b, double c) + { + double [] values = new double []{a, b, c}; + assertDoesNotThrow( () ->new PretreatmentParameters( method, values )); + } + + @DisplayName( "A wrong number of parameters for any method should raise a DataValidationException with a specific message" ) + @ParameterizedTest + @ValueSource(strings = { GAUSSIAN_BLUR, MEDIAN }) + + void gaussianBlurMethodWithLessThanThreeParameter_whenChecked_RaisesAnException(String method) + { + double [] values = new double []{0, 0, 0, 0}; + DataValidationException exception = assertThrows(DataValidationException.class, () -> new PretreatmentParameters( method, values)); + assertThat(exception).hasMessageContaining( method ); + } + + @DisplayName( "The wrong number of parameters for the median Filter should raise an exception" ) + @Test + void wrongDenoisingMethod_whenChecked_RaisesADataValidationException() + { + DataValidationException exception = assertThrows(DataValidationException.class, () -> + new PretreatmentParameters( MEDIAN, new double [0] )); + assertThat(exception).hasMessageContaining( MEDIAN); + } + +} diff --git a/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ProjectionParametersTest.java b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ProjectionParametersTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bf407212d58edf026a7dcccc25a18918bcad3e74 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/surfaceConstruction/parameters/ProjectionParametersTest.java @@ -0,0 +1,42 @@ +package fr.pasteur.ida.zellige.surfaceConstruction.parameters; + +import fr.pasteur.ida.zellige.exception.DataValidationException; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class ProjectionParametersTest +{ + + @Test + void rightProjectionParameters_WhenCreatingANewInstance_raisesAValidationDataException() + { + int delta = 1; + String projectionType = "MIP"; + assertDoesNotThrow( () -> new ProjectionParameters(delta, projectionType) ); + } + + @Test + void deltaUnderZero_WhenCreatingANewInstance_raisesAValidationDataException() + { + int delta = -1; + String projectionMethod = "MPI"; + DataValidationException exception = assertThrows(DataValidationException.class, () -> + new ProjectionParameters( delta, projectionMethod) ); + assertThat(exception).hasMessageContaining( "Delta" ); + } + + @Test + void unImplementedProjectionMethod_WhenCreatingANewInstance_raisesAValidationDataException() + { + int delta = 1; + String projectionMethod = "unknown method"; + DataValidationException exception = assertThrows(DataValidationException.class, () -> + new ProjectionParameters( delta, projectionMethod) ); + assertThat(exception).hasMessageContaining( projectionMethod ); + } + + +} diff --git a/src/test/java/fr/pasteur/ida/zellige/utils/LocalExtremaDetectionTest.java b/src/test/java/fr/pasteur/ida/zellige/utils/LocalExtremaDetectionTest.java new file mode 100644 index 0000000000000000000000000000000000000000..9a2e2793e2cfa9e805bbd078b3b30380647bca23 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/utils/LocalExtremaDetectionTest.java @@ -0,0 +1,19 @@ +package fr.pasteur.ida.zellige.utils; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class LocalExtremaDetectionTest +{ + + @Test + void findMinima() + { + } + + @Test + void findMaxima() + { + } +} \ No newline at end of file diff --git a/src/test/java/fr/pasteur/ida/zellige/utils/ReconstructionErrorsTest.java b/src/test/java/fr/pasteur/ida/zellige/utils/ReconstructionErrorsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..38e905753d985dfdd682bcca4d044d1edb5e2452 --- /dev/null +++ b/src/test/java/fr/pasteur/ida/zellige/utils/ReconstructionErrorsTest.java @@ -0,0 +1,106 @@ +package fr.pasteur.ida.zellige.utils; + +import fr.pasteur.ida.zellige.exception.DifferentReferenceTestedSizeException; +import fr.pasteur.ida.zellige.exception.NotAnHeightMapException; +import io.scif.img.IO; +import io.scif.img.SCIFIOImgPlus; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; + +public class ReconstructionErrorsTest +{ + + @DisplayName( " The reference height map is not null" ) + @Test + void whenTheReferenceHMisNullAnExceptionIsRaised() + { + ImgFactory< IntType> testedFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval<IntType> tested = testedFactory.create( 4, 5 ); + assertThrows( NullPointerException.class, ()-> new ReconstructionErrors<IntType, IntType>( null, tested )); + } + + @DisplayName( " The tested height map is not null" ) + @Test + void whenTheTestedHMisNullAnExceptionIsRaised() + { + ImgFactory< IntType> refFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval< IntType > ref = refFactory.create( 5, 5 ); + assertThrows( NullPointerException.class, ()-> new ReconstructionErrors<IntType, IntType>( ref, null )); + } + + @DisplayName( "The reference height map is a 2D image" ) + @Test + void a3DImageCannotBeAReferenceHeightMap() + { + ImgFactory< IntType> refFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval< IntType > ref = refFactory.create( 5, 5, 5 ); + ImgFactory< IntType> testedFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval<IntType> tested = testedFactory.create( 4, 5 ); + assertThrows( NotAnHeightMapException.class, ()-> new ReconstructionErrors<>( ref, tested )); + } + + @DisplayName( "The tested height map is a 2D image" ) + @Test + void a3DImageCannotBeATestedHeightMap() + { + ImgFactory< IntType> refFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval< IntType > ref = refFactory.create( 5, 5 ); + ImgFactory< IntType> testedFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval<IntType> tested = testedFactory.create( 4, 5, 2 ); + assertThrows( NotAnHeightMapException.class, ()-> new ReconstructionErrors<>( ref, tested )); + } + + @DisplayName( " The reference height map has the same size as the tested height map" ) + @Test + void whenTheReferenceHMAndTheTestedHMHaveADifferentSizeAnExceptionIsRaised() + { + ImgFactory< IntType> refFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval< IntType > ref = refFactory.create( 5, 5 ); + ImgFactory< IntType> testedFactory = new ArrayImgFactory<>(new IntType()); + RandomAccessibleInterval<IntType> tested = testedFactory.create( 4, 5 ); + assertThrows( DifferentReferenceTestedSizeException.class, ()-> new ReconstructionErrors<>( ref, tested )); + } + + + @DisplayName( "Two identical image has an RMSE equals to 0 " ) + @Test + < T extends RealType< T > & NativeType< T >, R extends RealType< R > & NativeType< R > > + void whenTheRefHMAndTheTestedHMAreIdenticalTheRMSEEqualsZero() throws DifferentReferenceTestedSizeException, NotAnHeightMapException + { + final String imagePath = "doc/fakeHeightMap.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + + final Img< T > ref = ( Img< T > ) imgPlus.getImg(); + final Img < R > tested = ( Img< R > ) imgPlus.getImg(); + ReconstructionErrors< T, R > determination = new ReconstructionErrors<>( ref, tested ); + determination.compute(); + assertThat(determination.getRMSEValue() == 0 ).isTrue(); + + } + + @DisplayName( "Two identical image has an reconstruction proportion equals to 1 " ) + @Test + < T extends RealType< T > & NativeType< T >, R extends RealType< R > & NativeType< R > > + void whenTheRefHMAndTheTestedHMAreIdenticalTheReconstructionProportionEqualsOne() throws DifferentReferenceTestedSizeException, NotAnHeightMapException + { + final String imagePath = "doc/fakeHeightMap.tif"; + final SCIFIOImgPlus< ? > imgPlus = IO.openImgs( imagePath ).get( 0 ); + + final Img< T > ref = ( Img< T > ) imgPlus.getImg(); + final Img < R > tested = ( Img< R > ) imgPlus.getImg(); + ReconstructionErrors< T, R > determination = new ReconstructionErrors<>( ref, tested ); + determination.compute(); + assertThat(determination.getReconstructedPixelsPercentage() == 1); + + } +} diff --git a/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/SandTest.java b/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/SandTest.java index 3780d51e8d843523d8bdc47ae43a400ec7cb2be1..361f5fc68942e111b64383ed1844f5344a44ae9a 100644 --- a/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/SandTest.java +++ b/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/SandTest.java @@ -7,12 +7,6 @@ import static org.junit.jupiter.api.Assertions.*; class SandTest { - @Test - void getIslandStatus() - { - Sand sand = new Sand( 0, 0, 0 ); - assertEquals( 0, sand.getIslandStatus() ); - } @Test void setIslandStatus() diff --git a/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/WorldTest.java b/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/WorldTest.java index f92aff74cc3c7ccc66d11c742c08ba366343c9e9..f2f14e76df1c955275af203eab336d1cf4eaa735 100644 --- a/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/WorldTest.java +++ b/src/test/java/fr/pasteur/ida/zellige/utils/islandSearch/WorldTest.java @@ -34,10 +34,10 @@ class WorldTest - world.run(); - assertEquals( world.getSand( 0, 1, 0 ).getIslandStatus(), 1 ); - assertEquals( world.getSand( 8, 0, 0 ).getIslandStatus(), - 1 ); - assertEquals( world.getSand( 0, 0, 1 ).getIslandStatus(), 1 ); +// world.run(); +// assertEquals( world.getSand( 0, 1, 0 ).getIslandStatus(), 1 ); +// assertEquals( world.getSand( 8, 0, 0 ).getIslandStatus(), - 1 ); +// assertEquals( world.getSand( 0, 0, 1 ).getIslandStatus(), 1 ); } @Test @@ -77,12 +77,8 @@ class WorldTest Sand s5 = world.getSand(0, 2, 1 ) ; Sand s6 = world.getSand(0, 0, 1 ) ; assertEquals( s3.getIslandStatus(), 1 ); - assertEquals( s4.getIslandStatus(), 0 ); - assertEquals( s5.getIslandStatus(), 0 ); - assertEquals( s6.getIslandStatus(), 0 ); - world.setIslandStatus( s6 ); - assertEquals( s6.getIslandStatus(), 1 ); + } diff --git a/src/test/resources/MaximumsParameterSweep.csv b/src/test/resources/MaximumsParameterSweep.csv new file mode 100644 index 0000000000000000000000000000000000000000..45b08fc5cb7e4f7e8ea7c77c86ec422c94bfeb55 --- /dev/null +++ b/src/test/resources/MaximumsParameterSweep.csv @@ -0,0 +1,50 @@ +;;Pretreatment;;Classification;;PostTreatment;;;;Construction;;;;; +;;Filter ;Filter Parameters;Amplitude Threshold;Otsu Threshold;IS Connexity;IS minimum size;XY blur;Z Blur;Starting threshold %;overlap;connexity rate;Starting threshold %;overlap;connexity rate +;;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0;Median;2;0;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.1;Median;2;0.1;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.2;Median;2;0.2;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.3;Median;2;0.3;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.4;Median;2;0.4;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.5;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.6;Median;2;0.6;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.7;Median;2;0.7;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.8;Median;2;0.8;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;0.9;Median;2;0.9;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +AmplitudeThreshold;1;Median;2;1;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0;Median;2;0.5;0;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.1;Median;2;0.5;0.1;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.2;Median;2;0.5;0.2;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.3;Median;2;0.5;0.3;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.4;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.5;Median;2;0.5;0.5;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.6;Median;2;0.5;0.6;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.7;Median;2;0.5;0.7;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.8;Median;2;0.5;0.8;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;0.9;Median;2;0.5;0.9;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Otsu Threshold;1;Median;2;0.5;1;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +IS Connexity;4;Median;2;0.5;0.4;4;5;2;1;0.75;15;0.8;0.1;10;0.9 +IS Connexity;8;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;0;Median;2;0.5;0.4;8;0;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;1;Median;2;0.5;0.4;8;1;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;2;Median;2;0.5;0.4;8;2;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;3;Median;2;0.5;0.4;8;3;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;4;Median;2;0.5;0.4;8;4;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;5;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;6;Median;2;0.5;0.4;8;6;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;7;Median;2;0.5;0.4;8;7;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;8;Median;2;0.5;0.4;8;8;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;9;Median;2;0.5;0.4;8;9;2;1;0.75;15;0.8;0.1;10;0.9 +IS Min Size;10;Median;2;0.5;0.4;8;10;2;1;0.75;15;0.8;0.1;10;0.9 +Blur XY;0;Median;2;0.5;0.4;8;5;0;1;0.75;15;0.8;0.1;10;0.9 +Blur XY;1;Median;2;0.5;0.4;8;5;1;1;0.75;15;0.8;0.1;10;0.9 +Blur XY;2;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +Blur XY;3;Median;2;0.5;0.4;8;5;3;1;0.75;15;0.8;0.1;10;0.9 +Blur XY;4;Median;2;0.5;0.4;8;5;4;1;0.75;15;0.8;0.1;10;0.9 +Blur XY;5;Median;2;0.5;0.4;8;5;5;1;0.75;15;0.8;0.1;10;0.9 +BlurZ;0;Median;2;0.5;0.4;8;5;2;0;0.75;15;0.8;0.1;10;0.9 +BlurZ;1;Median;2;0.5;0.4;8;5;2;1;0.75;15;0.8;0.1;10;0.9 +BlurZ;2;Median;2;0.5;0.4;8;5;2;2;0.75;15;0.8;0.1;10;0.9 +BlurZ;3;Median;2;0.5;0.4;8;5;2;3;0.75;15;0.8;0.1;10;0.9 +BlurZ;4;Median;2;0.5;0.4;8;5;2;4;0.75;15;0.8;0.1;10;0.9 +BlurZ;5;Median;2;0.5;0.4;8;5;2;5;0.75;15;0.8;0.1;10;0.9 diff --git a/src/test/resources/TestTTT.csv b/src/test/resources/TestTTT.csv new file mode 100644 index 0000000000000000000000000000000000000000..7322a1a9795b98925fec409ebe30207465af325b --- /dev/null +++ b/src/test/resources/TestTTT.csv @@ -0,0 +1,2 @@ +Median;2 +Prout;1 diff --git a/src/test/resources/test.csv b/src/test/resources/test.csv new file mode 100644 index 0000000000000000000000000000000000000000..eb2eaef1585db85b5b9e23a0d191eeaea7d68089 --- /dev/null +++ b/src/test/resources/test.csv @@ -0,0 +1,2 @@ +Median;1 +Median;2