From 232501b6762a9e381022457609c1593b2c2ef1ab Mon Sep 17 00:00:00 2001 From: kapoorlab Date: Thu, 8 Jul 2021 17:07:47 +0200 Subject: [PATCH] breaking changes --- .classpath | 41 +- comboSliderTextbox/SliderBoxGUI.java | 66 + dogGUI/CovistoDogPanel.java | 163 ++ functionPanel/CovistoFunctionPanel.java | 88 + kalmanGUI/CovistoKalmanPanel.java | 188 ++ kalmanGUI/CovistoNNPanel.java | 191 ++ layoutManager/Setlayout.java | 31 + loaddirectory/LeftRightDirectoryLoader.java | 54 + loadfile/CovistoFourChFileLoader.java | 64 + loadfile/CovistoOneChFileLoader.java | 48 + loadfile/CovistoThreeChForceFileLoader.java | 57 + loadfile/CovistoTwoChDropbox.java | 62 + loadfile/CovistoTwoChForceFileLoader.java | 53 + loadfile/CovistoTwoChOptionFileLoader.java | 53 + plugins.config | 1 - pom.xml | 50 +- removeSpotGUI/CovistoRemovePanel.java | 50 + scrollbar/Utility.java | 95 + src/main/java/analyzers/EdgeAnalyzer.java | 52 + src/main/java/analyzers/TrackAnalyzer.java | 66 + src/main/java/batchMode/ExecuteBatch.java | 1 - .../java/comboSliderTextbox/SliderBoxGUI.java | 66 + src/main/java/common3D/BinaryCreation.java | 103 ++ src/main/java/common3D/CommonWater.java | 139 ++ src/main/java/costMatrix/CostFunction.java | 32 + .../java/costMatrix/CostMatrixCreator.java | 63 + .../costMatrix/DefaultCostMatrixCreator.java | 265 +++ .../costMatrix/IntensityDiffCostFunction.java | 19 + .../JaqamanLinkingCostMatrixCreator.java | 189 ++ src/main/java/costMatrix/LAPJV.java | 525 ++++++ .../costMatrix/PixelRatioCostFunction.java | 28 + .../PixelratiowDistCostFunction.java | 47 + .../java/costMatrix/ResizableDoubleArray.java | 101 ++ .../java/costMatrix/SparseCostMatrix.java | 530 ++++++ .../costMatrix/SquareDistCostFunction.java | 26 + .../java/curvatureUtils/PointExtractor.java | 1 - .../java/distanceTransform/CreateBinary.java | 65 + .../CreateDistanceTransform.java | 164 ++ .../distanceTransform/CreateWatershed.java | 263 +++ .../java/distanceTransform/DistWatershed.java | 237 +++ .../DistWatershedBinary.java | 267 +++ .../distanceTransform/WatershedBinary.java | 220 +++ src/main/java/dog3D/ComputeDoG.java | 382 ++++ src/main/java/dog3D/ComputeParallelDoG.java | 323 ++++ src/main/java/dog3D/DogAll.java | 378 ++++ src/main/java/dogGUI/CovistoDogPanel.java | 163 ++ src/main/java/dogSeg/DOGSeg.java | 110 ++ src/main/java/drawUtils/DrawFunction.java | 2 - .../functionPanel/CovistoFunctionPanel.java | 88 + .../AdvancedSnakeListener.java | 75 + .../CovistoStandardMouseListener.java | 69 + .../DoDOGListener.java | 65 + .../DoMSERListener.java | 68 + .../DoWatershedListener.java | 68 + .../FindMaximaListener.java | 41 + .../FindMaximaMserListener.java | 41 + .../FindMinimaListener.java | 40 + .../FindMinimaMserListener.java | 39 + .../GradientListener.java | 31 + .../InteractiveMethods.java | 896 ++++++++++ .../IterationListener.java | 31 + .../MaxdistListener.java | 31 + .../PREAlphaListener.java | 52 + .../PREApplyDog3DListener.java | 51 + .../PREApplyWaterCurrentListener.java | 49 + .../PREBetaListener.java | 51 + .../PREDeltaListener.java | 56 + .../PREDoSegmodeListener.java | 33 + .../PREDoTrackmodeListener.java | 31 + .../PREIniSearchListener.java | 53 + .../PRELostFrameListener.java | 29 + .../PREMaxSearchListener.java | 58 + .../PREMaxSearchTListener.java | 53 + .../PREMaxSizeListener.java | 56 + .../PREMinDiversityListener.java | 55 + .../PREMinSizeListener.java | 52 + .../PRERoiListener.java | 28 + .../PREShowBinary.java | 27 + .../interactivePreprocessing/PREShowDist.java | 28 + .../PREShowWatershed.java | 30 + .../PREUnstability_ScoreListener.java | 52 + .../interactivePreprocessing/PREauto.java | 40 + .../interactivePreprocessing/PREdist.java | 35 + .../PreCurrentMovieListener.java | 28 + .../PreDistThresholdListener.java | 34 + .../PreNonStandardMouseListener.java | 35 + .../PreSigmaListener.java | 49 + .../PreThresholdListener.java | 55 + .../PreTimeDistThresholdListener.java | 35 + .../PreTimeListener.java | 67 + .../PreTimeSkipListener.java | 36 + .../PreTlocListener.java | 85 + .../PreUploadMovieListener.java | 50 + .../PreWaterListener.java | 48 + .../PreZListener.java | 72 + .../PreZlocListener.java | 85 + .../PreprocessingFileChooser.java | 190 ++ .../interactivePreprocessing/ShowView.java | 66 + .../java/interactivePreprocessing/ZMser.java | 129 ++ .../SortedSegmentDepthFirstIterator.java | 1 - .../java/kalmanGUI/CovistoKalmanPanel.java | 188 ++ src/main/java/kalmanGUI/CovistoNNPanel.java | 191 ++ src/main/java/layoutManager/Setlayout.java | 31 + src/main/java/linkers/AlphanumComparator.java | 83 + src/main/java/linkers/BlobTracker.java | 38 + src/main/java/linkers/CVMKalmanFilter.java | 184 ++ src/main/java/linkers/Dimension.java | 16 + src/main/java/linkers/ExtensionRealPoint.java | 29 + src/main/java/linkers/FeatureModel3D.java | 668 +++++++ src/main/java/linkers/FlagNode.java | 25 + .../java/linkers/FromContinuousBranches.java | 138 ++ src/main/java/linkers/Function1.java | 21 + src/main/java/linkers/JaqamanLinker.java | 274 +++ src/main/java/linkers/KFsearch.java | 371 ++++ src/main/java/linkers/Logger.java | 346 ++++ src/main/java/linkers/Model3D.java | 877 +++++++++ src/main/java/linkers/NNFlagsearchKDtree.java | 91 + src/main/java/linkers/PRENNsearch.java | 198 +++ .../linkers/SortedDepthFirstIterator.java | 544 ++++++ src/main/java/linkers/ThreeDBlobTracker.java | 39 + .../TimeDirectedDepthFirstIterator.java | 41 + .../TimeDirectedDepthFirstIterator3D.java | 41 + .../linkers/TimeDirectedNeighborIndex.java | 306 ++++ .../linkers/TimeDirectedNeighborIndex3D.java | 306 ++++ .../TimeDirectedSortedDepthFirstIterator.java | 68 + ...imeDirectedSortedDepthFirstIterator3D.java | 68 + src/main/java/linkers/TrackModel.java | 1396 +++++++++++++++ src/main/java/linkers/TrackModel3D.java | 1396 +++++++++++++++ src/main/java/listeners/AngleListener.java | 107 -- .../listeners/ManualInterventionListener.java | 25 - src/main/java/listeners/RedoListener.java | 63 - .../LeftRightDirectoryLoader.java | 54 + .../loadfile/CovistoFourChFileLoader.java | 64 + .../java/loadfile/CovistoOneChFileLoader.java | 48 + .../CovistoThreeChForceFileLoader.java | 57 + .../java/loadfile/CovistoTwoChDropbox.java | 62 + .../loadfile/CovistoTwoChForceFileLoader.java | 53 + .../CovistoTwoChOptionFileLoader.java | 53 + src/main/java/mser3D/ComputeCompTree.java | 114 ++ src/main/java/mser3D/MserAll.java | 213 +++ src/main/java/mserGUI/CovistoMserPanel.java | 209 +++ src/main/java/mserMethods/MSERSeg.java | 119 ++ .../CovistoNearestNPanel.java | 51 + src/main/java/pluginTools/ComputeAngles.java | 283 --- .../pluginTools/ComputeAnglesCurrent.java | 301 ---- src/main/java/pluginTools/ComputeManual.java | 247 --- src/main/java/pluginTools/Computeinwater.java | 82 +- src/main/java/pluginTools/EllipseTrack.java | 268 +-- .../InteractiveSimpleEllipseFit.java | 69 +- src/main/java/plugins.config | 2 +- src/main/java/preProcessing/AddGaussian.java | 146 ++ .../preProcessing/FlatFieldCorrection.java | 252 +++ .../java/preProcessing/FlatFieldOnly.java | 237 +++ .../java/preProcessing/GenericFilters.java | 189 ++ .../java/preProcessing/GetLocalmaxminMT.java | 891 ++++++++++ .../preProcessing/GlobalThresholding.java | 166 ++ src/main/java/preProcessing/Kernels.java | 888 +++++++++ .../java/preProcessing/MedianFilter2D.java | 159 ++ .../java/preProcessing/MedianFilterImg2D.java | 159 ++ .../java/preProcessing/MedianFilterOnly.java | 213 +++ .../java/preProcessing/MinMaxIntensity.java | 57 + .../java/preProcessing/Normalization.java | 93 + src/main/java/preProcessing/Otsu.java | 305 ++++ src/main/java/preProcessing/PrePipeline.java | 63 + src/main/java/preProcessing/Utils.java | 479 +++++ .../ransac/Catastrophy/DetectCatastrophy.java | 111 ++ .../java/ransac/Display/DisplayHistogram.java | 119 ++ .../java/ransac/Display/DisplayPoints.java | 41 + src/main/java/ransac/FLSobject.java | 18 + .../Lengthdistro/LengthDistribution.java | 357 ++++ .../ransac/PointFunctionMatch/Function.java | 41 + .../PointFunctionMatch.java | 50 + src/main/java/ransac/Rateobject.java | 21 + .../java/ransac/loadFiles/Filereader.java | 92 + src/main/java/ransac/loadFiles/Tracking.java | 667 +++++++ .../java/ransacPoly/AbstractFunction.java | 56 + .../java/ransacPoly/AbstractFunction2D.java | 199 +++ src/main/java/ransacPoly/Function.java | 61 + .../HigherOrderPolynomialFunction.java | 208 +++ .../ransacPoly/InterpolatedPolynomial.java | 454 +++++ src/main/java/ransacPoly/LinearFunction.java | 382 ++++ src/main/java/ransacPoly/MatrixFunctions.java | 140 ++ src/main/java/ransacPoly/MixedPolynomial.java | 79 + .../ransacPoly/MixedPolynomialFunction.java | 91 + src/main/java/ransacPoly/NewtonRaphson.java | 159 ++ src/main/java/ransacPoly/Polynomial.java | 42 + .../java/ransacPoly/QuadraticFunction.java | 255 +++ src/main/java/ransacPoly/RansacFunction.java | 87 + .../java/ransacPoly/RegressionFunction.java | 217 +++ src/main/java/ransacPoly/Sort.java | 32 + src/main/java/ransacPoly/Threepointfit.java | 103 ++ src/main/java/regionRemoval/RemoveTiny.java | 71 + .../removeSpotGUI/CovistoRemovePanel.java | 50 + .../java/saveTracks/CovistoSavePanel.java | 67 + src/main/java/scrollbar/Utility.java | 95 + src/main/java/snakeGUI/CovistoSnakePanel.java | 100 ++ .../Abstract3x3NeighbourhoodThinning.java | 167 ++ .../java/strategies/GuoHallAlgorithm.java | 177 ++ .../java/strategies/HilditchAlgorithm.java | 135 ++ .../strategies/MorphologicalThinning.java | 197 ++ .../java/strategies/ThinningStrategy.java | 42 + .../strategies/ThinningStrategyFactory.java | 125 ++ .../java/strategies/ZhangSuenAlgorithm.java | 155 ++ src/main/java/thinning/ThinningOp.java | 222 +++ .../java/timeGUI/CovistoTimeselectPanel.java | 79 + src/main/java/trackMateBridge/Bridging.java | 13 + src/main/java/userTESTING/BorisData.java | 42 + .../java/utility/ActionListenablePanel.java | 45 + src/main/java/utility/AnalyzeTTrack.java | 84 + src/main/java/utility/AnalzeZTrack.java | 82 + src/main/java/utility/ConfigurationPanel.java | 42 + src/main/java/utility/Convolvers.java | 1147 ++++++++++++ src/main/java/utility/CovistoModule.java | 79 + src/main/java/utility/CovistoSlicer.java | 288 +++ src/main/java/utility/CovistoUtils.java | 651 +++++++ src/main/java/utility/CovsitoProgressBar.java | 31 + src/main/java/utility/DetectorKeys.java | 111 ++ .../java/utility/ETrackScrollbarUtils.java | 45 + src/main/java/utility/FeatureAnalyzer3D.java | 56 + src/main/java/utility/FeatureFilter.java | 41 + src/main/java/utility/FinderUtils.java | 1583 +++++++++++++++++ .../utility/IlastikConnectedComponents.java | 263 +++ src/main/java/utility/Interpolator.java | 274 +++ src/main/java/utility/LabelRansac.java | 176 -- src/main/java/utility/Logger.java | 342 ++++ src/main/java/utility/ModelChangeEvent.java | 245 +++ .../java/utility/ModelChangeListener.java | 23 + src/main/java/utility/NNFlagsearchKDtree.java | 90 + src/main/java/utility/NormalIntersection.java | 157 -- src/main/java/utility/PreRoiobject.java | 333 ++++ src/main/java/utility/Settings.java | 763 ++++++++ src/main/java/utility/SortCoordinates.java | 35 + src/main/java/utility/SortListbyproperty.java | 105 ++ src/main/java/utility/SuperIntersection.java | 162 -- src/main/java/utility/ThreeDRoiobject.java | 444 +++++ .../java/utility/ThreeDRoiobjectAnalyzer.java | 15 + .../ThreeDRoiobjectAnalyzerFactory.java | 44 + .../utility/ThreeDRoiobjectCollection.java | 1208 +++++++++++++ .../java/utility/ThreeDRoiobjectDetector.java | 24 + .../ThreeDRoiobjectDetectorFactory.java | 127 ++ .../java/utility/ThreeDRoiobjectTracker.java | 44 + .../ThreeDRoiobjectTrackerFactory.java | 105 ++ src/main/java/utility/ViewUtils.java | 737 ++++++++ src/main/java/utils/AddGaussian.java | 148 ++ src/main/java/utils/DistanceMetrics.java | 19 + src/main/java/varun_algorithm/Algorithm.java | 50 + src/main/java/varun_algorithm/Benchmark.java | 45 + .../varun_algorithm/BenchmarkAlgorithm.java | 68 + .../java/varun_algorithm/MultiThreaded.java | 63 + .../MultiThreadedAlgorithm.java | 85 + .../MultiThreadedBenchmarkAlgorithm.java | 67 + .../java/varun_algorithm/OutputAlgorithm.java | 49 + .../varun_algorithm_binary/Thresholder.java | 167 ++ .../BuildComponentTree.java | 488 +++++ .../Component.java | 82 + .../ComponentForest.java | 57 + .../ComponentTree.java | 55 + .../PartialComponent.java | 144 ++ .../mser/ComputeDelta.java | 59 + .../mser/ComputeDeltaBrightToDark.java | 66 + .../mser/ComputeDeltaDarkToBright.java | 65 + .../mser/Mser.java | 196 ++ .../mser/MserEvaluationNode.java | 314 ++++ .../mser/MserPartialComponent.java | 206 +++ .../mser/MserPartialComponentGenerator.java | 104 ++ .../mser/MserTree.java | 428 +++++ .../pixellist/PixelList.java | 211 +++ .../pixellist/PixelListComponent.java | 151 ++ .../pixellist/PixelListComponentTree.java | 212 +++ .../pixellist/PixelListPartialComponent.java | 138 ++ .../PixelListPartialComponentGenerator.java | 104 ++ .../DifferenceOfGaussian.java | 253 +++ .../varun_algorithm_dog/DogDetection.java | 415 +++++ src/main/java/varun_algorithm_edge/Edgel.java | 99 ++ .../SubpixelEdgelDetection.java | 229 +++ .../java/varun_algorithm_fill/Filter.java | 50 + .../java/varun_algorithm_fill/FloodFill.java | 228 +++ .../java/varun_algorithm_fill/TypeWriter.java | 52 + .../java/varun_algorithm_fill/Writer.java | 48 + .../FloydSteinbergDithering.java | 287 +++ .../varun_algorithm_function/Function.java | 47 + .../varun_algorithm_function/NormMinMax.java | 60 + .../SubtractNorm.java | 62 + .../SubtractNormReal.java | 60 + .../SubtractReal.java | 52 + .../varun_algorithm_gauss/AbstractGauss.java | 911 ++++++++++ .../AbstractLineIterator.java | 158 ++ .../java/varun_algorithm_gauss/Gauss.java | 949 ++++++++++ .../varun_algorithm_gauss/GaussDouble.java | 371 ++++ .../varun_algorithm_gauss/GaussFloat.java | 371 ++++ .../varun_algorithm_gauss/GaussGeneral.java | 168 ++ .../GaussNativeType.java | 403 +++++ .../SamplingLineIterator.java | 120 ++ .../WritableLineIterator.java | 80 + .../ConvolverFactory.java | 75 + .../ConvolverNativeType.java | 179 ++ .../ConvolverNativeTypeBuffered.java | 171 ++ .../ConvolverNumericType.java | 167 ++ .../DoubleConvolverRealType.java | 195 ++ .../DoubleConvolverRealTypeBuffered.java | 146 ++ .../FloatConvolverRealType.java | 197 ++ .../FloatConvolverRealTypeBuffered.java | 148 ++ .../java/varun_algorithm_gauss3/Gauss3.java | 247 +++ .../SeparableSymmetricConvolution.java | 444 +++++ .../HessianMatrix.java | 390 ++++ .../PartialDerivative.java | 237 +++ .../ScaleAsFunctionOfPosition.java | 110 ++ .../varun_algorithm_integral/IntegralImg.java | 332 ++++ .../IntegralImgDouble.java | 94 + .../IntegralImgLong.java | 93 + .../ScaleAreaAveraging2d.java | 293 +++ .../ClipConvexPolytopeKDTree.java | 295 +++ .../ConvexPolytope.java | 100 ++ .../varun_algorithm_kdtree/HyperPlane.java | 105 ++ .../KDTreeNodeIterable.java | 122 ++ .../SplitHyperPlaneKDTree.java | 203 +++ .../VolumetricSearch.java | 257 +++ .../AllConnectedComponents.java | 297 ++++ .../ConnectedComponents.java | 715 ++++++++ .../varun_algorithm_labeling/Watershed.java | 354 ++++ .../EigenValues.java | 88 + .../EigenValues1D.java | 48 + .../EigenValues2DSquare.java | 67 + .../EigenValues2DSymmetric.java | 55 + .../EigenValuesSquare.java | 83 + .../EigenValuesSymmetric.java | 83 + .../TensorEigenValues.java | 393 ++++ .../RealCompositeMatrix.java | 127 ++ .../RealCompositeSquareMatrix.java | 62 + .../RealCompositeSymmetricMatrix.java | 86 + .../LocalExtrema.java | 240 +++ .../RefinedPeak.java | 79 + .../SubpixelLocalization.java | 536 ++++++ .../BlackTopHat.java | 634 +++++++ .../varun_algorithm_morphology/Closing.java | 639 +++++++ .../varun_algorithm_morphology/Dilation.java | 1012 +++++++++++ .../varun_algorithm_morphology/Erosion.java | 1001 +++++++++++ .../MorphologyUtils.java | 723 ++++++++ .../varun_algorithm_morphology/Opening.java | 632 +++++++ .../StructuringElements.java | 500 ++++++ .../varun_algorithm_morphology/TopHat.java | 646 +++++++ .../distance/Distance.java | 80 + .../distance/DistanceTransform.java | 802 +++++++++ .../EuclidianDistanceAnisotropic.java | 90 + .../distance/EuclidianDistanceIsotropic.java | 74 + .../table2d/Abstract3x3TableOperation.java | 118 ++ .../table2d/Branchpoints.java | 149 ++ .../table2d/Bridge.java | 146 ++ .../table2d/Clean.java | 145 ++ .../table2d/Endpoints.java | 147 ++ .../table2d/Fill.java | 145 ++ .../table2d/Hbreak.java | 145 ++ .../table2d/Life.java | 145 ++ .../table2d/Majority.java | 146 ++ .../table2d/Remove.java | 146 ++ .../table2d/Spur.java | 86 + .../table2d/Spur1.java | 137 ++ .../table2d/Spur2.java | 137 ++ .../table2d/Thicken.java | 152 ++ .../table2d/Thin.java | 100 ++ .../table2d/Thin1.java | 137 ++ .../table2d/Thin2.java | 137 ++ .../table2d/Vbreak.java | 145 ++ .../CenteredRectangleShape.java | 156 ++ .../DiamondTipsNeighborhood.java | 410 +++++ .../DiamondTipsNeighborhoodCursor.java | 168 ++ .../DiamondTipsNeighborhoodFactory.java | 41 + ...ondTipsNeighborhoodLocalizableSampler.java | 154 ++ .../DiamondTipsNeighborhoodRandomAccess.java | 162 ++ .../DiamondTipsNeighborhoodUnsafe.java | 77 + .../DiamondTipsShape.java | 209 +++ .../HorizontalLineNeighborhood.java | 404 +++++ .../HorizontalLineNeighborhoodCursor.java | 163 ++ .../HorizontalLineNeighborhoodFactory.java | 41 + ...talLineNeighborhoodLocalizableSampler.java | 163 ++ ...orizontalLineNeighborhoodRandomAccess.java | 161 ++ .../HorizontalLineNeighborhoodUnsafe.java | 79 + .../HorizontalLineShape.java | 246 +++ .../HyperSphereNeighborhood.java | 447 +++++ .../HyperSphereNeighborhoodCursor.java | 164 ++ .../HyperSphereNeighborhoodFactory.java | 49 + .../HyperSphereNeighborhoodRandomAccess.java | 149 ++ .../HyperSphereNeighborhoodUnsafe.java | 78 + .../HyperSphereShape.java | 191 ++ ...rsphereNeighborhoodLocalizableSampler.java | 156 ++ .../Neighborhood.java | 44 + .../PairOfPointsNeighborhood.java | 387 ++++ .../PairOfPointsNeighborhoodCursor.java | 162 ++ .../PairOfPointsNeighborhoodFactory.java | 42 + ...fPointsNeighborhoodLocalizableSampler.java | 155 ++ .../PairOfPointsNeighborhoodRandomAccess.java | 162 ++ .../PairOfPointsNeighborhoodUnsafe.java | 79 + .../PairOfPointsShape.java | 205 +++ .../PeriodicLineNeighborhood.java | 429 +++++ .../PeriodicLineNeighborhoodCursor.java | 162 ++ .../PeriodicLineNeighborhoodFactory.java | 41 + ...dicLineNeighborhoodLocalizableSampler.java | 158 ++ .../PeriodicLineNeighborhoodRandomAccess.java | 162 ++ .../PeriodicLineNeighborhoodUnsafe.java | 79 + .../PeriodicLineShape.java | 247 +++ .../RectangleNeighborhood.java | 379 ++++ .../RectangleNeighborhoodCursor.java | 182 ++ .../RectangleNeighborhoodFactory.java | 43 + ...ctangleNeighborhoodLocalizableSampler.java | 164 ++ .../RectangleNeighborhoodRandomAccess.java | 187 ++ .../RectangleNeighborhoodSkipCenter.java | 381 ++++ ...RectangleNeighborhoodSkipCenterUnsafe.java | 79 + .../RectangleNeighborhoodUnsafe.java | 79 + .../RectangleShape.java | 229 +++ .../varun_algorithm_neighborhood/Shape.java | 177 ++ .../Angleobject.java | 22 + .../BisectorEllipsoid.java | 110 ++ .../varun_algorithm_ransac_Ransac/Circle.java | 141 ++ .../ConnectedComponentCoordinates.java | 87 + .../DisplayasROI.java | 87 + .../DistPointHyperEllipsoid.java | 210 +++ .../EberlyCode.java | 1375 ++++++++++++++ .../Ellipsoid.java | 42 + .../FitCircle.java | 248 +++ .../FitEllipsoid.java | 108 ++ .../FitLocalEllipsoid.java | 56 + .../HyperEllipsoid.java | 221 +++ .../NewtonRaphsonEllipsoid.java | 175 ++ .../NewtonRaphsonPolynomial.java | 180 ++ .../NumericalSolvers.java | 8 + .../RansacFunctionEllipsoid.java | 34 + .../ResultRoot.java | 18 + .../Solvers.java | 575 ++++++ .../SortRealLocalizable.java | 115 ++ .../SortSegments.java | 43 + .../varun_algorithm_region/BresenhamLine.java | 304 ++++ .../varun_algorithm_region/CircleCursor.java | 335 ++++ .../java/varun_algorithm_region/Circles.java | 193 ++ .../varun_algorithm_region/EllipseCursor.java | 423 +++++ .../java/varun_algorithm_region/Ellipses.java | 209 +++ .../hypersphere/HyperSphere.java | 256 +++ .../hypersphere/HyperSphereCursor.java | 265 +++ .../old/LocalNeighborhood.java | 221 +++ .../old/LocalNeighborhood2.java | 219 +++ .../old/LocalNeighborhoodCursor.java | 287 +++ .../old/LocalNeighborhoodCursor2.java | 204 +++ .../varun_algorithm_stats/ComputeMinMax.java | 239 +++ .../java/varun_algorithm_stats/Histogram.java | 251 +++ .../HistogramBinMapper.java | 88 + .../varun_algorithm_stats/IntBinMapper.java | 150 ++ src/main/java/varun_algorithm_stats/Max.java | 65 + src/main/java/varun_algorithm_stats/Min.java | 66 + .../java/varun_algorithm_stats/Normalize.java | 78 + .../varun_algorithm_stats/RealBinMapper.java | 132 ++ .../java/varun_algorithm_tree/Forest.java | 58 + src/main/java/varun_algorithm_tree/Tree.java | 53 + .../java/varun_algorithm_tree/TreeNode.java | 63 + .../java/varun_algorithm_tree/TreeUtils.java | 92 + src/main/java/varun_multithreading/Chunk.java | 64 + .../SimpleMultiThreading.java | 177 ++ .../AbstractCovistoModelView.java | 124 ++ .../java/visualization/CategoryJComboBox.java | 290 +++ .../visualization/ColorByFeatureGUIPanel.java | 548 ++++++ .../java/visualization/CovistoModelView.java | 311 ++++ src/main/java/visualization/Draw3DLines.java | 94 + src/main/java/visualization/DrawCircles.java | 237 +++ .../DummyThreeDRoiobjectColorGenerator.java | 75 + .../DummyTrackColorGenerator.java | 79 + .../visualization/FeatureColorGenerator.java | 51 + .../visualization/HyperStackDisplayer.java | 0 .../visualization/InterpolatePaintScale.java | 147 ++ .../ManualEdgeColorAnalyzer.java | 157 ++ .../ManualSpotColorAnalyzerFactory.java | 160 ++ .../java/visualization/MinMaxAdjustable.java | 76 + src/main/java/visualization/NearestRoi.java | 72 + .../visualization/SelectionChangeEvent.java | 84 + .../SelectionChangeListener.java | 22 + .../java/visualization/SelectionModel.java | 320 ++++ .../visualization/ThreeDRoiobjectOverlay.java | 279 +++ .../visualization/TrackColorGenerator.java | 37 + src/main/java/visualization/TrackOverlay.java | 400 +++++ src/main/java/visualization/Visualize3D.java | 277 +++ src/main/java/watershed3D/WatershedAll.java | 112 ++ .../watershedCurrent/WatershedCurrent.java | 80 + .../watershedGUI/CovistoWatershedPanel.java | 99 ++ src/main/java/zGUI/CovistoZselectPanel.java | 70 + target/classes/batchMode/ExecuteBatch.class | Bin 4709 -> 4709 bytes target/classes/batchMode/LocalPrefs.class | Bin 18584 -> 18584 bytes .../curvatureUtils/DisplaySelected$1.class | Bin 1942 -> 1942 bytes .../curvatureUtils/DisplaySelected$2$1.class | Bin 1334 -> 1330 bytes .../curvatureUtils/DisplaySelected$2.class | Bin 3505 -> 3505 bytes .../curvatureUtils/PointExtractor.class | Bin 5081 -> 5081 bytes target/classes/drawUtils/DrawFunction.class | Bin 4915 -> 4915 bytes .../hashMapSorter/SortCoordinates$1.class | Bin 977 -> 973 bytes .../classes/hashMapSorter/SortNodes$1.class | Bin 1658 -> 1654 bytes .../classes/hashMapSorter/SortNodes$2.class | Bin 1664 -> 1660 bytes .../classes/hashMapSorter/SortTimeorZ$1.class | Bin 1325 -> 1321 bytes .../classes/hashMapSorter/SortTimeorZ$2.class | Bin 1496 -> 1492 bytes .../classes/hashMapSorter/SortTimeorZ$3.class | Bin 1521 -> 1517 bytes .../kalmanForSegments/KFSegmentsearch$1.class | Bin 1361 -> 1357 bytes .../KFSegmentsearch$ComparableRealPoint.class | Bin 1138 -> 1116 bytes .../SegmentobjectCollection$1.class | Bin 893 -> 889 bytes .../SegmentobjectCollection$2.class | Bin 1638 -> 1634 bytes .../SegmentobjectCollection$3.class | Bin 2346 -> 2342 bytes .../SegmentobjectCollection$4.class | Bin 2400 -> 2396 bytes .../SegmentobjectCollection$5.class | Bin 1116 -> 1112 bytes .../SegmentobjectCollection$6.class | Bin 2151 -> 2147 bytes ...DepthFirstIterator$DirectedSpecifics.class | Bin 1335 -> 1335 bytes ...thFirstIterator$FlyweightVertexEvent.class | Bin 1134 -> 1134 bytes ...dSegmentDepthFirstIterator$Specifics.class | Bin 769 -> 769 bytes ...pthFirstIterator$UndirectedSpecifics.class | Bin 1284 -> 1284 bytes ...SegmentDepthFirstIterator$VisitColor.class | Bin 1389 -> 1389 bytes .../SortedSegmentDepthFirstIterator.class | Bin 8523 -> 8523 bytes .../TrackSegmentModel$1.class | Bin 1315 -> 1311 bytes .../IntersectionobjectCollection$1.class | Bin 911 -> 907 bytes .../IntersectionobjectCollection$2.class | Bin 1672 -> 1668 bytes .../IntersectionobjectCollection$3.class | Bin 2380 -> 2376 bytes .../IntersectionobjectCollection$4.class | Bin 2434 -> 2430 bytes .../IntersectionobjectCollection$5.class | Bin 1126 -> 1122 bytes .../IntersectionobjectCollection$6.class | Bin 2187 -> 2183 bytes target/classes/kalmanTracker/KFsearch$1.class | Bin 1229 -> 1225 bytes .../KFsearch$ComparableRealPoint.class | Bin 1076 -> 1054 bytes .../classes/kalmanTracker/TrackModel$1.class | Bin 1275 -> 1271 bytes target/classes/listeners/AngleListener.class | Bin 2650 -> 0 bytes .../classes/listeners/DegreeListener$1.class | Bin 1213 -> 1209 bytes .../listeners/IncrementListener$1.class | Bin 1231 -> 1227 bytes .../listeners/InsideLocListener$1.class | Bin 1386 -> 1382 bytes .../ManualInterventionListener.class | Bin 747 -> 0 bytes .../listeners/MinInlierLocListener$1.class | Bin 1249 -> 1245 bytes target/classes/listeners/RedoListener.class | Bin 1811 -> 0 bytes .../listeners/ResolutionListener$1.class | Bin 1237 -> 1233 bytes .../listeners/SecDegreeListener$1.class | Bin 1231 -> 1227 bytes target/classes/listeners/TlocListener$1.class | Bin 2789 -> 2785 bytes target/classes/listeners/ZlocListener$1.class | Bin 2864 -> 2860 bytes .../classes/pluginTools/ComputeAngles$1.class | Bin 1327 -> 0 bytes .../classes/pluginTools/ComputeAngles$2.class | Bin 1419 -> 0 bytes .../classes/pluginTools/ComputeAngles$3.class | Bin 1419 -> 0 bytes .../classes/pluginTools/ComputeAngles$4.class | Bin 1027 -> 0 bytes .../classes/pluginTools/ComputeAngles.class | Bin 8296 -> 0 bytes .../pluginTools/ComputeAnglesCurrent$1.class | Bin 1355 -> 0 bytes .../pluginTools/ComputeAnglesCurrent$2.class | Bin 1461 -> 0 bytes .../pluginTools/ComputeAnglesCurrent$3.class | Bin 1461 -> 0 bytes .../pluginTools/ComputeAnglesCurrent$4.class | Bin 1069 -> 0 bytes .../pluginTools/ComputeAnglesCurrent.class | Bin 9251 -> 0 bytes .../pluginTools/ComputeCurvature$1.class | Bin 1389 -> 1385 bytes .../pluginTools/ComputeCurvature$2.class | Bin 1389 -> 1385 bytes .../pluginTools/ComputeCurvature$3.class | Bin 997 -> 993 bytes .../pluginTools/ComputeCurvature$4.class | Bin 997 -> 993 bytes .../pluginTools/ComputeCurvature$5.class | Bin 1394 -> 1390 bytes .../pluginTools/ComputeCurvature$6.class | Bin 1394 -> 1390 bytes .../pluginTools/ComputeCurvature$7.class | Bin 1002 -> 998 bytes .../pluginTools/ComputeCurvature$8.class | Bin 1002 -> 998 bytes .../classes/pluginTools/ComputeManual$1.class | Bin 1419 -> 0 bytes .../classes/pluginTools/ComputeManual$2.class | Bin 1419 -> 0 bytes .../classes/pluginTools/ComputeManual$3.class | Bin 1027 -> 0 bytes .../classes/pluginTools/ComputeManual.class | Bin 8113 -> 0 bytes .../classes/pluginTools/Computeinwater.class | Bin 9288 -> 6191 bytes target/classes/pluginTools/EllipseTrack.class | Bin 20987 -> 16713 bytes ...llipseFileChooser$ChannelAListener$1.class | Bin 1062 -> 1058 bytes ...llipseFileChooser$ChannelBListener$1.class | Bin 1062 -> 1058 bytes ...ipseFileChooser$ChannelSegListener$1.class | Bin 1074 -> 1070 bytes .../InteractiveSimpleEllipseFit$1.class | Bin 1094 -> 1090 bytes .../InteractiveSimpleEllipseFit$2.class | Bin 1090 -> 1086 bytes ...eractiveSimpleEllipseFit$ValueChange.class | Bin 1831 -> 1831 bytes .../InteractiveSimpleEllipseFit.class | Bin 106864 -> 106769 bytes target/classes/plugins.config | 2 +- target/classes/utility/DisplayAuto$1.class | Bin 2238 -> 2238 bytes target/classes/utility/DisplayAuto$2$1.class | Bin 1275 -> 1271 bytes target/classes/utility/DisplayAuto$2.class | Bin 5112 -> 5112 bytes target/classes/utility/LabelRansac.class | Bin 9332 -> 0 bytes .../classes/utility/NormalIntersection.class | Bin 6119 -> 0 bytes .../classes/utility/SuperIntersection.class | Bin 6304 -> 0 bytes timeGUI/CovistoTimeselectPanel.java | 79 + zGUI/CovistoZselectPanel.java | 70 + 569 files changed, 85957 insertions(+), 1931 deletions(-) create mode 100644 comboSliderTextbox/SliderBoxGUI.java create mode 100644 dogGUI/CovistoDogPanel.java create mode 100644 functionPanel/CovistoFunctionPanel.java create mode 100644 kalmanGUI/CovistoKalmanPanel.java create mode 100644 kalmanGUI/CovistoNNPanel.java create mode 100644 layoutManager/Setlayout.java create mode 100644 loaddirectory/LeftRightDirectoryLoader.java create mode 100644 loadfile/CovistoFourChFileLoader.java create mode 100644 loadfile/CovistoOneChFileLoader.java create mode 100644 loadfile/CovistoThreeChForceFileLoader.java create mode 100644 loadfile/CovistoTwoChDropbox.java create mode 100644 loadfile/CovistoTwoChForceFileLoader.java create mode 100644 loadfile/CovistoTwoChOptionFileLoader.java delete mode 100644 plugins.config create mode 100644 removeSpotGUI/CovistoRemovePanel.java create mode 100644 scrollbar/Utility.java create mode 100644 src/main/java/analyzers/EdgeAnalyzer.java create mode 100644 src/main/java/analyzers/TrackAnalyzer.java create mode 100644 src/main/java/comboSliderTextbox/SliderBoxGUI.java create mode 100644 src/main/java/common3D/BinaryCreation.java create mode 100644 src/main/java/common3D/CommonWater.java create mode 100644 src/main/java/costMatrix/CostFunction.java create mode 100644 src/main/java/costMatrix/CostMatrixCreator.java create mode 100644 src/main/java/costMatrix/DefaultCostMatrixCreator.java create mode 100644 src/main/java/costMatrix/IntensityDiffCostFunction.java create mode 100644 src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java create mode 100644 src/main/java/costMatrix/LAPJV.java create mode 100644 src/main/java/costMatrix/PixelRatioCostFunction.java create mode 100644 src/main/java/costMatrix/PixelratiowDistCostFunction.java create mode 100644 src/main/java/costMatrix/ResizableDoubleArray.java create mode 100644 src/main/java/costMatrix/SparseCostMatrix.java create mode 100644 src/main/java/costMatrix/SquareDistCostFunction.java create mode 100644 src/main/java/distanceTransform/CreateBinary.java create mode 100644 src/main/java/distanceTransform/CreateDistanceTransform.java create mode 100644 src/main/java/distanceTransform/CreateWatershed.java create mode 100644 src/main/java/distanceTransform/DistWatershed.java create mode 100644 src/main/java/distanceTransform/DistWatershedBinary.java create mode 100644 src/main/java/distanceTransform/WatershedBinary.java create mode 100644 src/main/java/dog3D/ComputeDoG.java create mode 100644 src/main/java/dog3D/ComputeParallelDoG.java create mode 100644 src/main/java/dog3D/DogAll.java create mode 100644 src/main/java/dogGUI/CovistoDogPanel.java create mode 100644 src/main/java/dogSeg/DOGSeg.java create mode 100644 src/main/java/functionPanel/CovistoFunctionPanel.java create mode 100644 src/main/java/interactivePreprocessing/AdvancedSnakeListener.java create mode 100644 src/main/java/interactivePreprocessing/CovistoStandardMouseListener.java create mode 100644 src/main/java/interactivePreprocessing/DoDOGListener.java create mode 100644 src/main/java/interactivePreprocessing/DoMSERListener.java create mode 100644 src/main/java/interactivePreprocessing/DoWatershedListener.java create mode 100644 src/main/java/interactivePreprocessing/FindMaximaListener.java create mode 100644 src/main/java/interactivePreprocessing/FindMaximaMserListener.java create mode 100644 src/main/java/interactivePreprocessing/FindMinimaListener.java create mode 100644 src/main/java/interactivePreprocessing/FindMinimaMserListener.java create mode 100644 src/main/java/interactivePreprocessing/GradientListener.java create mode 100644 src/main/java/interactivePreprocessing/InteractiveMethods.java create mode 100644 src/main/java/interactivePreprocessing/IterationListener.java create mode 100644 src/main/java/interactivePreprocessing/MaxdistListener.java create mode 100644 src/main/java/interactivePreprocessing/PREAlphaListener.java create mode 100644 src/main/java/interactivePreprocessing/PREApplyDog3DListener.java create mode 100644 src/main/java/interactivePreprocessing/PREApplyWaterCurrentListener.java create mode 100644 src/main/java/interactivePreprocessing/PREBetaListener.java create mode 100644 src/main/java/interactivePreprocessing/PREDeltaListener.java create mode 100644 src/main/java/interactivePreprocessing/PREDoSegmodeListener.java create mode 100644 src/main/java/interactivePreprocessing/PREDoTrackmodeListener.java create mode 100644 src/main/java/interactivePreprocessing/PREIniSearchListener.java create mode 100644 src/main/java/interactivePreprocessing/PRELostFrameListener.java create mode 100644 src/main/java/interactivePreprocessing/PREMaxSearchListener.java create mode 100644 src/main/java/interactivePreprocessing/PREMaxSearchTListener.java create mode 100644 src/main/java/interactivePreprocessing/PREMaxSizeListener.java create mode 100644 src/main/java/interactivePreprocessing/PREMinDiversityListener.java create mode 100644 src/main/java/interactivePreprocessing/PREMinSizeListener.java create mode 100644 src/main/java/interactivePreprocessing/PRERoiListener.java create mode 100644 src/main/java/interactivePreprocessing/PREShowBinary.java create mode 100644 src/main/java/interactivePreprocessing/PREShowDist.java create mode 100644 src/main/java/interactivePreprocessing/PREShowWatershed.java create mode 100644 src/main/java/interactivePreprocessing/PREUnstability_ScoreListener.java create mode 100644 src/main/java/interactivePreprocessing/PREauto.java create mode 100644 src/main/java/interactivePreprocessing/PREdist.java create mode 100644 src/main/java/interactivePreprocessing/PreCurrentMovieListener.java create mode 100644 src/main/java/interactivePreprocessing/PreDistThresholdListener.java create mode 100644 src/main/java/interactivePreprocessing/PreNonStandardMouseListener.java create mode 100644 src/main/java/interactivePreprocessing/PreSigmaListener.java create mode 100644 src/main/java/interactivePreprocessing/PreThresholdListener.java create mode 100644 src/main/java/interactivePreprocessing/PreTimeDistThresholdListener.java create mode 100644 src/main/java/interactivePreprocessing/PreTimeListener.java create mode 100644 src/main/java/interactivePreprocessing/PreTimeSkipListener.java create mode 100644 src/main/java/interactivePreprocessing/PreTlocListener.java create mode 100644 src/main/java/interactivePreprocessing/PreUploadMovieListener.java create mode 100644 src/main/java/interactivePreprocessing/PreWaterListener.java create mode 100644 src/main/java/interactivePreprocessing/PreZListener.java create mode 100644 src/main/java/interactivePreprocessing/PreZlocListener.java create mode 100644 src/main/java/interactivePreprocessing/PreprocessingFileChooser.java create mode 100644 src/main/java/interactivePreprocessing/ShowView.java create mode 100644 src/main/java/interactivePreprocessing/ZMser.java create mode 100644 src/main/java/kalmanGUI/CovistoKalmanPanel.java create mode 100644 src/main/java/kalmanGUI/CovistoNNPanel.java create mode 100644 src/main/java/layoutManager/Setlayout.java create mode 100644 src/main/java/linkers/AlphanumComparator.java create mode 100644 src/main/java/linkers/BlobTracker.java create mode 100644 src/main/java/linkers/CVMKalmanFilter.java create mode 100644 src/main/java/linkers/Dimension.java create mode 100644 src/main/java/linkers/ExtensionRealPoint.java create mode 100644 src/main/java/linkers/FeatureModel3D.java create mode 100644 src/main/java/linkers/FlagNode.java create mode 100644 src/main/java/linkers/FromContinuousBranches.java create mode 100644 src/main/java/linkers/Function1.java create mode 100644 src/main/java/linkers/JaqamanLinker.java create mode 100644 src/main/java/linkers/KFsearch.java create mode 100644 src/main/java/linkers/Logger.java create mode 100644 src/main/java/linkers/Model3D.java create mode 100644 src/main/java/linkers/NNFlagsearchKDtree.java create mode 100644 src/main/java/linkers/PRENNsearch.java create mode 100644 src/main/java/linkers/SortedDepthFirstIterator.java create mode 100644 src/main/java/linkers/ThreeDBlobTracker.java create mode 100644 src/main/java/linkers/TimeDirectedDepthFirstIterator.java create mode 100644 src/main/java/linkers/TimeDirectedDepthFirstIterator3D.java create mode 100644 src/main/java/linkers/TimeDirectedNeighborIndex.java create mode 100644 src/main/java/linkers/TimeDirectedNeighborIndex3D.java create mode 100644 src/main/java/linkers/TimeDirectedSortedDepthFirstIterator.java create mode 100644 src/main/java/linkers/TimeDirectedSortedDepthFirstIterator3D.java create mode 100644 src/main/java/linkers/TrackModel.java create mode 100644 src/main/java/linkers/TrackModel3D.java delete mode 100644 src/main/java/listeners/AngleListener.java delete mode 100644 src/main/java/listeners/ManualInterventionListener.java delete mode 100644 src/main/java/listeners/RedoListener.java create mode 100644 src/main/java/loaddirectory/LeftRightDirectoryLoader.java create mode 100644 src/main/java/loadfile/CovistoFourChFileLoader.java create mode 100644 src/main/java/loadfile/CovistoOneChFileLoader.java create mode 100644 src/main/java/loadfile/CovistoThreeChForceFileLoader.java create mode 100644 src/main/java/loadfile/CovistoTwoChDropbox.java create mode 100644 src/main/java/loadfile/CovistoTwoChForceFileLoader.java create mode 100644 src/main/java/loadfile/CovistoTwoChOptionFileLoader.java create mode 100644 src/main/java/mser3D/ComputeCompTree.java create mode 100644 src/main/java/mser3D/MserAll.java create mode 100644 src/main/java/mserGUI/CovistoMserPanel.java create mode 100644 src/main/java/mserMethods/MSERSeg.java create mode 100644 src/main/java/nearestNeighbourGUI/CovistoNearestNPanel.java delete mode 100644 src/main/java/pluginTools/ComputeAngles.java delete mode 100644 src/main/java/pluginTools/ComputeAnglesCurrent.java delete mode 100644 src/main/java/pluginTools/ComputeManual.java create mode 100644 src/main/java/preProcessing/AddGaussian.java create mode 100644 src/main/java/preProcessing/FlatFieldCorrection.java create mode 100644 src/main/java/preProcessing/FlatFieldOnly.java create mode 100644 src/main/java/preProcessing/GenericFilters.java create mode 100644 src/main/java/preProcessing/GetLocalmaxminMT.java create mode 100644 src/main/java/preProcessing/GlobalThresholding.java create mode 100644 src/main/java/preProcessing/Kernels.java create mode 100644 src/main/java/preProcessing/MedianFilter2D.java create mode 100644 src/main/java/preProcessing/MedianFilterImg2D.java create mode 100644 src/main/java/preProcessing/MedianFilterOnly.java create mode 100644 src/main/java/preProcessing/MinMaxIntensity.java create mode 100644 src/main/java/preProcessing/Normalization.java create mode 100644 src/main/java/preProcessing/Otsu.java create mode 100644 src/main/java/preProcessing/PrePipeline.java create mode 100644 src/main/java/preProcessing/Utils.java create mode 100644 src/main/java/ransac/Catastrophy/DetectCatastrophy.java create mode 100644 src/main/java/ransac/Display/DisplayHistogram.java create mode 100644 src/main/java/ransac/Display/DisplayPoints.java create mode 100644 src/main/java/ransac/FLSobject.java create mode 100644 src/main/java/ransac/Lengthdistro/LengthDistribution.java create mode 100644 src/main/java/ransac/PointFunctionMatch/Function.java create mode 100644 src/main/java/ransac/PointFunctionMatch/PointFunctionMatch.java create mode 100644 src/main/java/ransac/Rateobject.java create mode 100644 src/main/java/ransac/loadFiles/Filereader.java create mode 100644 src/main/java/ransac/loadFiles/Tracking.java create mode 100644 src/main/java/ransacPoly/AbstractFunction.java create mode 100644 src/main/java/ransacPoly/AbstractFunction2D.java create mode 100644 src/main/java/ransacPoly/Function.java create mode 100644 src/main/java/ransacPoly/HigherOrderPolynomialFunction.java create mode 100644 src/main/java/ransacPoly/InterpolatedPolynomial.java create mode 100644 src/main/java/ransacPoly/LinearFunction.java create mode 100644 src/main/java/ransacPoly/MatrixFunctions.java create mode 100644 src/main/java/ransacPoly/MixedPolynomial.java create mode 100644 src/main/java/ransacPoly/MixedPolynomialFunction.java create mode 100644 src/main/java/ransacPoly/NewtonRaphson.java create mode 100644 src/main/java/ransacPoly/Polynomial.java create mode 100644 src/main/java/ransacPoly/QuadraticFunction.java create mode 100644 src/main/java/ransacPoly/RansacFunction.java create mode 100644 src/main/java/ransacPoly/RegressionFunction.java create mode 100644 src/main/java/ransacPoly/Sort.java create mode 100644 src/main/java/ransacPoly/Threepointfit.java create mode 100644 src/main/java/regionRemoval/RemoveTiny.java create mode 100644 src/main/java/removeSpotGUI/CovistoRemovePanel.java create mode 100644 src/main/java/saveTracks/CovistoSavePanel.java create mode 100644 src/main/java/scrollbar/Utility.java create mode 100644 src/main/java/snakeGUI/CovistoSnakePanel.java create mode 100644 src/main/java/strategies/Abstract3x3NeighbourhoodThinning.java create mode 100644 src/main/java/strategies/GuoHallAlgorithm.java create mode 100644 src/main/java/strategies/HilditchAlgorithm.java create mode 100644 src/main/java/strategies/MorphologicalThinning.java create mode 100644 src/main/java/strategies/ThinningStrategy.java create mode 100644 src/main/java/strategies/ThinningStrategyFactory.java create mode 100644 src/main/java/strategies/ZhangSuenAlgorithm.java create mode 100644 src/main/java/thinning/ThinningOp.java create mode 100644 src/main/java/timeGUI/CovistoTimeselectPanel.java create mode 100644 src/main/java/trackMateBridge/Bridging.java create mode 100644 src/main/java/userTESTING/BorisData.java create mode 100644 src/main/java/utility/ActionListenablePanel.java create mode 100644 src/main/java/utility/AnalyzeTTrack.java create mode 100644 src/main/java/utility/AnalzeZTrack.java create mode 100644 src/main/java/utility/ConfigurationPanel.java create mode 100644 src/main/java/utility/Convolvers.java create mode 100644 src/main/java/utility/CovistoModule.java create mode 100644 src/main/java/utility/CovistoSlicer.java create mode 100644 src/main/java/utility/CovistoUtils.java create mode 100644 src/main/java/utility/CovsitoProgressBar.java create mode 100644 src/main/java/utility/DetectorKeys.java create mode 100644 src/main/java/utility/ETrackScrollbarUtils.java create mode 100644 src/main/java/utility/FeatureAnalyzer3D.java create mode 100644 src/main/java/utility/FeatureFilter.java create mode 100644 src/main/java/utility/FinderUtils.java create mode 100644 src/main/java/utility/IlastikConnectedComponents.java create mode 100644 src/main/java/utility/Interpolator.java delete mode 100644 src/main/java/utility/LabelRansac.java create mode 100644 src/main/java/utility/Logger.java create mode 100644 src/main/java/utility/ModelChangeEvent.java create mode 100644 src/main/java/utility/ModelChangeListener.java create mode 100644 src/main/java/utility/NNFlagsearchKDtree.java delete mode 100644 src/main/java/utility/NormalIntersection.java create mode 100644 src/main/java/utility/PreRoiobject.java create mode 100644 src/main/java/utility/Settings.java create mode 100644 src/main/java/utility/SortCoordinates.java create mode 100644 src/main/java/utility/SortListbyproperty.java delete mode 100644 src/main/java/utility/SuperIntersection.java create mode 100644 src/main/java/utility/ThreeDRoiobject.java create mode 100644 src/main/java/utility/ThreeDRoiobjectAnalyzer.java create mode 100644 src/main/java/utility/ThreeDRoiobjectAnalyzerFactory.java create mode 100644 src/main/java/utility/ThreeDRoiobjectCollection.java create mode 100644 src/main/java/utility/ThreeDRoiobjectDetector.java create mode 100644 src/main/java/utility/ThreeDRoiobjectDetectorFactory.java create mode 100644 src/main/java/utility/ThreeDRoiobjectTracker.java create mode 100644 src/main/java/utility/ThreeDRoiobjectTrackerFactory.java create mode 100644 src/main/java/utility/ViewUtils.java create mode 100644 src/main/java/utils/AddGaussian.java create mode 100644 src/main/java/utils/DistanceMetrics.java create mode 100644 src/main/java/varun_algorithm/Algorithm.java create mode 100644 src/main/java/varun_algorithm/Benchmark.java create mode 100644 src/main/java/varun_algorithm/BenchmarkAlgorithm.java create mode 100644 src/main/java/varun_algorithm/MultiThreaded.java create mode 100644 src/main/java/varun_algorithm/MultiThreadedAlgorithm.java create mode 100644 src/main/java/varun_algorithm/MultiThreadedBenchmarkAlgorithm.java create mode 100644 src/main/java/varun_algorithm/OutputAlgorithm.java create mode 100644 src/main/java/varun_algorithm_binary/Thresholder.java create mode 100644 src/main/java/varun_algorithm_componenttree/BuildComponentTree.java create mode 100644 src/main/java/varun_algorithm_componenttree/Component.java create mode 100644 src/main/java/varun_algorithm_componenttree/ComponentForest.java create mode 100644 src/main/java/varun_algorithm_componenttree/ComponentTree.java create mode 100644 src/main/java/varun_algorithm_componenttree/PartialComponent.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/ComputeDelta.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaBrightToDark.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaDarkToBright.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/Mser.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/MserEvaluationNode.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/MserPartialComponent.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/MserPartialComponentGenerator.java create mode 100644 src/main/java/varun_algorithm_componenttree/mser/MserTree.java create mode 100644 src/main/java/varun_algorithm_componenttree/pixellist/PixelList.java create mode 100644 src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponent.java create mode 100644 src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponentTree.java create mode 100644 src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponent.java create mode 100644 src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponentGenerator.java create mode 100644 src/main/java/varun_algorithm_dog/DifferenceOfGaussian.java create mode 100644 src/main/java/varun_algorithm_dog/DogDetection.java create mode 100644 src/main/java/varun_algorithm_edge/Edgel.java create mode 100644 src/main/java/varun_algorithm_edge/SubpixelEdgelDetection.java create mode 100644 src/main/java/varun_algorithm_fill/Filter.java create mode 100644 src/main/java/varun_algorithm_fill/FloodFill.java create mode 100644 src/main/java/varun_algorithm_fill/TypeWriter.java create mode 100644 src/main/java/varun_algorithm_fill/Writer.java create mode 100644 src/main/java/varun_algorithm_floydsteinberg/FloydSteinbergDithering.java create mode 100644 src/main/java/varun_algorithm_function/Function.java create mode 100644 src/main/java/varun_algorithm_function/NormMinMax.java create mode 100644 src/main/java/varun_algorithm_function/SubtractNorm.java create mode 100644 src/main/java/varun_algorithm_function/SubtractNormReal.java create mode 100644 src/main/java/varun_algorithm_function/SubtractReal.java create mode 100644 src/main/java/varun_algorithm_gauss/AbstractGauss.java create mode 100644 src/main/java/varun_algorithm_gauss/AbstractLineIterator.java create mode 100644 src/main/java/varun_algorithm_gauss/Gauss.java create mode 100644 src/main/java/varun_algorithm_gauss/GaussDouble.java create mode 100644 src/main/java/varun_algorithm_gauss/GaussFloat.java create mode 100644 src/main/java/varun_algorithm_gauss/GaussGeneral.java create mode 100644 src/main/java/varun_algorithm_gauss/GaussNativeType.java create mode 100644 src/main/java/varun_algorithm_gauss/SamplingLineIterator.java create mode 100644 src/main/java/varun_algorithm_gauss/WritableLineIterator.java create mode 100644 src/main/java/varun_algorithm_gauss3/ConvolverFactory.java create mode 100644 src/main/java/varun_algorithm_gauss3/ConvolverNativeType.java create mode 100644 src/main/java/varun_algorithm_gauss3/ConvolverNativeTypeBuffered.java create mode 100644 src/main/java/varun_algorithm_gauss3/ConvolverNumericType.java create mode 100644 src/main/java/varun_algorithm_gauss3/DoubleConvolverRealType.java create mode 100644 src/main/java/varun_algorithm_gauss3/DoubleConvolverRealTypeBuffered.java create mode 100644 src/main/java/varun_algorithm_gauss3/FloatConvolverRealType.java create mode 100644 src/main/java/varun_algorithm_gauss3/FloatConvolverRealTypeBuffered.java create mode 100644 src/main/java/varun_algorithm_gauss3/Gauss3.java create mode 100644 src/main/java/varun_algorithm_gauss3/SeparableSymmetricConvolution.java create mode 100644 src/main/java/varun_algorithm_gradient/HessianMatrix.java create mode 100644 src/main/java/varun_algorithm_gradient/PartialDerivative.java create mode 100644 src/main/java/varun_algorithm_gradient/ScaleAsFunctionOfPosition.java create mode 100644 src/main/java/varun_algorithm_integral/IntegralImg.java create mode 100644 src/main/java/varun_algorithm_integral/IntegralImgDouble.java create mode 100644 src/main/java/varun_algorithm_integral/IntegralImgLong.java create mode 100644 src/main/java/varun_algorithm_integral/ScaleAreaAveraging2d.java create mode 100644 src/main/java/varun_algorithm_kdtree/ClipConvexPolytopeKDTree.java create mode 100644 src/main/java/varun_algorithm_kdtree/ConvexPolytope.java create mode 100644 src/main/java/varun_algorithm_kdtree/HyperPlane.java create mode 100644 src/main/java/varun_algorithm_kdtree/KDTreeNodeIterable.java create mode 100644 src/main/java/varun_algorithm_kdtree/SplitHyperPlaneKDTree.java create mode 100644 src/main/java/varun_algorithm_kdtree/VolumetricSearch.java create mode 100644 src/main/java/varun_algorithm_labeling/AllConnectedComponents.java create mode 100644 src/main/java/varun_algorithm_labeling/ConnectedComponents.java create mode 100644 src/main/java/varun_algorithm_labeling/Watershed.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/EigenValues.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/EigenValues1D.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSquare.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSymmetric.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/EigenValuesSquare.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/EigenValuesSymmetric.java create mode 100644 src/main/java/varun_algorithm_linalg_eigen/TensorEigenValues.java create mode 100644 src/main/java/varun_algorithm_linalg_matrix/RealCompositeMatrix.java create mode 100644 src/main/java/varun_algorithm_linalg_matrix/RealCompositeSquareMatrix.java create mode 100644 src/main/java/varun_algorithm_linalg_matrix/RealCompositeSymmetricMatrix.java create mode 100644 src/main/java/varun_algorithm_localextrema/LocalExtrema.java create mode 100644 src/main/java/varun_algorithm_localextrema/RefinedPeak.java create mode 100644 src/main/java/varun_algorithm_localextrema/SubpixelLocalization.java create mode 100644 src/main/java/varun_algorithm_morphology/BlackTopHat.java create mode 100644 src/main/java/varun_algorithm_morphology/Closing.java create mode 100644 src/main/java/varun_algorithm_morphology/Dilation.java create mode 100644 src/main/java/varun_algorithm_morphology/Erosion.java create mode 100644 src/main/java/varun_algorithm_morphology/MorphologyUtils.java create mode 100644 src/main/java/varun_algorithm_morphology/Opening.java create mode 100644 src/main/java/varun_algorithm_morphology/StructuringElements.java create mode 100644 src/main/java/varun_algorithm_morphology/TopHat.java create mode 100644 src/main/java/varun_algorithm_morphology/distance/Distance.java create mode 100644 src/main/java/varun_algorithm_morphology/distance/DistanceTransform.java create mode 100644 src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceAnisotropic.java create mode 100644 src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceIsotropic.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Abstract3x3TableOperation.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Branchpoints.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Bridge.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Clean.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Endpoints.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Fill.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Hbreak.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Life.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Majority.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Remove.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Spur.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Spur1.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Spur2.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Thicken.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Thin.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Thin1.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Thin2.java create mode 100644 src/main/java/varun_algorithm_morphology/table2d/Vbreak.java create mode 100644 src/main/java/varun_algorithm_neighborhood/CenteredRectangleShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodFactory.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodLocalizableSampler.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodRandomAccess.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/DiamondTipsShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodFactory.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodLocalizableSampler.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodRandomAccess.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HorizontalLineShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodFactory.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodRandomAccess.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HyperSphereShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/HypersphereNeighborhoodLocalizableSampler.java create mode 100644 src/main/java/varun_algorithm_neighborhood/Neighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodFactory.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodLocalizableSampler.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodRandomAccess.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PairOfPointsShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodFactory.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodLocalizableSampler.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodRandomAccess.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/PeriodicLineShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhood.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodFactory.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodLocalizableSampler.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodRandomAccess.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenter.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenterUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodUnsafe.java create mode 100644 src/main/java/varun_algorithm_neighborhood/RectangleShape.java create mode 100644 src/main/java/varun_algorithm_neighborhood/Shape.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/Angleobject.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/BisectorEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/Circle.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/ConnectedComponentCoordinates.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/DisplayasROI.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/DistPointHyperEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/EberlyCode.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/Ellipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/FitCircle.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/FitEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/FitLocalEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/HyperEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonPolynomial.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/NumericalSolvers.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/RansacFunctionEllipsoid.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/ResultRoot.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/Solvers.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/SortRealLocalizable.java create mode 100644 src/main/java/varun_algorithm_ransac_Ransac/SortSegments.java create mode 100644 src/main/java/varun_algorithm_region/BresenhamLine.java create mode 100644 src/main/java/varun_algorithm_region/CircleCursor.java create mode 100644 src/main/java/varun_algorithm_region/Circles.java create mode 100644 src/main/java/varun_algorithm_region/EllipseCursor.java create mode 100644 src/main/java/varun_algorithm_region/Ellipses.java create mode 100644 src/main/java/varun_algorithm_region/hypersphere/HyperSphere.java create mode 100644 src/main/java/varun_algorithm_region/hypersphere/HyperSphereCursor.java create mode 100644 src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood.java create mode 100644 src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood2.java create mode 100644 src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor.java create mode 100644 src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor2.java create mode 100644 src/main/java/varun_algorithm_stats/ComputeMinMax.java create mode 100644 src/main/java/varun_algorithm_stats/Histogram.java create mode 100644 src/main/java/varun_algorithm_stats/HistogramBinMapper.java create mode 100644 src/main/java/varun_algorithm_stats/IntBinMapper.java create mode 100644 src/main/java/varun_algorithm_stats/Max.java create mode 100644 src/main/java/varun_algorithm_stats/Min.java create mode 100644 src/main/java/varun_algorithm_stats/Normalize.java create mode 100644 src/main/java/varun_algorithm_stats/RealBinMapper.java create mode 100644 src/main/java/varun_algorithm_tree/Forest.java create mode 100644 src/main/java/varun_algorithm_tree/Tree.java create mode 100644 src/main/java/varun_algorithm_tree/TreeNode.java create mode 100644 src/main/java/varun_algorithm_tree/TreeUtils.java create mode 100644 src/main/java/varun_multithreading/Chunk.java create mode 100644 src/main/java/varun_multithreading/SimpleMultiThreading.java create mode 100644 src/main/java/visualization/AbstractCovistoModelView.java create mode 100644 src/main/java/visualization/CategoryJComboBox.java create mode 100644 src/main/java/visualization/ColorByFeatureGUIPanel.java create mode 100644 src/main/java/visualization/CovistoModelView.java create mode 100644 src/main/java/visualization/Draw3DLines.java create mode 100644 src/main/java/visualization/DrawCircles.java create mode 100644 src/main/java/visualization/DummyThreeDRoiobjectColorGenerator.java create mode 100644 src/main/java/visualization/DummyTrackColorGenerator.java create mode 100644 src/main/java/visualization/FeatureColorGenerator.java create mode 100644 src/main/java/visualization/HyperStackDisplayer.java create mode 100644 src/main/java/visualization/InterpolatePaintScale.java create mode 100644 src/main/java/visualization/ManualEdgeColorAnalyzer.java create mode 100644 src/main/java/visualization/ManualSpotColorAnalyzerFactory.java create mode 100644 src/main/java/visualization/MinMaxAdjustable.java create mode 100644 src/main/java/visualization/NearestRoi.java create mode 100644 src/main/java/visualization/SelectionChangeEvent.java create mode 100644 src/main/java/visualization/SelectionChangeListener.java create mode 100644 src/main/java/visualization/SelectionModel.java create mode 100644 src/main/java/visualization/ThreeDRoiobjectOverlay.java create mode 100644 src/main/java/visualization/TrackColorGenerator.java create mode 100644 src/main/java/visualization/TrackOverlay.java create mode 100644 src/main/java/visualization/Visualize3D.java create mode 100644 src/main/java/watershed3D/WatershedAll.java create mode 100644 src/main/java/watershedCurrent/WatershedCurrent.java create mode 100644 src/main/java/watershedGUI/CovistoWatershedPanel.java create mode 100644 src/main/java/zGUI/CovistoZselectPanel.java delete mode 100644 target/classes/listeners/AngleListener.class delete mode 100644 target/classes/listeners/ManualInterventionListener.class delete mode 100644 target/classes/listeners/RedoListener.class delete mode 100644 target/classes/pluginTools/ComputeAngles$1.class delete mode 100644 target/classes/pluginTools/ComputeAngles$2.class delete mode 100644 target/classes/pluginTools/ComputeAngles$3.class delete mode 100644 target/classes/pluginTools/ComputeAngles$4.class delete mode 100644 target/classes/pluginTools/ComputeAngles.class delete mode 100644 target/classes/pluginTools/ComputeAnglesCurrent$1.class delete mode 100644 target/classes/pluginTools/ComputeAnglesCurrent$2.class delete mode 100644 target/classes/pluginTools/ComputeAnglesCurrent$3.class delete mode 100644 target/classes/pluginTools/ComputeAnglesCurrent$4.class delete mode 100644 target/classes/pluginTools/ComputeAnglesCurrent.class delete mode 100644 target/classes/pluginTools/ComputeManual$1.class delete mode 100644 target/classes/pluginTools/ComputeManual$2.class delete mode 100644 target/classes/pluginTools/ComputeManual$3.class delete mode 100644 target/classes/pluginTools/ComputeManual.class delete mode 100644 target/classes/utility/LabelRansac.class delete mode 100644 target/classes/utility/NormalIntersection.class delete mode 100644 target/classes/utility/SuperIntersection.class create mode 100644 timeGUI/CovistoTimeselectPanel.java create mode 100644 zGUI/CovistoZselectPanel.java diff --git a/.classpath b/.classpath index 2610521..7e5b9b1 100644 --- a/.classpath +++ b/.classpath @@ -2,9 +2,9 @@ + - @@ -23,8 +23,41 @@ - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/comboSliderTextbox/SliderBoxGUI.java b/comboSliderTextbox/SliderBoxGUI.java new file mode 100644 index 0000000..a0c3fc5 --- /dev/null +++ b/comboSliderTextbox/SliderBoxGUI.java @@ -0,0 +1,66 @@ +package comboSliderTextbox; + +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + + +public class SliderBoxGUI { + + + final String string; + final float valueDimension; + final float valueMax; + final int scrollbarSize; + final JScrollBar valueslider; + final TextField inputFieldvalue; + final Label valueText; + + public SliderBoxGUI(final String string, JScrollBar valueslider, TextField inputFieldvalue, Label valueText, final int scrollbarSize, final float valueDimension, final float valueMax) { + + this.string = string; + this.scrollbarSize = scrollbarSize; + this.valueDimension = valueDimension; + this.valueMax = valueMax; + this.valueslider = valueslider; + this.inputFieldvalue = inputFieldvalue; + this.valueText = valueText; + } + + + + public JPanel BuildDisplay() { + + + JPanel combosliderbox = new JPanel(); + + layoutManager.Setlayout.LayoutSetter(combosliderbox); + + + + combosliderbox.add(valueText, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + combosliderbox.add(valueslider, new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + combosliderbox.add(inputFieldvalue, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + + return combosliderbox; + + + + } + +} diff --git a/dogGUI/CovistoDogPanel.java b/dogGUI/CovistoDogPanel.java new file mode 100644 index 0000000..0e1d6c5 --- /dev/null +++ b/dogGUI/CovistoDogPanel.java @@ -0,0 +1,163 @@ +package dogGUI; + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoDogPanel { + + + public static JPanel DogPanel = new JPanel(); + + + public static final int scrollbarSize = 1000; + public static Label removeText = new Label("Distance Threshold ", Label.CENTER); + public static Label removetimeText = new Label("Time-Dist Threshold ", Label.CENTER); + public static Label timeblockText = new Label("Time Skip ", Label.CENTER); + + + public static TextField inputFieldSpot, inputtimedist, inputtimeskip; + public static double distthreshold = 10; + public static double timethreshold = 10; + public static int timeblock = 1; + + public static boolean lookForMaxima = true; + public static boolean lookForMinima = false; + public static int sigmaInit = 1; + public static float sigma = sigmaInit; + public static float sigmaMin = 1f; + public static float sigmaMax = 100f; + public static CheckboxGroup minormax = new CheckboxGroup(); + public static final Checkbox findminima = new Checkbox("Locate Minima", minormax, lookForMinima); + public static final Checkbox findmaxima = new Checkbox("Locate Maxima", minormax, lookForMaxima); + public static float thresholdMin = 1f; + public static float thresholdMax = 255f; + public static int thresholdInit = 2; + public static float sigma2 = 1.1f; + public static float threshold = 1f; + public static int sigmasliderInit = 0; + public static int thresholdsliderInit = 5; + public static Label sigmaText = new Label("Approximate object size = " + sigmaInit, Label.CENTER); + public static JScrollBar sigmaslider = new JScrollBar(Scrollbar.HORIZONTAL, sigmaInit, 10, 0, scrollbarSize + 10); + public static JScrollBar thresholdslider = new JScrollBar(Scrollbar.HORIZONTAL, thresholdInit, 10, 0, 10 + scrollbarSize); + public static Label thresholdText = new Label("Approximate peak intensity " + thresholdInit, Label.CENTER); + public static JButton AllDog = new JButton("DOG in 3D/4D"); + public static final String sigmastring = "Approximate object size"; + public static final String thresholdstring = "Approximate peak intensity"; + + public static double getInitialSigma() { + return sigma; + } + + public static void setInitialSigma(final float value) { + sigma = value; + sigmaInit = scrollbar.Utility.computeScrollbarPositionFromValue(sigma, sigmaMin, sigmaMax, scrollbarSize); + } + + public static double getInitialThreshold() { + return threshold; + } + + public static void setInitialThreshold(final float value) { + threshold = value; + thresholdInit = scrollbar.Utility.computeScrollbarPositionFromValue(threshold, thresholdMin, thresholdMax, scrollbarSize); + } + + + + + public static JPanel DogPanel() { + + + layoutManager.Setlayout.LayoutSetter(DogPanel); + + inputFieldSpot = new TextField(5); + inputFieldSpot.setText(Double.toString(distthreshold)); + + inputtimedist = new TextField(5); + inputtimedist.setText(Double.toString(timethreshold)); + + inputtimeskip = new TextField(5); + inputtimeskip.setText(Double.toString(timeblock)); + + + sigmaslider.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(sigmaInit, sigmaMin, sigmaMax, scrollbarSize)); + + thresholdslider.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(thresholdsliderInit, + thresholdMin, thresholdMax, scrollbarSize)); + + sigma = scrollbar.Utility.computeValueFromScrollbarPosition(sigmaslider.getValue(), sigmaMin, sigmaMax, + scrollbarSize); + + threshold = scrollbar.Utility.computeValueFromScrollbarPosition(thresholdslider.getValue(), thresholdMin, + thresholdMax, scrollbarSize); + + sigmaText = new Label("Approximate object size = " + sigma, Label.CENTER); + thresholdText = new Label("Approximate peak intensity " + threshold, Label.CENTER); + Border dogborder = new CompoundBorder(new TitledBorder("Difference of Gaussian detection"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + DogPanel.add(sigmaText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(sigmaslider, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + DogPanel.add(thresholdText, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(thresholdslider, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(findminima, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(findmaxima, new GridBagConstraints(2, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(removeText, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(removetimeText, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(timeblockText, new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(inputFieldSpot, new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(inputtimedist, new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + DogPanel.add(inputtimeskip, new GridBagConstraints(2, 7, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + + + DogPanel.add(AllDog, new GridBagConstraints(2, 8, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + DogPanel.setBorder(dogborder); + + + return DogPanel; + + } + + + +} diff --git a/functionPanel/CovistoFunctionPanel.java b/functionPanel/CovistoFunctionPanel.java new file mode 100644 index 0000000..26d435f --- /dev/null +++ b/functionPanel/CovistoFunctionPanel.java @@ -0,0 +1,88 @@ +package functionPanel; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoFunctionPanel { + + public static JPanel FunctionPanel = new JPanel(); + + public static Label Functionlabel, gradientlabel, distlabel; + public static TextField Functioniter, gradientthresh, maxdist; + + public static int Functioniterations = 200; + public static int displayFunction = Functioniterations / 2; + public static int Gradthresh = 1; + public static int DistMax = 100; + public static double Displacement_min = 0.5; + public static double Displacement_max = 5.0; + public static double Threshold_dist_positive = 10; + public static double Threshold_dist_negative = 10; + public static double Inv_alpha_min = 0.2; + public static double Inv_alpha_max = 10.0; + public static double Mul_factor = 0.99; + // maximum displacement + public static double force = 10; + // regulari1ation factors, min and max + public static double reg = 5; + public static double regmin, regmax; + + + + + public static JPanel FunctionPanel() { + + regmin = reg / 2.0; + regmax = reg; + + + layoutManager.Setlayout.LayoutSetter(FunctionPanel); + Border Functionborder = new CompoundBorder(new TitledBorder("User requested Function computer"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + Functionlabel = new Label("My function parameter"); + gradientlabel = new Label("My second function parameter"); + distlabel = new Label("Just a little bit more"); + + Functioniter = new TextField(1); + gradientthresh = new TextField(1); + maxdist = new TextField(1); + + Functioniter.setText(Integer.toString(Functioniterations)); + gradientthresh.setText(Integer.toString(Gradthresh)); + maxdist.setText(Integer.toString(DistMax)); + FunctionPanel.add(Functionlabel, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + FunctionPanel.add(Functioniter, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + FunctionPanel.add(gradientlabel, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + FunctionPanel.add(gradientthresh, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + FunctionPanel.add(distlabel, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + FunctionPanel.add(maxdist, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + + + FunctionPanel.setBorder(Functionborder); + + + return FunctionPanel; + + } + + +} diff --git a/kalmanGUI/CovistoKalmanPanel.java b/kalmanGUI/CovistoKalmanPanel.java new file mode 100644 index 0000000..42c2d27 --- /dev/null +++ b/kalmanGUI/CovistoKalmanPanel.java @@ -0,0 +1,188 @@ +package kalmanGUI; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoKalmanPanel { + + + public static final int scrollbarSize = 1000; + public static JPanel KalmanPanel = new JPanel(); + + public static final String maxSearchstringKalman = "Maximum search radius"; + public static final String initialSearchstring = "Initial search radius (um)"; + + + public static final String alphastring = "Distance/Angle cost ratio"; + public static final String betastring = "Weightage for pixel ratio based cost"; + public static int alphaInit = 1; + public static int betaInit = 0; + public static float initialSearchradius = 100; + public static int initialSearchradiusInit = (int) initialSearchradius; + public static float initialSearchradiusMin = 1; + public static float initialSearchradiusMax = 1000; + public static float alphaMin = 0; + public static float alphaMax = 1; + public static float betaMin = 0; + public static float betaMax = 1; + public static float alpha = 0.5f; + public static float beta = 0.5f; + public static float maxSearchradius = 100; + public static int maxSearchradiusInit = (int) maxSearchradius; + public static float maxSearchradiusMin = 1; + public static float maxSearchradiusMax = 1000; + + public static int maxframegap = 3; + public static int trackduration = 10; + public static Label lostlabel; + public static Label mintracklength; + + public static TextField lostframe; + public static TextField tracklength; + public static Label alphaText = new Label(alphastring + " = " + alphaInit, Label.CENTER); + public static Label betaText = new Label(betastring + " = " + betaInit, Label.CENTER); + + public static Label maxSearchTextKalman = new Label(maxSearchstringKalman + " = " + maxSearchradiusInit, Label.CENTER); + public static Label iniSearchText = new Label(initialSearchstring + " = " + initialSearchradiusInit, Label.CENTER); + + public static final JScrollBar maxSearchSS = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar initialSearchS = new JScrollBar(Scrollbar.HORIZONTAL, initialSearchradiusInit, 10, 0, + 10 + scrollbarSize); + public static final JScrollBar alphaS = new JScrollBar(Scrollbar.HORIZONTAL, alphaInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar betaS = new JScrollBar(Scrollbar.HORIZONTAL, betaInit, 10, 0, 10 + scrollbarSize); + public static final JButton Skeletontime = new JButton("Skeletonize Buddies"); + public static final JButton Timetrack = new JButton("Track Buddies"); + public static final JButton Restart = new JButton("Restart"); + public static final JScrollBar maxSearchKalman = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + + + public static void setInitialAlpha(final float value) { + alpha = value; + alphaInit = scrollbar.Utility.computeScrollbarPositionFromValue(alpha, alphaMin, alphaMax, scrollbarSize); + } + public static double getInitialAlpha(final float value) { + + return alpha; + + } + public static void setInitialBeta(final float value) { + beta = value; + betaInit = scrollbar.Utility.computeScrollbarPositionFromValue(beta, betaMin, betaMax, scrollbarSize); + } + + public static double getInitialBeta(final float value) { + + return beta; + + } + public static void setInitialsearchradius(final float value) { + initialSearchradius = value; + initialSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(initialSearchradius, initialSearchradiusMin, + initialSearchradiusMax, scrollbarSize); + } + + public static void setInitialmaxsearchradius(final float value) { + maxSearchradius = value; + maxSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(maxSearchradius, maxSearchradiusMin, maxSearchradiusMax, + scrollbarSize); + } + + public double getInitialsearchradius(final float value) { + + return initialSearchradius; + + } + public static JPanel KalmanPanel() { + + setInitialAlpha(alphaInit); + setInitialBeta(betaInit); + setInitialsearchradius(initialSearchradiusInit); + setInitialmaxsearchradius(maxSearchradius); + layoutManager.Setlayout.LayoutSetter(KalmanPanel); + lostframe = new TextField(1); + lostframe.setText(Integer.toString(maxframegap)); + + tracklength = new TextField(1); + tracklength.setText(Integer.toString(trackduration)); + + lostlabel = new Label("Allow link loosing for #frames"); + mintracklength = new Label("Minimum BudTrack length as percent of timeframes"); + + + alphaText = new Label(alphastring + " = " + alpha, Label.CENTER); + betaText = new Label(betastring + " = " + beta, Label.CENTER); + + iniSearchText = new Label(initialSearchstring + " = " + initialSearchradius, Label.CENTER); + + maxSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(maxSearchKalman.getValue(), + maxSearchradiusMin, maxSearchradiusMax, scrollbarSize); + initialSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(initialSearchS.getValue(), + initialSearchradiusMin, initialSearchradiusMax, scrollbarSize); + alpha = scrollbar.Utility.computeValueFromScrollbarPosition(alphaS.getValue(), alphaMin, alphaMax, + scrollbarSize); + beta = scrollbar.Utility.computeValueFromScrollbarPosition(betaS.getValue(), betaMin, betaMax, + scrollbarSize); + + alphaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(alphaInit, alphaMin, alphaMax, scrollbarSize)); + betaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(betaInit, betaMin, betaMax, scrollbarSize)); + + + Border Kalmanborder = new CompoundBorder(new TitledBorder("Kalman Filter Search in T"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + KalmanPanel.add(iniSearchText, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(initialSearchS, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(alphaText, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(alphaS, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(betaText, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(betaS, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(lostlabel, new GridBagConstraints(5, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(lostframe, new GridBagConstraints(5, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(mintracklength, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(tracklength, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(maxSearchTextKalman, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + // KalmanPanel.add(maxSearchKalman, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Restart, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(Skeletontime, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(Timetrack, new GridBagConstraints(5, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.setBorder(Kalmanborder); + + + return KalmanPanel; + } + + +} diff --git a/kalmanGUI/CovistoNNPanel.java b/kalmanGUI/CovistoNNPanel.java new file mode 100644 index 0000000..67f5ece --- /dev/null +++ b/kalmanGUI/CovistoNNPanel.java @@ -0,0 +1,191 @@ +package kalmanGUI; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoNNPanel { + + + public static final int scrollbarSize = 1000; + public static JPanel KalmanPanel = new JPanel(); + + public static final String maxSearchstringKalman = "Maximum search radius"; + public static final String initialSearchstring = "Initial search radius"; + + + public static final String alphastring = "Weightage for distance based cost"; + public static final String betastring = "Weightage for pixel ratio based cost"; + public static int alphaInit = 1; + public static int betaInit = 0; + public static int missedframes = 20; + public static float initialSearchradius = 100; + public static int initialSearchradiusInit = (int) initialSearchradius; + public static float initialSearchradiusMin = 1; + public static float initialSearchradiusMax = 1000; + public static float alphaMin = 0; + public static float alphaMax = 1; + public static float betaMin = 0; + public static float betaMax = 1; + public static float alpha = 0.5f; + public static float beta = 0.5f; + public static float maxSearchradius = 100; + public static int maxSearchradiusInit = (int) maxSearchradius; + public static float maxSearchradiusMin = 1; + public static float maxSearchradiusMax = 1000; + + public static int maxframegap = 10; + public static int trackduration = 50; + public static Label lostlabel; + public static Label mintracklength; + + public static TextField lostframe; + public static TextField tracklength; + public static Label alphaText = new Label(alphastring + " = " + alphaInit, Label.CENTER); + public static Label betaText = new Label(betastring + " = " + betaInit, Label.CENTER); + + public static Label maxSearchTextKalman = new Label(maxSearchstringKalman + " = " + maxSearchradiusInit, Label.CENTER); + public static Label iniSearchText = new Label(initialSearchstring + " = " + initialSearchradiusInit, Label.CENTER); + + public static final JScrollBar maxSearchSS = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar initialSearchS = new JScrollBar(Scrollbar.HORIZONTAL, initialSearchradiusInit, 10, 0, + 10 + scrollbarSize); + public static final JScrollBar alphaS = new JScrollBar(Scrollbar.HORIZONTAL, alphaInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar betaS = new JScrollBar(Scrollbar.HORIZONTAL, betaInit, 10, 0, 10 + scrollbarSize); + public static final JButton Skeletontime = new JButton("Skeletonize Buddies"); + public static final JButton Timetrack = new JButton("Track Buddies"); + public static final JButton Restart = new JButton("Restart"); + public static final JScrollBar maxSearchKalman = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + + + public static void setInitialAlpha(final float value) { + alpha = value; + alphaInit = scrollbar.Utility.computeScrollbarPositionFromValue(alpha, alphaMin, alphaMax, scrollbarSize); + } + public static double getInitialAlpha(final float value) { + + return alpha; + + } + public static void setInitialBeta(final float value) { + beta = value; + betaInit = scrollbar.Utility.computeScrollbarPositionFromValue(beta, betaMin, betaMax, scrollbarSize); + } + + public static double getInitialBeta(final float value) { + + return beta; + + } + public static void setInitialsearchradius(final float value) { + initialSearchradius = value; + initialSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(initialSearchradius, initialSearchradiusMin, + initialSearchradiusMax, scrollbarSize); + } + + public static void setInitialmaxsearchradius(final float value) { + maxSearchradius = value; + maxSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(maxSearchradius, maxSearchradiusMin, maxSearchradiusMax, + scrollbarSize); + } + + public double getInitialsearchradius(final float value) { + + return initialSearchradius; + + } + public static JPanel KalmanPanel() { + + setInitialAlpha(alphaInit); + setInitialBeta(betaInit); + setInitialsearchradius(initialSearchradiusInit); + setInitialmaxsearchradius(maxSearchradius); + layoutManager.Setlayout.LayoutSetter(KalmanPanel); + lostframe = new TextField(1); + lostframe.setText(Integer.toString(maxframegap)); + + tracklength = new TextField(1); + tracklength.setText(Integer.toString(trackduration)); + + lostlabel = new Label("Allow link loosing for #frames"); + mintracklength = new Label("Minimum BudTrack length as percent of timeframes"); + + + alphaText = new Label(alphastring + " = " + alpha, Label.CENTER); + betaText = new Label(betastring + " = " + beta, Label.CENTER); + + iniSearchText = new Label(initialSearchstring + " = " + initialSearchradius, Label.CENTER); + + maxSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(maxSearchKalman.getValue(), + maxSearchradiusMin, maxSearchradiusMax, scrollbarSize); + initialSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(initialSearchS.getValue(), + initialSearchradiusMin, initialSearchradiusMax, scrollbarSize); + alpha = scrollbar.Utility.computeValueFromScrollbarPosition(alphaS.getValue(), alphaMin, alphaMax, + scrollbarSize); + beta = scrollbar.Utility.computeValueFromScrollbarPosition(betaS.getValue(), betaMin, betaMax, + scrollbarSize); + + alphaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(alphaInit, alphaMin, alphaMax, scrollbarSize)); + betaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(betaInit, betaMin, betaMax, scrollbarSize)); + + + Border Kalmanborder = new CompoundBorder(new TitledBorder("Skeletonize n track buds"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + //KalmanPanel.add(iniSearchText, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(initialSearchS, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(alphaText, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(alphaS, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(betaText, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(betaS, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(lostlabel, new GridBagConstraints(5, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(lostframe, new GridBagConstraints(5, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + // KalmanPanel.add(mintracklength, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + // KalmanPanel.add(tracklength, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(maxSearchTextKalman, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + // KalmanPanel.add(maxSearchKalman, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Restart, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Skeletontime, new GridBagConstraints(3, 0, 2, 1, 1.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Timetrack, new GridBagConstraints(5, 4, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.setBorder(Kalmanborder); + + + return KalmanPanel; + } + + +} diff --git a/layoutManager/Setlayout.java b/layoutManager/Setlayout.java new file mode 100644 index 0000000..822a215 --- /dev/null +++ b/layoutManager/Setlayout.java @@ -0,0 +1,31 @@ +package layoutManager; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.JPanel; + +public class Setlayout { + + + public final static Insets insets = new Insets(10, 10, 0, 10); + public final static GridBagLayout layout = new GridBagLayout(); + public final static GridBagConstraints c = new GridBagConstraints(); + + public static void LayoutSetter(JPanel panel) { + + panel.setLayout(layout); + c.anchor = GridBagConstraints.BOTH; + c.ipadx = 5; + + c.gridwidth = 10; + c.gridheight = 10; + c.gridy = 1; + c.gridx = 0; + } + + + + +} diff --git a/loaddirectory/LeftRightDirectoryLoader.java b/loaddirectory/LeftRightDirectoryLoader.java new file mode 100644 index 0000000..7d56f63 --- /dev/null +++ b/loaddirectory/LeftRightDirectoryLoader.java @@ -0,0 +1,54 @@ +package loaddirectory; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class LeftRightDirectoryLoader { + + final File[] leftwings; + final File[] rightwings; + final String bordertitle; + + public LeftRightDirectoryLoader( final String bordertitle, final File[] leftwings, final File[] rightwings) { + + this.bordertitle = bordertitle; + + this.leftwings = leftwings; + + this.rightwings = rightwings; + + } + + public JPanel DirectoryLoader = new JPanel(); + public JButton LoadLeftWingDirectory; + public JButton LoadRightWingDirectory; + + public JPanel LoadDirectory() { + + layoutManager.Setlayout.LayoutSetter(DirectoryLoader); + + LoadLeftWingDirectory = new JButton("Load Left Wing Directory"); + LoadRightWingDirectory = new JButton("Load Right Wing Directory"); + + Border directorychooser = new CompoundBorder(new TitledBorder(bordertitle), new EmptyBorder(layoutManager.Setlayout.c.insets)); + + DirectoryLoader.add(LoadLeftWingDirectory, new GridBagConstraints(0, 0, 3, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10,10,0,10), 0,0)); + + DirectoryLoader.add(LoadRightWingDirectory, new GridBagConstraints(3, 0, 3, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10,10,0,10), 0,0)); + + DirectoryLoader.setBorder(directorychooser); + + + return DirectoryLoader; + + } + +} diff --git a/loadfile/CovistoFourChFileLoader.java b/loadfile/CovistoFourChFileLoader.java new file mode 100644 index 0000000..278773b --- /dev/null +++ b/loadfile/CovistoFourChFileLoader.java @@ -0,0 +1,64 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoFourChFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoFourChFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelFourChannel = new JPanel(); + + public JComboBox ChooseImage; + public JComboBox ChoosesecImage; + public JComboBox ChoosethirdImage; + public JComboBox ChoosefourthImage; + public JPanel FourChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelFourChannel); + ChooseImage = new JComboBox(blankimageNames); + ChoosesecImage = new JComboBox(blankimageNames); + ChoosethirdImage = new JComboBox(blankimageNames); + ChoosefourthImage = new JComboBox(blankimageNames); + + + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelFourChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelFourChannel.add(ChoosesecImage, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelFourChannel.add(ChoosethirdImage, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelFourChannel.add(ChoosefourthImage, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + panelFourChannel.setBorder(chooseoriginalfile); + + + + return panelFourChannel; + + } + +} diff --git a/loadfile/CovistoOneChFileLoader.java b/loadfile/CovistoOneChFileLoader.java new file mode 100644 index 0000000..ff131c8 --- /dev/null +++ b/loadfile/CovistoOneChFileLoader.java @@ -0,0 +1,48 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoOneChFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoOneChFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelSingleChannel = new JPanel(); + + public JComboBox ChooseImage; + public JPanel SingleChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelSingleChannel); + ChooseImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + panelSingleChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + panelSingleChannel.setBorder(chooseoriginalfile); + + + + return panelSingleChannel; + + } + +} diff --git a/loadfile/CovistoThreeChForceFileLoader.java b/loadfile/CovistoThreeChForceFileLoader.java new file mode 100644 index 0000000..efb2bab --- /dev/null +++ b/loadfile/CovistoThreeChForceFileLoader.java @@ -0,0 +1,57 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoThreeChForceFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoThreeChForceFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelThreeChannel = new JPanel(); + + public JComboBox ChooseImage; + public JComboBox ChoosesecImage; + public JComboBox ChoosethirdImage; + public JPanel ThreeChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelThreeChannel); + ChooseImage = new JComboBox(blankimageNames); + ChoosesecImage = new JComboBox(blankimageNames); + ChoosethirdImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelThreeChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelThreeChannel.add(ChoosesecImage, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelThreeChannel.add(ChoosethirdImage, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelThreeChannel.setBorder(chooseoriginalfile); + + + + return panelThreeChannel; + + } + +} diff --git a/loadfile/CovistoTwoChDropbox.java b/loadfile/CovistoTwoChDropbox.java new file mode 100644 index 0000000..9f6a6a0 --- /dev/null +++ b/loadfile/CovistoTwoChDropbox.java @@ -0,0 +1,62 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTwoChDropbox { + + + + final String bordertitle; + final String[] blankimageNames; + + public CovistoTwoChDropbox(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelTwoChannel = new JPanel(); + + public JComboBox ChooseImage; + public JButton ChooseFile; + public JPanel TwoChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelTwoChannel); + ChooseImage = new JComboBox(blankimageNames); + ChooseFile = new JButton("Load CSV File"); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelTwoChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.add(ChooseFile, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.setBorder(chooseoriginalfile); + + + + return panelTwoChannel; + + } + + + + + + +} diff --git a/loadfile/CovistoTwoChForceFileLoader.java b/loadfile/CovistoTwoChForceFileLoader.java new file mode 100644 index 0000000..bd4c371 --- /dev/null +++ b/loadfile/CovistoTwoChForceFileLoader.java @@ -0,0 +1,53 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTwoChForceFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoTwoChForceFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelTwoChannel = new JPanel(); + + public JComboBox ChooseImage; + public JComboBox ChoosesecImage; + public JPanel TwoChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelTwoChannel); + ChooseImage = new JComboBox(blankimageNames); + ChoosesecImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelTwoChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.add(ChoosesecImage, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.setBorder(chooseoriginalfile); + + + + return panelTwoChannel; + + } + +} diff --git a/loadfile/CovistoTwoChOptionFileLoader.java b/loadfile/CovistoTwoChOptionFileLoader.java new file mode 100644 index 0000000..f86d722 --- /dev/null +++ b/loadfile/CovistoTwoChOptionFileLoader.java @@ -0,0 +1,53 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTwoChOptionFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoTwoChOptionFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelTwoChannel = new JPanel(); + public boolean twochannel = false; + public Checkbox Godouble = new Checkbox("Load a second channel", twochannel); + + public JComboBox ChooseImage; + + public JPanel TwoChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelTwoChannel); + ChooseImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + panelTwoChannel.add(Godouble, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.add(ChooseImage, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.setBorder(chooseoriginalfile); + + + + return panelTwoChannel; + + } + +} diff --git a/plugins.config b/plugins.config deleted file mode 100644 index ebb9e94..0000000 --- a/plugins.config +++ /dev/null @@ -1 +0,0 @@ -Plugins>ETrack, " Ellipsoid and angle Fit ", pluginTools.Ellipse_FileChooser diff --git a/pom.xml b/pom.xml index afa6f60..5562df9 100644 --- a/pom.xml +++ b/pom.xml @@ -5,10 +5,9 @@ org.scijava pom-scijava - 25.0.0 + 29.2.1 - net.kapoor E_Track 3.0.0 @@ -50,9 +49,24 @@ None http://imagej.net/User:MTrack vkapoor + + + + Oozge Ozguc + http://imagej.net/User:MTrack + oozge + + + founder + debugger + reviewer + support + + + ImageJ Forum @@ -168,7 +182,37 @@ - + + + net.imglib2 + imglib2 + + + net.imglib2 + imglib2-algorithm + + + net.imglib2 + imglib2-algorithm-gpl + + + net.imglib2 + imglib2-ij + + + net.imglib2 + imglib2-roi + + + + math.geom2d + javaGeom + 0.11.1 + + + sc.fiji + bigdataviewer-vistools + diff --git a/removeSpotGUI/CovistoRemovePanel.java b/removeSpotGUI/CovistoRemovePanel.java new file mode 100644 index 0000000..aa16658 --- /dev/null +++ b/removeSpotGUI/CovistoRemovePanel.java @@ -0,0 +1,50 @@ +package removeSpotGUI; + +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.TextField; + +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoRemovePanel { + + + + + public static JPanel RemovePanel = new JPanel(); + public static Label removeText = new Label("Distance Threshold = ", Label.CENTER); + + public static TextField inputFieldSpot; + public static double distthreshold = 10; + + public static JPanel RemoveselectPanel() { + + layoutManager.Setlayout.LayoutSetter(RemovePanel); + + inputFieldSpot = new TextField(5); + inputFieldSpot.setText(Double.toString(distthreshold)); + + + Border removeborder = new CompoundBorder(new TitledBorder("Spurios Spot removal"), new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + RemovePanel.add(removeText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + RemovePanel.add(inputFieldSpot, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + RemovePanel.setBorder(removeborder); + + return RemovePanel; + + } + + + +} diff --git a/scrollbar/Utility.java b/scrollbar/Utility.java new file mode 100644 index 0000000..6c52301 --- /dev/null +++ b/scrollbar/Utility.java @@ -0,0 +1,95 @@ +package scrollbar; + +import javax.swing.JProgressBar; + +public class Utility { + + + + public static float computeValueFromScrollbarPosition(final int scrollbarPosition, final float min, + final float max, final int scrollbarSize) { + return min + (scrollbarPosition / (float) scrollbarSize) * (max - min); + } + + public static float computeIntValueFromScrollbarPosition(final int scrollbarPosition, final float min, + final float max, final int scrollbarSize) { + return min + (scrollbarPosition / (max)) * (max - min); + } + + public static int computeScrollbarPositionFromValue(final float sigma, final float min, final float max, + final int scrollbarSize) { + return Math.round(((sigma - min) / (max - min)) * scrollbarSize); + } + + public static int computeIntScrollbarPositionFromValue(final float thirdDimensionslider, final float min, + final float max, final int scrollbarSize) { + return Math.round(((thirdDimensionslider - min) / (max - min)) * max); + } + + public static float computeSigma2(final float sigma1, final int sensitivity) { + final float k = (float) computeK(sensitivity); + final float[] sigma = computeSigma(k, sigma1); + + return sigma[1]; + } + public static double computeK( final float stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); } + public static double computeK( final int stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); } + public static float computeKWeight( final float k ) { return 1.0f / (k - 1.0f); } + public static float[] computeSigma( final float k, final float initialSigma ) + { + final float[] sigma = new float[ 2 ]; + + sigma[ 0 ] = initialSigma; + sigma[ 1 ] = sigma[ 0 ] * k; + + return sigma; + } + + public static void SetProgressBar(JProgressBar jpb, double percent) { + + jpb.setValue((int) Math.round(percent)); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString("Finding MT ends"); + + } + + + public static void SetProgressBar(JProgressBar jpb, double percent, String message) { + + jpb.setValue((int) Math.round(percent)); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString(message); + + } + + public static void SetProgressBar(JProgressBar jpb) { + jpb.setValue(0); + jpb.setIndeterminate(true); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString("Pre-processing Image"); + + } + + public static void SetProgressBarTime(JProgressBar jpb, double percent, int framenumber, int thirdDimsize) { + + jpb.setValue((int) percent); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString("Time point = " + framenumber + "/" + thirdDimsize); + + } + + public static void SetProgressBarTime(JProgressBar jpb, double percent, int framenumber, int thirdDimsize, + String message) { + + jpb.setValue((int) percent); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString(message + "= " + framenumber + "/" + thirdDimsize); + + } + +} diff --git a/src/main/java/analyzers/EdgeAnalyzer.java b/src/main/java/analyzers/EdgeAnalyzer.java new file mode 100644 index 0000000..699f676 --- /dev/null +++ b/src/main/java/analyzers/EdgeAnalyzer.java @@ -0,0 +1,52 @@ + +package analyzers; + +import java.util.Collection; + +import org.jgrapht.graph.DefaultWeightedEdge; + +import linkers.Model3D; +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.MultiThreaded; +import utility.FeatureAnalyzer3D; + +/** + * Interface for analyzers that can compute scalar numerical features for an + * edge of a TrackMate model. An edge, or a link, is the single link that exists + * between two spots after tracking. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> + */ +public interface EdgeAnalyzer extends Benchmark, FeatureAnalyzer3D, MultiThreaded +{ + + /** + * Scores a collection of link between two spots. The results must be stored + * in the {@link fiji.plugin.trackmate.FeatureModel}. + *

+ * Note: ideally concrete implementation should work in a multi-threaded + * fashion for performance reason, when possible. + * + * @param edges + * the collection of edges whose features are to be calculated. + * @param model + * the {@link Model} they belong to. + */ + public void process( final Collection< DefaultWeightedEdge > edges, Model3D model ); + + /** + * Returns true if this analyzer is a local analyzer. That is: + * a modification that affects only one edge requires the edge features to + * be re-calculated only for this edge. If false, any model + * modification involving an edge will trigger a recalculation over the + * whole track this edge belong to. + *

+ * Example of local edge feature: the edge length (distance between the two + * spots). This one does not depend on other edge values. + *

+ * Example of non-local edge feature: the local curvature of the trajectory, + * which depends on the neighbor edges. + */ + public boolean isLocal(); + +} diff --git a/src/main/java/analyzers/TrackAnalyzer.java b/src/main/java/analyzers/TrackAnalyzer.java new file mode 100644 index 0000000..f55ebf7 --- /dev/null +++ b/src/main/java/analyzers/TrackAnalyzer.java @@ -0,0 +1,66 @@ +package analyzers; + +import java.util.Collection; + +import linkers.Model3D; +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.MultiThreaded; +import utility.FeatureAnalyzer3D; + + +/** + * Mother interface for the classes that can compute the feature of tracks. + * Target tracks are given through a {@link Model} and their IDs. Therefore and + * ideally, concrete implementations can be made stateless. + *

+ * Note: ideally concrete implementation should work in a multi-threaded fashion + * for performance reason, when possible. + *

+ * For {@link TrackAnalyzer}s, there is a mechanism intended to maintain the + * model integrity against manual, small changes. Something as simple as + * removing a spot in the middle of a track will generate two new tracks, which + * will invalidate all feature values for the old track. Analyzers are notified + * of such events, so that they can recompute track features after the change. + *

+ * A simple way would be to recompute all track features at once, but this might + * be too long and overkill for changes that do not affect all tracks + * (e.g. adding a lonely spot, or a new track is likely not to affect all + * tracks in some case). + *

+ * So the {@link #process(Collection, Model)} will be called selectively on new + * or modified tracks every time a change happens. It will be called from the + * {@link Model} after a {@link Model#endUpdate()}, before any listener gets + * notified. + * + * @author Jean-Yves Tinevez + */ +public interface TrackAnalyzer extends Benchmark, FeatureAnalyzer3D, MultiThreaded +{ + + /** + * Compute the features of the track whose ID is given. + * + * @param trackIDs + * the IDs of the track whose features are to be calculated. + * @param model + * the {@link Model} from which actual tracks are to be + * retrieved. + */ + public void process( final Collection< Integer > trackIDs, final Model3D model ); + + /** + * Returns true if this analyzer is a local analyzer. That is: + * a modification that affects only one track requires the track features to + * be re-calculated only for this track. If false, any model + * modification involving edges will trigger a recalculation over all the + * visible tracks of the model. + *

+ * Example of a local track feature: the number of spots in a track. It does + * not depend on the number of spots in other tracks. + *

+ * Example of a non-local track feature: the rank of the track sorted by its + * number of spots, compared to other tracks. + */ + public boolean isLocal(); + +} diff --git a/src/main/java/batchMode/ExecuteBatch.java b/src/main/java/batchMode/ExecuteBatch.java index 7680a97..75cb417 100644 --- a/src/main/java/batchMode/ExecuteBatch.java +++ b/src/main/java/batchMode/ExecuteBatch.java @@ -44,7 +44,6 @@ import net.imglib2.util.Pair; import net.imglib2.util.ValuePair; import net.imglib2.view.Views; -import pluginTools.ComputeAngles; import pluginTools.ComputeCurvature; import pluginTools.InteractiveSimpleEllipseFit; import pluginTools.InteractiveSimpleEllipseFit.ValueChange; diff --git a/src/main/java/comboSliderTextbox/SliderBoxGUI.java b/src/main/java/comboSliderTextbox/SliderBoxGUI.java new file mode 100644 index 0000000..a0c3fc5 --- /dev/null +++ b/src/main/java/comboSliderTextbox/SliderBoxGUI.java @@ -0,0 +1,66 @@ +package comboSliderTextbox; + +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + + +public class SliderBoxGUI { + + + final String string; + final float valueDimension; + final float valueMax; + final int scrollbarSize; + final JScrollBar valueslider; + final TextField inputFieldvalue; + final Label valueText; + + public SliderBoxGUI(final String string, JScrollBar valueslider, TextField inputFieldvalue, Label valueText, final int scrollbarSize, final float valueDimension, final float valueMax) { + + this.string = string; + this.scrollbarSize = scrollbarSize; + this.valueDimension = valueDimension; + this.valueMax = valueMax; + this.valueslider = valueslider; + this.inputFieldvalue = inputFieldvalue; + this.valueText = valueText; + } + + + + public JPanel BuildDisplay() { + + + JPanel combosliderbox = new JPanel(); + + layoutManager.Setlayout.LayoutSetter(combosliderbox); + + + + combosliderbox.add(valueText, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + combosliderbox.add(valueslider, new GridBagConstraints(0, 1, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + combosliderbox.add(inputFieldvalue, new GridBagConstraints(0, 2, 2, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + + return combosliderbox; + + + + } + +} diff --git a/src/main/java/common3D/BinaryCreation.java b/src/main/java/common3D/BinaryCreation.java new file mode 100644 index 0000000..dee9dd1 --- /dev/null +++ b/src/main/java/common3D/BinaryCreation.java @@ -0,0 +1,103 @@ +package common3D; + +import java.util.ArrayList; + +import dogGUI.CovistoDogPanel; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import net.imglib2.Cursor; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.region.hypersphere.HyperSphere; +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 net.imglib2.view.Views; +import utility.PreRoiobject; +import visualization.DrawCircles; +import net.imglib2.algorithm.region.Circles; +public class BinaryCreation { + + + public static & NativeType> void CreateBinary(final InteractiveMethods parent, RandomAccessibleInterval inputimg, RandomAccessibleInterval outimg, int z, int t) { + + + ArrayList pointlist = parent.AllEvents.get(z); + + for (double[] currentpoint : pointlist) { + + Point point = new Point(2); + point.setPosition(new long[] {(long)currentpoint[0], (long)currentpoint[1]}); + Circles.add(Views.expandZero(outimg, new long[] {(int)CovistoDogPanel.distthreshold + 1, (int)CovistoDogPanel.distthreshold + 1}), point, (int)CovistoDogPanel.distthreshold, new BitType(true)); + + + } + + + } + + + + public static & NativeType> void CreateBinaryDots(final InteractiveMethods parent, RandomAccessibleInterval inputimg, RandomAccessibleInterval outimg, int z, int t) { + + Cursor incursor = Views.iterable(inputimg).localizingCursor(); + RandomAccess outran = outimg.randomAccess(); + + + + + while (incursor.hasNext()) { + + incursor.fwd(); + outran.setPosition(incursor); + + + ArrayList pointlist = parent.AllEvents.get(z); + + for (double[] currentpoint : pointlist) { + + if ( incursor.getIntPosition(0) == (int)Math.round(currentpoint[0]) && incursor.getIntPosition(1) == (int)Math.round(currentpoint[1]) ) { + + + HyperSphere< BitType > hyperSphere = new HyperSphere( outimg, incursor, 2 ); + + // set every value inside the sphere to 1 + for ( BitType value : hyperSphere ) + value.setOne(); + + + } + + } + + } + + + } + +public static & NativeType> void CreateBinaryRoi(final InteractiveMethods parent, RandomAccessibleInterval inputimg, RandomAccessibleInterval outimg, final ArrayList Rois, int z, int t) { + + Cursor incursor = Views.iterable(inputimg).localizingCursor(); + RandomAccess outran = outimg.randomAccess(); + + while (incursor.hasNext()) { + + incursor.fwd(); + outran.setPosition(incursor); + + for (Roi currentroi : Rois) { + + if (currentroi.contains(incursor.getIntPosition(0), incursor.getIntPosition(1))) { + + outran.get().setOne(); + + } + + } + + } +} +} \ No newline at end of file diff --git a/src/main/java/common3D/CommonWater.java b/src/main/java/common3D/CommonWater.java new file mode 100644 index 0000000..b17b93c --- /dev/null +++ b/src/main/java/common3D/CommonWater.java @@ -0,0 +1,139 @@ +package common3D; + +import distanceTransform.DistWatershed; +import distanceTransform.WatershedBinary; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; +import preProcessing.GlobalThresholding; +import timeGUI.CovistoTimeselectPanel; +import watershedGUI.CovistoWatershedPanel; +import zGUI.CovistoZselectPanel; + +public class CommonWater { + + public static void Watershed( final InteractiveMethods parent,RandomAccessibleInterval newimg,RandomAccessibleInterval bitimg,RandomAccessibleInterval intimg, int t, int z ) { + + + + + CovistoZselectPanel.thirdDimension = z; + CovistoTimeselectPanel.fourthDimension = t; + + parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, z, CovistoZselectPanel.thirdDimensionSize, t, + CovistoTimeselectPanel.fourthDimensionSize); + parent.updatePreview(ValueChange.THIRDDIMmouse); + + RandomAccessibleInterval currentbitimg = utility.CovistoSlicer.getCurrentView(bitimg, z, CovistoZselectPanel.thirdDimensionSize, t, + CovistoTimeselectPanel.fourthDimensionSize); + + RandomAccessibleInterval currentintimg = utility.CovistoSlicer.getCurrentView(intimg, z, CovistoZselectPanel.thirdDimensionSize, t, + CovistoTimeselectPanel.fourthDimensionSize); + + RandomAccessibleInterval currentnewimg = utility.CovistoSlicer.getCurrentView(newimg, z, CovistoZselectPanel.thirdDimensionSize, t, + CovistoTimeselectPanel.fourthDimensionSize); + + processSlice(parent, parent.CurrentView, currentintimg, currentbitimg, currentnewimg); + + + + + + + + } + public static void processSlice(final InteractiveMethods parent, RandomAccessibleInterval< FloatType > slice, RandomAccessibleInterval< IntType > intoutputslice, RandomAccessibleInterval< BitType > bitoutputslice, + RandomAccessibleInterval< FloatType > distslice) { + + + RandomAccessibleInterval bitimg = new ArrayImgFactory().create(slice, new BitType()); + + + + if(parent.autothreshwater) { + CovistoWatershedPanel.thresholdWater = (float) ( GlobalThresholding.AutomaticThresholding(slice)); + CovistoWatershedPanel.thresholdWaterslider.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoWatershedPanel.thresholdWater, CovistoWatershedPanel.thresholdMinWater, + CovistoWatershedPanel.thresholdMaxWater, CovistoWatershedPanel.scrollbarSize)); + CovistoWatershedPanel.watertext.setText(CovistoWatershedPanel.waterstring + " = " + CovistoWatershedPanel.thresholdWater ); + CovistoWatershedPanel.thresholdWaterslider.validate(); + CovistoWatershedPanel.thresholdWaterslider.repaint(); + } + GetLocalmaxminMT.ThresholdingMTBit(slice, bitimg, CovistoWatershedPanel.thresholdWater); + + RandomAccessibleInterval waterint = null; + RandomAccessibleInterval distwater = null; + + + if (CovistoWatershedPanel.disttransform) { + DistWatershed WaterafterDisttransform = new DistWatershed(parent, slice, bitimg, + parent.jpb, parent.apply3D); + + WaterafterDisttransform.execute(); + waterint = WaterafterDisttransform.getResult(); + distwater = WaterafterDisttransform.getDistanceTransformedimg(); + + } + else { + + WatershedBinary Wateronly = new WatershedBinary(bitimg); + Wateronly.process(); + waterint = Wateronly.getResult(); + + } + + Cursor< BitType > bitcursor = Views.iterable(bitoutputslice).localizingCursor(); + Cursor< IntType > intcursor = Views.iterable(intoutputslice).localizingCursor(); + + + + RandomAccess ranac = bitimg.randomAccess(); + RandomAccess intranac = waterint.randomAccess(); + + + while(bitcursor.hasNext()) { + + bitcursor.fwd(); + + ranac.setPosition(bitcursor); + + bitcursor.get().set(ranac.get()); + + + } + + while(intcursor.hasNext()) { + + intcursor.fwd(); + + intranac.setPosition(intcursor); + + intcursor.get().set(intranac.get()); + + + } + if(distwater!=null) { + Cursor< FloatType > distcursor = Views.iterable(distslice).localizingCursor(); + RandomAccess distranac = distwater.randomAccess(); + while(distcursor.hasNext()) { + + distcursor.fwd(); + + distranac.setPosition(distcursor); + + distcursor.get().set(distranac.get()); + + } + } + + } + +} diff --git a/src/main/java/costMatrix/CostFunction.java b/src/main/java/costMatrix/CostFunction.java new file mode 100644 index 0000000..ebcdbaa --- /dev/null +++ b/src/main/java/costMatrix/CostFunction.java @@ -0,0 +1,32 @@ +package costMatrix; + +/** + * Interface representing a function that can calculate the cost to link a + * source object to a target object. + * + * @author Jean-Yves Tinevez - 2014 + * + * @param + * the type of the sources. + * @param + * the type of the targets. + */ +public interface CostFunction< K, J > +{ + + /** + * Returns the cost to link two objects. + * + * @param source + * the source object. + * @param target + * the target object. + * @return the cost as a double. + */ + public double linkingCost( K source, J target ); + + + + + +} \ No newline at end of file diff --git a/src/main/java/costMatrix/CostMatrixCreator.java b/src/main/java/costMatrix/CostMatrixCreator.java new file mode 100644 index 0000000..bdc5084 --- /dev/null +++ b/src/main/java/costMatrix/CostMatrixCreator.java @@ -0,0 +1,63 @@ +package costMatrix; + + +import java.util.List; + +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.OutputAlgorithm; + +/** + * Interface for function that can generate a {@link SparseCostMatrix} from + * assignment candidates. + * + * @author Jean-Yves Tinevez + * + */ +public interface CostMatrixCreator< K extends Comparable< K >, J extends Comparable< J >> extends Benchmark, OutputAlgorithm< SparseCostMatrix > +{ + + /** + * Returns the list of sources in the generated cost matrix. + * + * @return the list of object, such that sourceList.get( i ) is + * the source corresponding to the row i in the + * generated cost matrix. + * @see #getTargetList() + * @see #getResult() + */ + public List< K > getSourceList(); + + /** + * Returns the list of targets in the generated cost matrix. + * + * @return the list of objects, such that targetList.get( j ) + * is the target corresponding to the column j in the + * generated cost matrix. + * @see #getSourceList() + * @see #getResult() + */ + public List< J > getTargetList(); + + /** + * Returns the value of the no-linking alternative cost for the specified + * source. + * + * @param source + * the source object. + * @return the alternative cost. Belongs to the list returned by + * {@link #getSourceList()}. + */ + public double getAlternativeCostForSource( K source ); + + /** + * Returns the value of the no-linking alternative cost for the specified + * target. + * + * @param target + * the target object. Belongs to the list returned by + * {@link #getTargetList()}. + * @return the alternative cost. + */ + public double getAlternativeCostForTarget( J target ); + +} diff --git a/src/main/java/costMatrix/DefaultCostMatrixCreator.java b/src/main/java/costMatrix/DefaultCostMatrixCreator.java new file mode 100644 index 0000000..40b0a82 --- /dev/null +++ b/src/main/java/costMatrix/DefaultCostMatrixCreator.java @@ -0,0 +1,265 @@ +package costMatrix; + + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; + +import net.imglib2.util.Util; + +/** + * A {@link CostMatrixCreator} that build a cost matrix from 3 lists containing + * the sources, the targets and the associated costs. + * + * @author Jean-Yves Tinevez - 2014 + * + * @param + */ +public class DefaultCostMatrixCreator< K extends Comparable< K >, J extends Comparable< J > > implements CostMatrixCreator< K, J > +{ + + private static final String BASE_ERROR_MESSAGE = "[DefaultCostMatrixCreator] "; + + private SparseCostMatrix scm; + + private ArrayList< K > uniqueRows; + + private ArrayList< J > uniqueCols; + + private long processingTime; + + private String errorMessage; + + private double alternativeCost; + + private final List< K > rows; + + private final List< J > cols; + + private final double[] costs; + + private final double alternativeCostFactor; + + private final double percentile; + + public DefaultCostMatrixCreator( final List< K > rows, final List< J > cols, final double[] costs, final double alternativeCostFactor, final double percentile ) + { + this.rows = rows; + this.cols = cols; + this.costs = costs; + this.alternativeCostFactor = alternativeCostFactor; + this.percentile = percentile; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public SparseCostMatrix getResult() + { + return scm; + } + + @Override + public boolean checkInput() + { + if ( rows == null || rows.isEmpty() ) + { + errorMessage = BASE_ERROR_MESSAGE + "The row list is null or empty."; + return false; + } + if ( rows.size() != cols.size() ) { + errorMessage = BASE_ERROR_MESSAGE +"Row and column lists do not have the same number of elements. Found " + rows.size() + " and " + cols.size() + "." ; + return false; + } + if ( rows.size() != costs.length ) + { + errorMessage = BASE_ERROR_MESSAGE + "Row list and cost array do not have the same number of elements. Found " + rows.size() + " and " + costs.length + "."; + return false; + } + if ( alternativeCostFactor <= 0 ) + { + errorMessage = BASE_ERROR_MESSAGE + "The alternative cost factor must be greater than 0. Was: " + alternativeCostFactor + "."; + return false; + } + if ( percentile < 0 || percentile > 1 ) + { + errorMessage = BASE_ERROR_MESSAGE + "The percentile must no be smaller than 0 or greater than 1. Was: " + percentile; + return false; + } + return true; + } + + @Override + public boolean process() + { + uniqueRows = new ArrayList< K >( new HashSet< K >( rows ) ); + Collections.sort( uniqueRows ); + uniqueCols = new ArrayList< J >( new HashSet< J >( cols ) ); + Collections.sort( uniqueCols ); + + final List< Assignment > assignments = new ArrayList< Assignment >( costs.length ); + for ( int i = 0; i < costs.length; i++ ) + { + final K rowObj = rows.get( i ); + final J colObj = cols.get( i ); + final int r = Collections.binarySearch( uniqueRows, rowObj ); + final int c = Collections.binarySearch( uniqueCols, colObj ); + assignments.add( new Assignment( r, c, costs[ i ] ) ); + } + Collections.sort( assignments ); + + // Test we do not have duplicates. + Assignment previousAssgn = assignments.get( 0 ); + for ( int i = 1; i < assignments.size(); i++ ) + { + final Assignment assgn = assignments.get( i ); + if ( assgn.equals( previousAssgn ) ) + { + errorMessage = BASE_ERROR_MESSAGE + "Found duplicate assignment at index: " + assgn + "."; + return false; + } + previousAssgn = assgn; + } + + final int nRows = uniqueRows.size(); + final int nCols = uniqueCols.size(); + final int[] kk = new int[ costs.length ]; + final int[] number = new int[ nRows ]; + final double[] cc = new double[ costs.length ]; + + Assignment a = assignments.get( 0 ); + kk[ 0 ] = a.c; + cc[ 0 ] = a.cost; + int currentRow = a.r; + int nOfEl = 0; + for ( int i = 1; i < assignments.size(); i++ ) + { + a = assignments.get( i ); + + kk[ i ] = a.c; + cc[ i ] = a.cost; + nOfEl++; + + if ( a.r != currentRow ) + { + number[ currentRow ] = nOfEl; + nOfEl = 0; + currentRow = a.r; + } + } + number[ currentRow ] = nOfEl + 1; + + scm = new SparseCostMatrix( cc, kk, number, nCols ); + + alternativeCost = computeAlternativeCosts(); + + return true; + } + + protected double computeAlternativeCosts() + { + if ( percentile == 1 ) { return alternativeCostFactor * Util.max( costs ); } + return alternativeCostFactor * Util.percentile( costs, percentile ); + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } + + @Override + public List< K > getSourceList() + { + return uniqueRows; + } + + @Override + public List< J > getTargetList() + { + return uniqueCols; + } + + + @Override + public double getAlternativeCostForSource( final K source ) + { + return alternativeCost; + } + + @Override + public double getAlternativeCostForTarget( final J target ) + { + return alternativeCost; + } + + public final static class Assignment implements Comparable< Assignment > + { + private final int r; + + private final int c; + + private final double cost; + + public Assignment( final int r, final int c, final double cost ) + { + this.r = r; + this.c = c; + this.cost = cost; + } + + @Override + public int compareTo( final Assignment o ) + { + if ( r == o.r ) { return c - o.c; } + return r - o.r; + } + + @Override + public boolean equals( final Object obj ) + { + if ( obj instanceof Assignment ) + { + final Assignment o = ( Assignment ) obj; + return r == o.r && c == o.c; + } + return false; + } + + @Override + public int hashCode() + { + int hash = 23; + hash = hash * 31 + r; + hash = hash * 31 + c; + return hash; + } + + @Override + public String toString() + { + return "Assignment r = " + r + ", c = " + c + ", cost = " + cost; + } + + public int getC() + { + return c; + } + + public int getR() + { + return r; + } + + public double getCost() + { + return cost; + } + } +} diff --git a/src/main/java/costMatrix/IntensityDiffCostFunction.java b/src/main/java/costMatrix/IntensityDiffCostFunction.java new file mode 100644 index 0000000..f95cff6 --- /dev/null +++ b/src/main/java/costMatrix/IntensityDiffCostFunction.java @@ -0,0 +1,19 @@ +package costMatrix; + +import utility.PreRoiobject; + +public class IntensityDiffCostFunction implements CostFunction< PreRoiobject, PreRoiobject > + { + + + + + @Override + public double linkingCost( final PreRoiobject source, final PreRoiobject target ) + { + return source.IntensityDistanceTo(target ); + } + + + +} diff --git a/src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java b/src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java new file mode 100644 index 0000000..213805c --- /dev/null +++ b/src/main/java/costMatrix/JaqamanLinkingCostMatrixCreator.java @@ -0,0 +1,189 @@ +package costMatrix; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import costMatrix.SparseCostMatrix; +import costMatrix.LAPJV; + +/** + * A {@link CostMatrixCreator} that can generate a cost matrix from a list of + * sources, a list of targets and a {@link CostFunction} that can generate a + * cost for any combination. + * + * @author Jean-Yves Tinevez - 2014 + * + * @param + * @param + */ +public class JaqamanLinkingCostMatrixCreator< K extends Comparable< K >, J extends Comparable< J >> implements CostMatrixCreator< K, J > +{ + + private static final String BASE_ERROR_MSG = "[JaqamanLinkingCostMatrixCreator] "; + + private final Iterable< K > sources; + + private final Iterable< J > targets; + + private final CostFunction< K, J > costFunction; + + private SparseCostMatrix scm; + + private long processingTime; + + private String errorMessage; + + private final double costThreshold; + + private List< K > sourceList; + + private List< J > targetList; + + private double alternativeCost; + + private final double alternativeCostFactor; + + private final double percentile; + + public JaqamanLinkingCostMatrixCreator( final Iterable< K > sources, final Iterable< J > targets, final CostFunction< K, J > costFunction, final double costThreshold, final double alternativeCostFactor, final double percentile ) + { + this.sources = sources; + this.targets = targets; + this.costFunction = costFunction; + this.costThreshold = costThreshold; + this.alternativeCostFactor = alternativeCostFactor; + this.percentile = percentile; + } + + @Override + public boolean checkInput() + { + if ( null == sources || !sources.iterator().hasNext() ) + { + errorMessage = BASE_ERROR_MSG + "The source list is empty or null."; + return false; + } + if ( null == targets || !targets.iterator().hasNext() ) + { + errorMessage = BASE_ERROR_MSG + "The target list is empty or null."; + return false; + } + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final List< K > accSources = new ArrayList< K >(); + final List< J > accTargets = new ArrayList< J >(); + final ResizableDoubleArray costs = new ResizableDoubleArray(); + + for ( final K source : sources ) + { + for ( final J target : targets ) + { + + final double cost = costFunction.linkingCost( source, target ); + + if ( cost < costThreshold ) + { + accSources.add( source ); + accTargets.add( target ); + costs.add( cost ); + } + } + } + costs.trimToSize(); + + /* + * Check if accepted source or target lists are empty and deal with it. + */ + + if ( accSources.isEmpty() || accTargets.isEmpty() ) + { + + sourceList = Collections.emptyList(); + targetList = Collections.emptyList(); + alternativeCost = Double.NaN; + scm = null; + /* + * CAREFUL! We return null if no acceptable links are found. + */ + } + else + { + + final DefaultCostMatrixCreator< K, J > cmCreator = new DefaultCostMatrixCreator< K, J >( accSources, accTargets, costs.data, alternativeCostFactor, percentile ); + if ( !cmCreator.checkInput() || !cmCreator.process() ) + { + errorMessage = cmCreator.getErrorMessage(); + return false; + } + + scm = cmCreator.getResult(); + sourceList = cmCreator.getSourceList(); + targetList = cmCreator.getTargetList(); + alternativeCost = cmCreator.computeAlternativeCosts(); + } + + + final long end = System.currentTimeMillis(); + processingTime = end - start; + return true; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } + + /** + * Returns the cost matrix generated. + *

+ * Careful, it can be null if not acceptable costs have been + * found for the specified configuration. In that case, the lists returned + * by {@link #getSourceList()} and {@link #getTargetList()} are empty. + * + * @return a new {@link SparseCostMatrix} or null. + */ + @Override + public SparseCostMatrix getResult() + { + return scm; + } + + @Override + public List< K > getSourceList() + { + return sourceList; + } + + @Override + public List< J > getTargetList() + { + return targetList; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public double getAlternativeCostForSource( final K source ) + { + return alternativeCost; + } + + @Override + public double getAlternativeCostForTarget( final J target ) + { + return alternativeCost; + } + +} diff --git a/src/main/java/costMatrix/LAPJV.java b/src/main/java/costMatrix/LAPJV.java new file mode 100644 index 0000000..5d59965 --- /dev/null +++ b/src/main/java/costMatrix/LAPJV.java @@ -0,0 +1,525 @@ +package costMatrix; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.util.Util; + +/** + * Implements the Jonker-Volgenant algorithm for linear assignment problems, + * tailored for sparse cost matrices. + *

+ * We rely on the {@link SparseCostMatrix} class to represent these costs. The + * implementation itself is an unlikely mix between: + *

    + *
  • my (JYT) limited understanding of the original Volgemant paper ( + * Volgenant. Linear and semi-assignment problems: + * A core oriented approach. Computers & Operations Research (1996) vol. 23 (10) + * pp. 917-932);
  • + *
  • my limited understanding of Lee Kamensky python implementation of the + * same algorithm in python using numpy for the CellProfilter project;
  • + *
  • Johannes java implementation of the algorithm for non-sparse matrices.
  • + *
+ *

+ * Computation time performance degrades significantly compared to the + * non-sparse version. Benchmarks show that when increasing the density from + * 0.1% to 70%, the computation time increased by a factor ranging from 1.5 to 7 + * compared to the non-sparse version. For a given density, the comparison + * depends very weakly on the matrix size. + * + * + * @author Jean-Yves Tinevez - 2014 + * @author Johannes Schindelin + * @see A. Volgenant + * "Linear and semi-assignment problems: A core oriented approach" + */ +public class LAPJV implements OutputAlgorithm< int[] >, Benchmark +{ + + private static final String BASE_ERROR_MESSAGE = "[JonkerVolgenantSparseAlgorithm] "; + + private int[] output; + + private String errorMessage; + + private long processingTime; + + private final SparseCostMatrix cm; + + /** + * Instantiates a new Jonker-Volgenant algorithm for the specified sparse + * cost matrix. + * + * @param cm + * the cost matrix of the linear assignment problem to solve. + */ + public LAPJV( final SparseCostMatrix cm ) + { + this.cm = cm; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final int[] x = new int[ cm.nRows ]; + final int[] y = new int[ cm.nCols ]; + final double[] v = new double[ cm.nCols ]; + + final int[] col = new int[ cm.nCols ]; + for ( int j = 0; j < col.length; j++ ) + { + col[ j ] = j; + } + + /* + * Column reduction + */ + + Arrays.fill( v, Double.MAX_VALUE ); + for ( int i = 0; i < cm.nRows; i++ ) + { + for ( int k = cm.start[ i ]; k < cm.start[ i ] + cm.number[ i ]; k++ ) + { + final int j = cm.kk[ k ]; + if ( cm.cc[ k ] < v[ j ] ) + { + v[ j ] = cm.cc[ k ]; + y[ j ] = i + 1; + } + } + } + + for ( int j = cm.nCols - 1; j >= 0; j-- ) + { + final int i = y[ j ] - 1; + if ( x[ i ] == 0 ) + { + x[ i ] = j + 1; + } + else + { + if ( x[ i ] > 0 ) + { + x[ i ] = -x[ i ]; + } + y[ j ] = 0; + } + } + + /* + * Reduction transfer. + */ + + int f = 0; + final int[] free = new int[ cm.nRows ]; + for ( int i = 0; i < cm.nRows; i++ ) + { + if ( x[ i ] == 0 ) + { + // unassigned row in free-array + free[ f++ ] = i; + continue; + } + else if ( x[ i ] < 0 ) + { + // no reduction transfer possible + x[ i ] = -x[ i ]; + } + else + { + // reduction transfer from assigned row + final int j1 = x[ i ] - 1; + double min = Double.MAX_VALUE; + for ( int k = cm.start[ i ]; k < cm.start[ i ] + cm.number[ i ]; k++ ) + { + final int j = cm.kk[ k ]; + if ( j != j1 ) + { + if ( cm.cc[ k ] - v[ j ] < min ) + { + min = cm.cc[ k ] - v[ j ]; + } + } + } + v[ j1 ] -= min; + } + } + + if ( f == 0 ) { return true; } + + /* + * Augmenting row reduction. + */ + + for ( int count = 0; count < 2; count++ ) + { + int k = 0; + final int f0 = f; + f = 0; + while ( k < f0 ) + { + final int i = free[ k++ ]; + double v0 = Double.MAX_VALUE; + int j0 = 0, j1 = -1; + double vj = Double.MAX_VALUE; + for ( int kj = cm.start[ i ]; kj < cm.start[ i ] + cm.number[ i ]; kj++ ) + { + final int j = cm.kk[ kj ]; + final double h = cm.cc[ kj ] - v[ j ]; + if ( h < vj ) + { + if ( h > v0 ) + { + vj = h; + j1 = j; + } + else + { + vj = v0; + v0 = h; + j1 = j0; + j0 = j; + } + } + } + int i0 = y[ j0 ] - 1; + if ( v0 < vj ) + { + v[ j0 ] -= vj - v0; + } + else + { + if ( i0 >= 0 ) + { + j0 = j1; + i0 = y[ j1 ] - 1; + } + } + if ( i0 >= 0 ) + { + if ( v0 < vj ) + { + free[ --k ] = i0; + } + else + { + free[ f++ ] = i0; + } + } + x[ i ] = j0 + 1; + y[ j0 ] = i + 1; + } + } + + /* + * Augmentation. + */ + + final int f0 = f; + final double[] d = new double[ cm.nCols ]; + final int[] pred = new int[ cm.nCols ]; + for ( f = 0; f < f0; f++ ) + { + final int i1 = free[ f ]; + int low = 0, up = 0; + // initialize d- and pred-array + Arrays.fill( d, Double.MAX_VALUE ); + for ( int k = cm.start[ i1 ]; k < cm.start[ i1 ] + cm.number[ i1 ]; k++ ) + { + final int j = cm.kk[ k ]; + d[ j ] = cm.cc[ k ] - v[ j ]; + pred[ j ] = i1; + } + int last; + int i, j = -1; + double min = Double.MAX_VALUE; + LOOP: do + { + // find new columns with new value for minimum d + // unnecessary, even if it is in the paper: + // if (up == low) + { + last = low; + min = d[ col[ up++ ] ]; + for ( int k = up; k < cm.nCols; k++ ) + { + j = col[ k ]; + final double h = d[ j ]; + if ( h <= min ) + { + if ( h < min ) + { + up = low; + min = h; + } + col[ k ] = col[ up ]; + col[ up++ ] = j; + } + } + for ( int h = low; h < up; h++ ) + { + j = col[ h ]; + if ( y[ j ] == 0 ) + { + break LOOP; + } + } + } + // scan a row + do + { + final int j1 = col[ low++ ]; + i = y[ j1 ] - 1; + + final int kj1 = Arrays.binarySearch( cm.kk, cm.start[ i ], cm.start[ i ] + cm.number[ i ], j1 ); + if ( kj1 < 0 ) + { + continue; + } + + final double u1 = cm.cc[ kj1 ] - v[ j1 ] - min; + for ( int k = up; k < cm.nCols; k++ ) + { + j = col[ k ]; + final int kj = Arrays.binarySearch( cm.kk, cm.start[ i ], cm.start[ i ] + cm.number[ i ], j ); + if ( kj < 0 ) + { + continue; + } + + final double h = cm.cc[ kj ] - v[ j ] - u1; + if ( h < d[ j ] ) + { + d[ j ] = h; + pred[ j ] = i; + if ( h == min ) + { + if ( y[ j ] == 0 ) + { + break LOOP; + } + col[ k ] = col[ up ]; + col[ up++ ] = j; + } + } + } + } + while ( low != up ); + } + while ( low == up ); + + // updating of column pieces + for ( int k = 0; k < last; k++ ) + { + final int j0 = col[ k ]; + v[ j0 ] += d[ j0 ] - min; + } + + // augmentation + do + { + i = pred[ j ]; + y[ j ] = i + 1; + final int k = j; + j = x[ i ] - 1; + x[ i ] = k + 1; + } + while ( i1 != i ); + } + + /* + * Terminate and prepare outputs. + */ + + this.output = new int[ x.length ]; + for ( int i = 0; i < x.length; i++ ) + { + output[ i ] = x[ i ] - 1; + } + + final long end = System.currentTimeMillis(); + processingTime = end - start; + return true; + } + + /* + * ALGORITHM METHODS + */ + + @Override + public boolean checkInput() + { + if ( cm.nRows > cm.nCols ) + { + errorMessage = BASE_ERROR_MESSAGE + "This solver converges only if the cost matrix has more rows than column. Found " + cm.nRows + " rows and " + cm.nCols + " columns."; + return false; + } + final double minCost = Util.min( cm.cc ); + if ( minCost < 0 ) + { + errorMessage = BASE_ERROR_MESSAGE + "This solver only accept positive costs. Found " + minCost + "."; + return false; + } + return true; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + + /** + * Returns JVS results as row assignments. The row i is + * associated to the column x[i] in the cost matrix. + * + * @return the row assignments as an int[] array. This array is + * re-instantiated upon calling {@link #process()}. + */ + @Override + public int[] getResult() + { + return output; + } + + public String resultToString() + { + return resultToString( Collections.emptyList(), Collections.emptyList() ); + } + + public String resultToString( final List< ? > rows, final List< ? > cols ) + { + if ( null == output ) { return "Not solved yet. Process the algorithm prior to calling this method."; } + + final String[] colNames = new String[ cm.nCols ]; + // default names + for ( int j = 0; j < colNames.length; j++ ) + { + colNames[ j ] = "" + j; + } + final String[] rowNames = new String[ cm.nRows ]; + for ( int i = 0; i < rowNames.length; i++ ) + { + rowNames[ i ] = "" + i; + } + + for ( int j = 0; j < cols.size(); j++ ) + { + final Object col = cols.get( j ); + if ( null != col ) + { + final String str = col.toString(); + colNames[ j ] = str; + } + } + + int colWidth = -1; + for ( final String str : colNames ) + { + if ( str.length() > colWidth ) + { + colWidth = str.length(); + } + + } + colWidth = colWidth + 1; + colWidth = Math.max( colWidth, 7 ); + + final Set< String > unassignedColNames = new HashSet< String >( Arrays.asList( colNames ) ); + + for ( int i = 0; i < rows.size(); i++ ) + { + final Object row = rows.get( i ); + if ( null != row ) + { + final String str = row.toString(); + rowNames[ i ] = str; + } + } + + int rowWidth = -1; + for ( final String str : rowNames ) + { + if ( str.length() > rowWidth ) + { + rowWidth = str.length(); + } + + } + rowWidth = rowWidth + 1; + rowWidth = Math.max( 7, rowWidth ); + + final StringBuilder str = new StringBuilder(); + final double totalCost = cm.totalAssignmentCost( output ); + final int digits = ( int ) ( Math.log10( totalCost ) + 2 ); + + str.append( String.format( "Optimal assignment with total cost = %" + digits + ".1f:\n", totalCost ) ); + for ( int i = 0; i < output.length; i++ ) + { + final int j = output[ i ]; + final double cost = cm.get( i, j, Double.POSITIVE_INFINITY ); + + { + for ( int k = 0; k < ( rowWidth - rowNames[ i ].length() ); k++ ) + { + str.append( ' ' ); + } + str.append( rowNames[ i ] ); + } + str.append( " → " ); + { + str.append( colNames[ j ] ); + unassignedColNames.remove( colNames[ j ] ); + for ( int k = 0; k < ( colWidth - colNames[ j ].length() ); k++ ) + { + str.append( ' ' ); + } + } + str.append( String.format( " cost = %" + digits + ".1f\n", cost ) ); + } + if ( cm.nCols > cm.nRows ) + { + str.append( "Unassigned columns:\n" ); + for ( final String ucn : unassignedColNames ) + { + { + for ( int k = 0; k < rowWidth / 2; k++ ) + { + str.append( ' ' ); + } + str.append( 'ø' ); + for ( int k = 0; k < rowWidth - rowWidth / 2 - 1; k++ ) + { + str.append( ' ' ); + } + } + str.append( " → " ); + { + str.append( ucn ); + for ( int k = 0; k < ( colWidth - ucn.length() ); k++ ) + { + str.append( ' ' ); + } + } + str.append( '\n' ); + } + } + + return str.toString(); + } +} diff --git a/src/main/java/costMatrix/PixelRatioCostFunction.java b/src/main/java/costMatrix/PixelRatioCostFunction.java new file mode 100644 index 0000000..59923e4 --- /dev/null +++ b/src/main/java/costMatrix/PixelRatioCostFunction.java @@ -0,0 +1,28 @@ +package costMatrix; + +import utility.PreRoiobject; + +public class PixelRatioCostFunction implements CostFunction< PreRoiobject, PreRoiobject > { + + + + /** + * Implementation of various cost functions + * + * + */ + + + @Override + public double linkingCost( final PreRoiobject source, final PreRoiobject target ) + { + return source.numberofPixelsRatioTo(target); + } + + + + + + + +} diff --git a/src/main/java/costMatrix/PixelratiowDistCostFunction.java b/src/main/java/costMatrix/PixelratiowDistCostFunction.java new file mode 100644 index 0000000..8f7ba8f --- /dev/null +++ b/src/main/java/costMatrix/PixelratiowDistCostFunction.java @@ -0,0 +1,47 @@ +package costMatrix; + +import utility.PreRoiobject; +import utility.ThreeDRoiobject; + +public class PixelratiowDistCostFunction implements CostFunction< ThreeDRoiobject, ThreeDRoiobject > + { + + + // Alpha is the weightage given to distance and Beta is the weightage given to the ratio of pixels + public final double beta; + public final double alpha; + + + + + public double getAlpha(){ + + return alpha; + } + + + public double getBeta(){ + + return beta; + } + + public PixelratiowDistCostFunction (double alpha, double beta){ + + this.alpha = alpha; + this.beta = beta; + + } + + + @Override + public double linkingCost( final ThreeDRoiobject source, final ThreeDRoiobject target ) + { + return source.NormalizedPixelratioandDistanceTo(target, alpha, beta); + } + + + + + + +} diff --git a/src/main/java/costMatrix/ResizableDoubleArray.java b/src/main/java/costMatrix/ResizableDoubleArray.java new file mode 100644 index 0000000..4cfc72f --- /dev/null +++ b/src/main/java/costMatrix/ResizableDoubleArray.java @@ -0,0 +1,101 @@ +package costMatrix; + + +import java.util.Arrays; + +public class ResizableDoubleArray +{ + + /* + * PUBLIC FIELDS + */ + + public double[] data; + + + public int size; + + /* + * CONSTRUCTORS + */ + + public ResizableDoubleArray( final double[] data ) + { + this.data = data; + this.size = data.length; + } + + + public ResizableDoubleArray( final int initialCapacity ) + { + this.data = new double[ initialCapacity ]; + this.size = 0; + } + + /** + * Creates an empty ResizableIntArray with the a initial capacity of 10. + */ + public ResizableDoubleArray() + { + this( 10 ); + } + + /* + * METHODS + */ + + public void trimToSize() + { + final int oldCapacity = data.length; + if ( size < oldCapacity ) + { + data = Arrays.copyOf( data, size ); + } + } + + public void ensureCapacity( final int minCapacity ) + { + final int oldCapacity = data.length; + if ( minCapacity > oldCapacity ) + { + // The heuristics of ArrayList + int newCapacity = ( oldCapacity * 3 ) / 2 + 1; + if ( newCapacity < minCapacity ) + { + newCapacity = minCapacity; + } + data = Arrays.copyOf( data, newCapacity ); + } + } + + /** + * Returns true if this list contains no elements. + * + * @return true if this list contains no elements + */ + public boolean isEmpty() + { + return size == 0; + } + + public void add( final double val ) + { + ensureCapacity( size + 1 ); + data[ size ] = val; + size++; + } + + @Override + public String toString() + { + if ( isEmpty() ) { return "()"; } + final StringBuilder str = new StringBuilder(); + str.append( '(' ); + for ( int i = 0; i < size - 1; i++ ) + { + str.append( data[ i ] + ", " ); + } + str.append( data[ size - 1 ] + "), size = " + size ); + return str.toString(); + } +} diff --git a/src/main/java/costMatrix/SparseCostMatrix.java b/src/main/java/costMatrix/SparseCostMatrix.java new file mode 100644 index 0000000..5ac0781 --- /dev/null +++ b/src/main/java/costMatrix/SparseCostMatrix.java @@ -0,0 +1,530 @@ +package costMatrix; + + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * A class to represent a sparse cost matrix. + *

+ * This class aims at representing in a memory-efficient way a possibly + * rectangular double matrix used in linear assignment problems (LAP). It is + * useful when the number of sources (rows of the matrix) and the number of + * targets (columns of the matrix) are large, but only a fraction of the costs + * are not infinite (assignment forbidden). In this case, infinite cost can be + * omitted and it is implicitly understood that missing values of the matrix + * represent infinite costs. This situation is very common in single-particle + * tracking for Life-Sciences, and this class is especially designed for these + * problems. + *

+ * This class does not do much. It just stores the arrays (described below) that + * represent the matrix. It is the caller responsibility to ensure they are + * properly arranged and that they represent the desired cost matrix. The arrays + * are accessible via default visibility. + *

+ * This matrix follow the row compressed storage convention, taken from the + * Volgenant paper: Volgenant. Linear and semi-assignment problems: A core + * oriented approach. Computers & Operations Research (1996) vol. 23 (10) pp. + * 917-932 + * + * @author Jean-Yves Tinevez - 2014 + */ +public class SparseCostMatrix +{ + + /** + * The linear array of non-infinite costs. + */ + final double[] cc; + + /** + * The linear array storing the column index of each non infinite-cost. + * Column indices are stored adjacently row by row, and for each row, are + * stored in ascending order. + */ + final int[] kk; + + /** + * The array of the number of non-infinite costs for each row. + */ + final int[] number; + + /** + * The number of rows in the cost matrix. + */ + final int nRows; + + /** + * The number of columns in the cost matrix. + */ + final int nCols; + + /** + * The number of non-infinite costs in the matrix. + */ + final int cardinality; + + /** + * The array of indices in {@link #kk} the column index where a row stats. + */ + final int[] start; + + /** + * Instantiate a new sparse cost matrix. The caller must provide 3 arrays: + *

    + *
  1. cc, the double[] array containing all the + * non-infinite costs. + *
  2. kk, an int[] array of the same length that + * cc, and that contains the columns of the cost. + *
+ * These two arrays must be arranged row by row, starting with the first + * one. And in each row, the columns must be sorted in increasing order (to + * facilitate index search). Also, each row must have at least one + * non-infinte cost. If not, an {@link IllegalArgumentException} is thrown. + *
    + *
  1. number an int[] array, with one element per + * row, that contains the number of non infinite cost for a row. + *
+ * + * @param cc + * the cost array. + * @param kk + * the column index of each cost. + * @param number + * the number of element for each row. + * @throws IllegalArgumentException + * if the cost and column arrays are not of the same size, if + * the column array is not sorted row by row, of if one row has + * 0 non-infinite costs. + */ + public SparseCostMatrix( final double[] cc, final int[] kk, final int[] number, final int nCols ) + { + this.cc = cc; + this.kk = kk; + this.number = number; + this.nCols = nCols; + + // Check sizes + if (cc.length != kk.length) { + throw new IllegalArgumentException( "Cost and column indices arrays must have the same length. Found " + cc.length + " and " + kk.length + "." ); + } + + this.cardinality = cc.length; + this.nRows = number.length; + // loop on each row + this.start = new int[ nRows ]; + if ( nRows > 0 ) + { + start[ 0 ] = 0; + } + for ( int i = 1; i < nRows; i++ ) + { + if ( number[ i ] == 0 ) { throw new IllegalArgumentException( "All the rows must have at least one cost. Row " + i + " have none." ); } + start[ i ] = start[ i - 1 ] + number[ i - 1 ]; + } + + final int[] colHistogram = new int[ nCols ]; + for ( int i = 0; i < nRows; i++ ) + { + // Iterate through each column + int previousK = -1; + for ( int j = start[ i ]; j < start[ i ] + number[ i ]; j++ ) + { + final int k = kk[ j ]; + if ( k >= nCols ) { throw new IllegalArgumentException( "At line " + i + ", the column indices array contains a column index (" + k + ") that is larger than or equal to the declared number of column (" + nCols + ")." ); } + colHistogram[ k ]++; + if ( k <= previousK ) { throw new IllegalArgumentException( "The column indices array must be sorted within each row. The column elements at line " + i + " are not properly sorted." ); } + previousK = k; + } + } + + // Check that each column have at least one assignment + for ( int j = 0; j < colHistogram.length; j++ ) + { + if ( colHistogram[ j ] == 0 ) { throw new IllegalArgumentException( "All the columns must have at least one cost. The column " + j + " has none." ); } + } + } + + @Override + public String toString() + { + return toString( Collections.EMPTY_LIST, Collections.EMPTY_LIST ); + } + + public String toString( final List< ? > rows, final List< ? > columns ) + { + final String[] colNames = new String[ nCols ]; + // default names + for ( int j = 0; j < colNames.length; j++ ) + { + colNames[ j ] = "" + j; + } + final String[] rowNames = new String[ nRows ]; + for ( int i = 0; i < rowNames.length; i++ ) + { + rowNames[ i ] = "" + i; + } + + for ( int j = 0; j < columns.size(); j++ ) + { + final Object col = columns.get( j ); + if ( null != col ) + { + final String str = col.toString(); + colNames[ j ] = str; + } + } + + int colWidth = -1; + for ( final String str : colNames ) + { + if ( str.length() > colWidth ) + { + colWidth = str.length(); + } + + } + colWidth = colWidth + 1; + colWidth = Math.max( colWidth, 7 ); + + for ( int i = 0; i < rows.size(); i++ ) + { + final Object row = rows.get( i ); + if ( null != row ) + { + final String str = row.toString(); + rowNames[ i ] = str; + } + } + + int rowWidth = -1; + for ( final String str : rowNames ) + { + if ( str.length() > rowWidth ) + { + rowWidth = str.length(); + } + + } + rowWidth = rowWidth + 1; + rowWidth = Math.max( 7, rowWidth ); + + final StringBuilder str = new StringBuilder(); + str.append( super.toString() + '\n' ); + str.append( " " + nRows + " × " + nCols + " matrix with " + cardinality + " non-null elements. " ); + str.append( String.format( "Density = %.2f%%.\n", ( double ) cardinality / ( nRows * nCols ) * 100d ) ); + + for ( int i = 0; i < rowWidth; i++ ) + { + str.append( ' ' ); + } + str.append( '|' ); + for ( int c = 0; c < nCols; c++ ) + { + for ( int i = 0; i < ( colWidth - colNames[ c ].length() ); i++ ) + { + str.append( ' ' ); + } + str.append( colNames[ c ] ); + } + str.append( '\n' ); + + for ( int i = 0; i < rowWidth; i++ ) + { + str.append( '_' ); + } + str.append( '|' ); + final char[] line = new char[ colWidth * nCols ]; + Arrays.fill( line, '_' ); + str.append( line ); + str.append( '\n' ); + + for ( int r = 0; r < nRows; r++ ) + { + str.append( rowNames[ r ] ); + for ( int i = 0; i < rowWidth - rowNames[ r ].length(); i++ ) + { + str.append( ' ' ); + } + str.append( '|' ); + + final StringBuilder rowStr = new StringBuilder(); + final char[] spaces = new char[ colWidth * nCols ]; + Arrays.fill( spaces, ' ' ); + rowStr.append( spaces ); + + for ( int k = start[ r ]; k < start[ r ] + number[ r ]; k++ ) + { + final int col = kk[ k ]; + final double cost = cc[ k ]; + rowStr.replace( col * colWidth, ( col + 1 ) * colWidth, String.format( "% " + colWidth + ".1f", cost ) ); + } + rowStr.append( '\n' ); + str.append( rowStr.toString() ); + } + + return str.toString(); + } + + /** + * Computes the total cost for an assignment specified by row. It is + * supposed that row i is assigned to column + * rowAssignment[i]. + * + * @param rowAssignment + * the assignment, specified by row. + * @return the total cost for this assignment. + */ + public double totalAssignmentCost( final int[] rowAssignment ) + { + double sum = 0; + for ( int i = 0; i < rowAssignment.length; i++ ) + { + final int j = rowAssignment[ i ]; + final int kj = Arrays.binarySearch( kk, start[ i ], start[ i ] + number[ i ], j ); + sum += cc[ kj ]; + } + return sum; + } + + /** + * Creates and returns a new double[][] matrix representing a + * non-sparse version of this cost matrix. Missing costs are replace by + * {@link Double#MAX_VALUE}. + * + * @return a new double[][] + */ + public double[][] toFullMatrix() + { + final double[][] cm = new double[ nRows ][ nCols ]; + for ( final double[] ds : cm ) + { + Arrays.fill( ds, Double.MAX_VALUE ); + } + + for ( int r = 0; r < nRows; r++ ) + { + for ( int k = start[ r ]; k < start[ r ] + number[ r ]; k++ ) + { + final int c = kk[ k ]; + final double cost = cc[ k ]; + cm[ r ][ c ] = cost; + } + } + + return cm; + } + + /** + * Returns the value stored by this matrix at the specified row and column. + * If a value is not present in the sparse matrix, the specified missing + * value is returned. + * + * @param i + * the row. + * @param j + * the column. + * @param missingValue + * what to return if the sparse matrix does not store a value at + * the specified row and column. + * @return the value. + */ + public final double get( final int i, final int j, final double missingValue ) + { + final int k = Arrays.binarySearch( kk, start[ i ], start[ i ] + number[ i ], j ); + if ( k < 0 ) + { + return missingValue; + } + else + { + return cc[ k ]; + } + } + + /** + * Exposes the array of all the non-infinite costs. + * + * @return the costs. + */ + public double[] getCosts() + { + return cc; + } + + public int getNCols() + { + return nCols; + } + + public int getNRows() + { + return nRows; + } + + /** + * Returns the vertical concatenation of this matrix with the specified one. + * So that if this matrix is A and the specified matrix is B, you get + * + *
+	 * -----
+	 * | A |
+	 * | B |
+	 * -----
+	 * 
+ * + * @param B + * the matrix to concatenate this matrix with + * @return a new sparse matrix. + * @throws IllegalArgumentException + * if B does not have the same number of columns as this matrix. + */ + public final SparseCostMatrix vcat( final SparseCostMatrix B ) + { + if ( nCols != B.nCols ) { throw new IllegalArgumentException( "Matrices A & B do not have the same number of columns. Found " + nCols + " and " + B.nCols + " respectively." ); } + + final double[] cc2 = new double[ cardinality + B.cardinality ]; + final int[] kk2 = new int[ cardinality + B.cardinality ]; + final int[] number2 = new int[ nRows + B.nRows ]; + + // Append A + System.arraycopy( kk, 0, kk2, 0, cardinality ); + System.arraycopy( cc, 0, cc2, 0, cardinality ); + System.arraycopy( number, 0, number2, 0, nRows ); + + // Append B + System.arraycopy( B.kk, 0, kk2, cardinality, B.cardinality ); + System.arraycopy( B.cc, 0, cc2, cardinality, B.cardinality ); + System.arraycopy( B.number, 0, number2, nRows, B.nRows ); + + return new SparseCostMatrix( cc2, kk2, number2, nCols ); + } + + /** + * Returns the horizontal concatenation of this matrix with the specified + * one. So that if this matrix is A and the specified matrix is B, you get + * + *
+	 * -------
+	 * | A B |
+	 * -------
+	 * 
+ * + * @param B + * the matrix to concatenate this matrix with + * @return a new sparse matrix. + * @throws IllegalArgumentException + * if B does not have the same number of rows as this matrix. + */ + public final SparseCostMatrix hcat( final SparseCostMatrix B ) + { + if ( nRows != B.nRows ) { throw new IllegalArgumentException( "Matrices A & B do not have the same number of rows. Found " + nRows + " and " + B.nRows + " respectively." ); } + + final double[] cc2 = new double[ cardinality + B.cardinality ]; + final int[] kk2 = new int[ cardinality + B.cardinality ]; + final int[] number2 = new int[ nRows ]; + + // Append line by line + int Aindex = 0; + int Bindex = 0; + int Cindex = 0; + for ( int i = 0; i < nRows; i++ ) + { + // A + System.arraycopy( cc, Aindex, cc2, Cindex, number[ i ] ); + System.arraycopy( kk, Aindex, kk2, Cindex, number[ i ] ); + Aindex += number[ i ]; + Cindex += number[ i ]; + + // B + System.arraycopy( B.cc, Bindex, cc2, Cindex, B.number[ i ] ); + // For the columns, we need to increment them by A.nCols + for ( int j = 0; j < B.number[ i ]; j++ ) + { + kk2[ Cindex + j ] = B.kk[ Bindex + j ] + nCols; + } + Bindex += B.number[ i ]; + Cindex += B.number[ i ]; + + // number + number2[ i ] = number[ i ] + B.number[ i ]; + } + + return new SparseCostMatrix( cc2, kk2, number2, nCols + B.nCols ); + } + + /** + * Returns the transpose of this matrix. + * + * @return a new sparse matrix. + */ + public final SparseCostMatrix transpose() + { + // Build column histogram, which will give the transposed number + final int[] number2 = new int[ nCols ]; + for ( final int j : kk ) + { + number2[ j ]++; + } + + // Prepare column & cost storage. + final int[][] cols = new int[ nCols ][]; + final double[][] costs = new double[ nCols ][]; + for ( int j = 0; j < cols.length; j++ ) + { + cols[ j ] = new int[ number2[ j ] ]; + costs[ j ] = new double[ number2[ j ] ]; + } + + // Parse source column array and store at what line they happen. Add to + // cost arrays. + int currentLine = 0; + int previousJ = -1; + int walked = 0; + final int[] colIndex = new int[ nCols ]; + for ( int k = 0; k < cardinality; k++ ) + { + final int j = kk[ k ]; + final double c = cc[ k ]; + + // Determine whether we changed line. + if ( j <= previousJ || walked >= number[ currentLine ] ) + { + currentLine++; + walked = 0; + } + walked++; + previousJ = j; + + cols[ j ][ colIndex[ j ] ] = currentLine; + costs[ j ][ colIndex[ j ] ] = c; + + colIndex[ j ]++; + } + + // Concatenate + final double[] cc2 = new double[ cardinality ]; + final int[] kk2 = new int[ cardinality ]; + int index = 0; + for ( int i = 0; i < cols.length; i++ ) + { + System.arraycopy( cols[ i ], 0, kk2, index, number2[ i ] ); + System.arraycopy( costs[ i ], 0, cc2, index, number2[ i ] ); + index += number2[ i ]; + } + return new SparseCostMatrix( cc2, kk2, number2, nRows ); + } + + /** + * Replace all the non-infinite values of this matrix by the specified + * value. + * + * @param value + * the value to write in this matrix. + */ + public void fillWith( final double value ) + { + Arrays.fill( cc, value ); + } +} \ No newline at end of file diff --git a/src/main/java/costMatrix/SquareDistCostFunction.java b/src/main/java/costMatrix/SquareDistCostFunction.java new file mode 100644 index 0000000..fc347cf --- /dev/null +++ b/src/main/java/costMatrix/SquareDistCostFunction.java @@ -0,0 +1,26 @@ +package costMatrix; + +import utility.PreRoiobject; + +/** + * Implementation of various cost functions + * + * + */ + +// Cost function base don minimizing the squared distances + +public class SquareDistCostFunction implements CostFunction< PreRoiobject, PreRoiobject > +{ + + @Override + public double linkingCost( final PreRoiobject source, final PreRoiobject target ) + { + return source.squareDistanceTo(target ); + } + + + + + +} diff --git a/src/main/java/curvatureUtils/PointExtractor.java b/src/main/java/curvatureUtils/PointExtractor.java index 1afeb98..855f65b 100644 --- a/src/main/java/curvatureUtils/PointExtractor.java +++ b/src/main/java/curvatureUtils/PointExtractor.java @@ -6,7 +6,6 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import org.apache.poi.poifs.property.Parent; import curvatureFinder.LineProfileCircle; import ellipsoidDetector.Distance; diff --git a/src/main/java/distanceTransform/CreateBinary.java b/src/main/java/distanceTransform/CreateBinary.java new file mode 100644 index 0000000..4fffc93 --- /dev/null +++ b/src/main/java/distanceTransform/CreateBinary.java @@ -0,0 +1,65 @@ +package distanceTransform; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; +import preProcessing.GlobalThresholding; + +public class CreateBinary { + + public static RandomAccessibleInterval CreateBinaryImage(RandomAccessibleInterval inputimage, + final FloatType threshold) { + + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimage, new BitType()); + RandomAccessibleInterval binaryimage = factory.create(inputimage, new BitType()); + + Cursor cursor = Views.iterable(inputimage).localizingCursor(); + RandomAccess ranac = binaryimage.randomAccess(); + + while (cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + if (cursor.get().compareTo(threshold) >= 1) + ranac.get().setOne(); + else + ranac.get().setZero(); + + } + + return binaryimage; + } + + public static RandomAccessibleInterval CreateAutoBinaryImage(RandomAccessibleInterval inputimage) { + + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimage, new BitType()); + RandomAccessibleInterval binaryimage = factory.create(inputimage, new BitType()); + + Cursor cursor = Views.iterable(inputimage).localizingCursor(); + RandomAccess ranac = binaryimage.randomAccess(); + + Float threshold = GlobalThresholding.AutomaticThresholding(inputimage); + + while (cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + if (cursor.get().get() >= (threshold)) + ranac.get().setOne(); + else + ranac.get().setZero(); + + } + + return binaryimage; + } + +} diff --git a/src/main/java/distanceTransform/CreateDistanceTransform.java b/src/main/java/distanceTransform/CreateDistanceTransform.java new file mode 100644 index 0000000..1f8216c --- /dev/null +++ b/src/main/java/distanceTransform/CreateDistanceTransform.java @@ -0,0 +1,164 @@ +package distanceTransform; + +import java.util.Iterator; + +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + + + + + public class CreateDistanceTransform > extends BenchmarkAlgorithm + implements OutputAlgorithm > { + + private static final String BASE_ERROR_MSG = "[WatershedDistimg] "; + private final RandomAccessibleInterval source; + + private final RandomAccessibleInterval bitimg; + RandomAccessibleInterval distimg; + /** + * Do watershedding after doing distance transformation on the biimg + * provided by the user using a user set threshold value. + * + * @param source + * The image to be watershedded. + * @param bitimg + * The image used to compute distance transform and seeds for watershedding. + */ + public CreateDistanceTransform(final RandomAccessibleInterval source, final RandomAccessibleInterval bitimg){ + + this.source = source; + this.bitimg = bitimg; + } + + + + @Override + public boolean checkInput() { + if (source.numDimensions() > 2) { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got " + + source.numDimensions() + "D."; + return false; + } + return true; + } + + @Override + public boolean process() { + + // Perform the distance transform + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new FloatType()); + distimg = factory.create(source, new FloatType()); + + DistanceTransformImage(source, distimg); + + + return true; + } + + @Override + public RandomAccessibleInterval getResult() { + + return distimg; + } + + + + /*** + * + * Do the distance transform of the input image using the bit image + * provided. + * + * @param inputimg + * The pre-processed input image as RandomAccessibleInterval + * @param outimg + * The distance transormed image having the same dimensions as + * the input image. + * @param invtype + * Straight: The intensity value is set to the distance, gives + * white on black background. Inverse: The intensity is set to + * the negative of the distance, gives black on white background. + */ + + private void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval outimg) { + int n = inputimg.numDimensions(); + + // make an empty list + final RealPointSampleList list = new RealPointSampleList(n); + + // cursor on the binary image + final Cursor cursor = Views.iterable(bitimg).localizingCursor(); + + // for every pixel that is 1, make a new RealPoint at that location + while (cursor.hasNext()) + if (cursor.next().getInteger() == 1) + list.add(new RealPoint(cursor), cursor.get()); + + // build the KD-Tree from the list of points that == 1 + final KDTree tree = new KDTree(list); + + // Instantiate a nearest neighbor search on the tree (does not modifiy + // the tree, just uses it) + final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree); + + // randomaccess on the output + final RandomAccess ranac = outimg.randomAccess(); + + // reset cursor for the input (or make a new one) + cursor.reset(); + + // for every pixel of the binary image + while (cursor.hasNext()) { + cursor.fwd(); + + // set the randomaccess to the same location + ranac.setPosition(cursor); + + // if value == 0, look for the nearest 1-valued pixel + if (cursor.get().getInteger() == 0) { + // search the nearest 1 to the location of the cursor (the + // current 0) + search.search(cursor); + + // get the distance (the previous call could return that, this + // for generality that it is two calls) + + + ranac.get().setReal(search.getDistance()); + + } else { + // if value == 1, no need to search + ranac.get().setZero(); + } + } + + } + + + + +} diff --git a/src/main/java/distanceTransform/CreateWatershed.java b/src/main/java/distanceTransform/CreateWatershed.java new file mode 100644 index 0000000..a50bef7 --- /dev/null +++ b/src/main/java/distanceTransform/CreateWatershed.java @@ -0,0 +1,263 @@ +package distanceTransform; + +import java.util.Iterator; + +import javax.swing.JProgressBar; + +import interactivePreprocessing.InteractiveMethods; +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + + + +@SuppressWarnings("deprecation") + public class CreateWatershed > extends BenchmarkAlgorithm + implements OutputAlgorithm > { + + private static final String BASE_ERROR_MSG = "[WatershedDistimg] "; + private final RandomAccessibleInterval source; + + private final RandomAccessibleInterval bitimg; + public final JProgressBar jpb; + public final InteractiveMethods parent; + private RandomAccessibleInterval watershedimage; + RandomAccessibleInterval distimg; + /** + * Do watershedding after doing distance transformation on the biimg + * provided by the user using a user set threshold value. + * + * @param source + * The image to be watershedded. + * @param bitimg + * The image used to compute distance transform and seeds for watershedding. + */ + public CreateWatershed(final InteractiveMethods parent, final RandomAccessibleInterval source, final RandomAccessibleInterval bitimg, final JProgressBar jpb){ + + this.parent = parent; + this.source = source; + this.bitimg = bitimg; + this.jpb = jpb; + + } + + + + @Override + public boolean checkInput() { + if (source.numDimensions() > 2) { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got " + + source.numDimensions() + "D."; + return false; + } + return true; + } + + @Override + public boolean process() { + + // Perform the distance transform + final T type = source.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new UnsignedByteType()); + distimg = factory.create(source, new UnsignedByteType()); + utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, "Doing Distance Transformed Watershedding, Please Wait..."); + parent.panelFirst.validate(); + parent.panelFirst.repaint(); + DistanceTransformImage(source, distimg); + + // Prepare seed image for watershedding + NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + oldseedLabeling = PrepareSeedImage(source); + // Do watershedding on the distance transformed image + + NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + + outputLabeling = GetlabeledImage(distimg, oldseedLabeling); + + watershedimage = outputLabeling.getStorageImg(); + + + return true; + } + + @Override + public RandomAccessibleInterval getResult() { + + return watershedimage; + } + public RandomAccessibleInterval getDistanceTransformedimg() { + + return distimg; + } + + + /*** + * + * Do the distance transform of the input image using the bit image + * provided. + * + * @param inputimg + * The pre-processed input image as RandomAccessibleInterval + * @param outimg + * The distance transormed image having the same dimensions as + * the input image. + * @param invtype + * Straight: The intensity value is set to the distance, gives + * white on black background. Inverse: The intensity is set to + * the negative of the distance, gives black on white background. + */ + + private void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval outimg) { + int n = inputimg.numDimensions(); + + // make an empty list + final RealPointSampleList list = new RealPointSampleList(n); + + // cursor on the binary image + final Cursor cursor = Views.iterable(bitimg).localizingCursor(); + + // for every pixel that is 1, make a new RealPoint at that location + while (cursor.hasNext()) + if (cursor.next().getInteger() == 1) + list.add(new RealPoint(cursor), cursor.get()); + + // build the KD-Tree from the list of points that == 1 + final KDTree tree = new KDTree(list); + + // Instantiate a nearest neighbor search on the tree (does not modifiy + // the tree, just uses it) + final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree); + + // randomaccess on the output + final RandomAccess ranac = outimg.randomAccess(); + + // reset cursor for the input (or make a new one) + cursor.reset(); + + // for every pixel of the binary image + while (cursor.hasNext()) { + cursor.fwd(); + + // set the randomaccess to the same location + ranac.setPosition(cursor); + + // if value == 0, look for the nearest 1-valued pixel + if (cursor.get().getInteger() == 0) { + // search the nearest 1 to the location of the cursor (the + // current 0) + search.search(cursor); + + // get the distance (the previous call could return that, this + // for generality that it is two calls) + + + ranac.get().setReal(search.getDistance()); + + } else { + // if value == 1, no need to search + ranac.get().setZero(); + } + } + + } + + private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) { + + // New Labeling type + final ImgLabeling seedLabeling = new ImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // Old Labeling type + final NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // The label generator for both new and old type + final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0); + + + + // Getting unique labelled image (old version) + AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, bitimg, labelGenerator, + AllConnectedComponents.getStructuringElement(inputimg.numDimensions())); + return oldseedLabeling; + } + + + public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) { + + // To get maximum Labels on the image + Cursor intCursor = Views.iterable(intimg).cursor(); + int currentLabel = 1; + boolean anythingFound = true; + while (anythingFound) { + anythingFound = false; + intCursor.reset(); + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == currentLabel) { + + anythingFound = true; + + } + } + currentLabel++; + } + + return currentLabel; + + } + + public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg, + NativeImgLabeling seedLabeling) { + + int n = inputimg.numDimensions(); + long[] dimensions = new long[n]; + + for (int d = 0; d < n; ++d) + dimensions[d] = inputimg.dimension(d); + final NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + final Watershed watershed = new Watershed(); + + watershed.setSeeds(seedLabeling); + watershed.setIntensityImage(inputimg); + watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2)); + watershed.setOutputLabeling(outputLabeling); + watershed.process(); + DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory(); + LabelingROIStrategy> factory = deffactory + .createLabelingROIStrategy(watershed.getResult()); + outputLabeling.setLabelingCursorStrategy(factory); + + return outputLabeling; + + } + + +} diff --git a/src/main/java/distanceTransform/DistWatershed.java b/src/main/java/distanceTransform/DistWatershed.java new file mode 100644 index 0000000..ebe8dd9 --- /dev/null +++ b/src/main/java/distanceTransform/DistWatershed.java @@ -0,0 +1,237 @@ +package distanceTransform; + +import java.util.Iterator; +import java.util.concurrent.ExecutionException; + +import javax.swing.JProgressBar; +import javax.swing.SwingWorker; + +import ij.IJ; +import interactivePreprocessing.InteractiveMethods; +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + +public class DistWatershed > { + + + final InteractiveMethods parent; + final JProgressBar jpb; + private final RandomAccessibleInterval source; + private RandomAccessibleInterval watershedimage; + + RandomAccessibleInterval distimg; + private final RandomAccessibleInterval bitimg; + public boolean apply3D = false; + + + public DistWatershed(final InteractiveMethods parent, final RandomAccessibleInterval source, final RandomAccessibleInterval bitimg, final JProgressBar jpb, boolean apply3D) { + + this.parent = parent; + this.source = source; + this.bitimg = bitimg; + this.jpb = jpb; + this.apply3D = apply3D; + + } + + + public void execute() { + // Perform the distance transform + final T type = source.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new FloatType()); + distimg = factory.create(source, new FloatType()); + + utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, "Doing Distance Transformed Watershedding, Please Wait..."); + DistanceTransformImage(source, distimg); + + // Prepare seed image for watershedding + NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + oldseedLabeling = PrepareSeedImage(source); + // Do watershedding on the distance transformed image + + NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + + outputLabeling = GetlabeledImage(distimg, oldseedLabeling); + watershedimage = outputLabeling.getStorageImg(); + + } + public RandomAccessibleInterval getResult() { + + return watershedimage; + } + public RandomAccessibleInterval getDistanceTransformedimg() { + + return distimg; + } + /*** + * + * Do the distance transform of the input image using the bit image + * provided. + * + * @param inputimg + * The pre-processed input image as RandomAccessibleInterval + * @param outimg + * The distance transormed image having the same dimensions as + * the input image. + * @param invtype + * Straight: The intensity value is set to the distance, gives + * white on black background. Inverse: The intensity is set to + * the negative of the distance, gives black on white background. + */ + + private void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval outimg) { + int n = inputimg.numDimensions(); + + // make an empty list + final RealPointSampleList list = new RealPointSampleList(n); + + // cursor on the binary image + final Cursor cursor = Views.iterable(bitimg).localizingCursor(); + + // for every pixel that is 1, make a new RealPoint at that location + while (cursor.hasNext()) + if (cursor.next().getInteger() == 1) + list.add(new RealPoint(cursor), cursor.get()); + + // build the KD-Tree from the list of points that == 1 + final KDTree tree = new KDTree(list); + + // Instantiate a nearest neighbor search on the tree (does not modifiy + // the tree, just uses it) + final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree); + + // randomaccess on the output + final RandomAccess ranac = outimg.randomAccess(); + + // reset cursor for the input (or make a new one) + cursor.reset(); + + // for every pixel of the binary image + while (cursor.hasNext()) { + cursor.fwd(); + + // set the randomaccess to the same location + ranac.setPosition(cursor); + + // if value == 0, look for the nearest 1-valued pixel + if (cursor.get().getInteger() == 0) { + // search the nearest 1 to the location of the cursor (the + // current 0) + search.search(cursor); + + // get the distance (the previous call could return that, this + // for generality that it is two calls) + + + ranac.get().setReal(search.getDistance()); + + } else { + // if value == 1, no need to search + ranac.get().setZero(); + } + } + + } + + private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) { + + // New Labeling type + final ImgLabeling seedLabeling = new ImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // Old Labeling type + final NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // The label generator for both new and old type + final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0); + + + + // Getting unique labelled image (old version) + AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, bitimg, labelGenerator, + AllConnectedComponents.getStructuringElement(inputimg.numDimensions())); + return oldseedLabeling; + } + + + public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) { + + // To get maximum Labels on the image + Cursor intCursor = Views.iterable(intimg).cursor(); + int currentLabel = 0; + boolean anythingFound = true; + while (anythingFound) { + anythingFound = false; + intCursor.reset(); + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == currentLabel) { + + anythingFound = true; + + } + } + currentLabel++; + } + + return currentLabel; + + } + + public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg, + NativeImgLabeling seedLabeling) { + + int n = inputimg.numDimensions(); + long[] dimensions = new long[n]; + + for (int d = 0; d < n; ++d) + dimensions[d] = inputimg.dimension(d); + final NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + final Watershed watershed = new Watershed(); + + watershed.setSeeds(seedLabeling); + watershed.setIntensityImage(inputimg); + watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2)); + watershed.setOutputLabeling(outputLabeling); + watershed.process(); + DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory(); + LabelingROIStrategy> factory = deffactory + .createLabelingROIStrategy(watershed.getResult()); + outputLabeling.setLabelingCursorStrategy(factory); + + return outputLabeling; + + } + + + +} diff --git a/src/main/java/distanceTransform/DistWatershedBinary.java b/src/main/java/distanceTransform/DistWatershedBinary.java new file mode 100644 index 0000000..dc0cf00 --- /dev/null +++ b/src/main/java/distanceTransform/DistWatershedBinary.java @@ -0,0 +1,267 @@ +package distanceTransform; + +import java.util.Iterator; + +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + + + +@SuppressWarnings("deprecation") + public class DistWatershedBinary extends BenchmarkAlgorithm + implements OutputAlgorithm > { + + private static final String BASE_ERROR_MSG = "[WatershedDistimg] "; + private final RandomAccessibleInterval source; + + private RandomAccessibleInterval watershedimage; + /** + * Do watershedding after doing distance transformation on the biimg + * provided by the user using a user set threshold value. + * + * @param source + * The image to be watershedded. + * @param bitimg + * The image used to compute distance transform and seeds for watershedding. + */ + public DistWatershedBinary(final RandomAccessibleInterval source){ + + this.source = source; + } + + + + @Override + public boolean checkInput() { + if (source.numDimensions() > 2) { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got " + + source.numDimensions() + "D."; + return false; + } + return true; + } + + @Override + public boolean process() { + // Perform the distance transform + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new UnsignedByteType()); + RandomAccessibleInterval distimg = factory.create(source, new UnsignedByteType()); + + DistanceTransformImage(source, distimg); + + // Prepare seed image for watershedding + NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + oldseedLabeling = PrepareSeedImage(source); + // Do watershedding on the distance transformed image + + NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + + outputLabeling = GetlabeledImageByte(distimg, oldseedLabeling); + + watershedimage = outputLabeling.getStorageImg(); + return true; + } + + @Override + public RandomAccessibleInterval getResult() { + + return watershedimage; + } + + public NativeImgLabeling GetlabeledImageByte(RandomAccessibleInterval inputimg, + NativeImgLabeling seedLabeling) { + + int n = inputimg.numDimensions(); + long[] dimensions = new long[n]; + + for (int d = 0; d < n; ++d) + dimensions[d] = inputimg.dimension(d); + final NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + final Watershed watershed = new Watershed(); + + watershed.setSeeds(seedLabeling); + watershed.setIntensityImage(inputimg); + watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2)); + watershed.setOutputLabeling(outputLabeling); + watershed.process(); + DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory(); + LabelingROIStrategy> factory = deffactory + .createLabelingROIStrategy(watershed.getResult()); + outputLabeling.setLabelingCursorStrategy(factory); + + return outputLabeling; + + } + + /*** + * + * Do the distance transform of the input image using the bit image + * provided. + * + * @param inputimg + * The pre-processed input image as RandomAccessibleInterval + * @param outimg + * The distance transormed image having the same dimensions as + * the input image. + * @param invtype + * Straight: The intensity value is set to the distance, gives + * white on black background. Inverse: The intensity is set to + * the negative of the distance, gives black on white background. + */ + + private void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval outimg) { + int n = inputimg.numDimensions(); + + // make an empty list + final RealPointSampleList list = new RealPointSampleList(n); + + // cursor on the binary image + final Cursor cursor = Views.iterable(source).localizingCursor(); + + // for every pixel that is 1, make a new RealPoint at that location + while (cursor.hasNext()) + if (cursor.next().getInteger() == 1) + list.add(new RealPoint(cursor), cursor.get()); + + // build the KD-Tree from the list of points that == 1 + final KDTree tree = new KDTree(list); + + // Instantiate a nearest neighbor search on the tree (does not modifiy + // the tree, just uses it) + final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree); + + // randomaccess on the output + final RandomAccess ranac = outimg.randomAccess(); + + // reset cursor for the input (or make a new one) + cursor.reset(); + + // for every pixel of the binary image + while (cursor.hasNext()) { + cursor.fwd(); + + // set the randomaccess to the same location + ranac.setPosition(cursor); + + // if value == 0, look for the nearest 1-valued pixel + if (cursor.get().getInteger() == 0) { + // search the nearest 1 to the location of the cursor (the + // current 0) + search.search(cursor); + + // get the distance (the previous call could return that, this + // for generality that it is two calls) + + + ranac.get().setReal(search.getDistance()); + + } else { + // if value == 1, no need to search + ranac.get().setZero(); + } + } + + } + + private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) { + + // New Labeling type + final ImgLabeling seedLabeling = new ImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // Old Labeling type + final NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // The label generator for both new and old type + final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0); + + + + // Getting unique labelled image (old version) + AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, source, labelGenerator, + AllConnectedComponents.getStructuringElement(inputimg.numDimensions())); + return oldseedLabeling; + } + + + public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) { + + // To get maximum Labels on the image + Cursor intCursor = Views.iterable(intimg).cursor(); + int currentLabel = 1; + boolean anythingFound = true; + while (anythingFound) { + anythingFound = false; + intCursor.reset(); + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == currentLabel) { + + anythingFound = true; + + } + } + currentLabel++; + } + + return currentLabel; + + } + + public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg, + NativeImgLabeling seedLabeling) { + + int n = inputimg.numDimensions(); + long[] dimensions = new long[n]; + + for (int d = 0; d < n; ++d) + dimensions[d] = inputimg.dimension(d); + final NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + final Watershed watershed = new Watershed(); + + watershed.setSeeds(seedLabeling); + watershed.setIntensityImage(inputimg); + watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2)); + watershed.setOutputLabeling(outputLabeling); + watershed.process(); + DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory(); + LabelingROIStrategy> factory = deffactory + .createLabelingROIStrategy(watershed.getResult()); + outputLabeling.setLabelingCursorStrategy(factory); + + return outputLabeling; + + } + + +} diff --git a/src/main/java/distanceTransform/WatershedBinary.java b/src/main/java/distanceTransform/WatershedBinary.java new file mode 100644 index 0000000..20b4254 --- /dev/null +++ b/src/main/java/distanceTransform/WatershedBinary.java @@ -0,0 +1,220 @@ +package distanceTransform; + +import java.util.Iterator; + +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + + + +@SuppressWarnings("deprecation") + public class WatershedBinary extends BenchmarkAlgorithm + implements OutputAlgorithm > { + + private static final String BASE_ERROR_MSG = "[WatershedDistimg] "; + private final RandomAccessibleInterval source; + + private RandomAccessibleInterval watershedimage; + /** + * Do watershedding after doing distance transformation on the biimg + * provided by the user using a user set threshold value. + * + * @param source + * The image to be watershedded. + * @param bitimg + * The image used to compute distance transform and seeds for watershedding. + */ + public WatershedBinary(final RandomAccessibleInterval source){ + + this.source = source; + } + + + + @Override + public boolean checkInput() { + if (source.numDimensions() > 2) { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D, make slices of your stack . Got " + + source.numDimensions() + "D."; + return false; + } + return true; + } + + @Override + public boolean process() { + + // Perform the distance transform + final BitType type = source.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new UnsignedByteType()); + + + // Prepare seed image for watershedding + NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + oldseedLabeling = PrepareSeedImage(source); + // Do watershedding on the distance transformed image + + NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + + outputLabeling = GetlabeledImage(source, oldseedLabeling); + + watershedimage = outputLabeling.getStorageImg(); + + return true; + } + + @Override + public RandomAccessibleInterval getResult() { + + return watershedimage; + } + + + + /*** + * + * Do the distance transform of the input image using the bit image + * provided. + * + * @param inputimg + * The pre-processed input image as RandomAccessibleInterval + * @param outimg + * The distance transormed image having the same dimensions as + * the input image. + * @param invtype + * Straight: The intensity value is set to the distance, gives + * white on black background. Inverse: The intensity is set to + * the negative of the distance, gives black on white background. + */ + + private void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval outimg) { + int n = inputimg.numDimensions(); + + // make an empty list + final RealPointSampleList list = new RealPointSampleList(n); + + // cursor on the binary image + final Cursor cursor = Views.iterable(source).localizingCursor(); + + // for every pixel that is 1, make a new RealPoint at that location + while (cursor.hasNext()) + if (cursor.next().getInteger() == 1) + list.add(new RealPoint(cursor), cursor.get()); + + // build the KD-Tree from the list of points that == 1 + final KDTree tree = new KDTree(list); + + // Instantiate a nearest neighbor search on the tree (does not modifiy + // the tree, just uses it) + final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree); + + // randomaccess on the output + final RandomAccess ranac = outimg.randomAccess(); + + // reset cursor for the input (or make a new one) + cursor.reset(); + + // for every pixel of the binary image + while (cursor.hasNext()) { + cursor.fwd(); + + // set the randomaccess to the same location + ranac.setPosition(cursor); + + // if value == 0, look for the nearest 1-valued pixel + if (cursor.get().getInteger() == 0) { + // search the nearest 1 to the location of the cursor (the + // current 0) + search.search(cursor); + + // get the distance (the previous call could return that, this + // for generality that it is two calls) + + + ranac.get().setReal(search.getDistance()); + + } else { + // if value == 1, no need to search + ranac.get().setZero(); + } + } + + } + + private NativeImgLabeling PrepareSeedImage(RandomAccessibleInterval inputimg) { + + + final long[] dimensions = new long[] { inputimg.dimension(0), inputimg.dimension(1) }; + final ArrayImgFactory< IntType > labelingFactory = new ArrayImgFactory( ); + final NativeImgLabeling< Integer, IntType > labeling = new NativeImgLabeling<>( labelingFactory.create( dimensions, new IntType() ) ); + + final Iterator< Integer > names = AllConnectedComponents.getIntegerNames( -100 ); + + AllConnectedComponents.labelAllConnectedComponents( labeling, inputimg, names ); + + return labeling; + } + + + public int GetMaxlabelsseeded(RandomAccessibleInterval intimg) { + + // To get maximum Labels on the image + Cursor intCursor = Views.iterable(intimg).cursor(); + int currentLabel = 0; + boolean anythingFound = true; + while (anythingFound) { + anythingFound = false; + intCursor.reset(); + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == currentLabel) { + + anythingFound = true; + + } + } + currentLabel++; + } + + return currentLabel; + + } + + public NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg, + NativeImgLabeling seedLabeling) { + + + return seedLabeling; + + } + + +} diff --git a/src/main/java/dog3D/ComputeDoG.java b/src/main/java/dog3D/ComputeDoG.java new file mode 100644 index 0000000..ff716d3 --- /dev/null +++ b/src/main/java/dog3D/ComputeDoG.java @@ -0,0 +1,382 @@ +package dog3D; + +import java.awt.Rectangle; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ExecutionException; + +import javax.swing.JProgressBar; +import javax.swing.SwingWorker; + +import org.ojalgo.type.keyvalue.MapEntry; + +import dogGUI.CovistoDogPanel; +import ij.IJ; +import ij.gui.OvalRoi; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.componenttree.mser.MserTree; +import net.imglib2.algorithm.dog.DogDetection; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.algorithm.localextrema.RefinedPeak; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.Util; +import net.imglib2.view.Views; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class ComputeDoG & NativeType> { + + final InteractiveMethods parent; + final JProgressBar jpb; + public final RandomAccessibleInterval source; + + public RandomAccessibleInterval bitimg; + public RandomAccessibleInterval bitdotimg; + public RandomAccessibleInterval afterremovebitimg; + + public boolean apply3D; + public int z; + public int t; + + public ComputeDoG(final InteractiveMethods parent, final RandomAccessibleInterval source, + final JProgressBar jpb, boolean apply3D, int z, int t) { + + this.parent = parent; + this.source = source; + this.jpb = jpb; + this.apply3D = apply3D; + this.z = z; + this.t = t; + + bitimg = new ArrayImgFactory().create(source, new BitType()); + bitdotimg = new ArrayImgFactory().create(source, new BitType()); + afterremovebitimg = new ArrayImgFactory().create(source, new BitType()); + + } + + public void execute() { + + final DogDetection.ExtremaType type; + if (CovistoDogPanel.lookForMaxima) + type = DogDetection.ExtremaType.MINIMA; + else + type = DogDetection.ExtremaType.MAXIMA; + CovistoDogPanel.sigma2 = utility.ETrackScrollbarUtils.computeSigma2(CovistoDogPanel.sigma, parent.sensitivity); + final DogDetection newdog = new DogDetection(Views.extendBorder(source), + parent.interval, new double[] { 1, 1 }, CovistoDogPanel.sigma, CovistoDogPanel.sigma2, type, CovistoDogPanel.threshold, true); + + parent.peaks = newdog.getSubpixelPeaks(); + parent.Rois = utility.FinderUtils.getcurrentRois(parent.peaks, CovistoDogPanel.sigma, CovistoDogPanel.sigma2); + parent.CurrentPreRoiobject = new ArrayList(); + parent.AfterRemovedRois = new ArrayList(); + + + + parent.overlay.clear(); + + + for (int index = 0; index < parent.peaks.size(); ++index) { + + Roi or = parent.Rois.get(index); + + or.setStrokeColor(parent.colorDrawDog); + parent.overlay.add(or); + } + + for (Map.Entry> entry : parent.ZTRois.entrySet()) { + + ArrayList current = entry.getValue(); + for (PreRoiobject currentroi : current) { + + if (currentroi.fourthDimension == CovistoTimeselectPanel.fourthDimension && currentroi.thirdDimension == CovistoZselectPanel.thirdDimension) { + + currentroi.rois.setStrokeColor(parent.colorSnake); + parent.overlay.add(currentroi.rois); + + } + + } + } + parent.imp.setOverlay(parent.overlay); + parent.imp.updateAndDraw(); + ArrayList Rois = parent.Rois; + + + ArrayList> peaks = parent.peaks; + + ArrayList points = new ArrayList(); + ArrayList mergepoints = new ArrayList(); + + + for(RefinedPeak peak : peaks ) { + + double[] currentpoint = new double[] {(float)peak.getDoublePosition(0), (float)peak.getDoublePosition(1)}; + + points.add(currentpoint); + } + + + utility.SortCoordinates.sortByXY(points); + + ArrayList allmerged = new ArrayList(); + + System.out.println(points.size() + " " + "division before"); + if(points.size() > 0) { + do{ + + + + mergepoints = new ArrayList(); + allmerged = new ArrayList(); + ArrayList copylist = new ArrayList(points); + Iterator listiter = points.iterator(); + + + + + while(listiter.hasNext()) { + + + double[] currentpoint = listiter.next(); + + + + + copylist.remove(currentpoint); + Pair mergepointbol = utility.FinderUtils.mergeNearestRois(source, copylist, currentpoint, CovistoDogPanel.distthreshold); + copylist.add(currentpoint); + + if(mergepointbol!=null) { + if(!mergepointbol.getB()) { + + mergepoints.add(currentpoint); + boolean merged = true; + allmerged.add(merged); + + } + else { + + double[] mean = new double[] {(currentpoint[0] + mergepointbol.getA()[0])/ 2, (currentpoint[1] + mergepointbol.getA()[1])/ 2 }; + mergepoints.add(mean); + listiter.remove(); + } + + + } + + if(mergepointbol==null) + mergepoints.add(currentpoint); + + } + + RemoveDuplicates(mergepoints); + + points = new ArrayList(); + points.addAll(mergepoints); + + }while(allmerged.size() < points.size()); + + //System.out.println(allmerged.size() + " " + "merged" + " " + points.size()); + for(double[] center:mergepoints) { + + int width = 1; + int height = 1; + int radius = (int)CovistoDogPanel.distthreshold; + Roi Bigroi = new OvalRoi(Util.round(center[0] -(width + radius)/2), Util.round(center[1] - (height + radius)/2 ), Util.round(width + radius), + Util.round(height + radius)); + parent.AfterRemovedRois.add(Bigroi); + + } + + parent.AllEvents.put(z, mergepoints); + + + if(z >= CovistoDogPanel.timeblock) { + + + ArrayList currentlist = parent.AllEvents.get(z); + ArrayList copylist = new ArrayList(currentlist); + for(int i = 1; i <=CovistoDogPanel.timeblock; ++i ) { + ArrayList previouslist = parent.AllEvents.get(z - i); + + + + copylist = RemoveTimeDuplicates(currentlist, previouslist, copylist); + + + parent.AllEvents.replace(z, copylist); + + } + } + + + else if (z1) { + + + ArrayList currentlist = parent.AllEvents.get(z); + ArrayList copylist = new ArrayList(currentlist); + for(int i = z - 1; i >=0; --i ) { + ArrayList previouslist = parent.AllEvents.get(z - i); + + + + copylist = RemoveTimeDuplicates(currentlist, previouslist, copylist); + + + parent.AllEvents.replace(z, copylist); + + } + } + + + for (Roi currentroi : parent.Rois) { + + + + + final double[] geocenter = currentroi.getContourCentroid(); + final Pair Intensityandpixels = PreRoiobject.getIntensity(currentroi, source); + final double intensity = Intensityandpixels.getA(); + final double numberofpixels = Intensityandpixels.getB(); + final double averageintensity = intensity / numberofpixels; + PreRoiobject currentobject = new PreRoiobject(currentroi, + new double[] { geocenter[0], geocenter[1], CovistoZselectPanel.thirdDimension }, numberofpixels, intensity, + averageintensity, CovistoZselectPanel.thirdDimension, CovistoTimeselectPanel.fourthDimension); + parent.CurrentPreRoiobject.add(currentobject); + } + + String uniqueID = Integer.toString(z) + Integer.toString(t); + parent.ZTRois.put(uniqueID, parent.CurrentPreRoiobject); + + common3D.BinaryCreation.CreateBinaryRoi(parent, source, bitimg,parent.Rois, z, t); + common3D.BinaryCreation.CreateBinary(parent, source, afterremovebitimg, z, t); + common3D.BinaryCreation.CreateBinaryDots(parent, source, bitdotimg, z, t); + System.out.println(" Division after " + parent.AllEvents.get(z).size()); + } + + } + public ArrayList RemoveTimeDuplicates(ArrayList Currentpoints, ArrayList Previouspoints, ArrayListcopylist) { + + + + ArrayList allmerged = new ArrayList(); + + if(Previouspoints!=null) { + for (int i = 0; i < Previouspoints.size(); ++i) { + + double[] previouspoint = Previouspoints.get(i); + allmerged = new ArrayList(); + + do { + + Pair mergepointbol = utility.FinderUtils.mergeNearestRois(source, copylist, previouspoint, CovistoDogPanel.timethreshold); + + + if(mergepointbol!=null) { + if(!mergepointbol.getB()) { + + boolean merged = true; + allmerged.add(merged); + + } + else { + + copylist.remove(mergepointbol.getA()); + } + + + } + + + + }while(allmerged.size() < Previouspoints.size()); + + + + } + + RemoveDuplicates(copylist); + } + return copylist; + + + } + + public void RemoveDuplicates(ArrayList points) { + + int j = 0; + + for (int i = 0; i < points.size(); ++i) { + + j = i + 1; + while (j < points.size()) { + + if (points.get(i)[0] == points.get(j)[0] && points.get(i)[1] == points.get(j)[1] ) { + + points.remove(j); + + } + + else { + ++j; + } + + } + + } + + } + + + + public RandomAccessibleInterval getBinaryimg() { + + return bitimg; + } + + public RandomAccessibleInterval getafterremoveBinaryimg() { + + return afterremovebitimg; + } + + public RandomAccessibleInterval getBinarydotimg() { + + return bitdotimg; + + } + +} diff --git a/src/main/java/dog3D/ComputeParallelDoG.java b/src/main/java/dog3D/ComputeParallelDoG.java new file mode 100644 index 0000000..97f2d51 --- /dev/null +++ b/src/main/java/dog3D/ComputeParallelDoG.java @@ -0,0 +1,323 @@ +package dog3D; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import javax.swing.JProgressBar; + +import dogGUI.CovistoDogPanel; +import ij.gui.OvalRoi; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import net.imglib2.Point; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.dog.DogDetection; +import net.imglib2.algorithm.localextrema.RefinedPeak; +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.util.Pair; +import net.imglib2.util.Util; +import net.imglib2.view.Views; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class ComputeParallelDoG & NativeType> { + + final InteractiveMethods parent; + final JProgressBar jpb; + public final RandomAccessibleInterval source; + + public RandomAccessibleInterval bitimg; + public RandomAccessibleInterval bitdotimg; + public RandomAccessibleInterval afterremovebitimg; + + public boolean apply3D; + public int z; + public int t; + + public ComputeParallelDoG(final InteractiveMethods parent, final RandomAccessibleInterval source, + final JProgressBar jpb, boolean apply3D, int z, int t) { + + this.parent = parent; + this.source = source; + + this.jpb = jpb; + this.apply3D = apply3D; + this.z = z; + this.t = t; + + bitimg = new ArrayImgFactory().create(source, new BitType()); + bitdotimg = new ArrayImgFactory().create(source, new BitType()); + afterremovebitimg = new ArrayImgFactory().create(source, new BitType()); + + } + + public void execute() { + ArrayList> peaks = new ArrayList>(); + ArrayList Rois = new ArrayList(); + final DogDetection.ExtremaType type; + if (CovistoDogPanel.lookForMaxima) + type = DogDetection.ExtremaType.MINIMA; + else + type = DogDetection.ExtremaType.MAXIMA; + CovistoDogPanel.sigma2 = utility.ETrackScrollbarUtils.computeSigma2(CovistoDogPanel.sigma, parent.sensitivity); + final DogDetection newdog = new DogDetection(Views.extendBorder(source), + parent.interval, new double[] { 1, 1 }, CovistoDogPanel.sigma, CovistoDogPanel.sigma2, type, CovistoDogPanel.threshold, true); + + peaks = newdog.getSubpixelPeaks(); + Rois = utility.FinderUtils.getcurrentRois(peaks, CovistoDogPanel.sigma, CovistoDogPanel.sigma2); + ArrayList CurrentPreRoiobject = new ArrayList(); + ArrayList AfterRemovedRois = new ArrayList(); + + + + + + + + ArrayList points = new ArrayList(); + ArrayList mergepoints = new ArrayList(); + + + for(RefinedPeak peak : peaks ) { + + double[] currentpoint = new double[] {(float)peak.getDoublePosition(0), (float)peak.getDoublePosition(1)}; + + points.add(currentpoint); + } + + + utility.SortCoordinates.sortByXY(points); + + ArrayList allmerged = new ArrayList(); + + System.out.println(points.size() + " " + "division before"); + if(points.size() > 0) { + do{ + + + + mergepoints = new ArrayList(); + allmerged = new ArrayList(); + ArrayList copylist = new ArrayList(points); + Iterator listiter = points.iterator(); + + + + + while(listiter.hasNext()) { + + + double[] currentpoint = listiter.next(); + + + + + copylist.remove(currentpoint); + Pair mergepointbol = utility.FinderUtils.mergeNearestRois(source, copylist, currentpoint, CovistoDogPanel.distthreshold); + copylist.add(currentpoint); + + if(mergepointbol!=null) { + if(!mergepointbol.getB()) { + + mergepoints.add(currentpoint); + boolean merged = true; + allmerged.add(merged); + + } + else { + + double[] mean = new double[] {(currentpoint[0] + mergepointbol.getA()[0])/ 2, (currentpoint[1] + mergepointbol.getA()[1])/ 2 }; + mergepoints.add(mean); + listiter.remove(); + } + + + } + + if(mergepointbol==null) + mergepoints.add(currentpoint); + + } + + RemoveDuplicates(mergepoints); + + points = new ArrayList(); + points.addAll(mergepoints); + + }while(allmerged.size() < points.size()); + + //System.out.println(allmerged.size() + " " + "merged" + " " + points.size()); + for(double[] center:mergepoints) { + + int width = 1; + int height = 1; + int radius = (int)CovistoDogPanel.distthreshold; + Roi Bigroi = new OvalRoi(Util.round(center[0] -(width + radius)/2), Util.round(center[1] - (height + radius)/2 ), Util.round(width + radius), + Util.round(height + radius)); + AfterRemovedRois.add(Bigroi); + + } + + parent.AllEvents.put(z, mergepoints); + + + if(z >= CovistoDogPanel.timeblock) { + + + ArrayList currentlist = parent.AllEvents.get(z); + ArrayList copylist = new ArrayList(currentlist); + for(int i = 1; i <=CovistoDogPanel.timeblock; ++i ) { + ArrayList previouslist = parent.AllEvents.get(z - i); + + + + copylist = RemoveTimeDuplicates(currentlist, previouslist, copylist); + + + parent.AllEvents.replace(z, copylist); + + } + } + + + else if (z1) { + + + ArrayList currentlist = parent.AllEvents.get(z); + ArrayList copylist = new ArrayList(currentlist); + for(int i = z - 1; i >=0; --i ) { + ArrayList previouslist = parent.AllEvents.get(z - i); + + + + copylist = RemoveTimeDuplicates(currentlist, previouslist, copylist); + + + parent.AllEvents.replace(z, copylist); + + } + } + + + for (Roi currentroi : Rois) { + + + + + final double[] geocenter = currentroi.getContourCentroid(); + final Pair Intensityandpixels = PreRoiobject.getIntensity(currentroi, source); + final double intensity = Intensityandpixels.getA(); + final double numberofpixels = Intensityandpixels.getB(); + final double averageintensity = intensity / numberofpixels; + PreRoiobject currentobject = new PreRoiobject(currentroi, + new double[] { geocenter[0], geocenter[1], CovistoZselectPanel.thirdDimension }, numberofpixels, intensity, + averageintensity, CovistoZselectPanel.thirdDimension, CovistoTimeselectPanel.fourthDimension); + CurrentPreRoiobject.add(currentobject); + } + + String uniqueID = Integer.toString(z) + Integer.toString(t); + parent.ZTRois.put(uniqueID, CurrentPreRoiobject); + + common3D.BinaryCreation.CreateBinaryRoi(parent, source, bitimg,Rois, z, t); + common3D.BinaryCreation.CreateBinary(parent, source, afterremovebitimg, z, t); + common3D.BinaryCreation.CreateBinaryDots(parent, source, bitdotimg, z, t); + System.out.println(" Division after " + parent.AllEvents.get(z).size()); + } + + } + public ArrayList RemoveTimeDuplicates(ArrayList Currentpoints, ArrayList Previouspoints, ArrayListcopylist) { + + + + ArrayList allmerged = new ArrayList(); + + if(Previouspoints!=null) { + for (int i = 0; i < Previouspoints.size(); ++i) { + + double[] previouspoint = Previouspoints.get(i); + allmerged = new ArrayList(); + + do { + + Pair mergepointbol = utility.FinderUtils.mergeNearestRois(source, copylist, previouspoint, CovistoDogPanel.timethreshold); + + + if(mergepointbol!=null) { + if(!mergepointbol.getB()) { + + boolean merged = true; + allmerged.add(merged); + + } + else { + + copylist.remove(mergepointbol.getA()); + } + + + } + + + + }while(allmerged.size() < Previouspoints.size()); + + + + } + + RemoveDuplicates(copylist); + } + return copylist; + + + } + + public void RemoveDuplicates(ArrayList points) { + + int j = 0; + + for (int i = 0; i < points.size(); ++i) { + + j = i + 1; + while (j < points.size()) { + + if (points.get(i)[0] == points.get(j)[0] && points.get(i)[1] == points.get(j)[1] ) { + + points.remove(j); + + } + + else { + ++j; + } + + } + + } + + } + + + + public RandomAccessibleInterval getBinaryimg() { + + return bitimg; + } + + public RandomAccessibleInterval getafterremoveBinaryimg() { + + return afterremovebitimg; + } + + public RandomAccessibleInterval getBinarydotimg() { + + return bitdotimg; + + } + +} \ No newline at end of file diff --git a/src/main/java/dog3D/DogAll.java b/src/main/java/dog3D/DogAll.java new file mode 100644 index 0000000..4a6a78a --- /dev/null +++ b/src/main/java/dog3D/DogAll.java @@ -0,0 +1,378 @@ +package dog3D; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.swing.SwingWorker; + +import distanceTransform.DistWatershed; +import dogGUI.CovistoDogPanel; +import ij.IJ; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.Cursor; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.localextrema.RefinedPeak; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; +import preProcessing.GlobalThresholding; +import preProcessing.Utils; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class DogAll extends SwingWorker { + + final InteractiveMethods parent; + + public DogAll(final InteractiveMethods parent) { + + this.parent = parent; + + } + + + + public class ParallelCalls implements Callable { + + + public final InteractiveMethods parent; + public final int z; + public final int t; + public RandomAccessibleInterval currentbitimg; + public RandomAccessibleInterval afterremovecurrentbitimg; + public RandomAccessibleInterval Dotafterremovecurrentbitimg; + + public ParallelCalls(InteractiveMethods parent,RandomAccessibleInterval currentbitimg,RandomAccessibleInterval afterremovecurrentbitimg,RandomAccessibleInterval Dotafterremovecurrentbitimg, int z, int t) { + + + this.parent = parent; + this.currentbitimg = currentbitimg; + this.afterremovecurrentbitimg = afterremovecurrentbitimg; + this.Dotafterremovecurrentbitimg = Dotafterremovecurrentbitimg; + this.z = z; + this.t = t; + + } + + + @Override + public Void call() throws Exception { + + double percent = t +z; + RandomAccessibleInterval CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, z, + CovistoZselectPanel.thirdDimensionSize, t, CovistoTimeselectPanel.fourthDimensionSize); + utility.CovsitoProgressBar.CovistoSetProgressBar(parent.jpb, 100 *(percent / (CovistoTimeselectPanel.fourthDimensionSize + CovistoZselectPanel.thirdDimensionSize + 1 )) ,"Computing"); + // UnsignedByteType image created here + parent.updatePreview(ValueChange.THIRDDIMmouse); + RandomAccessibleInterval newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + + + + processParallelSlice(newimg, currentbitimg, afterremovecurrentbitimg,Dotafterremovecurrentbitimg, z, t); + + return null; + } + + + + } + + + @Override + protected Void doInBackground() throws Exception { + + parent.apply3D = true; + + RandomAccessibleInterval bitimg = new ArrayImgFactory().create(parent.originalimg, + new BitType()); + + RandomAccessibleInterval bitdotimg = new ArrayImgFactory().create(parent.originalimg, + new BitType()); + + RandomAccessibleInterval afterremovebitimg = new ArrayImgFactory().create(parent.originalimg, + new BitType()); + + List> list = new ArrayList>(); + int nThreads = Runtime.getRuntime().availableProcessors(); + final ExecutorService taskExecutor = Executors.newFixedThreadPool(nThreads); + for (int t = CovistoTimeselectPanel.fourthDimensionsliderInit; t <= CovistoTimeselectPanel.fourthDimensionSize; ++t) { + + for (int z = CovistoZselectPanel.thirdDimensionsliderInit; z <= CovistoZselectPanel.thirdDimensionSize; ++z) { + + if(IJ.escapePressed()) { + + IJ.resetEscape(); + break; + + } + + + CovistoZselectPanel.thirdDimension = z; + CovistoTimeselectPanel.fourthDimension = t; + + + RandomAccessibleInterval currentbitimg = utility.CovistoSlicer.getCurrentView(bitimg, z, + CovistoZselectPanel.thirdDimensionSize, t, CovistoTimeselectPanel.fourthDimensionSize); + RandomAccessibleInterval afterremovecurrentbitimg = utility.CovistoSlicer.getCurrentView(afterremovebitimg, z, + CovistoZselectPanel.thirdDimensionSize, t, CovistoTimeselectPanel.fourthDimensionSize); + RandomAccessibleInterval Dotafterremovecurrentbitimg = utility.CovistoSlicer.getCurrentView(bitdotimg, z, + CovistoZselectPanel.thirdDimensionSize, t, CovistoTimeselectPanel.fourthDimensionSize); + + + ParallelCalls call = new ParallelCalls(parent, currentbitimg, afterremovecurrentbitimg, Dotafterremovecurrentbitimg, z, t); + + Future Futureresult = taskExecutor.submit(call); + list.add(Futureresult); + + } + + } + + for (Future fut : list) { + + + fut.get(); + + } + + + ImageJFunctions.show(bitimg).setTitle("Binary Image"); + ImageJFunctions.show(afterremovebitimg).setTitle("Merge Points Binary Image"); + ImageJFunctions.show(bitdotimg).setTitle("Merge Points Binary Image (Dot)"); + utility.CovsitoProgressBar.CovistoSetProgressBar(parent.jpb, "Done"); + + Savefunction(parent.AllEvents); + return null; + } + + + + public void Savefunction(ConcurrentHashMap> Mergedpoints) { + + + + File file = new File(parent.inputfile + "//" + parent.userfile.replace(".tif","") + ".txt"); + + FileWriter fw; + + try { + + + fw = new FileWriter(file); + + BufferedWriter bw = new BufferedWriter(fw); + + bw.write(" Time (px), X, Y \n" ); + + for(Entry> entry : Mergedpoints.entrySet()) { + + + ArrayList timedlist = entry.getValue(); + for(int index = 0; index < timedlist.size(); ++index) { + + + int time = entry.getKey(); + + double X = timedlist.get(index)[0]; + + double Y = timedlist.get(index)[1]; + + + bw.write(time + "," + parent.nf.format(X) + "," + parent.nf.format(Y) + "\n" ); + + + } + + + } + bw.close(); + fw.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + + } + + + protected void processSlice(RandomAccessibleInterval slice, + RandomAccessibleInterval bitoutputslice, RandomAccessibleInterval afterremovebitoutputslice,RandomAccessibleInterval Dotafterremovecurrentbitimg, int z, int t) { + + + parent.CurrentPreRoiobject = new ArrayList(); + ComputeDoG ComputeDOG = new ComputeDoG(parent, slice, parent.jpb, + parent.apply3D, z, t); + ComputeDOG.execute(); + + RandomAccessibleInterval bitimg = ComputeDOG.getBinaryimg(); + RandomAccessibleInterval afterremovebitimg = ComputeDOG.getafterremoveBinaryimg(); + RandomAccessibleInterval Dotafterremovebitimg = ComputeDOG.getBinarydotimg(); + + Cursor bitcursor = Views.iterable(bitoutputslice).localizingCursor(); + + Cursor afterremovebitcursor = Views.iterable(afterremovebitoutputslice).localizingCursor(); + + Cursor Dotafterremovebitcursor = Views.iterable(Dotafterremovecurrentbitimg).localizingCursor(); + + RandomAccess ranac = bitimg.randomAccess(); + + RandomAccess afterremoveranac = afterremovebitimg.randomAccess(); + + RandomAccess Dotafterremoveranac = Dotafterremovebitimg.randomAccess(); + + while (bitcursor.hasNext()) { + + bitcursor.fwd(); + + ranac.setPosition(bitcursor); + + bitcursor.get().set(ranac.get()); + + } + + while (afterremovebitcursor.hasNext()) { + + afterremovebitcursor.fwd(); + + afterremoveranac.setPosition(afterremovebitcursor); + + afterremovebitcursor.get().set(afterremoveranac.get()); + + } + + while (Dotafterremovebitcursor.hasNext()) { + + Dotafterremovebitcursor.fwd(); + + Dotafterremoveranac.setPosition(Dotafterremovebitcursor); + + Dotafterremovebitcursor.get().set(Dotafterremoveranac.get()); + + } + + + } + + + + protected void processParallelSlice(RandomAccessibleInterval slice, + RandomAccessibleInterval bitoutputslice, RandomAccessibleInterval afterremovebitoutputslice,RandomAccessibleInterval Dotafterremovecurrentbitimg, int z, int t) { + + + + + ComputeParallelDoG ComputeDOG = new ComputeParallelDoG<>(parent, slice, parent.jpb, parent.apply3D, z, t); + ComputeDOG.execute(); + + RandomAccessibleInterval bitimg = ComputeDOG.getBinaryimg(); + RandomAccessibleInterval afterremovebitimg = ComputeDOG.getafterremoveBinaryimg(); + RandomAccessibleInterval Dotafterremovebitimg = ComputeDOG.getBinarydotimg(); + + Cursor bitcursor = Views.iterable(bitoutputslice).localizingCursor(); + + Cursor afterremovebitcursor = Views.iterable(afterremovebitoutputslice).localizingCursor(); + + Cursor Dotafterremovebitcursor = Views.iterable(Dotafterremovecurrentbitimg).localizingCursor(); + + RandomAccess ranac = bitimg.randomAccess(); + + RandomAccess afterremoveranac = afterremovebitimg.randomAccess(); + + RandomAccess Dotafterremoveranac = Dotafterremovebitimg.randomAccess(); + + while (bitcursor.hasNext()) { + + bitcursor.fwd(); + + ranac.setPosition(bitcursor); + + bitcursor.get().set(ranac.get()); + + } + + while (afterremovebitcursor.hasNext()) { + + afterremovebitcursor.fwd(); + + afterremoveranac.setPosition(afterremovebitcursor); + + afterremovebitcursor.get().set(afterremoveranac.get()); + + } + + while (Dotafterremovebitcursor.hasNext()) { + + Dotafterremovebitcursor.fwd(); + + Dotafterremoveranac.setPosition(Dotafterremovebitcursor); + + Dotafterremovebitcursor.get().set(Dotafterremoveranac.get()); + + } + + + } + + + protected void processSliceremove(RandomAccessibleInterval slice, + RandomAccessibleInterval bitoutputslice, int z, int t) { + + ComputeDoG ComputeDOG = new ComputeDoG(parent, slice, parent.jpb, + parent.apply3D, z, t); + ComputeDOG.execute(); + + RandomAccessibleInterval bitimg = ComputeDOG.getafterremoveBinaryimg(); + Cursor bitcursor = Views.iterable(bitoutputslice).localizingCursor(); + + RandomAccess ranac = bitimg.randomAccess(); + + while (bitcursor.hasNext()) { + + bitcursor.fwd(); + + ranac.setPosition(bitcursor); + + bitcursor.get().set(ranac.get()); + + } + + } + + + @Override + protected void done() { + try { + + parent.apply3D = false; + get(); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/dogGUI/CovistoDogPanel.java b/src/main/java/dogGUI/CovistoDogPanel.java new file mode 100644 index 0000000..0e1d6c5 --- /dev/null +++ b/src/main/java/dogGUI/CovistoDogPanel.java @@ -0,0 +1,163 @@ +package dogGUI; + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoDogPanel { + + + public static JPanel DogPanel = new JPanel(); + + + public static final int scrollbarSize = 1000; + public static Label removeText = new Label("Distance Threshold ", Label.CENTER); + public static Label removetimeText = new Label("Time-Dist Threshold ", Label.CENTER); + public static Label timeblockText = new Label("Time Skip ", Label.CENTER); + + + public static TextField inputFieldSpot, inputtimedist, inputtimeskip; + public static double distthreshold = 10; + public static double timethreshold = 10; + public static int timeblock = 1; + + public static boolean lookForMaxima = true; + public static boolean lookForMinima = false; + public static int sigmaInit = 1; + public static float sigma = sigmaInit; + public static float sigmaMin = 1f; + public static float sigmaMax = 100f; + public static CheckboxGroup minormax = new CheckboxGroup(); + public static final Checkbox findminima = new Checkbox("Locate Minima", minormax, lookForMinima); + public static final Checkbox findmaxima = new Checkbox("Locate Maxima", minormax, lookForMaxima); + public static float thresholdMin = 1f; + public static float thresholdMax = 255f; + public static int thresholdInit = 2; + public static float sigma2 = 1.1f; + public static float threshold = 1f; + public static int sigmasliderInit = 0; + public static int thresholdsliderInit = 5; + public static Label sigmaText = new Label("Approximate object size = " + sigmaInit, Label.CENTER); + public static JScrollBar sigmaslider = new JScrollBar(Scrollbar.HORIZONTAL, sigmaInit, 10, 0, scrollbarSize + 10); + public static JScrollBar thresholdslider = new JScrollBar(Scrollbar.HORIZONTAL, thresholdInit, 10, 0, 10 + scrollbarSize); + public static Label thresholdText = new Label("Approximate peak intensity " + thresholdInit, Label.CENTER); + public static JButton AllDog = new JButton("DOG in 3D/4D"); + public static final String sigmastring = "Approximate object size"; + public static final String thresholdstring = "Approximate peak intensity"; + + public static double getInitialSigma() { + return sigma; + } + + public static void setInitialSigma(final float value) { + sigma = value; + sigmaInit = scrollbar.Utility.computeScrollbarPositionFromValue(sigma, sigmaMin, sigmaMax, scrollbarSize); + } + + public static double getInitialThreshold() { + return threshold; + } + + public static void setInitialThreshold(final float value) { + threshold = value; + thresholdInit = scrollbar.Utility.computeScrollbarPositionFromValue(threshold, thresholdMin, thresholdMax, scrollbarSize); + } + + + + + public static JPanel DogPanel() { + + + layoutManager.Setlayout.LayoutSetter(DogPanel); + + inputFieldSpot = new TextField(5); + inputFieldSpot.setText(Double.toString(distthreshold)); + + inputtimedist = new TextField(5); + inputtimedist.setText(Double.toString(timethreshold)); + + inputtimeskip = new TextField(5); + inputtimeskip.setText(Double.toString(timeblock)); + + + sigmaslider.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(sigmaInit, sigmaMin, sigmaMax, scrollbarSize)); + + thresholdslider.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(thresholdsliderInit, + thresholdMin, thresholdMax, scrollbarSize)); + + sigma = scrollbar.Utility.computeValueFromScrollbarPosition(sigmaslider.getValue(), sigmaMin, sigmaMax, + scrollbarSize); + + threshold = scrollbar.Utility.computeValueFromScrollbarPosition(thresholdslider.getValue(), thresholdMin, + thresholdMax, scrollbarSize); + + sigmaText = new Label("Approximate object size = " + sigma, Label.CENTER); + thresholdText = new Label("Approximate peak intensity " + threshold, Label.CENTER); + Border dogborder = new CompoundBorder(new TitledBorder("Difference of Gaussian detection"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + DogPanel.add(sigmaText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(sigmaslider, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + DogPanel.add(thresholdText, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(thresholdslider, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(findminima, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(findmaxima, new GridBagConstraints(2, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(removeText, new GridBagConstraints(0, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(removetimeText, new GridBagConstraints(0, 6, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(timeblockText, new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(inputFieldSpot, new GridBagConstraints(2, 5, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + DogPanel.add(inputtimedist, new GridBagConstraints(2, 6, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + DogPanel.add(inputtimeskip, new GridBagConstraints(2, 7, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + + + DogPanel.add(AllDog, new GridBagConstraints(2, 8, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + DogPanel.setBorder(dogborder); + + + return DogPanel; + + } + + + +} diff --git a/src/main/java/dogSeg/DOGSeg.java b/src/main/java/dogSeg/DOGSeg.java new file mode 100644 index 0000000..e43f081 --- /dev/null +++ b/src/main/java/dogSeg/DOGSeg.java @@ -0,0 +1,110 @@ +package dogSeg; + +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import javax.swing.JProgressBar; +import javax.swing.SwingWorker; + +import dogGUI.CovistoDogPanel; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.algorithm.dog.DogDetection; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.view.Views; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class DOGSeg extends SwingWorker { + + final InteractiveMethods parent; + final JProgressBar jpb; + + public DOGSeg(final InteractiveMethods parent, final JProgressBar jpb) { + + this.parent = parent; + this.jpb = jpb; + + } + + @Override + protected Void doInBackground() throws Exception { + if (!parent.snakeongoing) { + final DogDetection.ExtremaType type; + if (CovistoDogPanel.lookForMaxima) + type = DogDetection.ExtremaType.MINIMA; + else + type = DogDetection.ExtremaType.MAXIMA; + CovistoDogPanel.sigma2 = utility.ETrackScrollbarUtils.computeSigma2(CovistoDogPanel.sigma, parent.sensitivity); + final DogDetection newdog = new DogDetection(Views.extendBorder(parent.CurrentView), + parent.interval, new double[] { 1, 1 }, CovistoDogPanel.sigma, CovistoDogPanel.sigma2, type, CovistoDogPanel.threshold, true); + parent.overlay.clear(); + parent.peaks = newdog.getSubpixelPeaks(); + } + return null; + } + + @Override + protected void done() { + if (!parent.snakeongoing) { + parent.overlay.clear(); + + parent.Rois = utility.FinderUtils.getcurrentRois(parent.peaks, CovistoDogPanel.sigma, CovistoDogPanel.sigma2); + + parent.CurrentPreRoiobject = new ArrayList(); + for (int index = 0; index < parent.peaks.size(); ++index) { + + Roi or = parent.Rois.get(index); + + or.setStrokeColor(parent.colorDrawDog); + parent.overlay.add(or); + } + + for (Roi currentroi : parent.Rois) { + + final double[] geocenter = currentroi.getContourCentroid(); + final Pair Intensityandpixels = PreRoiobject.getIntensity(currentroi, + parent.CurrentView); + final double intensity = Intensityandpixels.getA(); + final double numberofpixels = Intensityandpixels.getB(); + final double averageintensity = intensity / numberofpixels; + PreRoiobject currentobject = new PreRoiobject(currentroi, + new double[] { geocenter[0], geocenter[1], CovistoZselectPanel.thirdDimension }, numberofpixels, intensity, + averageintensity, CovistoZselectPanel.thirdDimension, CovistoTimeselectPanel.fourthDimension); + parent.CurrentPreRoiobject.add(currentobject); + } + for (Map.Entry> entry : parent.ZTRois.entrySet()) { + + ArrayList current = entry.getValue(); + for (PreRoiobject currentroi : current) { + + if (currentroi.fourthDimension == CovistoTimeselectPanel.fourthDimension && currentroi.thirdDimension == CovistoZselectPanel.thirdDimension) { + + currentroi.rois.setStrokeColor(parent.colorSnake); + parent.overlay.add(currentroi.rois); + + } + + } + } + parent.imp.setOverlay(parent.overlay); + parent.imp.updateAndDraw(); + utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, "Done"); + } + if (parent.snakeongoing) + parent.updatePreview(ValueChange.SNAKE); + try { + get(); + } catch (InterruptedException e) { + + } catch (ExecutionException e) { + + } + + } + +} diff --git a/src/main/java/drawUtils/DrawFunction.java b/src/main/java/drawUtils/DrawFunction.java index 8079c25..7e30162 100644 --- a/src/main/java/drawUtils/DrawFunction.java +++ b/src/main/java/drawUtils/DrawFunction.java @@ -5,13 +5,11 @@ import java.util.Iterator; import java.util.List; -import com.itextpdf.awt.geom.Rectangle; import fiji.tool.SliceListener; import fiji.tool.SliceObserver; import ij.ImagePlus; import ij.gui.Overlay; -import landmarks.RegistrationResult; import net.imglib2.Cursor; import net.imglib2.FinalInterval; import net.imglib2.Localizable; diff --git a/src/main/java/functionPanel/CovistoFunctionPanel.java b/src/main/java/functionPanel/CovistoFunctionPanel.java new file mode 100644 index 0000000..26d435f --- /dev/null +++ b/src/main/java/functionPanel/CovistoFunctionPanel.java @@ -0,0 +1,88 @@ +package functionPanel; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoFunctionPanel { + + public static JPanel FunctionPanel = new JPanel(); + + public static Label Functionlabel, gradientlabel, distlabel; + public static TextField Functioniter, gradientthresh, maxdist; + + public static int Functioniterations = 200; + public static int displayFunction = Functioniterations / 2; + public static int Gradthresh = 1; + public static int DistMax = 100; + public static double Displacement_min = 0.5; + public static double Displacement_max = 5.0; + public static double Threshold_dist_positive = 10; + public static double Threshold_dist_negative = 10; + public static double Inv_alpha_min = 0.2; + public static double Inv_alpha_max = 10.0; + public static double Mul_factor = 0.99; + // maximum displacement + public static double force = 10; + // regulari1ation factors, min and max + public static double reg = 5; + public static double regmin, regmax; + + + + + public static JPanel FunctionPanel() { + + regmin = reg / 2.0; + regmax = reg; + + + layoutManager.Setlayout.LayoutSetter(FunctionPanel); + Border Functionborder = new CompoundBorder(new TitledBorder("User requested Function computer"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + Functionlabel = new Label("My function parameter"); + gradientlabel = new Label("My second function parameter"); + distlabel = new Label("Just a little bit more"); + + Functioniter = new TextField(1); + gradientthresh = new TextField(1); + maxdist = new TextField(1); + + Functioniter.setText(Integer.toString(Functioniterations)); + gradientthresh.setText(Integer.toString(Gradthresh)); + maxdist.setText(Integer.toString(DistMax)); + FunctionPanel.add(Functionlabel, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + FunctionPanel.add(Functioniter, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + FunctionPanel.add(gradientlabel, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + FunctionPanel.add(gradientthresh, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + FunctionPanel.add(distlabel, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + FunctionPanel.add(maxdist, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + + + FunctionPanel.setBorder(Functionborder); + + + return FunctionPanel; + + } + + +} diff --git a/src/main/java/interactivePreprocessing/AdvancedSnakeListener.java b/src/main/java/interactivePreprocessing/AdvancedSnakeListener.java new file mode 100644 index 0000000..ce4fc85 --- /dev/null +++ b/src/main/java/interactivePreprocessing/AdvancedSnakeListener.java @@ -0,0 +1,75 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import ij.gui.GenericDialog; +import snakeGUI.CovistoSnakePanel; + +public class AdvancedSnakeListener implements ItemListener { + + + final InteractiveMethods parent; + + + public AdvancedSnakeListener(final InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(ItemEvent e) { + + if (e.getStateChange() == ItemEvent.DESELECTED) { + + parent.advancedSnake = false; + + } + + if (e.getStateChange() == ItemEvent.SELECTED) { + + parent.advancedSnake = true; + AdvancedDialog(); + + } + +} + + public boolean AdvancedDialog() { + + // dialog + GenericDialog gd = new GenericDialog("Snake Advanced"); + gd.addNumericField("Distance_Search", CovistoSnakePanel.DistMax, 0); + gd.addNumericField("Displacement_min", CovistoSnakePanel.Displacement_min, 2); + gd.addNumericField("Displacement_max", CovistoSnakePanel.Displacement_max, 2); + gd.addNumericField("Threshold_dist_positive", CovistoSnakePanel.Threshold_dist_positive, 0); + gd.addNumericField("Threshold_dist_negative", CovistoSnakePanel.Threshold_dist_negative, 0); + gd.addNumericField("Inv_alpha_min", CovistoSnakePanel.Inv_alpha_min, 2); + gd.addNumericField("Inv_alpha_max", CovistoSnakePanel.Inv_alpha_max, 2); + gd.addNumericField("Reg_min", CovistoSnakePanel.regmin, 2); + gd.addNumericField("Reg_max", CovistoSnakePanel.regmax, 2); + gd.addNumericField("Mul_factor", CovistoSnakePanel.Mul_factor, 4); + // show dialog + gd.showDialog(); + + CovistoSnakePanel.DistMax = (int) gd.getNextNumber(); + CovistoSnakePanel.Displacement_min = gd.getNextNumber(); + CovistoSnakePanel.Displacement_max = gd.getNextNumber(); + CovistoSnakePanel.Threshold_dist_positive = gd.getNextNumber(); + CovistoSnakePanel.Threshold_dist_negative = gd.getNextNumber(); + CovistoSnakePanel.Inv_alpha_min = gd.getNextNumber(); + CovistoSnakePanel.Inv_alpha_max = gd.getNextNumber(); + CovistoSnakePanel.regmin = gd.getNextNumber(); + CovistoSnakePanel.regmax = gd.getNextNumber(); + CovistoSnakePanel.Mul_factor = gd.getNextNumber(); + + return !gd.wasCanceled(); + + } + + + + +} diff --git a/src/main/java/interactivePreprocessing/CovistoStandardMouseListener.java b/src/main/java/interactivePreprocessing/CovistoStandardMouseListener.java new file mode 100644 index 0000000..b9f7aa6 --- /dev/null +++ b/src/main/java/interactivePreprocessing/CovistoStandardMouseListener.java @@ -0,0 +1,69 @@ +package interactivePreprocessing; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; + + + + + +/** + * Updates when mouse is released + * + * @author spreibi + * + */ +public class CovistoStandardMouseListener implements MouseListener +{ + final InteractiveMethods parent; + final ValueChange change; + + public CovistoStandardMouseListener( final InteractiveMethods parent, final ValueChange change) + { + this.parent = parent; + this.change = change; + } + + + + @Override + public void mouseReleased( MouseEvent arg0 ) + { + + + parent.updatePreview(change); + + + + } + + @Override + public void mousePressed( MouseEvent arg0 ){ + + + /* + deltascrollbar.setLocation(arg0.getLocationOnScreen()); + deltascrollbar.repaint(); + deltascrollbar.validate(); + System.out.println(arg0.getLocationOnScreen()); + */ + } + + @Override + public void mouseExited( MouseEvent arg0 ) { + + } + + @Override + public void mouseEntered( MouseEvent arg0 ) { + } + + @Override + public void mouseClicked( MouseEvent arg0 ) {} +} + + diff --git a/src/main/java/interactivePreprocessing/DoDOGListener.java b/src/main/java/interactivePreprocessing/DoDOGListener.java new file mode 100644 index 0000000..06728ce --- /dev/null +++ b/src/main/java/interactivePreprocessing/DoDOGListener.java @@ -0,0 +1,65 @@ +package interactivePreprocessing; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; +import watershedGUI.CovistoWatershedPanel; + +public class DoDOGListener implements ItemListener { + + final InteractiveMethods parent; + + public DoDOGListener( InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + parent.showDOG = false; + parent.showMSER = false; + parent.showWatershed = false; + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + parent.showDOG = true; + parent.showMSER = false; + parent.showWatershed = false; + + CovistoDogPanel.sigmaslider.setVisible(true); + CovistoDogPanel.thresholdslider.setVisible(true); + CovistoDogPanel.findmaxima.setVisible(true); + CovistoDogPanel.findminima.setVisible(true); + + CovistoMserPanel.deltaS.setVisible(false); + CovistoMserPanel.Unstability_ScoreS.setVisible(false); + CovistoMserPanel.minDiversityS.setVisible(false); + CovistoMserPanel.minSizeS.setVisible(false); + CovistoMserPanel.maxSizeS.setVisible(false); + CovistoMserPanel.findminimaMser.setVisible(false); + CovistoMserPanel.findmaximaMser.setVisible(false); + + CovistoWatershedPanel.displayWater.setVisible(false); + CovistoWatershedPanel.displayBinary.setVisible(false); + CovistoWatershedPanel.displayDist.setVisible(false); + CovistoWatershedPanel.autothreshold.setVisible(false); + CovistoWatershedPanel.thresholdWaterslider.setVisible(false); + + parent.SnakePanel.setVisible(true); + + } + + } + + +} diff --git a/src/main/java/interactivePreprocessing/DoMSERListener.java b/src/main/java/interactivePreprocessing/DoMSERListener.java new file mode 100644 index 0000000..c753ee4 --- /dev/null +++ b/src/main/java/interactivePreprocessing/DoMSERListener.java @@ -0,0 +1,68 @@ +package interactivePreprocessing; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; +import watershedGUI.CovistoWatershedPanel; + +public class DoMSERListener implements ItemListener { + + final InteractiveMethods parent; + + public DoMSERListener( InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + parent.showDOG = false; + parent.showMSER = false; + parent.showWatershed = false; + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + parent.showDOG = false; + parent.showMSER = true; + parent.showWatershed = false; + CovistoDogPanel.sigmaslider.setVisible(false); + CovistoDogPanel.thresholdslider.setVisible(false); + CovistoDogPanel.findmaxima.setVisible(false); + CovistoDogPanel.findminima.setVisible(false); + + + + + CovistoMserPanel.deltaS.setVisible(true); + CovistoMserPanel.Unstability_ScoreS.setVisible(true); + CovistoMserPanel.minDiversityS.setVisible(true); + CovistoMserPanel.minSizeS.setVisible(true); + CovistoMserPanel.maxSizeS.setVisible(true); + CovistoMserPanel.findminimaMser.setVisible(true); + CovistoMserPanel.findmaximaMser.setVisible(true); + + + CovistoWatershedPanel.displayWater.setVisible(false); + CovistoWatershedPanel.displayBinary.setVisible(false); + CovistoWatershedPanel.displayDist.setVisible(false); + CovistoWatershedPanel.autothreshold.setVisible(false); + CovistoWatershedPanel.thresholdWaterslider.setVisible(false); + + parent.SnakePanel.setVisible(true); + + } + + } + + +} diff --git a/src/main/java/interactivePreprocessing/DoWatershedListener.java b/src/main/java/interactivePreprocessing/DoWatershedListener.java new file mode 100644 index 0000000..be77a29 --- /dev/null +++ b/src/main/java/interactivePreprocessing/DoWatershedListener.java @@ -0,0 +1,68 @@ +package interactivePreprocessing; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; +import watershedGUI.CovistoWatershedPanel; + +public class DoWatershedListener implements ItemListener { + + final InteractiveMethods parent; + + public DoWatershedListener( InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + parent.showDOG = false; + parent.showMSER = false; + parent.showWatershed = false; + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + parent.showDOG = false; + parent.showMSER = false; + parent.showWatershed = true; + + CovistoDogPanel.sigmaslider.setVisible(false); + CovistoDogPanel.thresholdslider.setVisible(false); + CovistoDogPanel.findmaxima.setVisible(false); + CovistoDogPanel.findminima.setVisible(false); + + CovistoMserPanel.deltaS.setVisible(false); + CovistoMserPanel.Unstability_ScoreS.setVisible(false); + CovistoMserPanel.minDiversityS.setVisible(false); + CovistoMserPanel.minSizeS.setVisible(false); + CovistoMserPanel.maxSizeS.setVisible(false); + CovistoMserPanel.findminimaMser.setVisible(false); + CovistoMserPanel.findmaximaMser.setVisible(false); + + + + CovistoWatershedPanel.displayWater.setVisible(true); + CovistoWatershedPanel.displayBinary.setVisible(true); + CovistoWatershedPanel.displayDist.setVisible(true); + CovistoWatershedPanel.autothreshold.setVisible(true); + CovistoWatershedPanel.thresholdWaterslider.setVisible(true); + + parent.SnakePanel.setVisible(false); + + + } + + } + + +} diff --git a/src/main/java/interactivePreprocessing/FindMaximaListener.java b/src/main/java/interactivePreprocessing/FindMaximaListener.java new file mode 100644 index 0000000..9ab59a0 --- /dev/null +++ b/src/main/java/interactivePreprocessing/FindMaximaListener.java @@ -0,0 +1,41 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; + +public class FindMaximaListener implements ItemListener { + + final InteractiveMethods parent; + public FindMaximaListener( InteractiveMethods parent) { + + this.parent = parent; + } + + + + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + CovistoDogPanel.lookForMinima = false; + CovistoDogPanel.lookForMaxima = false; + + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + CovistoDogPanel.lookForMinima = false; + CovistoDogPanel.lookForMaxima = true; + parent.updatePreview(ValueChange.DOG); + } + + } + + +} + diff --git a/src/main/java/interactivePreprocessing/FindMaximaMserListener.java b/src/main/java/interactivePreprocessing/FindMaximaMserListener.java new file mode 100644 index 0000000..9f0c708 --- /dev/null +++ b/src/main/java/interactivePreprocessing/FindMaximaMserListener.java @@ -0,0 +1,41 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class FindMaximaMserListener implements ItemListener { + + final InteractiveMethods parent; + + public FindMaximaMserListener( InteractiveMethods parent) { + + this.parent = parent; + + } + + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + CovistoMserPanel.darktobright = false; + CovistoMserPanel.brighttodark = false; + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + CovistoMserPanel.darktobright = true; + CovistoMserPanel.brighttodark = false; + parent.updatePreview(ValueChange.MSER); + } + + } + + +} + diff --git a/src/main/java/interactivePreprocessing/FindMinimaListener.java b/src/main/java/interactivePreprocessing/FindMinimaListener.java new file mode 100644 index 0000000..6866075 --- /dev/null +++ b/src/main/java/interactivePreprocessing/FindMinimaListener.java @@ -0,0 +1,40 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; + +public class FindMinimaListener implements ItemListener { + + final InteractiveMethods parent; + public FindMinimaListener( InteractiveMethods parent) { + + this.parent = parent; + + } + + + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + CovistoDogPanel.lookForMinima = false; + CovistoDogPanel.lookForMaxima = false; + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + CovistoDogPanel.lookForMinima = true; + CovistoDogPanel.lookForMaxima = false; + parent.updatePreview(ValueChange.DOG); + } + + } + + +} + diff --git a/src/main/java/interactivePreprocessing/FindMinimaMserListener.java b/src/main/java/interactivePreprocessing/FindMinimaMserListener.java new file mode 100644 index 0000000..81c2265 --- /dev/null +++ b/src/main/java/interactivePreprocessing/FindMinimaMserListener.java @@ -0,0 +1,39 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class FindMinimaMserListener implements ItemListener { + + final InteractiveMethods parent; + public FindMinimaMserListener( InteractiveMethods parent) { + + this.parent = parent; + } + + + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + + + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + CovistoMserPanel.darktobright = false; + CovistoMserPanel.brighttodark = false; + } else if (arg0.getStateChange() == ItemEvent.SELECTED) { + CovistoMserPanel.darktobright = false; + CovistoMserPanel.brighttodark = true; + parent.updatePreview(ValueChange.MSER); + } + + } + + +} + diff --git a/src/main/java/interactivePreprocessing/GradientListener.java b/src/main/java/interactivePreprocessing/GradientListener.java new file mode 100644 index 0000000..b8ffac6 --- /dev/null +++ b/src/main/java/interactivePreprocessing/GradientListener.java @@ -0,0 +1,31 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import snakeGUI.CovistoSnakePanel; + +public class GradientListener implements TextListener { + + + final InteractiveMethods parent; + + public GradientListener(final InteractiveMethods parent) { + + + this.parent = parent; + + } + + @Override + public void textValueChanged(TextEvent e) { + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if (s.length() > 0) + CovistoSnakePanel.Gradthresh = Integer.parseInt(s); + + } + +} diff --git a/src/main/java/interactivePreprocessing/InteractiveMethods.java b/src/main/java/interactivePreprocessing/InteractiveMethods.java new file mode 100644 index 0000000..72e7fa6 --- /dev/null +++ b/src/main/java/interactivePreprocessing/InteractiveMethods.java @@ -0,0 +1,896 @@ +package interactivePreprocessing; + +import java.awt.BorderLayout; +import java.awt.Button; +import java.awt.CardLayout; +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; +import java.awt.event.ActionEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.io.File; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +import timeGUI.CovistoTimeselectPanel; + +import javax.swing.AbstractAction; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JProgressBar; +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.JTextField; +import javax.swing.ScrollPaneConstants; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import costMatrix.CostFunction; +import distanceTransform.CreateWatershed; +import distanceTransform.DistWatershed; +import dogGUI.CovistoDogPanel; +import dogSeg.DOGSeg; +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.gui.Overlay; +import ij.gui.Roi; +import ij.plugin.frame.RoiManager; +import ij.process.ColorProcessor; +import kalmanGUI.CovistoKalmanPanel; +import linkers.Model3D; +import linkers.PRENNsearch; +import mpicbg.imglib.util.Util; +import mserGUI.CovistoMserPanel; +import mserMethods.MSERSeg; +import nearestNeighbourGUI.CovistoNearestNPanel; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Point; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.componenttree.mser.MserTree; +import net.imglib2.algorithm.dog.DogDetection; +import net.imglib2.algorithm.localextrema.RefinedPeak; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; +import preProcessing.GlobalThresholding; +import preProcessing.Kernels; +import snakeGUI.CovistoSnakePanel; +import utility.PreRoiobject; +import utility.ThreeDRoiobject; +import visualization.CovistoModelView; +import visualization.Draw3DLines; +import visualization.DummyTrackColorGenerator; +import visualization.SelectionModel; +import watershedGUI.CovistoWatershedPanel; +import zGUI.CovistoZselectPanel; + +public class InteractiveMethods { + + public NumberFormat nf; + public RandomAccessibleInterval originalimg; + public int ndims; + + public int Maxlabel; + public int thresholdsliderInit = 125; + + public ImagePlus imp; + public boolean showMSER = false; + public boolean showDOG = false; + public boolean showWatershed = false; + public HashMap Accountedframes; + public HashMap AccountedZ; + public boolean displayWatershedimg = true; + public boolean displayBinaryimg = false; + public boolean displayDistTransimg = false; + public RandomAccessibleInterval newimg; + public RandomAccessibleInterval CurrentView; + + + + public RoiManager roimanager; + public final int scrollbarSize = 1000; + public JProgressBar jpb; + public File inputfile; + public boolean autothreshwater = false; + public boolean snakeongoing = false; + public int Progressmin = 0; + public int Progressmax = 100; + public int max = Progressmax; + public MserTree newtree; + public Roi nearestRoiCurr; + public int rowchoice; + public static int standardSensitivity = 4; + public int sensitivity = standardSensitivity; + public Color colorChange = Color.RED; + + public MouseMotionListener ml; + public MouseListener mvl; + public int timeMin = 1; + + + public boolean onlySeg = true; + + public boolean TrackandSeg = false; + + public int tablesize; + + + public ArrayList Rois; + public ArrayList AfterRemovedRois; + public ArrayList CurrentPreRoiobject; + public ArrayList NearestNeighbourRois; + public ArrayList BiggerRois; + public JTable table; + public int row; + + public CostFunction UserchosenCostFunction; + public Color colorDrawMser = Color.green; + public Color colorDrawDog = Color.red; + public Color colorConfirm = Color.blue; + public Color colorSnake = Color.YELLOW; + public Color colorTrack = Color.GREEN; + public Overlay overlay; + public FinalInterval interval; + + public RandomAccessibleInterval bitimg; + + + public RandomAccessibleInterval Segbitimg; + public RandomAccessibleInterval Segafterremovebitimg; + + + public RandomAccessibleInterval bitimgFloat; + public RandomAccessibleInterval intimg; + public HashMap> Timetracks; + public ArrayList ZTPreRoiobject; + public ConcurrentHashMap> ZTRois; + public HashMap> threeDTRois; + public ConcurrentHashMap> AllEvents; + public ArrayList> peaks; + + public String uniqueID, ZID, TID; + public boolean apply3D = false; + public Model3D model = new Model3D(); + public SelectionModel selmode = new SelectionModel(model); + + public ImageStack prestack; + public boolean SegMode; + + public ColorProcessor cp = null; + public final String userfile; + public boolean AutoSnake = true; + public boolean advancedSnake = false; + + public static enum ValueChange { + + ALL, MSER, DOG, SNAKE, WATER, DIST, DISTWATER, GAUSS, THRESHOLD, SIGMA, FOURTHDIMmouse, THIRDDIMmouse, + THIRDDIM, MINDIVERSITY, DELTA, MINSIZE, MAXSIZE, MAXVAR, DARKTOBRIGHT, PREROI, NearestN, Kalman, ALPHA, BETA, ThreeDTrackDisplay, ThreeDTrackDisplayALL; + + } + + + + public InteractiveMethods() { + + nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMaximumFractionDigits(3); + nf.setGroupingUsed(false); + this.userfile = null; + } + + + + public InteractiveMethods(final RandomAccessibleInterval originalimg, File file, final boolean SegMode, + final boolean TrackandSeg, String userfile) { + + this.originalimg = originalimg; + this.inputfile = file; + this.SegMode = SegMode; + this.TrackandSeg = TrackandSeg; + nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMaximumFractionDigits(3); + nf.setGroupingUsed(false); + this.userfile = userfile; + this.ndims = originalimg.numDimensions(); + } + + public void run(String arg0) { + FloatType minval = new FloatType(0); + FloatType maxval = new FloatType(255); + Normalize.normalize(Views.iterable(originalimg), minval, maxval); + prestack = new ImageStack((int) originalimg.dimension(0), (int) originalimg.dimension(1), + java.awt.image.ColorModel.getRGBdefault()); + Accountedframes = new HashMap(); + AccountedZ = new HashMap(); + AllEvents = new ConcurrentHashMap>(); + AfterRemovedRois = new ArrayList(); + //universe = new Image3DUniverse((int) originalimg.dimension(0), (int) originalimg.dimension(1)); + jpb = new JProgressBar(); + overlay = new Overlay(); + interval = new FinalInterval(originalimg.dimension(0), originalimg.dimension(1)); + peaks = new ArrayList>(); + ZTRois = new ConcurrentHashMap>(); + threeDTRois = new HashMap>(); + CurrentPreRoiobject = new ArrayList(); + ZTPreRoiobject = new ArrayList(); + Timetracks = new HashMap>(); + + Segbitimg = new ArrayImgFactory().create(originalimg, + new BitType()); + + Segafterremovebitimg = new ArrayImgFactory().create(originalimg, + new BitType()); + + + if (ndims < 3) { + + CovistoZselectPanel.thirdDimensionSize = 0; + CovistoTimeselectPanel.fourthDimensionSize = 0; + } + + if (ndims == 3) { + + CovistoTimeselectPanel.fourthDimension = 0; + CovistoTimeselectPanel.fourthDimensionsliderInit = 0; + CovistoZselectPanel.thirdDimension = 1; + CovistoTimeselectPanel.fourthDimensionSize = 0; + CovistoZselectPanel.thirdDimensionSize = (int) originalimg.dimension(2); + + } + + if (ndims == 4) { + + CovistoTimeselectPanel.fourthDimension = 1; + CovistoZselectPanel.thirdDimension = 1; + CovistoZselectPanel.thirdDimensionSize = (int) originalimg.dimension(2); + CovistoTimeselectPanel.fourthDimensionSize = (int) originalimg.dimension(3); + + prestack = new ImageStack((int) originalimg.dimension(0), (int) originalimg.dimension(1), + java.awt.image.ColorModel.getRGBdefault()); + } + + CurrentView = utility.CovistoSlicer.getCurrentView(originalimg, CovistoTimeselectPanel.fourthDimension, + CovistoZselectPanel.thirdDimensionSize, CovistoZselectPanel.thirdDimension, + CovistoTimeselectPanel.fourthDimensionSize); + + imp = ImageJFunctions.show(CurrentView); + + imp.setTitle("Active image" + " " + "time point : " + CovistoTimeselectPanel.fourthDimension + " " + " Z: " + + CovistoZselectPanel.thirdDimension); + roimanager = RoiManager.getInstance(); + + if (roimanager == null) { + roimanager = new RoiManager(); + } + + updatePreview(ValueChange.ALL); + + Cardframe.repaint(); + Cardframe.validate(); + panelFirst.repaint(); + panelFirst.validate(); + + Card(); + + } + + public void updatePreview(final ValueChange change) { + + overlay = imp.getOverlay(); + int localthirddim = CovistoZselectPanel.thirdDimension, localfourthdim = CovistoTimeselectPanel.fourthDimension; + uniqueID = Integer.toString(CovistoZselectPanel.thirdDimension) + + Integer.toString(CovistoTimeselectPanel.fourthDimension); + ZID = Integer.toString(CovistoZselectPanel.thirdDimension); + TID = Integer.toString(CovistoTimeselectPanel.fourthDimension); + if (overlay == null) { + + overlay = new Overlay(); + imp.setOverlay(overlay); + } + + roimanager = RoiManager.getInstance(); + + if (roimanager == null) { + roimanager = new RoiManager(); + } + + + + if (change == ValueChange.SNAKE) { + + overlay.clear(); + Accountedframes.put(TID, CovistoTimeselectPanel.fourthDimension); + + AccountedZ.put(ZID, CovistoZselectPanel.thirdDimension); + + for (Map.Entry> entry : ZTRois.entrySet()) { + + ArrayList current = entry.getValue(); + for (PreRoiobject currentroi : current) { + + if (currentroi.fourthDimension == CovistoTimeselectPanel.fourthDimension + && currentroi.thirdDimension == CovistoZselectPanel.thirdDimension) { + + currentroi.rois.setStrokeColor(colorSnake); + overlay.add(currentroi.rois); + localthirddim = currentroi.thirdDimension; + localfourthdim = currentroi.fourthDimension; + } + + } + } + imp.setOverlay(overlay); + imp.updateAndDraw(); + CovistoZselectPanel.zText.setText("Current Z = " + localthirddim); + CovistoZselectPanel.zgenText.setText("Current Z / T = " + localthirddim); + CovistoZselectPanel.zslider.setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue(localthirddim, + CovistoZselectPanel.thirdDimensionsliderInit, CovistoZselectPanel.thirdDimensionSize, + scrollbarSize)); + CovistoZselectPanel.zslider.repaint(); + CovistoZselectPanel.zslider.validate(); + + CovistoTimeselectPanel.timeText.setText("Current T = " + localfourthdim); + CovistoTimeselectPanel.timeslider.setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue( + localfourthdim, CovistoTimeselectPanel.fourthDimensionsliderInit, + CovistoTimeselectPanel.fourthDimensionSize, scrollbarSize)); + CovistoTimeselectPanel.timeslider.repaint(); + CovistoTimeselectPanel.timeslider.validate(); + + } + + if (change == ValueChange.PREROI) { + + ZTPreRoiobject.clear(); + for (Roi currentroi : Rois) { + + final double[] geocenter = currentroi.getContourCentroid(); + final Pair Intensityandpixels = PreRoiobject.getIntensity(currentroi, CurrentView); + final double intensity = Intensityandpixels.getA(); + final double numberofpixels = Intensityandpixels.getB(); + final double averageintensity = intensity / numberofpixels; + PreRoiobject currentobject = new PreRoiobject(currentroi, + new double[] { geocenter[0], geocenter[1], CovistoZselectPanel.thirdDimension }, numberofpixels, + intensity, averageintensity, CovistoZselectPanel.thirdDimension, + CovistoTimeselectPanel.fourthDimension); + ZTPreRoiobject.add(currentobject); + } + Accountedframes.put(TID, CovistoTimeselectPanel.fourthDimension); + + AccountedZ.put(ZID, CovistoZselectPanel.thirdDimension); + ZTRois.put(uniqueID, ZTPreRoiobject); + + if (overlay != null) + overlay.clear(); + + for (Map.Entry> entry : ZTRois.entrySet()) { + + ArrayList current = entry.getValue(); + for (PreRoiobject currentroi : current) { + + if (currentroi.fourthDimension == CovistoTimeselectPanel.fourthDimension + && currentroi.thirdDimension == CovistoZselectPanel.thirdDimension) { + + currentroi.rois.setStrokeColor(colorConfirm); + overlay.add(currentroi.rois); + } + + } + } + imp.setOverlay(overlay); + imp.updateAndDraw(); + + } + + if (change == ValueChange.THIRDDIM) { + + if (imp == null) { + imp = ImageJFunctions.show(CurrentView); + + } + + else { + + final float[] pixels = (float[]) imp.getProcessor().getPixels(); + final Cursor c = Views.iterable(CurrentView).cursor(); + + for (int i = 0; i < pixels.length; ++i) + pixels[i] = c.next().get(); + + imp.updateAndDraw(); + + } + + imp.setTitle("Active image" + " " + "time point : " + CovistoTimeselectPanel.fourthDimension + " " + " Z: " + + CovistoZselectPanel.thirdDimension); + + newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + + } + + if (change == ValueChange.FOURTHDIMmouse || change == ValueChange.THIRDDIMmouse) { + + if(!apply3D) { + + if (imp == null) { + imp = ImageJFunctions.show(CurrentView); + + } + + else { + + final float[] pixels = (float[]) imp.getProcessor().getPixels(); + final Cursor c = Views.iterable(CurrentView).cursor(); + + for (int i = 0; i < pixels.length; ++i) + pixels[i] = c.next().get(); + + imp.updateAndDraw(); + + } + + imp.setTitle("Active image" + " " + "time point : " + CovistoTimeselectPanel.fourthDimension + " " + " Z: " + + CovistoZselectPanel.thirdDimension); + + newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + + if (showMSER ) { + + MSERSeg computeMSER = new MSERSeg(this, jpb); + computeMSER.execute(); + + } + + if (showDOG ) { + + DOGSeg computeDOG = new DOGSeg(this, jpb); + computeDOG.execute(); + } + } + CovistoZselectPanel.zText.setText("Current Z = " + localthirddim); + CovistoZselectPanel.zgenText.setText("Current Z / T = " + localthirddim); + CovistoZselectPanel.zslider.setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue(localthirddim, + CovistoZselectPanel.thirdDimensionsliderInit, CovistoZselectPanel.thirdDimensionSize, + scrollbarSize)); + CovistoZselectPanel.zslider.repaint(); + CovistoZselectPanel.zslider.validate(); + + CovistoTimeselectPanel.timeText.setText("Current T = " + localfourthdim); + CovistoTimeselectPanel.timeslider.setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue( + localfourthdim, CovistoTimeselectPanel.fourthDimensionsliderInit, + CovistoTimeselectPanel.fourthDimensionSize, CovistoTimeselectPanel.scrollbarSize)); + CovistoTimeselectPanel.timeslider.repaint(); + CovistoTimeselectPanel.timeslider.validate(); + } + + if (change == ValueChange.MSER) { + if (imp == null) { + imp = ImageJFunctions.show(CurrentView); + + } + + else { + + final float[] pixels = (float[]) imp.getProcessor().getPixels(); + final Cursor c = Views.iterable(CurrentView).cursor(); + + for (int i = 0; i < pixels.length; ++i) + pixels[i] = c.next().get(); + + imp.updateAndDraw(); + + } + + imp.setTitle("Active image" + " " + "time point : " + CovistoTimeselectPanel.fourthDimension + " " + " Z: " + + CovistoZselectPanel.thirdDimension); + + newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + + MSERSeg computeMSER = new MSERSeg(this, jpb); + computeMSER.execute(); + + } + + if (change == ValueChange.WATER) { + + if (overlay != null) + overlay.clear(); + + newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + bitimg = new ArrayImgFactory().create(newimg, new BitType()); + bitimgFloat = new ArrayImgFactory().create(newimg, new FloatType()); + GetLocalmaxminMT.ThresholdingMTBit(CurrentView, bitimg, CovistoWatershedPanel.thresholdWater); + if (displayBinaryimg && !apply3D) + ImageJFunctions.show(bitimg); + DistWatershed WaterafterDisttransform = new DistWatershed(this, CurrentView, bitimg, + jpb, false); + WaterafterDisttransform.execute(); + if (displayWatershedimg && !apply3D) + ImageJFunctions.show(WaterafterDisttransform.getResult()); + + if (displayDistTransimg && !apply3D) + ImageJFunctions.show(WaterafterDisttransform.getDistanceTransformedimg()); + + } + + if (change == ValueChange.DOG) { + + if (imp == null) { + imp = ImageJFunctions.show(CurrentView); + + } + + else { + + final float[] pixels = (float[]) imp.getProcessor().getPixels(); + final Cursor c = Views.iterable(CurrentView).cursor(); + + for (int i = 0; i < pixels.length; ++i) + pixels[i] = c.next().get(); + + imp.updateAndDraw(); + + } + + imp.setTitle("Active image" + " " + "time point : " + CovistoTimeselectPanel.fourthDimension + " " + " Z: " + + CovistoZselectPanel.thirdDimension); + + newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + + DOGSeg computeDOG = new DOGSeg(this, jpb); + computeDOG.execute(); + } + + } + + public JFrame Cardframe = new JFrame("Computer Vision Segmentation Tools (CoViSto)"); + public JPanel panelCont = new JPanel(); + public JPanel panelFirst = new JPanel(); + public JPanel panelSecond = new JPanel(); + public JPanel panelThird = new JPanel(); + public JPanel Timeselect = new JPanel(); + public JPanel Zselect = new JPanel(); + public JPanel MserPanel = new JPanel(); + public JPanel DogPanel = new JPanel(); + public JPanel WaterPanel = new JPanel(); + public JPanel SnakePanel = new JPanel(); + public JPanel RoiPanel = new JPanel(); + public JPanel DetectionPanel = new JPanel(); + public JPanel RemovalPanel = new JPanel(); + + public JPanel NearestNPanel = new JPanel(); + public JPanel KalmanPanel = new JPanel(); + + final String maxSearchstring = "Maximum search radius"; + + public JButton Roibutton = new JButton("Confirm current roi selection"); + + public CheckboxGroup detection = new CheckboxGroup(); + final Checkbox Watershed = new Checkbox("Do watershedding", detection, showWatershed); + final Checkbox DOG = new Checkbox("Do DoG detection", detection, showDOG); + final Checkbox MSER = new Checkbox("Do MSER detection", detection, showMSER); + + public final Insets insets = new Insets(10, 0, 0, 0); + public final GridBagLayout layout = new GridBagLayout(); + public final GridBagConstraints c = new GridBagConstraints(); + + public TextField inputField = new TextField(); + public TextField inputtrackField; + + public int SizeXbig = 400; + public int SizeXsmall = 200; + public int SizeX = 400; + public int SizeY = 200; + public int SizeYsmall = 200; + public int SizeYbig = 500; + public JScrollPane scrollPane; + public JPanel PanelSelectFile = new JPanel(); + public Border selectfile = new CompoundBorder(new TitledBorder("Select Track"), new EmptyBorder(c.insets)); + public JPanel controlnextthird = new JPanel(); + public JPanel controlprevthird = new JPanel(); + + public void Card() { + + CardLayout cl = new CardLayout(); + + c.insets = new Insets(5, 5, 5, 5); + panelCont.setLayout(cl); + + panelCont.add(panelFirst, "1"); + + panelCont.add(panelSecond, "2"); + + panelCont.add(panelThird, "3"); + + panelFirst.setLayout(layout); + panelSecond.setLayout(layout); + panelThird.setLayout(layout); + DetectionPanel.setLayout(layout); + RoiPanel.setLayout(layout); + + RemovalPanel.setLayout(layout); + inputField.setColumns(10); + + Border methodborder = new CompoundBorder(new TitledBorder("Choose a segmentation algorithm"), + new EmptyBorder(c.insets)); + + + + c.anchor = GridBagConstraints.BOTH; + c.ipadx = 35; + + c.gridwidth = 10; + c.gridheight = 10; + c.gridy = 1; + c.gridx = 0; + + // Put time slider + Timeselect = CovistoTimeselectPanel.TimeselectPanel(ndims); + + panelFirst.add(Timeselect, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + // Put z slider + + Zselect = CovistoZselectPanel.ZselectPanel(ndims); + + panelFirst.add(Zselect, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + + // Choice of segmentation algorithm panel + DetectionPanel.add(Watershed, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + DetectionPanel.add(DOG, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + DetectionPanel.add(MSER, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + + DetectionPanel.setBorder(methodborder); + panelFirst.add(DetectionPanel, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + + // Watershed panel + WaterPanel = CovistoWatershedPanel.WaterPanel(); + panelFirst.add(WaterPanel, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.RELATIVE, insets, 0, 0)); + + // Difference of Gaussian detection panel + DogPanel = CovistoDogPanel.DogPanel(); + panelFirst.add(DogPanel, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + + // Mser detection panel + MserPanel = CovistoMserPanel.MserPanel(); + panelFirst.add(MserPanel, new GridBagConstraints(3, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.RELATIVE, new Insets(10, 10, 0, 10), 0, 0)); + + final JButton Zsnakes = new JButton("Apply snakes in Z"); + final JButton Tsnakes = new JButton("Apply snakes in T"); + final JButton Allsnakes = new JButton("Apply snakes in Z & T"); + + final JButton SinglethreeD = new JButton("Track in Z for currrent T"); + + JPanel controlprev = new JPanel(); + JPanel controlnext = new JPanel(); + + controlprev.setLayout(layout); + controlnext.setLayout(layout); + controlnextthird.setLayout(layout); + controlprevthird.setLayout(layout); + controlprev.add(new JButton(new AbstractAction("\u22b2Prev") { + + @Override + public void actionPerformed(ActionEvent e) { + CardLayout cl = (CardLayout) panelCont.getLayout(); + cl.previous(panelCont); + } + })); + + controlnext.add(new JButton(new AbstractAction("Next\u22b3") { + + @Override + public void actionPerformed(ActionEvent e) { + CardLayout cl = (CardLayout) panelCont.getLayout(); + cl.next(panelCont); + } + })); + + controlnextthird.add(new JButton(new AbstractAction("Next\u22b3") { + + @Override + public void actionPerformed(ActionEvent e) { + CardLayout cl = (CardLayout) panelCont.getLayout(); + cl.next(panelCont); + } + })); + controlprevthird.add(new JButton(new AbstractAction("\u22b2Prev") { + + @Override + public void actionPerformed(ActionEvent e) { + CardLayout cl = (CardLayout) panelCont.getLayout(); + cl.previous(panelCont); + } + })); + + // Active contour refinement panel + SnakePanel = CovistoSnakePanel.SnakePanel(originalimg.numDimensions()); + panelSecond.add(SnakePanel, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + // Tracking panel + if (!SegMode) { + + NearestNPanel = CovistoNearestNPanel.NearestNPanel(); + + KalmanPanel = CovistoKalmanPanel.KalmanPanel(); + + panelThird.add(KalmanPanel, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + CovistoKalmanPanel.lostframe.addTextListener(new PRELostFrameListener(this)); + CovistoKalmanPanel.alphaS.addAdjustmentListener(new PREAlphaListener(this, CovistoKalmanPanel.alphaText, + CovistoKalmanPanel.alphastring, CovistoKalmanPanel.alphaMin, CovistoKalmanPanel.alphaMax, + CovistoKalmanPanel.scrollbarSize, CovistoKalmanPanel.alphaS)); + CovistoKalmanPanel.betaS.addAdjustmentListener(new PREBetaListener(this, CovistoKalmanPanel.betaText, + CovistoKalmanPanel.betastring, CovistoKalmanPanel.betaMin, CovistoKalmanPanel.betaMax, + CovistoKalmanPanel.scrollbarSize, CovistoKalmanPanel.betaS)); + CovistoNearestNPanel.maxSearchNearest.addAdjustmentListener(new PREMaxSearchListener(this, + CovistoNearestNPanel.maxSearchTextNearest, CovistoNearestNPanel.maxSearchstringNearest, + CovistoNearestNPanel.maxSearchradiusMinNearest, CovistoNearestNPanel.maxSearchradiusMaxNearest, + CovistoNearestNPanel.scrollbarSize, CovistoNearestNPanel.maxSearchNearest)); + CovistoKalmanPanel.maxSearchKalman.addAdjustmentListener(new PREMaxSearchTListener(this, + CovistoKalmanPanel.maxSearchTextKalman, CovistoKalmanPanel.maxSearchstringKalman, + CovistoKalmanPanel.maxSearchradiusMin, CovistoKalmanPanel.maxSearchradiusMax, + CovistoKalmanPanel.scrollbarSize, CovistoKalmanPanel.maxSearchSS)); + CovistoKalmanPanel.initialSearchS.addAdjustmentListener(new PREIniSearchListener(this, + CovistoKalmanPanel.iniSearchText, CovistoKalmanPanel.initialSearchstring, + CovistoKalmanPanel.initialSearchradiusMin, CovistoKalmanPanel.initialSearchradiusMax, + CovistoKalmanPanel.scrollbarSize, CovistoKalmanPanel.initialSearchS)); + } + + Watershed.addItemListener(new DoWatershedListener(this)); + DOG.addItemListener(new DoDOGListener(this)); + MSER.addItemListener(new DoMSERListener(this)); + CovistoWatershedPanel.autothreshold.addItemListener(new PREauto(this)); + CovistoWatershedPanel.dodist.addItemListener(new PREdist(this)); + CovistoWatershedPanel.tryWater.addActionListener(new PREApplyWaterCurrentListener(this)); + CovistoDogPanel.findminima.addItemListener(new FindMinimaListener(this)); + CovistoDogPanel.findmaxima.addItemListener(new FindMaximaListener(this)); + CovistoMserPanel.findminimaMser.addItemListener(new FindMinimaMserListener(this)); + CovistoMserPanel.findmaximaMser.addItemListener(new FindMaximaMserListener(this)); + + CovistoWatershedPanel.displayBinary.addItemListener(new PREShowBinary(this)); + CovistoWatershedPanel.displayWater.addItemListener(new PREShowWatershed(this)); + CovistoWatershedPanel.displayDist.addItemListener(new PREShowDist(this)); + CovistoDogPanel.AllDog.addActionListener(new PREApplyDog3DListener(this)); + + + + CovistoSnakePanel.advanced.addItemListener(new AdvancedSnakeListener(this)); + CovistoSnakePanel.Snakeiter.addTextListener(new IterationListener(this)); + CovistoSnakePanel.gradientthresh.addTextListener(new GradientListener(this)); + CovistoSnakePanel.maxdist.addTextListener(new MaxdistListener(this)); + + CovistoWatershedPanel.thresholdWaterslider.addAdjustmentListener( + new PreWaterListener(this, CovistoWatershedPanel.watertext, CovistoWatershedPanel.waterstring, + CovistoWatershedPanel.thresholdMinWater, CovistoWatershedPanel.thresholdMaxWater, scrollbarSize, + CovistoWatershedPanel.thresholdWaterslider)); + + + CovistoDogPanel.sigmaslider.addAdjustmentListener(new PreSigmaListener(this, CovistoDogPanel.sigmaText, + CovistoDogPanel.sigmastring, CovistoDogPanel.sigmaMin, CovistoDogPanel.sigmaMax, + CovistoDogPanel.scrollbarSize, CovistoDogPanel.sigmaslider)); + + CovistoMserPanel.deltaS.addAdjustmentListener(new PREDeltaListener(this, CovistoMserPanel.deltaText, + CovistoMserPanel.deltastring, CovistoMserPanel.deltaMin, CovistoMserPanel.deltaMax, + CovistoMserPanel.scrollbarSize, CovistoMserPanel.deltaS)); + + CovistoMserPanel.Unstability_ScoreS.addAdjustmentListener(new PREUnstability_ScoreListener(this, + CovistoMserPanel.Unstability_ScoreText, CovistoMserPanel.Unstability_Scorestring, + CovistoMserPanel.Unstability_ScoreMin, CovistoMserPanel.Unstability_ScoreMax, + CovistoMserPanel.scrollbarSize, CovistoMserPanel.Unstability_ScoreS)); + + CovistoMserPanel.minDiversityS.addAdjustmentListener(new PREMinDiversityListener(this, + CovistoMserPanel.minDivText, CovistoMserPanel.minDivstring, CovistoMserPanel.minDiversityMin, + CovistoMserPanel.minDiversityMax, CovistoMserPanel.scrollbarSize, CovistoMserPanel.minDiversityS)); + + CovistoMserPanel.minSizeS.addAdjustmentListener(new PREMinSizeListener(this, CovistoMserPanel.minSizeText, + CovistoMserPanel.minSizestring, CovistoMserPanel.minSizemin, CovistoMserPanel.minSizemax, + CovistoMserPanel.scrollbarSize, CovistoMserPanel.minSizeS)); + + CovistoMserPanel.maxSizeS.addAdjustmentListener(new PREMaxSizeListener(this, CovistoMserPanel.maxSizeText, + CovistoMserPanel.maxSizestring, CovistoMserPanel.minSizemax, CovistoMserPanel.maxSizeInit, + CovistoMserPanel.scrollbarSize, CovistoMserPanel.maxSizeS)); + + CovistoDogPanel.thresholdslider.addAdjustmentListener(new PreThresholdListener(this, + CovistoDogPanel.thresholdText, CovistoDogPanel.thresholdstring, CovistoDogPanel.thresholdMin, + CovistoDogPanel.thresholdMax, CovistoDogPanel.scrollbarSize, CovistoDogPanel.thresholdslider)); + + + CovistoDogPanel.inputFieldSpot.addTextListener(new PreDistThresholdListener(this, CovistoDogPanel.distthreshold)); + CovistoDogPanel.inputtimedist.addTextListener(new PreTimeDistThresholdListener(this, CovistoDogPanel.timethreshold)); + CovistoDogPanel.inputtimeskip.addTextListener(new PreTimeSkipListener(this, CovistoDogPanel.timeblock)); + + + + + CovistoTimeselectPanel.timeslider.addAdjustmentListener( + new PreTimeListener(this, CovistoTimeselectPanel.timeText, CovistoTimeselectPanel.timestring, + CovistoTimeselectPanel.fourthDimensionsliderInit, CovistoTimeselectPanel.fourthDimensionSize, + CovistoTimeselectPanel.scrollbarSize, CovistoTimeselectPanel.timeslider)); + + if (ndims > 3) + CovistoZselectPanel.zslider.addAdjustmentListener(new PreZListener(this, CovistoZselectPanel.zText, + CovistoZselectPanel.zstring, CovistoZselectPanel.thirdDimensionsliderInit, + CovistoZselectPanel.thirdDimensionSize, scrollbarSize, CovistoZselectPanel.zslider)); + else + CovistoZselectPanel.zslider.addAdjustmentListener(new PreZListener(this, CovistoZselectPanel.zgenText, + CovistoZselectPanel.zgenstring, CovistoZselectPanel.thirdDimensionsliderInit, + CovistoZselectPanel.thirdDimensionSize, scrollbarSize, CovistoZselectPanel.zslider)); + + CovistoZselectPanel.inputFieldZ.addTextListener(new PreZlocListener(this, false)); + CovistoTimeselectPanel.inputFieldT.addTextListener(new PreTlocListener(this, false)); + + panelSecond.add(controlprev, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.RELATIVE, new Insets(10, 10, 0, 10), 0, 0)); + if (!SegMode) { + panelSecond.add(controlnextthird, new GridBagConstraints(2, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.RELATIVE, new Insets(10, 10, 0, 10), 0, 0)); + } + panelSecond.setPreferredSize(SnakePanel.getPreferredSize()); + controlnextthird.setEnabled(false); + panelFirst.add(controlnext, new GridBagConstraints(3, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.RELATIVE, new Insets(10, 10, 0, 10), 0, 0)); + panelThird.add(PanelSelectFile, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + panelThird.add(controlprevthird, new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.ABOVE_BASELINE, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + Cardframe.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); + cl.show(panelCont, "1"); + + Cardframe.add(panelCont, "Center"); + Cardframe.add(jpb, "Last"); + panelFirst.setVisible(true); + Cardframe.pack(); + Cardframe.setVisible(true); + } + + public static void main(String[] args) { + + new ImageJ(); + JFrame frame = new JFrame(""); + PreprocessingFileChooser panel = new PreprocessingFileChooser(); + + frame.getContentPane().add(panel, "Center"); + frame.setSize(panel.getPreferredSize()); + } + +} diff --git a/src/main/java/interactivePreprocessing/IterationListener.java b/src/main/java/interactivePreprocessing/IterationListener.java new file mode 100644 index 0000000..ce742a2 --- /dev/null +++ b/src/main/java/interactivePreprocessing/IterationListener.java @@ -0,0 +1,31 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import snakeGUI.CovistoSnakePanel; + +public class IterationListener implements TextListener { + + + final InteractiveMethods parent; + + public IterationListener(final InteractiveMethods parent) { + + + this.parent = parent; + + } + + @Override + public void textValueChanged(TextEvent e) { + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if (s.length() > 0) + CovistoSnakePanel.snakeiterations = Integer.parseInt(s); + + } + +} diff --git a/src/main/java/interactivePreprocessing/MaxdistListener.java b/src/main/java/interactivePreprocessing/MaxdistListener.java new file mode 100644 index 0000000..9b116dd --- /dev/null +++ b/src/main/java/interactivePreprocessing/MaxdistListener.java @@ -0,0 +1,31 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import snakeGUI.CovistoSnakePanel; + +public class MaxdistListener implements TextListener { + + + final InteractiveMethods parent; + + public MaxdistListener(final InteractiveMethods parent) { + + + this.parent = parent; + + } + + @Override + public void textValueChanged(TextEvent e) { + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if (s.length() > 0) + CovistoSnakePanel.DistMax = Integer.parseInt(s); + + } + +} diff --git a/src/main/java/interactivePreprocessing/PREAlphaListener.java b/src/main/java/interactivePreprocessing/PREAlphaListener.java new file mode 100644 index 0000000..db5edf1 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREAlphaListener.java @@ -0,0 +1,52 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import kalmanGUI.CovistoKalmanPanel; + +public class PREAlphaListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREAlphaListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.ALPHA ) ); + + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoKalmanPanel.alpha = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoKalmanPanel.alpha , min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoKalmanPanel.alpha )); + + + + } + + + +} diff --git a/src/main/java/interactivePreprocessing/PREApplyDog3DListener.java b/src/main/java/interactivePreprocessing/PREApplyDog3DListener.java new file mode 100644 index 0000000..998c8ad --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREApplyDog3DListener.java @@ -0,0 +1,51 @@ +package interactivePreprocessing; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.SwingUtilities; + +import dog3D.DogAll; +import timeGUI.CovistoTimeselectPanel; +import zGUI.CovistoZselectPanel; + +public class PREApplyDog3DListener implements ActionListener { + + final InteractiveMethods parent; + + public PREApplyDog3DListener(final InteractiveMethods parent) { + + this.parent = parent; + + } + + + + @Override + public void actionPerformed(final ActionEvent arg0) { + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + + go(); + + } + + }); + + } + + public void go() { + + + + + + + DogAll dosnake = new DogAll(parent); + dosnake.execute(); + + } + +} diff --git a/src/main/java/interactivePreprocessing/PREApplyWaterCurrentListener.java b/src/main/java/interactivePreprocessing/PREApplyWaterCurrentListener.java new file mode 100644 index 0000000..3805207 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREApplyWaterCurrentListener.java @@ -0,0 +1,49 @@ +package interactivePreprocessing; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.SwingUtilities; + +import watershed3D.WatershedAll; +import watershedCurrent.WatershedCurrent; + +public class PREApplyWaterCurrentListener implements ActionListener { + + final InteractiveMethods parent; + + public PREApplyWaterCurrentListener(final InteractiveMethods parent) { + + + this.parent = parent; + } + + + + @Override + public void actionPerformed(ActionEvent e) { + + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + + goWater(); + + } + + }); + + } + + + public void goWater() { + + + WatershedCurrent dowater = new WatershedCurrent(parent); + dowater.execute(); + + } + +} + \ No newline at end of file diff --git a/src/main/java/interactivePreprocessing/PREBetaListener.java b/src/main/java/interactivePreprocessing/PREBetaListener.java new file mode 100644 index 0000000..dd0b5c7 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREBetaListener.java @@ -0,0 +1,51 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import kalmanGUI.CovistoKalmanPanel; + +public class PREBetaListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREBetaListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.BETA ) ); + + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoKalmanPanel.beta = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoKalmanPanel.beta, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoKalmanPanel.beta)); + + + } + + + +} diff --git a/src/main/java/interactivePreprocessing/PREDeltaListener.java b/src/main/java/interactivePreprocessing/PREDeltaListener.java new file mode 100644 index 0000000..4574274 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREDeltaListener.java @@ -0,0 +1,56 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class PREDeltaListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREDeltaListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.MSER ) ); + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoMserPanel.delta = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoMserPanel.delta, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoMserPanel.delta)); + + + + } + + + +} diff --git a/src/main/java/interactivePreprocessing/PREDoSegmodeListener.java b/src/main/java/interactivePreprocessing/PREDoSegmodeListener.java new file mode 100644 index 0000000..ef36af2 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREDoSegmodeListener.java @@ -0,0 +1,33 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class PREDoSegmodeListener implements ItemListener { + + public final PreprocessingFileChooser parent; + + public PREDoSegmodeListener(final PreprocessingFileChooser parent) { + + this.parent = parent; + + } + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + parent.onlySeg = false; + parent.TrackandSeg = true; + + } + else if (arg0.getStateChange() == ItemEvent.SELECTED) { + parent.onlySeg = true; + parent.TrackandSeg = false; + + } + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PREDoTrackmodeListener.java b/src/main/java/interactivePreprocessing/PREDoTrackmodeListener.java new file mode 100644 index 0000000..9d53df9 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREDoTrackmodeListener.java @@ -0,0 +1,31 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class PREDoTrackmodeListener implements ItemListener { + + public final PreprocessingFileChooser parent; + + public PREDoTrackmodeListener(final PreprocessingFileChooser parent) { + + this.parent = parent; + + } + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + parent.TrackandSeg = false; + parent.onlySeg = true; + } + else if (arg0.getStateChange() == ItemEvent.SELECTED) { + parent.TrackandSeg = true; + parent.onlySeg = false; + } + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PREIniSearchListener.java b/src/main/java/interactivePreprocessing/PREIniSearchListener.java new file mode 100644 index 0000000..c65beb9 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREIniSearchListener.java @@ -0,0 +1,53 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import kalmanGUI.CovistoKalmanPanel; +import zGUI.CovistoZselectPanel; + +public class PREIniSearchListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREIniSearchListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoKalmanPanel.initialSearchradius = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoKalmanPanel.initialSearchradius, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoKalmanPanel.initialSearchradius)); + + + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PRELostFrameListener.java b/src/main/java/interactivePreprocessing/PRELostFrameListener.java new file mode 100644 index 0000000..772a89c --- /dev/null +++ b/src/main/java/interactivePreprocessing/PRELostFrameListener.java @@ -0,0 +1,29 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import kalmanGUI.CovistoKalmanPanel; + +public class PRELostFrameListener implements TextListener { + + public InteractiveMethods parent; + + public PRELostFrameListener(final InteractiveMethods parent) { + + this.parent = parent; + } + + @Override + public void textValueChanged(TextEvent e) { + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if (s.length() > 0) + CovistoKalmanPanel.maxframegap = Integer.parseInt(s); + + + } + +} diff --git a/src/main/java/interactivePreprocessing/PREMaxSearchListener.java b/src/main/java/interactivePreprocessing/PREMaxSearchListener.java new file mode 100644 index 0000000..bb4376f --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREMaxSearchListener.java @@ -0,0 +1,58 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; +import java.text.DecimalFormat; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import kalmanGUI.CovistoKalmanPanel; +import mserGUI.CovistoMserPanel; +import nearestNeighbourGUI.CovistoNearestNPanel; + +public class PREMaxSearchListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREMaxSearchListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.NearestN ) ); + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoNearestNPanel.maxSearchradiusNearest = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoNearestNPanel.maxSearchradiusNearest , min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoNearestNPanel.maxSearchradiusNearest)); + + + + + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PREMaxSearchTListener.java b/src/main/java/interactivePreprocessing/PREMaxSearchTListener.java new file mode 100644 index 0000000..0caa226 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREMaxSearchTListener.java @@ -0,0 +1,53 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import kalmanGUI.CovistoKalmanPanel; +import nearestNeighbourGUI.CovistoNearestNPanel; + +public class PREMaxSearchTListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREMaxSearchTListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoKalmanPanel.maxSearchradius = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoKalmanPanel.maxSearchradius, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoKalmanPanel.maxSearchradius)); + + + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PREMaxSizeListener.java b/src/main/java/interactivePreprocessing/PREMaxSizeListener.java new file mode 100644 index 0000000..42d4e3a --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREMaxSizeListener.java @@ -0,0 +1,56 @@ +package interactivePreprocessing; + + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class PREMaxSizeListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREMaxSizeListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.MSER ) ); + + } + + + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoMserPanel.maxSize = (long) utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoMserPanel.maxSize, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoMserPanel.maxSize)); + + + } + + +} + + + diff --git a/src/main/java/interactivePreprocessing/PREMinDiversityListener.java b/src/main/java/interactivePreprocessing/PREMinDiversityListener.java new file mode 100644 index 0000000..72eda03 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREMinDiversityListener.java @@ -0,0 +1,55 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class PREMinDiversityListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREMinDiversityListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.MSER ) ); + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoMserPanel.minDiversity = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoMserPanel.minDiversity, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoMserPanel.minDiversity)); + + + + + } + +} diff --git a/src/main/java/interactivePreprocessing/PREMinSizeListener.java b/src/main/java/interactivePreprocessing/PREMinSizeListener.java new file mode 100644 index 0000000..fc5c031 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREMinSizeListener.java @@ -0,0 +1,52 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class PREMinSizeListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREMinSizeListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.MSER ) ); + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoMserPanel.minSize = (long) utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoMserPanel.minSize, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoMserPanel.minSize)); + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PRERoiListener.java b/src/main/java/interactivePreprocessing/PRERoiListener.java new file mode 100644 index 0000000..6b7df07 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PRERoiListener.java @@ -0,0 +1,28 @@ +package interactivePreprocessing; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import interactivePreprocessing.InteractiveMethods.ValueChange; + + +public class PRERoiListener implements ActionListener { + + final InteractiveMethods parent; + + public PRERoiListener(final InteractiveMethods parent) { + + this.parent = parent; + } + + + @Override + public void actionPerformed(ActionEvent e) { + + parent.updatePreview(ValueChange.PREROI); + + } + + + +} diff --git a/src/main/java/interactivePreprocessing/PREShowBinary.java b/src/main/java/interactivePreprocessing/PREShowBinary.java new file mode 100644 index 0000000..ab3efd1 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREShowBinary.java @@ -0,0 +1,27 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class PREShowBinary implements ItemListener { + + + final InteractiveMethods parent; + + public PREShowBinary (final InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) + parent.displayBinaryimg = false; + else if (arg0.getStateChange() == ItemEvent.SELECTED) + parent.displayBinaryimg = true; + + } +} diff --git a/src/main/java/interactivePreprocessing/PREShowDist.java b/src/main/java/interactivePreprocessing/PREShowDist.java new file mode 100644 index 0000000..bc2e630 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREShowDist.java @@ -0,0 +1,28 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class PREShowDist implements ItemListener { + + + final InteractiveMethods parent; + + public PREShowDist (final InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) + parent.displayDistTransimg = false; + else if (arg0.getStateChange() == ItemEvent.SELECTED) + parent.displayDistTransimg = true; + + } + +} diff --git a/src/main/java/interactivePreprocessing/PREShowWatershed.java b/src/main/java/interactivePreprocessing/PREShowWatershed.java new file mode 100644 index 0000000..113890a --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREShowWatershed.java @@ -0,0 +1,30 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +public class PREShowWatershed implements ItemListener { + + + final InteractiveMethods parent; + + public PREShowWatershed (final InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) + parent.displayWatershedimg = false; + else if (arg0.getStateChange() == ItemEvent.SELECTED) + parent.displayWatershedimg = true; + + } + + + +} diff --git a/src/main/java/interactivePreprocessing/PREUnstability_ScoreListener.java b/src/main/java/interactivePreprocessing/PREUnstability_ScoreListener.java new file mode 100644 index 0000000..5ad480d --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREUnstability_ScoreListener.java @@ -0,0 +1,52 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + +public class PREUnstability_ScoreListener implements AdjustmentListener { + + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + + public PREUnstability_ScoreListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + + this.parent = parent; + this.label = label; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + this.scrollbar = scrollbar; + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.MSER ) ); + + } + + + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoMserPanel.Unstability_Score = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoMserPanel.Unstability_Score, min, max, scrollbarSize)); + + label.setText(string + " = " + CovistoMserPanel.Unstability_Score); + + + } + +} diff --git a/src/main/java/interactivePreprocessing/PREauto.java b/src/main/java/interactivePreprocessing/PREauto.java new file mode 100644 index 0000000..93c7337 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREauto.java @@ -0,0 +1,40 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import preProcessing.GlobalThresholding; +import watershedGUI.CovistoWatershedPanel; + +public class PREauto implements ItemListener { + +final InteractiveMethods parent; + + public PREauto (final InteractiveMethods parent) { + + this.parent = parent; + + } + + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) + parent.autothreshwater = false; + else if (arg0.getStateChange() == ItemEvent.SELECTED) { + parent.autothreshwater = true; + CovistoWatershedPanel.thresholdWater = (float) ( GlobalThresholding.AutomaticThresholding(parent.CurrentView)); + CovistoWatershedPanel.thresholdWaterslider.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoWatershedPanel.thresholdWater, CovistoWatershedPanel.thresholdMinWater, CovistoWatershedPanel.thresholdMaxWater, CovistoWatershedPanel.scrollbarSize)); + CovistoWatershedPanel.watertext.setText(CovistoWatershedPanel.waterstring + " = " + CovistoWatershedPanel.thresholdWater ); + CovistoWatershedPanel.thresholdWaterslider.validate(); + CovistoWatershedPanel.thresholdWaterslider.repaint(); + + + } + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PREdist.java b/src/main/java/interactivePreprocessing/PREdist.java new file mode 100644 index 0000000..99a355e --- /dev/null +++ b/src/main/java/interactivePreprocessing/PREdist.java @@ -0,0 +1,35 @@ +package interactivePreprocessing; + +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; + +import watershedGUI.CovistoWatershedPanel; + +public class PREdist implements ItemListener { + + final InteractiveMethods parent; + + public PREdist(final InteractiveMethods parent) { + + this.parent = parent; + + } + + @Override + public void itemStateChanged(final ItemEvent arg0) { + + if (arg0.getStateChange() == ItemEvent.DESELECTED) { + CovistoWatershedPanel.disttransform = false; + CovistoWatershedPanel.displayDist.setEnabled(false); + parent.displayDistTransimg = false; + + } + else if (arg0.getStateChange() == ItemEvent.SELECTED) { + CovistoWatershedPanel.disttransform = true; + CovistoWatershedPanel.displayDist.setEnabled(true); + parent.displayDistTransimg = true; + } + + } + +} diff --git a/src/main/java/interactivePreprocessing/PreCurrentMovieListener.java b/src/main/java/interactivePreprocessing/PreCurrentMovieListener.java new file mode 100644 index 0000000..d4c3651 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreCurrentMovieListener.java @@ -0,0 +1,28 @@ +package interactivePreprocessing; +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import ij.IJ; + + +public class PreCurrentMovieListener implements ActionListener { + + final PreprocessingFileChooser parent; + + public PreCurrentMovieListener(PreprocessingFileChooser parent) { + + this.parent = parent; + + } + + @Override + public void actionPerformed(final ActionEvent arg0) { + + parent.impA = IJ.getImage(); + + if(parent.impA!=null) + parent.DoneCurr(parent.Cardframe); + } + +} diff --git a/src/main/java/interactivePreprocessing/PreDistThresholdListener.java b/src/main/java/interactivePreprocessing/PreDistThresholdListener.java new file mode 100644 index 0000000..6a1cdcd --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreDistThresholdListener.java @@ -0,0 +1,34 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import dogGUI.CovistoDogPanel; + +public class PreDistThresholdListener implements TextListener { + + final InteractiveMethods parent; + + final double distthreshold; + + public PreDistThresholdListener(final InteractiveMethods parent, final double distthreshold) { + + this.parent = parent; + + this.distthreshold = distthreshold; + + } + + @Override + public void textValueChanged(TextEvent e) { + + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if(s.length() > 0) + CovistoDogPanel.distthreshold = Float.parseFloat(s); + + } + +} diff --git a/src/main/java/interactivePreprocessing/PreNonStandardMouseListener.java b/src/main/java/interactivePreprocessing/PreNonStandardMouseListener.java new file mode 100644 index 0000000..3a2c746 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreNonStandardMouseListener.java @@ -0,0 +1,35 @@ +package interactivePreprocessing; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import interactivePreprocessing.InteractiveMethods.ValueChange; + + + +public class PreNonStandardMouseListener implements MouseMotionListener +{ + final InteractiveMethods parent; + final ValueChange change; + + public PreNonStandardMouseListener( final InteractiveMethods parent, final ValueChange change ) + { + this.parent = parent; + this.change = change; + } + + @Override + public void mouseDragged(MouseEvent arg0) { + + + + parent.updatePreview(change); + + } + + @Override + public void mouseMoved(MouseEvent arg0) { + + } + +} diff --git a/src/main/java/interactivePreprocessing/PreSigmaListener.java b/src/main/java/interactivePreprocessing/PreSigmaListener.java new file mode 100644 index 0000000..ba1502b --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreSigmaListener.java @@ -0,0 +1,49 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; + + +public class PreSigmaListener implements AdjustmentListener { + final Label label; + final InteractiveMethods parent; + final String string; + final float min, max; + final int scrollbarSize; + final JScrollBar sigmaScrollbar1; + + public PreSigmaListener(final InteractiveMethods parent, final Label label,final String string, final float min, final float max, final int scrollbarSize, + final JScrollBar sigmaScrollbar1) { + this.label = label; + this.min = min; + this.max = max; + this.string = string; + this.scrollbarSize = scrollbarSize; + this.parent = parent; + this.sigmaScrollbar1 = sigmaScrollbar1; + sigmaScrollbar1.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.DOG ) ); + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoDogPanel.sigma = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + + sigmaScrollbar1.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoDogPanel.sigma, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoDogPanel.sigma)); + + + } +} diff --git a/src/main/java/interactivePreprocessing/PreThresholdListener.java b/src/main/java/interactivePreprocessing/PreThresholdListener.java new file mode 100644 index 0000000..1f6a1d1 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreThresholdListener.java @@ -0,0 +1,55 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import dogGUI.CovistoDogPanel; +import interactivePreprocessing.InteractiveMethods.ValueChange; + + + +public class PreThresholdListener implements AdjustmentListener { + final Label label; + final InteractiveMethods parent; + final String string; + final float min, max; + final int scrollbarSize; + final JScrollBar scrollbar; + + public PreThresholdListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, final int scrollbarSize, final JScrollBar scrollbar) { + this.parent = parent; + this.label = label; + this.string = string; + + this.min = min; + this.max = max; + this.scrollbar = scrollbar; + this.scrollbarSize = scrollbarSize; + scrollbar.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.DOG ) ); + scrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + scrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + + } + + + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + + CovistoDogPanel.threshold = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, + scrollbarSize); + scrollbar.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoDogPanel.threshold, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoDogPanel.threshold)); + + + + + } +} + + diff --git a/src/main/java/interactivePreprocessing/PreTimeDistThresholdListener.java b/src/main/java/interactivePreprocessing/PreTimeDistThresholdListener.java new file mode 100644 index 0000000..98e198f --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreTimeDistThresholdListener.java @@ -0,0 +1,35 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import dogGUI.CovistoDogPanel; + +public class PreTimeDistThresholdListener implements TextListener { + + final InteractiveMethods parent; + + final double timethreshold; + + public PreTimeDistThresholdListener(final InteractiveMethods parent, final double timethreshold) { + + this.parent = parent; + + this.timethreshold = timethreshold; + + } + + @Override + public void textValueChanged(TextEvent e) { + + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if(s.length() > 0) + CovistoDogPanel.timethreshold = Float.parseFloat(s); + + } + +} + diff --git a/src/main/java/interactivePreprocessing/PreTimeListener.java b/src/main/java/interactivePreprocessing/PreTimeListener.java new file mode 100644 index 0000000..8f6198b --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreTimeListener.java @@ -0,0 +1,67 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import dogGUI.CovistoDogPanel; +import ij.IJ; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.logic.BitType; +import timeGUI.CovistoTimeselectPanel; + + + +public class PreTimeListener implements AdjustmentListener { + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar deltaScrollbar; + + public PreTimeListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, + final int scrollbarSize, final JScrollBar deltaScrollbar) { + this.label = label; + this.parent = parent; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + + this.deltaScrollbar = deltaScrollbar; + deltaScrollbar.addMouseListener(new CovistoStandardMouseListener(parent, ValueChange.FOURTHDIMmouse)); + + deltaScrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + deltaScrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + + + CovistoTimeselectPanel.fourthDimension = (int) Math.round(utility.CovistoSlicer.computeValueFromScrollbarPosition(e.getValue(), min, max, scrollbarSize)); + + + deltaScrollbar + .setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue(CovistoTimeselectPanel.fourthDimension, min, max, scrollbarSize)); + + label.setText(string + " = " + CovistoTimeselectPanel.fourthDimension); + + + parent.panelFirst.validate(); + parent.panelFirst.repaint(); + ShowView show = new ShowView(parent); + show.shownewT(); + + } + + + +} \ No newline at end of file diff --git a/src/main/java/interactivePreprocessing/PreTimeSkipListener.java b/src/main/java/interactivePreprocessing/PreTimeSkipListener.java new file mode 100644 index 0000000..c74ff6f --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreTimeSkipListener.java @@ -0,0 +1,36 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import dogGUI.CovistoDogPanel; + +public class PreTimeSkipListener implements TextListener { + + final InteractiveMethods parent; + + final int timeblock; + + public PreTimeSkipListener(final InteractiveMethods parent, final int timeblock) { + + this.parent = parent; + + this.timeblock = timeblock; + + } + + @Override + public void textValueChanged(TextEvent e) { + + final TextComponent tc = (TextComponent)e.getSource(); + String s = tc.getText(); + + if(s.length() > 0) + CovistoDogPanel.timeblock = Integer.parseInt(s); + + } + +} + + diff --git a/src/main/java/interactivePreprocessing/PreTlocListener.java b/src/main/java/interactivePreprocessing/PreTlocListener.java new file mode 100644 index 0000000..b60467c --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreTlocListener.java @@ -0,0 +1,85 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import ij.IJ; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.logic.BitType; +import timeGUI.CovistoTimeselectPanel; + + +public class PreTlocListener implements TextListener { + + + final InteractiveMethods parent; + + boolean pressed; + public PreTlocListener(final InteractiveMethods parent, final boolean pressed) { + + this.parent = parent; + this.pressed = pressed; + + } + + + + @Override + public void textValueChanged(TextEvent e) { + final TextComponent tc = (TextComponent)e.getSource(); + + tc.addKeyListener(new KeyListener(){ + @Override + public void keyTyped(KeyEvent arg0) { + + } + + @Override + public void keyReleased(KeyEvent arg0) { + + if (arg0.getKeyChar() == KeyEvent.VK_ENTER ) { + + + pressed = false; + + } + + } + + @Override + public void keyPressed(KeyEvent arg0) { + String s = tc.getText(); + if (arg0.getKeyChar() == KeyEvent.VK_ENTER&& !pressed) { + pressed = true; + if (CovistoTimeselectPanel.fourthDimension > CovistoTimeselectPanel.fourthDimensionSize) { + IJ.log("Max frame number exceeded, moving to last frame instead"); + CovistoTimeselectPanel.fourthDimension = CovistoTimeselectPanel.fourthDimensionSize; + } else + CovistoTimeselectPanel.fourthDimension = Integer.parseInt(s); + ShowView show = new ShowView(parent); + show.shownewT(); + CovistoTimeselectPanel.timeText.setText("Current T = " + CovistoTimeselectPanel.fourthDimension); + + parent.updatePreview(ValueChange.FOURTHDIMmouse); + + + } + CovistoTimeselectPanel.timeslider.setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue(CovistoTimeselectPanel.fourthDimension, CovistoTimeselectPanel.fourthDimensionsliderInit, + CovistoTimeselectPanel.fourthDimensionSize, CovistoTimeselectPanel.scrollbarSize)); + CovistoTimeselectPanel.timeslider.repaint(); + CovistoTimeselectPanel.timeslider.validate(); + + } + }); + + + + +} + +} diff --git a/src/main/java/interactivePreprocessing/PreUploadMovieListener.java b/src/main/java/interactivePreprocessing/PreUploadMovieListener.java new file mode 100644 index 0000000..95d484e --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreUploadMovieListener.java @@ -0,0 +1,50 @@ +package interactivePreprocessing; + +import java.awt.Frame; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JFileChooser; + +import ij.io.Opener; + +public class PreUploadMovieListener implements ActionListener { + + + final PreprocessingFileChooser parent; + + public PreUploadMovieListener(PreprocessingFileChooser parent) { + + this.parent = parent; + + } + + @Override + public void actionPerformed(final ActionEvent arg0) { + + int result; + + parent.chooserA = new JFileChooser(); + parent.chooserA.setCurrentDirectory(new java.io.File(".")); + parent.chooserA.setDialogTitle(parent.choosertitleA); + parent.chooserA.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES); + // + + // + if (parent.chooserA.showOpenDialog(parent) == JFileChooser.APPROVE_OPTION) { + System.out.println("getCurrentDirectory(): " + parent.chooserA.getCurrentDirectory()); + System.out.println("getSelectedFile() : " + parent.chooserA.getSelectedFile()); + parent.impA = new Opener().openImage(parent.chooserA.getSelectedFile().getPath()); + + parent.Done(parent.Cardframe); + } else { + System.out.println("No Selection "); + } + + + + + } + + +} diff --git a/src/main/java/interactivePreprocessing/PreWaterListener.java b/src/main/java/interactivePreprocessing/PreWaterListener.java new file mode 100644 index 0000000..5f143db --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreWaterListener.java @@ -0,0 +1,48 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; +import watershedGUI.CovistoWatershedPanel; + + +public class PreWaterListener implements AdjustmentListener { + final Label label; + InteractiveMethods parent; + + final String string; + final float min, max; + final int scrollbarSize; + final JScrollBar sigmaScrollbar1; + + public PreWaterListener(final InteractiveMethods parent, final Label label,final String string, final float min, final float max, final int scrollbarSize, + final JScrollBar sigmaScrollbar1) { + this.label = label; + this.min = min; + this.max = max; + this.string = string; + this.scrollbarSize = scrollbarSize; + this.parent = parent; + this.sigmaScrollbar1 = sigmaScrollbar1; + sigmaScrollbar1.addMouseListener( new CovistoStandardMouseListener( parent, ValueChange.WATER ) ); + } + + @Override + public void adjustmentValueChanged(final AdjustmentEvent event) { + CovistoWatershedPanel.thresholdWater = utility.ETrackScrollbarUtils.computeValueFromScrollbarPosition(event.getValue(), min, max, scrollbarSize); + + + + sigmaScrollbar1.setValue(utility.ETrackScrollbarUtils.computeScrollbarPositionFromValue(CovistoWatershedPanel.thresholdWater, min, max, scrollbarSize)); + + label.setText(string + " = " + parent.nf.format(CovistoWatershedPanel.thresholdWater)); + + + } +} diff --git a/src/main/java/interactivePreprocessing/PreZListener.java b/src/main/java/interactivePreprocessing/PreZListener.java new file mode 100644 index 0000000..0234fd2 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreZListener.java @@ -0,0 +1,72 @@ +package interactivePreprocessing; + +import java.awt.Label; +import java.awt.event.AdjustmentEvent; +import java.awt.event.AdjustmentListener; + +import javax.swing.JScrollBar; + +import ij.IJ; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.logic.BitType; +import watershedGUI.CovistoWatershedPanel; +import zGUI.CovistoZselectPanel; + + +public class PreZListener implements AdjustmentListener { + final Label label; + final String string; + final InteractiveMethods parent; + final float min, max; + final int scrollbarSize; + final JScrollBar deltaScrollbar; + + public PreZListener(final InteractiveMethods parent, final Label label, final String string, final float min, final float max, + final int scrollbarSize, final JScrollBar deltaScrollbar) { + this.label = label; + this.parent = parent; + this.string = string; + this.min = min; + this.max = max; + this.scrollbarSize = scrollbarSize; + + this.deltaScrollbar = deltaScrollbar; + deltaScrollbar.addMouseListener(new CovistoStandardMouseListener(parent, ValueChange.THIRDDIMmouse)); + + + deltaScrollbar.setBlockIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + deltaScrollbar.setUnitIncrement(utility.CovistoSlicer.computeScrollbarPositionFromValue(2, min, max, scrollbarSize)); + } + + + + @Override + public void adjustmentValueChanged(AdjustmentEvent e) { + + CovistoZselectPanel.thirdDimension = (int) Math.round(utility.CovistoSlicer.computeValueFromScrollbarPosition(e.getValue(), min, max, scrollbarSize)); + + deltaScrollbar + .setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue(CovistoZselectPanel.thirdDimension, min, max, scrollbarSize)); + + + + label.setText(string + " = " + CovistoZselectPanel.thirdDimension); + + + + + CovistoZselectPanel.inputFieldZ.setText(Integer.toString((int)CovistoZselectPanel.thirdDimension)); + parent.panelFirst.validate(); + parent.panelFirst.repaint(); + + + ShowView show = new ShowView(parent); + show.shownewZ(); + + } + + + +} \ No newline at end of file diff --git a/src/main/java/interactivePreprocessing/PreZlocListener.java b/src/main/java/interactivePreprocessing/PreZlocListener.java new file mode 100644 index 0000000..d0fcd51 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreZlocListener.java @@ -0,0 +1,85 @@ +package interactivePreprocessing; + +import java.awt.TextComponent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.TextEvent; +import java.awt.event.TextListener; + +import ij.IJ; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.logic.BitType; +import zGUI.CovistoZselectPanel; + + +public class PreZlocListener implements TextListener { + + final InteractiveMethods parent; + + boolean pressed; + public PreZlocListener(final InteractiveMethods parent, boolean pressed) { + + this.parent = parent; + + this.pressed = pressed; + + } + + + @Override + public void textValueChanged(TextEvent e) { + final TextComponent tc = (TextComponent) e.getSource(); + + + tc.addKeyListener(new KeyListener() { + @Override + public void keyTyped(KeyEvent arg0) { + + } + + @Override + public void keyReleased(KeyEvent arg0) { + + if (arg0.getKeyChar() == KeyEvent.VK_ENTER ) { + + + pressed = false; + + } + + + } + + @Override + public void keyPressed(KeyEvent arg0) { + String s = tc.getText(); + if (arg0.getKeyChar() == KeyEvent.VK_ENTER && !pressed) { + pressed = true; + if (CovistoZselectPanel.thirdDimension > CovistoZselectPanel.thirdDimensionSize) { + IJ.log("Max frame number exceeded, moving to last frame instead"); + CovistoZselectPanel.thirdDimension = CovistoZselectPanel.thirdDimensionSize; + } else + CovistoZselectPanel.thirdDimension = Integer.parseInt(s); + ShowView show = new ShowView(parent); + show.shownewZ(); + + CovistoZselectPanel.zText.setText("Current Z = " + CovistoZselectPanel.thirdDimension); + CovistoZselectPanel.zgenText.setText("Current Z / T = " + CovistoZselectPanel.thirdDimension); + parent.updatePreview(ValueChange.THIRDDIMmouse); + + + } + CovistoZselectPanel.zslider.setValue(utility.CovistoSlicer.computeScrollbarPositionFromValue(CovistoZselectPanel.thirdDimension, CovistoZselectPanel.thirdDimensionsliderInit, + CovistoZselectPanel.thirdDimensionSize, CovistoZselectPanel.scrollbarSize)); + CovistoZselectPanel.zslider.repaint(); + CovistoZselectPanel.zslider.validate(); + + } + }); + + + + } +} \ No newline at end of file diff --git a/src/main/java/interactivePreprocessing/PreprocessingFileChooser.java b/src/main/java/interactivePreprocessing/PreprocessingFileChooser.java new file mode 100644 index 0000000..31f82f3 --- /dev/null +++ b/src/main/java/interactivePreprocessing/PreprocessingFileChooser.java @@ -0,0 +1,190 @@ +package interactivePreprocessing; + +import java.awt.BorderLayout; +import java.awt.CardLayout; +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Dimension; +import java.awt.Frame; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +import ij.ImagePlus; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.numeric.real.FloatType; + + +public class PreprocessingFileChooser extends JPanel { + + + /** + * + */ + private static final long serialVersionUID = 1L; + public boolean wasDone = false; + public boolean isFinished = false; + public JButton TrackMeasure; + public JButton Done; + public JFileChooser chooserA; + public String choosertitleA; + public ImagePlus impA; + public JFileChooser chooserB; + public String choosertitleB; + public boolean onlySeg = true; + + public boolean TrackandSeg = false; + public JFrame Cardframe = new JFrame("Welcome to Computer Vision Segmentation Tools (Covisto)"); + public JPanel panelCont = new JPanel(); + public JPanel panelFirst = new JPanel(); + + public Label ImageType = new Label("Image format supported : XYZT/XYZ/XYT"); + public JPanel Panelfile = new JPanel(); + public static final Insets insets = new Insets(10, 0, 0, 0); + public final GridBagLayout layout = new GridBagLayout(); + public final GridBagConstraints c = new GridBagConstraints(); + public Border selectfile = new CompoundBorder(new TitledBorder("Select file"), new EmptyBorder(c.insets)); + + public CheckboxGroup mode = new CheckboxGroup(); + + final Checkbox SegMode = new Checkbox("Load Segmentation Tools only", mode, onlySeg); + final Checkbox TrackMode = new Checkbox("Load Segmentation and Tracking Tools", mode, TrackandSeg); + public JPanel ModePanel = new JPanel(); + + public PreprocessingFileChooser () { + + + Border methodborder = new CompoundBorder(new TitledBorder("Choose run mode"), + new EmptyBorder(c.insets)); + + panelFirst.setLayout(layout); + Panelfile.setLayout(layout); + ModePanel.setLayout(layout); + CardLayout cl = new CardLayout(); + + panelCont.setLayout(cl); + panelCont.add(panelFirst, "1"); + JButton Measureserial = new JButton("Load movie"); + + JButton Current = new JButton("Use Current movie"); + + + ModePanel.add(SegMode, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, insets, 0, 0)); + // ModePanel.add(TrackMode, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, insets, 0, 0)); + panelFirst.add(ModePanel, new GridBagConstraints(0, 0, 3, 1, 0.0D, 0.0D, 17, + -1, new Insets(10, 10, 0, 10), 0, 0)); + ModePanel.setBorder(methodborder); + + Panelfile.add( ImageType, new GridBagConstraints(0, 0, 1, 1, 0.0D, 0.0D, 17, + 2, insets, 0, 1)); + + + Panelfile.add(Measureserial, new GridBagConstraints(0, 1, 1, 1, 0.0D, 0.0D, 17, + 2, insets, 0, 1)); + + Panelfile.add(Current, new GridBagConstraints(1, 1, 1, 1, 0.0D, 0.0D, 17, + 2, insets, 0, 0)); + + Panelfile.setBorder(selectfile); + + panelFirst.add(Panelfile, new GridBagConstraints(0, 3, 3, 1, 0.0D, 0.0D, 17, + -1, new Insets(10, 10, 0, 10), 0, 0)); + + + + + + Measureserial.addActionListener(new PreUploadMovieListener(this)); + Current.addActionListener(new PreCurrentMovieListener(this)); + + SegMode.addItemListener(new PREDoSegmodeListener(this)); + TrackMode.addItemListener(new PREDoTrackmodeListener(this)); + + panelFirst.setVisible(true); + Cardframe.addWindowListener(new FrameListener(Cardframe)); + Cardframe.add(panelCont, BorderLayout.CENTER); + Cardframe.pack(); + Cardframe.setVisible(true); + + } + + protected class FrameListener extends WindowAdapter { + final Frame parent; + + public FrameListener(Frame parent) { + super(); + this.parent = parent; + } + + @Override + public void windowClosing(WindowEvent e) { + close(parent); + } + } + + + + public void Done(Frame parent){ + + // Tracking and Measurement is done with imageA + + RandomAccessibleInterval image = ImageJFunctions.convertFloat(impA); + + new InteractiveMethods(image, chooserA.getSelectedFile().getParentFile(), onlySeg, TrackandSeg, impA.getOriginalFileInfo().fileName).run(null); + close(parent); + + + } + + + public void DoneCurr(Frame parent){ + + // Tracking and Measurement is done with imageA + + + RandomAccessibleInterval image = ImageJFunctions.convertFloat(impA); + + + new InteractiveMethods(image,new File(impA.getOriginalFileInfo().directory), onlySeg, TrackandSeg, impA.getOriginalFileInfo().fileName).run(null); + close(parent); + if(impA!=null) + impA.close(); + + } + + + + + + + protected final void close(final Frame parent) { + if (parent != null) + parent.dispose(); + + isFinished = true; + } + + public Dimension getPreferredSize() { + return new Dimension(500, 300); + } + + + + +} diff --git a/src/main/java/interactivePreprocessing/ShowView.java b/src/main/java/interactivePreprocessing/ShowView.java new file mode 100644 index 0000000..9626433 --- /dev/null +++ b/src/main/java/interactivePreprocessing/ShowView.java @@ -0,0 +1,66 @@ +package interactivePreprocessing; + +import ij.IJ; +import timeGUI.CovistoTimeselectPanel; +import zGUI.CovistoZselectPanel; + +public class ShowView { + + + final InteractiveMethods parent; + + + public ShowView(final InteractiveMethods parent) { + + this.parent = parent; + + } + + + public void shownewZ() { + + if (CovistoZselectPanel.thirdDimension > CovistoZselectPanel.thirdDimensionSize) { + IJ.log("Max Z stack exceeded, moving to last Z instead"); + CovistoZselectPanel.thirdDimension = CovistoZselectPanel.thirdDimensionSize; + + + parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, (int)CovistoZselectPanel.thirdDimension, + (int)CovistoZselectPanel.thirdDimensionSize, (int)CovistoTimeselectPanel.fourthDimension, (int)CovistoTimeselectPanel.fourthDimensionSize); + + } else { + + parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, (int)CovistoZselectPanel.thirdDimension, + (int)CovistoZselectPanel.thirdDimensionSize, (int)CovistoTimeselectPanel.fourthDimension, (int)CovistoTimeselectPanel.fourthDimensionSize); + + } + + + } + + + + public void shownewT() { + + if (CovistoTimeselectPanel.fourthDimension > CovistoTimeselectPanel.fourthDimensionSize) { + IJ.log("Max time point exceeded, moving to last time point instead"); + CovistoTimeselectPanel.fourthDimension = CovistoTimeselectPanel.fourthDimensionSize; + + + parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg,(int) CovistoZselectPanel.thirdDimension, + (int)CovistoZselectPanel.thirdDimensionSize,(int) CovistoTimeselectPanel.fourthDimension, (int)CovistoTimeselectPanel.fourthDimensionSize); + + } else { + + parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg,(int) CovistoZselectPanel.thirdDimension, + (int)CovistoZselectPanel.thirdDimensionSize, (int)CovistoTimeselectPanel.fourthDimension, (int)CovistoTimeselectPanel.fourthDimensionSize); + + } + + + + + + + } + +} diff --git a/src/main/java/interactivePreprocessing/ZMser.java b/src/main/java/interactivePreprocessing/ZMser.java new file mode 100644 index 0000000..8f4bbbc --- /dev/null +++ b/src/main/java/interactivePreprocessing/ZMser.java @@ -0,0 +1,129 @@ +package interactivePreprocessing; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.concurrent.ExecutionException; + +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; + +import ij.ImagePlus; +import ij.gui.EllipseRoi; +import ij.gui.Roi; +import ij.process.ColorProcessor; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; +import net.imglib2.Interval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.componenttree.mser.MserTree; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Intervals; +import net.imglib2.util.Pair; +import net.imglib2.view.Views; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class ZMser extends SwingWorker { + + final InteractiveMethods parent; + + public ZMser(final InteractiveMethods parent) { + + this.parent = parent; + + } + + @Override + protected Void doInBackground() throws Exception { + + ImagePlus Localimp = ImageJFunctions.show(parent.originalimg); + // int count = 0; + // int[] totalcount = new int[parent.thirdDimensionSize + 1]; + // int expectedMT = 61; + for (int z = CovistoZselectPanel.thirdDimensionsliderInit; z <= CovistoZselectPanel.thirdDimensionSize; ++z) { + + CovistoZselectPanel.thirdDimension = z; + + parent.CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, z, CovistoZselectPanel.thirdDimensionSize, + CovistoTimeselectPanel.fourthDimension, CovistoTimeselectPanel.fourthDimensionSize); + parent.updatePreview(ValueChange.THIRDDIM); + + parent.prestack.addSlice(Localimp.getImageStack().getProcessor(z).convertToRGB()); + parent.cp = (ColorProcessor) (parent.prestack.getProcessor(z).duplicate()); + utility.CovsitoProgressBar.CovistoSetProgressBar(parent.jpb, "Computing Component Tree for MSER, Please Wait..."); + + if (CovistoMserPanel.darktobright) + + parent.newtree = MserTree.buildMserTree(parent.newimg, CovistoMserPanel.delta, CovistoMserPanel.minSize, CovistoMserPanel.maxSize, + CovistoMserPanel.Unstability_Score, CovistoMserPanel.minDiversity, true); + + else + + parent.newtree = MserTree.buildMserTree(parent.newimg, CovistoMserPanel.delta, CovistoMserPanel.minSize, CovistoMserPanel.maxSize, + CovistoMserPanel.Unstability_Score, CovistoMserPanel.minDiversity, false); + parent.overlay.clear(); + parent.Rois = utility.FinderUtils.getcurrentRois(parent.newtree); + + // count += (expectedMT - parent.Rois.size()); + + // totalcount[z] = expectedMT - parent.Rois.size(); + + parent.CurrentPreRoiobject = new ArrayList(); + ArrayList centerRoi = utility.FinderUtils.getRoiMean(parent.newtree); + + for (int index = 0; index < centerRoi.size(); ++index) { + + Roi or = parent.Rois.get(index); + + or.setStrokeColor(parent.colorDrawMser); + parent.overlay.add(or); + + } + for (Roi currentroi : parent.Rois) { + + currentroi.setStrokeColor(Color.red); + + parent.cp.setColor(Color.red); + parent.cp.setLineWidth(1); + parent.cp.draw(currentroi); + + } + parent.imp.setOverlay(parent.overlay); + parent.imp.updateAndDraw(); + + parent.prestack.setPixels(parent.cp.getPixels(), z); + Localimp.hide(); + } + + //double meanfaliure = count /(parent.thirdDimensionSize ); + //double stdev = 0; + //for (int z = parent.thirdDimensionsliderInit; z <= parent.thirdDimensionSize; ++z) { + + // stdev+= (totalcount[z] - meanfaliure) * (totalcount[z] - meanfaliure); + // } + + + //System.out.println("Count" + " " + count + "STDEV: " + Math.sqrt(stdev/parent.thirdDimensionSize)); + return null; + + } + + @Override + protected void done() { + + new ImagePlus("Sim", parent.prestack).show(); + + try { + utility.CovsitoProgressBar.CovistoSetProgressBar(parent.jpb, "Done"); + get(); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/kalmanForSegments/SortedSegmentDepthFirstIterator.java b/src/main/java/kalmanForSegments/SortedSegmentDepthFirstIterator.java index 490056e..3a2f643 100644 --- a/src/main/java/kalmanForSegments/SortedSegmentDepthFirstIterator.java +++ b/src/main/java/kalmanForSegments/SortedSegmentDepthFirstIterator.java @@ -16,7 +16,6 @@ import org.jgrapht.Graph; import org.jgrapht.Graphs; import org.jgrapht.event.ConnectedComponentTraversalEvent; -import org.jgrapht.event.EdgeTraversalEvent; import org.jgrapht.event.VertexTraversalEvent; import org.jgrapht.traverse.AbstractGraphIterator; import org.jgrapht.traverse.CrossComponentIterator; diff --git a/src/main/java/kalmanGUI/CovistoKalmanPanel.java b/src/main/java/kalmanGUI/CovistoKalmanPanel.java new file mode 100644 index 0000000..42c2d27 --- /dev/null +++ b/src/main/java/kalmanGUI/CovistoKalmanPanel.java @@ -0,0 +1,188 @@ +package kalmanGUI; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoKalmanPanel { + + + public static final int scrollbarSize = 1000; + public static JPanel KalmanPanel = new JPanel(); + + public static final String maxSearchstringKalman = "Maximum search radius"; + public static final String initialSearchstring = "Initial search radius (um)"; + + + public static final String alphastring = "Distance/Angle cost ratio"; + public static final String betastring = "Weightage for pixel ratio based cost"; + public static int alphaInit = 1; + public static int betaInit = 0; + public static float initialSearchradius = 100; + public static int initialSearchradiusInit = (int) initialSearchradius; + public static float initialSearchradiusMin = 1; + public static float initialSearchradiusMax = 1000; + public static float alphaMin = 0; + public static float alphaMax = 1; + public static float betaMin = 0; + public static float betaMax = 1; + public static float alpha = 0.5f; + public static float beta = 0.5f; + public static float maxSearchradius = 100; + public static int maxSearchradiusInit = (int) maxSearchradius; + public static float maxSearchradiusMin = 1; + public static float maxSearchradiusMax = 1000; + + public static int maxframegap = 3; + public static int trackduration = 10; + public static Label lostlabel; + public static Label mintracklength; + + public static TextField lostframe; + public static TextField tracklength; + public static Label alphaText = new Label(alphastring + " = " + alphaInit, Label.CENTER); + public static Label betaText = new Label(betastring + " = " + betaInit, Label.CENTER); + + public static Label maxSearchTextKalman = new Label(maxSearchstringKalman + " = " + maxSearchradiusInit, Label.CENTER); + public static Label iniSearchText = new Label(initialSearchstring + " = " + initialSearchradiusInit, Label.CENTER); + + public static final JScrollBar maxSearchSS = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar initialSearchS = new JScrollBar(Scrollbar.HORIZONTAL, initialSearchradiusInit, 10, 0, + 10 + scrollbarSize); + public static final JScrollBar alphaS = new JScrollBar(Scrollbar.HORIZONTAL, alphaInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar betaS = new JScrollBar(Scrollbar.HORIZONTAL, betaInit, 10, 0, 10 + scrollbarSize); + public static final JButton Skeletontime = new JButton("Skeletonize Buddies"); + public static final JButton Timetrack = new JButton("Track Buddies"); + public static final JButton Restart = new JButton("Restart"); + public static final JScrollBar maxSearchKalman = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + + + public static void setInitialAlpha(final float value) { + alpha = value; + alphaInit = scrollbar.Utility.computeScrollbarPositionFromValue(alpha, alphaMin, alphaMax, scrollbarSize); + } + public static double getInitialAlpha(final float value) { + + return alpha; + + } + public static void setInitialBeta(final float value) { + beta = value; + betaInit = scrollbar.Utility.computeScrollbarPositionFromValue(beta, betaMin, betaMax, scrollbarSize); + } + + public static double getInitialBeta(final float value) { + + return beta; + + } + public static void setInitialsearchradius(final float value) { + initialSearchradius = value; + initialSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(initialSearchradius, initialSearchradiusMin, + initialSearchradiusMax, scrollbarSize); + } + + public static void setInitialmaxsearchradius(final float value) { + maxSearchradius = value; + maxSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(maxSearchradius, maxSearchradiusMin, maxSearchradiusMax, + scrollbarSize); + } + + public double getInitialsearchradius(final float value) { + + return initialSearchradius; + + } + public static JPanel KalmanPanel() { + + setInitialAlpha(alphaInit); + setInitialBeta(betaInit); + setInitialsearchradius(initialSearchradiusInit); + setInitialmaxsearchradius(maxSearchradius); + layoutManager.Setlayout.LayoutSetter(KalmanPanel); + lostframe = new TextField(1); + lostframe.setText(Integer.toString(maxframegap)); + + tracklength = new TextField(1); + tracklength.setText(Integer.toString(trackduration)); + + lostlabel = new Label("Allow link loosing for #frames"); + mintracklength = new Label("Minimum BudTrack length as percent of timeframes"); + + + alphaText = new Label(alphastring + " = " + alpha, Label.CENTER); + betaText = new Label(betastring + " = " + beta, Label.CENTER); + + iniSearchText = new Label(initialSearchstring + " = " + initialSearchradius, Label.CENTER); + + maxSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(maxSearchKalman.getValue(), + maxSearchradiusMin, maxSearchradiusMax, scrollbarSize); + initialSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(initialSearchS.getValue(), + initialSearchradiusMin, initialSearchradiusMax, scrollbarSize); + alpha = scrollbar.Utility.computeValueFromScrollbarPosition(alphaS.getValue(), alphaMin, alphaMax, + scrollbarSize); + beta = scrollbar.Utility.computeValueFromScrollbarPosition(betaS.getValue(), betaMin, betaMax, + scrollbarSize); + + alphaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(alphaInit, alphaMin, alphaMax, scrollbarSize)); + betaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(betaInit, betaMin, betaMax, scrollbarSize)); + + + Border Kalmanborder = new CompoundBorder(new TitledBorder("Kalman Filter Search in T"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + KalmanPanel.add(iniSearchText, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(initialSearchS, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(alphaText, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(alphaS, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(betaText, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(betaS, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(lostlabel, new GridBagConstraints(5, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(lostframe, new GridBagConstraints(5, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(mintracklength, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(tracklength, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(maxSearchTextKalman, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + // KalmanPanel.add(maxSearchKalman, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Restart, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(Skeletontime, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.add(Timetrack, new GridBagConstraints(5, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.setBorder(Kalmanborder); + + + return KalmanPanel; + } + + +} diff --git a/src/main/java/kalmanGUI/CovistoNNPanel.java b/src/main/java/kalmanGUI/CovistoNNPanel.java new file mode 100644 index 0000000..67f5ece --- /dev/null +++ b/src/main/java/kalmanGUI/CovistoNNPanel.java @@ -0,0 +1,191 @@ +package kalmanGUI; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoNNPanel { + + + public static final int scrollbarSize = 1000; + public static JPanel KalmanPanel = new JPanel(); + + public static final String maxSearchstringKalman = "Maximum search radius"; + public static final String initialSearchstring = "Initial search radius"; + + + public static final String alphastring = "Weightage for distance based cost"; + public static final String betastring = "Weightage for pixel ratio based cost"; + public static int alphaInit = 1; + public static int betaInit = 0; + public static int missedframes = 20; + public static float initialSearchradius = 100; + public static int initialSearchradiusInit = (int) initialSearchradius; + public static float initialSearchradiusMin = 1; + public static float initialSearchradiusMax = 1000; + public static float alphaMin = 0; + public static float alphaMax = 1; + public static float betaMin = 0; + public static float betaMax = 1; + public static float alpha = 0.5f; + public static float beta = 0.5f; + public static float maxSearchradius = 100; + public static int maxSearchradiusInit = (int) maxSearchradius; + public static float maxSearchradiusMin = 1; + public static float maxSearchradiusMax = 1000; + + public static int maxframegap = 10; + public static int trackduration = 50; + public static Label lostlabel; + public static Label mintracklength; + + public static TextField lostframe; + public static TextField tracklength; + public static Label alphaText = new Label(alphastring + " = " + alphaInit, Label.CENTER); + public static Label betaText = new Label(betastring + " = " + betaInit, Label.CENTER); + + public static Label maxSearchTextKalman = new Label(maxSearchstringKalman + " = " + maxSearchradiusInit, Label.CENTER); + public static Label iniSearchText = new Label(initialSearchstring + " = " + initialSearchradiusInit, Label.CENTER); + + public static final JScrollBar maxSearchSS = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar initialSearchS = new JScrollBar(Scrollbar.HORIZONTAL, initialSearchradiusInit, 10, 0, + 10 + scrollbarSize); + public static final JScrollBar alphaS = new JScrollBar(Scrollbar.HORIZONTAL, alphaInit, 10, 0, 10 + scrollbarSize); + public static final JScrollBar betaS = new JScrollBar(Scrollbar.HORIZONTAL, betaInit, 10, 0, 10 + scrollbarSize); + public static final JButton Skeletontime = new JButton("Skeletonize Buddies"); + public static final JButton Timetrack = new JButton("Track Buddies"); + public static final JButton Restart = new JButton("Restart"); + public static final JScrollBar maxSearchKalman = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchradiusInit, 10, 0, 10 + scrollbarSize); + + + public static void setInitialAlpha(final float value) { + alpha = value; + alphaInit = scrollbar.Utility.computeScrollbarPositionFromValue(alpha, alphaMin, alphaMax, scrollbarSize); + } + public static double getInitialAlpha(final float value) { + + return alpha; + + } + public static void setInitialBeta(final float value) { + beta = value; + betaInit = scrollbar.Utility.computeScrollbarPositionFromValue(beta, betaMin, betaMax, scrollbarSize); + } + + public static double getInitialBeta(final float value) { + + return beta; + + } + public static void setInitialsearchradius(final float value) { + initialSearchradius = value; + initialSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(initialSearchradius, initialSearchradiusMin, + initialSearchradiusMax, scrollbarSize); + } + + public static void setInitialmaxsearchradius(final float value) { + maxSearchradius = value; + maxSearchradiusInit = scrollbar.Utility.computeScrollbarPositionFromValue(maxSearchradius, maxSearchradiusMin, maxSearchradiusMax, + scrollbarSize); + } + + public double getInitialsearchradius(final float value) { + + return initialSearchradius; + + } + public static JPanel KalmanPanel() { + + setInitialAlpha(alphaInit); + setInitialBeta(betaInit); + setInitialsearchradius(initialSearchradiusInit); + setInitialmaxsearchradius(maxSearchradius); + layoutManager.Setlayout.LayoutSetter(KalmanPanel); + lostframe = new TextField(1); + lostframe.setText(Integer.toString(maxframegap)); + + tracklength = new TextField(1); + tracklength.setText(Integer.toString(trackduration)); + + lostlabel = new Label("Allow link loosing for #frames"); + mintracklength = new Label("Minimum BudTrack length as percent of timeframes"); + + + alphaText = new Label(alphastring + " = " + alpha, Label.CENTER); + betaText = new Label(betastring + " = " + beta, Label.CENTER); + + iniSearchText = new Label(initialSearchstring + " = " + initialSearchradius, Label.CENTER); + + maxSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(maxSearchKalman.getValue(), + maxSearchradiusMin, maxSearchradiusMax, scrollbarSize); + initialSearchradius = scrollbar.Utility.computeValueFromScrollbarPosition(initialSearchS.getValue(), + initialSearchradiusMin, initialSearchradiusMax, scrollbarSize); + alpha = scrollbar.Utility.computeValueFromScrollbarPosition(alphaS.getValue(), alphaMin, alphaMax, + scrollbarSize); + beta = scrollbar.Utility.computeValueFromScrollbarPosition(betaS.getValue(), betaMin, betaMax, + scrollbarSize); + + alphaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(alphaInit, alphaMin, alphaMax, scrollbarSize)); + betaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(betaInit, betaMin, betaMax, scrollbarSize)); + + + Border Kalmanborder = new CompoundBorder(new TitledBorder("Skeletonize n track buds"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + //KalmanPanel.add(iniSearchText, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(initialSearchS, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(alphaText, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(alphaS, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(betaText, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(betaS, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(lostlabel, new GridBagConstraints(5, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + //KalmanPanel.add(lostframe, new GridBagConstraints(5, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + // KalmanPanel.add(mintracklength, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + // KalmanPanel.add(tracklength, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + //KalmanPanel.add(maxSearchTextKalman, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + // KalmanPanel.add(maxSearchKalman, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + // GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Restart, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Skeletontime, new GridBagConstraints(3, 0, 2, 1, 1.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + KalmanPanel.add(Timetrack, new GridBagConstraints(5, 4, 2, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + KalmanPanel.setBorder(Kalmanborder); + + + return KalmanPanel; + } + + +} diff --git a/src/main/java/layoutManager/Setlayout.java b/src/main/java/layoutManager/Setlayout.java new file mode 100644 index 0000000..822a215 --- /dev/null +++ b/src/main/java/layoutManager/Setlayout.java @@ -0,0 +1,31 @@ +package layoutManager; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.JPanel; + +public class Setlayout { + + + public final static Insets insets = new Insets(10, 10, 0, 10); + public final static GridBagLayout layout = new GridBagLayout(); + public final static GridBagConstraints c = new GridBagConstraints(); + + public static void LayoutSetter(JPanel panel) { + + panel.setLayout(layout); + c.anchor = GridBagConstraints.BOTH; + c.ipadx = 5; + + c.gridwidth = 10; + c.gridheight = 10; + c.gridy = 1; + c.gridx = 0; + } + + + + +} diff --git a/src/main/java/linkers/AlphanumComparator.java b/src/main/java/linkers/AlphanumComparator.java new file mode 100644 index 0000000..6bd9e51 --- /dev/null +++ b/src/main/java/linkers/AlphanumComparator.java @@ -0,0 +1,83 @@ +package linkers; + +import java.util.Comparator; + +public class AlphanumComparator implements Comparator + { + public static final AlphanumComparator instance = new AlphanumComparator(); + + private final boolean isDigit(final char ch) { + return ch >= 48 && ch <= 57; + } + + // Singleton + private AlphanumComparator() {} + + /** Length of string is passed in for improved efficiency (only need to calculate it once) **/ + private final String getChunk(final String s, final int slength, int marker) { + final StringBuilder chunk = new StringBuilder(); + char c = s.charAt(marker); + chunk.append(c); + marker++; + if (isDigit(c)) { + while (marker < slength) { + c = s.charAt(marker); + if (!isDigit(c)) + break; + chunk.append(c); + marker++; + } + } else { + while (marker < slength) { + c = s.charAt(marker); + if (isDigit(c)) + break; + chunk.append(c); + marker++; + } + } + return chunk.toString(); + } + + @Override + public int compare(final String s1, final String s2) { + + int thisMarker = 0; + int thatMarker = 0; + final int s1Length = s1.length(); + final int s2Length = s2.length(); + + while (thisMarker < s1Length && thatMarker < s2Length) { + final String thisChunk = getChunk(s1, s1Length, thisMarker); + thisMarker += thisChunk.length(); + + final String thatChunk = getChunk(s2, s2Length, thatMarker); + thatMarker += thatChunk.length(); + + // If both chunks contain numeric characters, sort them numerically + int result = 0; + if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { + // Simple chunk comparison by length. + final int thisChunkLength = thisChunk.length(); + result = thisChunkLength - thatChunk.length(); + // If equal, the first different number counts + if (result == 0) { + for (int i = 0; i < thisChunkLength; i++) { + result = thisChunk.charAt(i) - thatChunk.charAt(i); + if (result != 0) { + return result; + } + } + } + } else { + result = thisChunk.compareTo(thatChunk); + } + + if (result != 0) + return result; + } + + return s1Length - s2Length; + } + } + diff --git a/src/main/java/linkers/BlobTracker.java b/src/main/java/linkers/BlobTracker.java new file mode 100644 index 0000000..5e25955 --- /dev/null +++ b/src/main/java/linkers/BlobTracker.java @@ -0,0 +1,38 @@ +package linkers; + + + +import java.util.ArrayList; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import net.imglib2.algorithm.MultiThreaded; +import net.imglib2.algorithm.OutputAlgorithm; +import utility.PreRoiobject; + +/** + * + * links objects across multiple frames in time-lapse images, Creates a new graph from a list of blobs, the blob properties of the current frame + * are enumerated in the static properties + * @author varunkapoor + * + */ + + +public interface BlobTracker extends OutputAlgorithm< SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > > + { + /** + * Sets the {@link Logger} instance that will receive messages from this + * {@link SpotTracker}. + * + * @param logger + * the logger to echo messages to. + */ + public void setLogger( final Logger logger ); + + + + } + + diff --git a/src/main/java/linkers/CVMKalmanFilter.java b/src/main/java/linkers/CVMKalmanFilter.java new file mode 100644 index 0000000..2bc4e6b --- /dev/null +++ b/src/main/java/linkers/CVMKalmanFilter.java @@ -0,0 +1,184 @@ +package linkers; +import Jama.Matrix; +// 2 Dimensional Kalman Filter +public class CVMKalmanFilter { + + /** + * The evolution matrix, or state transition matrix. In our case, it is the + * matrix that links position evolution and velocity through + * x(k+1) = x(k) + v × dt. We assume + * v is constant and measured in unit of frames, so + * dt = 1. + */ + private final Matrix A; + + /** + * The a posteriori error covariance matrix, measure the accuracy of + * the state estimate. + */ + private Matrix P; + + /** + * Covariance matrix of the process noise. Determine how noisy the process + * is. + */ + private final Matrix Q; + + /** + * Covariance matrix of the observation noise. Determine how noisy our + * measurements are. + */ + private final Matrix R; + + /** Current state. */ + private Matrix X; + + private final Matrix H; + + /** Prediction. */ + private Matrix Xp; + + /** + * Number of occlusions (no measurements) that happened so far. + */ + private int nOcclusion; + + /** + * Initialize a new Kalman filter with the specified initial state. + * + * + * @param X0 + * initial state estimate. Must be a 4 elements + * double[] array with + * x0, y0,vx0, vy0 with velocity in + * length/frame units. + * @param initStateCovariance + * the initial state covariance. Give it a large value if you do + * not trust the initial state estimate (e.g. 100), a + * small value otherwise (e.g.1e-2). + * @param positionProcessStd + * the std of the additive white gaussian noise affecting the + * position evolution. Large values means that the + * position undergoes heavy fluctuations. + * @param velocityProcessStd + * the std of the additive white gaussian noise affecting the + * velocity evolution. Careful, we expect it to be in + * units of length/frame. Large values means that + * the velocity undergoes heavy fluctuations. + * @param positionMeasurementStd + * the std of the additive white gaussian noise affecting the + * position measurement. Large values means that the + * positions measured are not accurate. + */ + public CVMKalmanFilter( final double[] X0, final double initStateCovariance, final double positionProcessStd, final double velocityProcessStd, final double positionMeasurementStd ) + { + // Initial state + X = new Matrix( X0, 4 ); + + // Evolution matrix + A = Matrix.identity( 4, 4 ); + for ( int i = 0; i < 2; i++ ) + { + A.set( i, 2 + i, 1 ); + } + + // Measurement matrix + H = Matrix.identity( 2, 4 ); + + // State covariance + P = Matrix.identity( 4, 4 ).times( initStateCovariance ); + + // Process covariance + Q = Matrix.identity( 4, 4 ); + for ( int i = 0; i < 2; i++ ) + { + Q.set( i, i, positionProcessStd * positionProcessStd ); + Q.set( 2 + i, 2 + i, velocityProcessStd * velocityProcessStd ); + } + + R = Matrix.identity( 2, 2 ).times( positionMeasurementStd * positionMeasurementStd ); + } + + /** + * Runs the prediction step of the Kalman filter and returns the state + * predicted by the evolution process. + * + * @return a new double[] of 4 elements containing the + * predicted state: x, y, vx, vy with velocity + * in length/frame units. + * + */ + public double[] predict() + { + Xp = A.times( X ); + P = A.times( P.times( A.transpose() ) ).plus( Q ); + return Xp.getColumnPackedCopy(); + } + + /** + * Runs the update step of the Kalman filter based on the specified + * measurement. + * + * @param Xm + * the measured position, must be specified as a 2 elements + * double[]array, containing the measured + * x, y position. It can be null; + * the filter then assumes an occlusion occurred, and update its + * state based on solely the prediction step. + */ + public void update( final double[] Xm ) + { + if ( null == Xm ) + { + // Occlusion. + nOcclusion++; + X = Xp; + } + else + { + final Matrix XM = new Matrix( Xm, 2 ); + final Matrix TEMP = H.times( P.times( H.transpose() ) ).plus( R ); + final Matrix K = P.times( H.transpose() ).times( TEMP.inverse() ); + // State + X = Xp.plus( K.times( XM.minus( H.times( Xp ) ) ) ); + // Covariance + P = ( Matrix.identity( 4, 4 ).minus( K.times( H ) ) ).times( P ); + } + } + + /** + * Return the root mean square error on position estimated through the state + * covariance matrix. + * + * @return the estimated error on position. + */ + public double getPositionError() + { + return Math.sqrt( ( P.get( 0, 0 ) + P.get( 1, 1 ) ) / 2d ); + } + + /** + * Return the root mean square error on velocity estimated through the state + * covariance matrix. + * + * @return the estimated error on velocity, in length/frame + * units. + */ + public double getVelocityError() + { + return Math.sqrt( ( P.get( 2, 2 ) + P.get( 4, 4 ) ) / 2d ); + } + + + /** + * Returns the number of occlusion events that occurred since the + * instantiation of this filter. + * + * @return the number of occlusions. + */ + public int getNOcclusion() + { + return nOcclusion; + } + +} diff --git a/src/main/java/linkers/Dimension.java b/src/main/java/linkers/Dimension.java new file mode 100644 index 0000000..8ecc5e4 --- /dev/null +++ b/src/main/java/linkers/Dimension.java @@ -0,0 +1,16 @@ +package linkers; + +public enum Dimension { + NONE, + QUALITY, + INTENSITY, + INTENSITY_SQUARED, + POSITION, + VELOCITY, + LENGTH, // we separate length and position so that x,y,z are plotted on a different graph from spot sizes + TIME, + ANGLE, + RATE, // count per frames + STRING; // for non-numeric features +} + diff --git a/src/main/java/linkers/ExtensionRealPoint.java b/src/main/java/linkers/ExtensionRealPoint.java new file mode 100644 index 0000000..2da0d91 --- /dev/null +++ b/src/main/java/linkers/ExtensionRealPoint.java @@ -0,0 +1,29 @@ +package linkers; + +import net.imglib2.RealPoint; + +public class ExtensionRealPoint { + + private static final class ComparableRealPoint extends RealPoint implements Comparable { + public ComparableRealPoint(final double[] A) { + // Wrap array. + super(A, false); + } + + /** + * Sort based on X, Y + */ + @Override + public int compareTo(final ComparableRealPoint o) { + int i = 0; + while (i < n) { + if (getDoublePosition(i) != o.getDoublePosition(i)) { + return (int) Math.signum(getDoublePosition(i) - o.getDoublePosition(i)); + } + i++; + } + return hashCode() - o.hashCode(); + } + } + +} diff --git a/src/main/java/linkers/FeatureModel3D.java b/src/main/java/linkers/FeatureModel3D.java new file mode 100644 index 0000000..18f0cec --- /dev/null +++ b/src/main/java/linkers/FeatureModel3D.java @@ -0,0 +1,668 @@ +package linkers; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.jgrapht.graph.DefaultWeightedEdge; + +import utility.ThreeDRoiobject; + +/** + * This class represents the part of the {@link Model} that is in charge of + * dealing with ThreeDRoiobject features and track features. + * + * @author Jean-Yves Tinevez, 2011, 2012 + * + */ +public class FeatureModel3D +{ + + /* + * FIELDS + */ + + private final Collection< String > trackFeatures = new LinkedHashSet< >(); + + private final Map< String, String > trackFeatureNames = new HashMap< >(); + + private final Map< String, String > trackFeatureShortNames = new HashMap< >(); + + private final Map< String, Dimension > trackFeatureDimensions = new HashMap< >(); + + private final Map< String, Boolean > trackFeatureIsInt = new HashMap< >(); + + /** + * Feature storage. We use a Map of Map as a 2D Map. The list maps each + * track to its feature map. The feature map maps each feature to the double + * value for the specified feature. + */ + Map< Integer, Map< String, Double >> trackFeatureValues = new ConcurrentHashMap<>(); + + /** + * Feature storage for edges. + */ + private final ConcurrentHashMap< DefaultWeightedEdge, ConcurrentHashMap< String, Double >> edgeFeatureValues = new ConcurrentHashMap<>(); + + private final Collection< String > edgeFeatures = new LinkedHashSet< >(); + + private final Map< String, String > edgeFeatureNames = new HashMap< >(); + + private final Map< String, String > edgeFeatureShortNames = new HashMap< >(); + + private final Map< String, Dimension > edgeFeatureDimensions = new HashMap< >(); + + private final Map< String, Boolean > edgeFeatureIsInt = new HashMap< >(); + + private final Collection< String > ThreeDRoiobjectFeatures = new LinkedHashSet< >(); + + private final Map< String, String > ThreeDRoiobjectFeatureNames = new HashMap< >(); + + private final Map< String, String > ThreeDRoiobjectFeatureShortNames = new HashMap< >(); + + private final Map< String, Dimension > ThreeDRoiobjectFeatureDimensions = new HashMap< >(); + + private final Map< String, Boolean > ThreeDRoiobjectFeatureIsInt = new HashMap< >(); + + private final Model3D model; + + + + /* + * CONSTRUCTOR + */ + + /** + * Instantiates a new feature model. The basic ThreeDRoiobject features (POSITON_*, + * RADIUS, FRAME, QUALITY) are declared. Edge and track feature declarations + * are left blank. + * + * @param model + * the parent {@link Model}. + */ + protected FeatureModel3D( final Model3D model ) + { + this.model = model; + // Adds the base ThreeDRoiobject features + declareThreeDRoiobjectFeatures( ThreeDRoiobject.FEATURES, ThreeDRoiobject.FEATURE_NAMES, ThreeDRoiobject.FEATURE_SHORT_NAMES, ThreeDRoiobject.FEATURE_DIMENSIONS, ThreeDRoiobject.IS_INT ); + } + + /* + * METHODS + */ + + /** + * Returns a new double array with all the values for the specified track + * feature. + * + * @param trackFeature + * the track feature to parse. Throw an + * {@link IllegalArgumentException} if the feature is unknown. + * @param visibleOnly + * if true, will only include visible tracks, all + * the tracks otherwise. + * @return a new double[], one element per track. + */ + public double[] getTrackFeatureValues( final String trackFeature, final boolean visibleOnly ) + { + if ( !trackFeatures.contains( trackFeature ) ) { throw new IllegalArgumentException( "Unknown track feature: " + trackFeature ); } + final Set< Integer > keys = model.getTrackModel().trackIDs( visibleOnly ); + final double[] val = new double[ keys.size() ]; + int index = 0; + for ( final Integer trackID : keys ) + { + final Double tf = getTrackFeature( trackID, trackFeature ); + if ( null == tf ) + continue; + val[ index++ ] = tf.doubleValue(); + } + return val; + } + + /** + * Returns a new double array with all the values for the specified edge + * feature. + * + * @param edgeFeature + * the track feature to parse. Throw an + * {@link IllegalArgumentException} if the feature is unknown. + * @param visibleOnly + * if true, will only include edges in visible + * tracks, in all the tracks otherwise. + * @return a new double[], one element per edge. + */ + public double[] getEdgeFeatureValues( final String edgeFeature, final boolean visibleOnly ) + { + if ( !edgeFeatures.contains( edgeFeature ) ) { throw new IllegalArgumentException( "Unknown edge feature: " + edgeFeature ); } + final Set< Integer > keys = model.getTrackModel().trackIDs( visibleOnly ); + int nvals = 0; + for ( final Integer trackID : keys ) + { + nvals += model.getTrackModel().trackEdges( trackID ).size(); + } + + final double[] val = new double[ nvals ]; + int index = 0; + for ( final Integer trackID : keys ) + { + for ( final DefaultWeightedEdge edge : model.getTrackModel().trackEdges( trackID ) ) + { + final Double ef = getEdgeFeature( edge, edgeFeature ); + if ( null == ef ) + continue; + val[ index++ ] = ef.doubleValue(); + } + } + return val; + } + + /* + * EDGE FEATURES + */ + + /** + * Stores a numerical feature for an edge of this model. + *

+ * Note that no checks are made to ensures that the edge exists in the + * {@link TrackModel}, and that the feature is declared in this + * {@link FeatureModel}. + * + * @param edge + * the edge whose features to update. + * @param feature + * the feature. + * @param value + * the feature value + */ + public synchronized void putEdgeFeature( final DefaultWeightedEdge edge, final String feature, final Double value ) + { + ConcurrentHashMap< String, Double > map = edgeFeatureValues.get( edge ); + if ( null == map ) + { + map = new ConcurrentHashMap< >(); + edgeFeatureValues.put( edge, map ); + } + map.put( feature, value ); + } + + public Double getEdgeFeature( final DefaultWeightedEdge edge, final String featureName ) + { + final ConcurrentHashMap< String, Double > map = edgeFeatureValues.get( edge ); + if ( null == map ) { return null; } + return map.get( featureName ); + } + + /** + * Returns edge features as declared in this model. + * + * @return the edge features. + */ + public Collection< String > getEdgeFeatures() + { + return edgeFeatures; + } + + /** + * Declares edge features, by specifying their name, short name and + * dimension. An {@link IllegalArgumentException} will be thrown if any of + * the map misses a feature. + * + * @param features + * the list of edge features to register. + * @param featureNames + * the name of these features. + * @param featureShortNames + * the short name of these features. + * @param featureDimensions + * the dimension of these features. + * @param isIntFeature + * whether some of these features are made of ints ( + * true) or doubles (false + * ). + */ + public void declareEdgeFeatures( final Collection< String > features, final Map< String, String > featureNames, final Map< String, String > featureShortNames, final Map< String, Dimension > featureDimensions, final Map< String, Boolean > isIntFeature ) + { + edgeFeatures.addAll( features ); + for ( final String feature : features ) + { + final String name = featureNames.get( feature ); + if ( null == name ) { throw new IllegalArgumentException( "Feature " + feature + " misses a name." ); } + edgeFeatureNames.put( feature, name ); + + final String shortName = featureShortNames.get( feature ); + if ( null == shortName ) { throw new IllegalArgumentException( "Feature " + feature + " misses a short name." ); } + edgeFeatureShortNames.put( feature, shortName ); + + final Dimension dimension = featureDimensions.get( feature ); + if ( null == dimension ) { throw new IllegalArgumentException( "Feature " + feature + " misses a dimension." ); } + edgeFeatureDimensions.put( feature, dimension ); + + final Boolean isInt = isIntFeature.get( feature ); + if ( null == isInt ) { throw new IllegalArgumentException( "Feature " + feature + " misses the isInt flag." ); } + edgeFeatureIsInt.put( feature, isInt ); + } + } + + /** + * Returns the name mapping of the edge features that are dealt with in this + * model. + * + * @return the map of edge feature names. + */ + public Map< String, String > getEdgeFeatureNames() + { + return edgeFeatureNames; + } + + /** + * Returns the short name mapping of the edge features that are dealt with + * in this model. + * + * @return the map of edge short names. + */ + public Map< String, String > getEdgeFeatureShortNames() + { + return edgeFeatureShortNames; + } + + /** + * Returns the dimension mapping of the edge features that are dealt with in + * this model. + * + * @return the map of edge feature dimensions. + */ + public Map< String, Dimension > getEdgeFeatureDimensions() + { + return edgeFeatureDimensions; + } + + /** + * Returns the map that states whether the target feature is integer values + * (true) or double valued (false). + * + * @return the map of isInt flag. + */ + public Map< String, Boolean > getEdgeFeatureIsInt() + { + return edgeFeatureIsInt; + } + + /* + * TRACK FEATURES + */ + + /** + * Returns the track features that are dealt with in this model. + * + * @return the collection of track features managed in this model. + */ + public Collection< String > getTrackFeatures() + { + return trackFeatures; + } + + /** + * Declares track features, by specifying their names, short name and + * dimension. An {@link IllegalArgumentException} will be thrown if any of + * the map misses a feature. + * + * @param features + * the list of track feature to register. + * @param featureNames + * the name of these features. + * @param featureShortNames + * the short name of these features. + * @param featureDimensions + * the dimension of these features. + * @param isIntFeature + * whether some of these features are made of ints ( + * true) or doubles (false + * ). + */ + public void declareTrackFeatures( final Collection< String > features, final Map< String, String > featureNames, final Map< String, String > featureShortNames, final Map< String, Dimension > featureDimensions, final Map< String, Boolean > isIntFeature ) + { + trackFeatures.addAll( features ); + for ( final String feature : features ) + { + + final String name = featureNames.get( feature ); + if ( null == name ) { throw new IllegalArgumentException( "Feature " + feature + " misses a name." ); } + trackFeatureNames.put( feature, name ); + + final String shortName = featureShortNames.get( feature ); + if ( null == shortName ) { throw new IllegalArgumentException( "Feature " + feature + " misses a short name." ); } + trackFeatureShortNames.put( feature, shortName ); + + final Dimension dimension = featureDimensions.get( feature ); + if ( null == dimension ) { throw new IllegalArgumentException( "Feature " + feature + " misses a dimension." ); } + trackFeatureDimensions.put( feature, dimension ); + + final Boolean isInt = isIntFeature.get( feature ); + if ( null == isInt ) { throw new IllegalArgumentException( "Feature " + feature + " misses the isInt flag." ); } + trackFeatureIsInt.put( feature, isInt ); + } + } + + /** + * Returns the name mapping of the track features that are dealt with in + * this model. + * + * @return the feature name. + */ + public Map< String, String > getTrackFeatureNames() + { + return trackFeatureNames; + } + + /** + * Returns the short name mapping of the track features that are dealt with + * in this model. + * + * @return the feature short name. + */ + public Map< String, String > getTrackFeatureShortNames() + { + return trackFeatureShortNames; + } + + /** + * Returns the dimension mapping of the track features that are dealt with + * in this model. + * + * @return the feature dimension. + */ + public Map< String, Dimension > getTrackFeatureDimensions() + { + return trackFeatureDimensions; + } + + /** + * Returns the map that states whether the target feature is integer values + * (true) or double valued (false). + * + * @return the map of isInt flag. + */ + public Map< String, Boolean > getTrackFeatureIsInt() + { + return trackFeatureIsInt; + } + + /** + * Stores a track numerical feature. + *

+ * Note that no checks are made to ensures that the track ID exists in the + * {@link TrackModel}, and that the feature is declared in this + * {@link FeatureModel}. + * + * @param trackID + * the ID of the track. It must be an existing track ID. + * @param feature + * the feature. + * @param value + * the feature value. + */ + public synchronized void putTrackFeature( final Integer trackID, final String feature, final Double value ) + { + Map< String, Double > trackFeatureMap = trackFeatureValues.get( trackID ); + if ( null == trackFeatureMap ) + { + trackFeatureMap = new HashMap< >( trackFeatures.size() ); + trackFeatureValues.put( trackID, trackFeatureMap ); + } + trackFeatureMap.put( feature, value ); + } + + /** + * Returns the numerical value of the specified track feature for the + * specified track. + * + * @param trackID + * the track ID to quest. + * @param feature + * the desired feature. + * @return the value of the specified feature. + */ + public Double getTrackFeature( final Integer trackID, final String feature ) + { + final Map< String, Double > valueMap = trackFeatureValues.get( trackID ); + return valueMap.get( feature ); + } + + /** + * Returns the map of all track features declared for all tracks of the + * model. + * + * @return a new mapping of feature vs its numerical values. + */ + public Map< String, double[] > getTrackFeatureValues() + { + final Map< String, double[] > featureValues = new HashMap< >(); + Double val; + final int nTracks = model.getTrackModel().nTracks( false ); + for ( final String feature : trackFeatures ) + { + // Make a double array to comply to JFreeChart histograms + boolean noDataFlag = true; + final double[] values = new double[ nTracks ]; + int index = 0; + for ( final Integer trackID : model.getTrackModel().trackIDs( false ) ) + { + val = getTrackFeature( trackID, feature ); + if ( null == val ) + { + continue; + } + values[ index++ ] = val; + noDataFlag = false; + } + + if ( noDataFlag ) + { + featureValues.put( feature, new double[ 0 ] ); // Empty array to + // signal no + // data + } + else + { + featureValues.put( feature, values ); + } + } + return featureValues; + } + + /* + * ThreeDRoiobject FEATURES the ThreeDRoiobject features are stored in the ThreeDRoiobject object themselves, + * but we declare them here. + */ + + /** + * Declares ThreeDRoiobject features, by specifying their names, short name and + * dimension. An {@link IllegalArgumentException} will be thrown if any of + * the map misses a feature. + * + * @param features + * the list of ThreeDRoiobject feature to register. + * @param featureNames + * the name of these features. + * @param featureShortNames + * the short name of these features. + * @param featureDimensions + * the dimension of these features. + * @param isIntFeature + * whether some of these features are made of ints ( + * true) or doubles (false + * ). + */ + public void declareThreeDRoiobjectFeatures( final Collection< String > features, final Map< String, String > featureNames, final Map< String, String > featureShortNames, final Map< String, Dimension > featureDimensions, final Map< String, Boolean > isIntFeature ) + { + ThreeDRoiobjectFeatures.addAll( features ); + for ( final String feature : features ) + { + + final String name = featureNames.get( feature ); + if ( null == name ) { throw new IllegalArgumentException( "Feature " + feature + " misses a name." ); } + ThreeDRoiobjectFeatureNames.put( feature, name ); + + final String shortName = featureShortNames.get( feature ); + if ( null == shortName ) { throw new IllegalArgumentException( "Feature " + feature + " misses a short name." ); } + ThreeDRoiobjectFeatureShortNames.put( feature, shortName ); + + final Dimension dimension = featureDimensions.get( feature ); + if ( null == dimension ) { throw new IllegalArgumentException( "Feature " + feature + " misses a dimension." ); } + ThreeDRoiobjectFeatureDimensions.put( feature, dimension ); + + final Boolean isInt = isIntFeature.get( feature ); + if ( null == isInt ) { throw new IllegalArgumentException( "Feature " + feature + " misses the isInt flag." ); } + ThreeDRoiobjectFeatureIsInt.put( feature, isInt ); + + } + } + + /** + * Returns ThreeDRoiobject features as declared in this model. + * + * @return the ThreeDRoiobject features. + */ + public Collection< String > getThreeDRoiobjectFeatures() + { + return ThreeDRoiobjectFeatures; + } + + /** + * Returns the name mapping of the ThreeDRoiobject features that are dealt with in this + * model. + * + * @return the map of ThreeDRoiobject feature names. + */ + public Map< String, String > getThreeDRoiobjectFeatureNames() + { + return ThreeDRoiobjectFeatureNames; + } + + /** + * Returns the short name mapping of the ThreeDRoiobject features that are dealt with + * in this model. + * + * @return the map of ThreeDRoiobject short names. + */ + public Map< String, String > getThreeDRoiobjectFeatureShortNames() + { + return ThreeDRoiobjectFeatureShortNames; + } + + /** + * Returns the dimension mapping of the ThreeDRoiobject features that are dealt with in + * this model. + * + * @return the map of ThreeDRoiobject feature dimensions. + */ + public Map< String, Dimension > getThreeDRoiobjectFeatureDimensions() + { + return ThreeDRoiobjectFeatureDimensions; + } + + /** + * Returns the map that states whether the target feature is integer values + * (true) or double valued (false). + * + * @return the map of isInt flag. + */ + public Map< String, Boolean > getThreeDRoiobjectFeatureIsInt() + { + return ThreeDRoiobjectFeatureIsInt; + } + + @Override + public String toString() + { + final StringBuilder str = new StringBuilder(); + + // ThreeDRoiobjects + str.append( "ThreeDRoiobject features declared:\n" ); + appendFeatureDeclarations( str, ThreeDRoiobjectFeatures, ThreeDRoiobjectFeatureNames, ThreeDRoiobjectFeatureShortNames, ThreeDRoiobjectFeatureDimensions, ThreeDRoiobjectFeatureIsInt ); + str.append( '\n' ); + + // Edges + str.append( "Edge features declared:\n" ); + appendFeatureDeclarations( str, edgeFeatures, edgeFeatureNames, edgeFeatureShortNames, edgeFeatureDimensions, edgeFeatureIsInt ); + str.append( '\n' ); + + // Track + str.append( "Track features declared:\n" ); + appendFeatureDeclarations( str, trackFeatures, trackFeatureNames, trackFeatureShortNames, trackFeatureDimensions, trackFeatureIsInt ); + str.append( '\n' ); + + return str.toString(); + } + + /** + * Echoes the full content of this {@link FeatureModel}. + * + * @return a String representation of the full content of this model. + */ + public String echo() + { + final StringBuilder str = new StringBuilder(); + + // ThreeDRoiobjects + str.append( "ThreeDRoiobject features:\n" ); + str.append( " - Declared:\n" ); + appendFeatureDeclarations( str, ThreeDRoiobjectFeatures, ThreeDRoiobjectFeatureNames, ThreeDRoiobjectFeatureShortNames, ThreeDRoiobjectFeatureDimensions, ThreeDRoiobjectFeatureIsInt ); + str.append( '\n' ); + + // Edges + str.append( "Edge features:\n" ); + str.append( " - Declared:\n" ); + appendFeatureDeclarations( str, edgeFeatures, edgeFeatureNames, edgeFeatureShortNames, edgeFeatureDimensions, edgeFeatureIsInt ); + str.append( '\n' ); + str.append( " - Values:\n" ); + appendFeatureValues( str, edgeFeatureValues ); + + // Track + str.append( "Track features:\n" ); + str.append( " - Declared:\n" ); + appendFeatureDeclarations( str, trackFeatures, trackFeatureNames, trackFeatureShortNames, trackFeatureDimensions, trackFeatureIsInt ); + str.append( '\n' ); + str.append( " - Values:\n" ); + appendFeatureValues( str, trackFeatureValues ); + + return str.toString(); + } + + /* + * STATIC UTILS + */ + + private static final < K > void appendFeatureValues( final StringBuilder str, final Map< K, ? extends Map< String, Double >> values ) + { + for ( final K key : values.keySet() ) + { + final String header = " - " + key.toString() + ":\n"; + str.append( header ); + final Map< String, Double > map = values.get( key ); + for ( final String feature : map.keySet() ) + { + str.append( " - " + feature + " = " + map.get( feature ) + '\n' ); + } + } + } + + private static final void appendFeatureDeclarations( final StringBuilder str, final Collection< String > features, final Map< String, String > featureNames, final Map< String, String > featureShortNames, final Map< String, Dimension > featureDimensions, final Map< String, Boolean > isIntFeature ) + { + for ( final String feature : features ) + { + str.append( " - " + feature + ": " + featureNames.get( feature ) + ", '" + featureShortNames.get( feature ) + "' (" + featureDimensions.get( feature ) + ")" ); + if ( isIntFeature.get( feature ).booleanValue() ) + { + str.append( " - integer valued.\n" ); + } + else + { + str.append( " - double valued.\n" ); + } + } + } +} diff --git a/src/main/java/linkers/FlagNode.java b/src/main/java/linkers/FlagNode.java new file mode 100644 index 0000000..6e28cbc --- /dev/null +++ b/src/main/java/linkers/FlagNode.java @@ -0,0 +1,25 @@ +package linkers; +public class FlagNode { + private K value; + private boolean visited = false; + + public FlagNode(K value) { + this.setValue(value); + } + + public boolean isVisited() { + return visited; + } + + public void setVisited(boolean visited) { + this.visited = visited; + } + + public K getValue() { + return value; + } + + public void setValue(K value) { + this.value = value; + } +} \ No newline at end of file diff --git a/src/main/java/linkers/FromContinuousBranches.java b/src/main/java/linkers/FromContinuousBranches.java new file mode 100644 index 0000000..2ae4b40 --- /dev/null +++ b/src/main/java/linkers/FromContinuousBranches.java @@ -0,0 +1,138 @@ +package linkers; + + + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.OutputAlgorithm; +import utility.ThreeDRoiobject; +import utility.ThreeDRoiobject; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +public class FromContinuousBranches implements OutputAlgorithm< SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > >, Benchmark +{ + + private static final String BASE_ERROR_MSG = "[FromContinuousBranches] "; + + private long processingTime; + + private final Collection< List< ThreeDRoiobject >> branches; + + private final Collection< List< ThreeDRoiobject >> links; + + private String errorMessage; + + private SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > graph; + + public FromContinuousBranches( final Collection< List< ThreeDRoiobject >> branches, final Collection< List< ThreeDRoiobject >> links ) + { + this.branches = branches; + this.links = links; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public boolean checkInput() + { + final long start = System.currentTimeMillis(); + if ( null == branches ) + { + errorMessage = BASE_ERROR_MSG + "branches are null."; + return false; + } + if ( null == links ) + { + errorMessage = BASE_ERROR_MSG + "links are null."; + return false; + } + for ( final List< ThreeDRoiobject > link : links ) + { + if ( link.size() != 2 ) + { + errorMessage = BASE_ERROR_MSG + "A link is not made of two ThreeDRoiobjects."; + return false; + } + if ( !checkIfInBranches( link.get( 0 ) ) ) + { + errorMessage = BASE_ERROR_MSG + "A ThreeDRoiobject in a link is not present in the branch collection: " + link.get( 0 ) + " in the link " + link.get( 0 ) + "-" + link.get( 1 ) + "."; + return false; + } + if ( !checkIfInBranches( link.get( 1 ) ) ) + { + errorMessage = BASE_ERROR_MSG + "A ThreeDRoiobject in a link is not present in the branch collection: " + link.get( 1 ) + " in the link " + link.get( 0 ) + "-" + link.get( 1 ) + "."; + return false; + } + } + final long end = System.currentTimeMillis(); + processingTime = end - start; + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + graph = new SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge >( DefaultWeightedEdge.class ); + for ( final List< ThreeDRoiobject > branch : branches ) + { + for ( final ThreeDRoiobject ThreeDRoiobject : branch ) + { + graph.addVertex( ThreeDRoiobject ); + } + } + + for ( final List< ThreeDRoiobject > branch : branches ) + { + final Iterator< ThreeDRoiobject > it = branch.iterator(); + ThreeDRoiobject previous = it.next(); + while ( it.hasNext() ) + { + final ThreeDRoiobject ThreeDRoiobject = it.next(); + graph.addEdge( previous, ThreeDRoiobject ); + previous = ThreeDRoiobject; + } + } + + for ( final List< ThreeDRoiobject > link : links ) + { + graph.addEdge( link.get( 0 ), link.get( 1 ) ); + } + + final long end = System.currentTimeMillis(); + processingTime = end - start; + return true; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } + + @Override + public SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > getResult() + { + return graph; + } + + private final boolean checkIfInBranches( final ThreeDRoiobject ThreeDRoiobject ) + { + for ( final List< ThreeDRoiobject > branch : branches ) + { + if ( branch.contains( ThreeDRoiobject ) ) { return true; } + } + return false; + } + +} diff --git a/src/main/java/linkers/Function1.java b/src/main/java/linkers/Function1.java new file mode 100644 index 0000000..85a738c --- /dev/null +++ b/src/main/java/linkers/Function1.java @@ -0,0 +1,21 @@ +package linkers; + + +/** + * Interface for function that compute values from a single input and store it in + * an output. + * @author Jean-Yves Inevez + * + * @param type of input instances + * @param type of output instances + */ +public interface Function1 { + + /** + * Compute a value from the data in input, and store in output. + * @param input the input instance to compute on + * @param output the output instance that will store the computation result + */ + public void compute(final T1 input, final T2 output); + +} diff --git a/src/main/java/linkers/JaqamanLinker.java b/src/main/java/linkers/JaqamanLinker.java new file mode 100644 index 0000000..c66daab --- /dev/null +++ b/src/main/java/linkers/JaqamanLinker.java @@ -0,0 +1,274 @@ +package linkers; + + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import costMatrix.CostMatrixCreator; +import costMatrix.LAPJV; +import costMatrix.SparseCostMatrix; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.util.Util; + + public class JaqamanLinker< K extends Comparable< K >, J extends Comparable< J > > extends BenchmarkAlgorithm implements OutputAlgorithm< Map< K, J > > + { + private Map< K, J > assignments; + + private Map< K, Double > costs; + + private final CostMatrixCreator< K, J > costMatrixCreator; + + private final Logger logger; + + /** + * Creates a new linker for the specified cost matrix creator. See Jaqaman + * et al., Nature Methods, 2008, Figure 1b. + * + * @param costMatrixCreator + * the class in charge of creating linking costs. + * @param logger + * a logger that will receive progress messages. + */ + public JaqamanLinker( final CostMatrixCreator< K, J > costMatrixCreator, final Logger logger ) + { + this.costMatrixCreator = costMatrixCreator; + this.logger = logger; + } + + public JaqamanLinker( final CostMatrixCreator< K, J > costMatrixCreator ) + { + this( costMatrixCreator, Logger.VOID_LOGGER ); + } + + /** + * Returns the resulting assignments from this algorithm. + *

+ * It takes the shape of a map, such that if source is a key of + * the map, it is assigned to target = map.get(source). + * + * @return the assignment map. + * @see #getAssignmentCosts() + */ + @Override + public Map< K, J > getResult() + { + return assignments; + } + + /** + * Returns the costs associated to the assignment results. + *

+ * It takes the shape of a map, such that if source is a key of + * the map, its assignment as a cost cost = map.get(source). + * + * @return the assignment costs. + * @see #getResult() + */ + public Map< K, Double > getAssignmentCosts() + { + return costs; + } + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + /* + * Generate the cost matrix + */ + + logger.setStatus( "Creating the main cost matrix..." ); + if ( !costMatrixCreator.checkInput() || !costMatrixCreator.process() ) + { + errorMessage = costMatrixCreator.getErrorMessage(); + return false; + } + logger.setProgress( 0.5 ); + + final SparseCostMatrix tl = costMatrixCreator.getResult(); + final List< K > matrixRows = costMatrixCreator.getSourceList(); + final List< J > matrixCols = costMatrixCreator.getTargetList(); + + if ( matrixCols.isEmpty() || matrixRows.isEmpty() ) + { + assignments = Collections.emptyMap(); + costs = Collections.emptyMap(); + final long end = System.currentTimeMillis(); + processingTime = end - start; + return true; + } + + /* + * Complement the cost matrix with alternative no linking cost matrix. + */ + + logger.setStatus( "Completing the cost matrix..." ); + + final int nCols = tl.getNCols(); + final int nRows = tl.getNRows(); + + /* + * Top right + */ + + final double[] cctr = new double[ nRows ]; + final int[] kktr = new int[ nRows ]; + for ( int i = 0; i < nRows; i++ ) + { + kktr[ i ] = i; + cctr[ i ] = costMatrixCreator.getAlternativeCostForSource( matrixRows.get( i ) ); + } + final int[] numbertr = new int[ nRows ]; + Arrays.fill( numbertr, 1 ); + final SparseCostMatrix tr = new SparseCostMatrix( cctr, kktr, numbertr, nRows ); + + /* + * Bottom left + */ + final double[] ccbl = new double[ nCols ]; + final int[] kkbl = new int[ nCols ]; + for ( int i = 0; i < kkbl.length; i++ ) + { + kkbl[ i ] = i; + ccbl[ i ] = costMatrixCreator.getAlternativeCostForTarget( matrixCols.get( i ) ); + } + final int[] numberbl = new int[ nCols ]; + Arrays.fill( numberbl, 1 ); + final SparseCostMatrix bl = new SparseCostMatrix( ccbl, kkbl, numberbl, nCols ); + + /* + * Bottom right. + * + * Alt. cost is the overall min of alternative costs. This deviate or + * extend a bit the u-track code. + */ + final double minCost = Math.min( Util.min( ccbl ), Util.min( cctr ) ); + final SparseCostMatrix br = tl.transpose(); + br.fillWith( minCost ); + + /* + * Stitch them together + */ + final SparseCostMatrix full = ( tl.hcat( tr ) ).vcat( bl.hcat( br ) ); + logger.setProgress( 0.6 ); + + /* + * Solve the full cost matrix. + */ + logger.setStatus( "Solving the cost matrix..." ); + final LAPJV solver = new LAPJV( full ); + if ( !solver.checkInput() || !solver.process() ) + { + errorMessage = solver.getErrorMessage(); + return false; + } + + final int[] assgn = solver.getResult(); + assignments = new HashMap< K, J >(); + costs = new HashMap< K, Double >(); + for ( int i = 0; i < assgn.length; i++ ) + { + final int j = assgn[ i ]; + if ( i < matrixRows.size() && j < matrixCols.size() ) + { + final K source = matrixRows.get( i ); + final J target = matrixCols.get( j ); + assignments.put( source, target ); + + final double cost = full.get( i, j, Double.POSITIVE_INFINITY ); + costs.put( source, Double.valueOf( cost ) ); + } + } + + logger.setProgress( 1 ); + logger.setStatus( "" ); + final long end = System.currentTimeMillis(); + processingTime = end - start; + + return true; + } + + public String resultToString() + { + if ( null == assignments ) { return "Not solved yet. Process the algorithm prior to calling this method."; } + + final HashSet< K > unassignedSources = new HashSet< K >( costMatrixCreator.getSourceList() ); + final HashSet< J > unassignedTargets = new HashSet< J >( costMatrixCreator.getTargetList() ); + + int sw = -1; + for ( final K source : unassignedSources ) + { + if ( source.toString().length() > sw ) + { + sw = source.toString().length(); + } + } + sw = sw + 1; + + int tw = -1; + for ( final J target : unassignedTargets ) + { + if ( target.toString().length() > tw ) + { + tw = target.toString().length(); + } + } + tw = tw + 1; + + int cw = 0; + for ( final K source : assignments.keySet() ) + { + final double cost = costs.get( source ).doubleValue(); + if ( Math.log10( cost ) > cw ) + { + cw = ( int ) Math.log10( cost ); + } + } + cw = cw + 1; + + final StringBuilder str = new StringBuilder(); + str.append( "Found " + assignments.size() + " assignments:\n" ); + for ( final K source : assignments.keySet() ) + { + final J target = assignments.get( source ); + + unassignedSources.remove( source ); + unassignedTargets.remove( target ); + + final double cost = costs.get( source ).doubleValue(); + str.append( String.format( "%1$-" + sw + "s → %2$" + tw + "s, cost = %3$" + cw + ".1f\n", source.toString(), target.toString(), cost ) ); + } + + if ( !unassignedSources.isEmpty() ) + { + str.append( "Found " + unassignedSources.size() + " unassigned sources:\n" ); + for ( final K us : unassignedSources ) + { + str.append( String.format( "%1$-" + sw + "s → %2$" + tw + "s\n", us.toString(), 'ø' ) ); + } + } + + if ( !unassignedTargets.isEmpty() ) + { + str.append( "Found " + unassignedTargets.size() + " unassigned targets:\n" ); + for ( final J ut : unassignedTargets ) + { + str.append( String.format( "%1$-" + sw + "s → %2$" + tw + "s\n", 'ø', ut.toString() ) ); + } + } + + return str.toString(); + } +} diff --git a/src/main/java/linkers/KFsearch.java b/src/main/java/linkers/KFsearch.java new file mode 100644 index 0000000..7f8fef7 --- /dev/null +++ b/src/main/java/linkers/KFsearch.java @@ -0,0 +1,371 @@ +package linkers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; + +import javax.swing.JProgressBar; + +import net.imglib2.RealPoint; +import utility.PreRoiobject; +import utility.ThreeDRoiobject; +import utility.ThreeDRoiobjectCollection; + +import java.util.HashSet; +import java.util.Iterator; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import costMatrix.CostFunction; +import costMatrix.JaqamanLinkingCostMatrixCreator; + + + +public class KFsearch implements ThreeDBlobTracker { + + private static final double ALTERNATIVE_COST_FACTOR = 1.05d; + + private static final double PERCENTILE = 1d; + + private static final String BASE_ERROR_MSG = "[KalmanTracker] "; + + private final ThreeDRoiobjectCollection Allblobs; + public final JProgressBar jpb; + private final double maxsearchRadius; + private final double initialsearchRadius; + private final CostFunction UserchosenCostFunction; + private final int maxframeGap; + private HashMap AccountedT; + private SimpleWeightedGraph graph; + + protected Logger logger = Logger.DEFAULT_LOGGER; + protected String errorMessage; + ArrayList> Allblobscopy; + private ThreeDRoiobjectCollection predictionsCollection; + + public KFsearch(final ThreeDRoiobjectCollection Allblobs, + final CostFunction UserchosenCostFunction, final double maxsearchRadius, + final double initialsearchRadius, final int maxframeGap, + final HashMap AccountedT, final JProgressBar jpb) { + + this.Allblobs = Allblobs; + this.jpb = jpb; + this.UserchosenCostFunction = UserchosenCostFunction; + this.initialsearchRadius = initialsearchRadius; + this.maxsearchRadius = maxsearchRadius; + this.maxframeGap = maxframeGap; + this.AccountedT = AccountedT; + + } + + @Override + public SimpleWeightedGraph getResult() { + return graph; + } + + @Override + public boolean checkInput() { + final StringBuilder errrorHolder = new StringBuilder(); + ; + final boolean ok = checkInput(); + if (!ok) { + errorMessage = errrorHolder.toString(); + } + return ok; + } + + @Override + public boolean process() { + + /* + * Outputs + */ + + + System.out.println(AccountedT.size()); + graph = new SimpleWeightedGraph(DefaultWeightedEdge.class); + predictionsCollection = new ThreeDRoiobjectCollection(); + + + + // Find first and second non-empty frames. + final NavigableSet< Integer > keySet = Allblobs.keySet(); + final Iterator< Integer > frameIterator = keySet.iterator(); + int uniqueID = frameIterator.next(); + int uniqueIDnext = frameIterator.next(); + + Collection Firstorphan = generateSpotList(Allblobs, uniqueID); + + Collection Secondorphan = generateSpotList(Allblobs, uniqueIDnext); + + // Max KF search cost. + final double maxCost = maxsearchRadius * maxsearchRadius; + + // Max cost to nucleate KFs. + final double maxInitialCost = initialsearchRadius * initialsearchRadius; + + /* + * Estimate Kalman filter variances. + * + * The search radius is used to derive an estimate of the noise that affects + * position and velocity. The two are linked: if we need a large search radius, + * then the fluoctuations over predicted states are large. + */ + final double positionProcessStd = maxsearchRadius / 2d; + final double velocityProcessStd = maxsearchRadius / 2d; + + double meanSpotRadius = 0d; + for (final ThreeDRoiobject Blob : Secondorphan) { + + + meanSpotRadius += Blob.volume; + } + meanSpotRadius /= Secondorphan.size(); + final double positionMeasurementStd = meanSpotRadius / 1d; + + final Map kalmanFiltersMap = new HashMap( + Secondorphan.size()); + + // Loop from the second frame to the last frame and build + // KalmanFilterMap + Iterator> itSec = AccountedT.entrySet().iterator(); + int percent = 0; + if (itSec.hasNext()) + itSec.next(); + while (itSec.hasNext()) { + percent++; + utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, 100 * percent / (AccountedT.size() - 1), + "Kalman Filter Search for " + " T = " + uniqueID); + int currentT = itSec.next().getValue(); + uniqueID = currentT; + + SimpleWeightedGraph subgraph = new SimpleWeightedGraph( + DefaultWeightedEdge.class); + + List measurements = generateSpotList(Allblobs, uniqueID); + + // Make the preditiction map + final Map predictionMap = new HashMap( + kalmanFiltersMap.size()); + + for (final CVMKalmanFilter kf : kalmanFiltersMap.keySet()) { + final double[] X = kf.predict(); + final ComparableRealPoint point = new ComparableRealPoint(X); + predictionMap.put(point, kf); + + + + } + final List predictions = new ArrayList(predictionMap.keySet()); + + // Orphans are dealt with later + final Collection childlessKFs = new HashSet(kalmanFiltersMap.keySet()); + + /* + * Here we simply link based on minimizing the squared distances to get an + * initial starting point, more advanced Kalman filter costs will be built in + * the next step + */ + + if (!predictions.isEmpty() && !measurements.isEmpty()) { + // Only link measurements to predictions if we have predictions. + + final JaqamanLinkingCostMatrixCreator crm = new JaqamanLinkingCostMatrixCreator( + predictions, measurements, DistanceBasedcost, maxCost, ALTERNATIVE_COST_FACTOR, PERCENTILE); + + final JaqamanLinker linker = new JaqamanLinker( + crm); + if (!linker.checkInput() || !linker.process()) { + errorMessage = BASE_ERROR_MSG + "Error linking candidates in frame " + currentT + ": " + + linker.getErrorMessage(); + return false; + } + final Map agnts = linker.getResult(); + final Map costs = linker.getAssignmentCosts(); + + // Deal with found links. + Secondorphan = new HashSet(measurements); + for (final ComparableRealPoint cm : agnts.keySet()) { + final CVMKalmanFilter kf = predictionMap.get(cm); + + // Create links for found match. + final ThreeDRoiobject source = kalmanFiltersMap.get(kf); + final ThreeDRoiobject target = agnts.get(cm); + + graph.addVertex(source); + graph.addVertex(target); + final DefaultWeightedEdge edge = graph.addEdge(source, target); + final double cost = costs.get(cm); + graph.setEdgeWeight(edge, cost); + + subgraph.addVertex(source); + subgraph.addVertex(target); + final DefaultWeightedEdge subedge = subgraph.addEdge(source, target); + subgraph.setEdgeWeight(subedge, cost); + + // Update Kalman filter + kf.update(MeasureBlob(target)); + + // Update Kalman track PreRoiobject + kalmanFiltersMap.put(kf, target); + + // Remove from orphan set + Secondorphan.remove(target); + + // Remove from childless KF set + childlessKFs.remove(kf); + } + } + + // Deal with orphans from the previous frame. + // Here is the real linking with the actual cost function + + if (!Firstorphan.isEmpty() && !Secondorphan.isEmpty()) { + + // Trying to link orphans with unlinked candidates. + + final JaqamanLinkingCostMatrixCreator ic = new JaqamanLinkingCostMatrixCreator( + Firstorphan, Secondorphan, UserchosenCostFunction, maxInitialCost, ALTERNATIVE_COST_FACTOR, + PERCENTILE); + final JaqamanLinker newLinker = new JaqamanLinker( + ic); + if (!newLinker.checkInput() || !newLinker.process()) { + errorMessage = BASE_ERROR_MSG + "Error linking Blobs from frame " + (currentT - 1) + " to frame " + + currentT + ": " + newLinker.getErrorMessage(); + return false; + } + final Map newAssignments = newLinker.getResult(); + final Map assignmentCosts = newLinker.getAssignmentCosts(); + + // Build links and new KFs from these links. + for (final ThreeDRoiobject source : newAssignments.keySet()) { + final ThreeDRoiobject target = newAssignments.get(source); + + // Remove from orphan collection. + + // Derive initial state and create Kalman filter. + final double[] XP = estimateInitialState(source, target); + final CVMKalmanFilter kt = new CVMKalmanFilter(XP, Double.MIN_NORMAL, positionProcessStd, + velocityProcessStd, positionMeasurementStd); + // We trust the initial state a lot. + + // Store filter and source + kalmanFiltersMap.put(kt, target); + synchronized (graph) { + // Add edge to the graph. + graph.addVertex(source); + graph.addVertex(target); + final DefaultWeightedEdge edge = graph.addEdge(source, target); + final double cost = assignmentCosts.get(source); + graph.setEdgeWeight(edge, cost); + + subgraph.addVertex(source); + subgraph.addVertex(target); + final DefaultWeightedEdge subedge = subgraph.addEdge(source, target); + subgraph.setEdgeWeight(subedge, cost); + } + + } + } + + Firstorphan = Secondorphan; + // Deal with childless KFs. + for (final CVMKalmanFilter kf : childlessKFs) { + // Echo we missed a measurement + kf.update(null); + + // We can bridge a limited number of gaps. If too much, we die. + // If not, we will use predicted state next time. + if (kf.getNOcclusion() > maxframeGap) { + kalmanFiltersMap.remove(kf); + } + } + + } + return true; + } + + @Override + public void setLogger(final Logger logger) { + this.logger = logger; + + } + + @Override + public String getErrorMessage() { + + return errorMessage; + } + + private static final class ComparableRealPoint extends RealPoint implements Comparable { + public ComparableRealPoint(final double[] A) { + // Wrap array. + super(A, false); + } + + /** + * Sort based on X, Y + */ + @Override + public int compareTo(final ComparableRealPoint o) { + int i = 0; + while (i < n) { + if (getDoublePosition(i) != o.getDoublePosition(i)) { + return (int) Math.signum(getDoublePosition(i) - o.getDoublePosition(i)); + } + i++; + } + return hashCode() - o.hashCode(); + } + } + private static final List< ThreeDRoiobject > generateSpotList( final ThreeDRoiobjectCollection spots, final int frame ) + { + final List< ThreeDRoiobject > list = new ArrayList< >( spots.getNThreeDRoiobjects( frame, true ) ); + for ( final Iterator< ThreeDRoiobject > iterator = spots.iterator( frame, true ); iterator.hasNext(); ) + { + list.add( iterator.next() ); + } + return list; + } + + + + private static final double[] MeasureBlob(final ThreeDRoiobject target) { + final double[] location = new double[] { target.geometriccenter[0], target.geometriccenter[1], + target.geometriccenter[2] }; + return location; + } + + private static final double[] estimateInitialState(final ThreeDRoiobject first, final ThreeDRoiobject second) { + final double[] xp = new double[] { second.geometriccenter[0], second.geometriccenter[1], + second.geometriccenter[2], second.diffTo(first, 0), second.diffTo(first, 1), second.diffTo(first, 2) }; + return xp; + } + + /** + * + * Implementations of various cost functions, starting with the simplest one, + * based on minimizing the distances between the links, followed by minimizing + * cost function based on intensity differences between the links. + * + * Cost function that returns the square distance between a KF state and a Blob. + */ + private static final CostFunction DistanceBasedcost = new CostFunction() { + + @Override + public double linkingCost(final ComparableRealPoint state, final ThreeDRoiobject Blob) { + final double dx = state.getDoublePosition(0) - Blob.geometriccenter[0]; + final double dy = state.getDoublePosition(1) - Blob.geometriccenter[1]; + final double dz = state.getDoublePosition(2) - Blob.geometriccenter[2]; + return dx * dx + dy * dy + dz * dz + Double.MIN_NORMAL; + // So that it's never 0 + } + }; + +} \ No newline at end of file diff --git a/src/main/java/linkers/Logger.java b/src/main/java/linkers/Logger.java new file mode 100644 index 0000000..62a9b3c --- /dev/null +++ b/src/main/java/linkers/Logger.java @@ -0,0 +1,346 @@ +package linkers; + + +import java.awt.Color; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +import ij.IJ; + +/** + * This class is used to log messages occurring during TrackMate execution. + */ +public abstract class Logger extends PrintWriter +{ + + public Logger() + { + // Call super with a dummy writer + super( new Writer() + { + @Override + public void write( final char[] cbuf, final int off, final int len ) throws IOException + {} + + @Override + public void flush() throws IOException + {} + + @Override + public void close() throws IOException + {} + } ); + // Replace by a useful writer + this.out = new Writer() + { + @Override + public void close() throws IOException + {} + + @Override + public void flush() throws IOException + {} + + @Override + public void write( final char[] cbuf, final int off, final int len ) throws IOException + { + String str = ""; + for ( int i = off; i < len; i++ ) + str += cbuf[ i ]; + log( str ); + } + }; + } + + public static final Color NORMAL_COLOR = Color.BLACK; + + public static final Color ERROR_COLOR = new Color( 0.8f, 0, 0 ); + + public static final Color GREEN_COLOR = new Color( 0, 0.6f, 0 ); + + public static final Color BLUE_COLOR = new Color( 0, 0, 0.7f ); + + /** + * Append the message to the logger, with the specified color. + * + * @param message + * the message to append. + * @param color + * the color to use. + */ + public abstract void log( String message, Color color ); + + /** + * Send the message to the error channel of this logger. + * + * @param message + * the message to send. + */ + public abstract void error( String message ); + + /** + * Append the message to the logger with default black color. + * + * @param message + * the message to append. + */ + public void log( final String message ) + { + log( message, NORMAL_COLOR ); + } + + /** + * Set the progress value of the process logged by this logger. Values + * should be between 0 and 1, 1 meaning the process if finished. + * + * @param val + * the progress value (double from 0 to 1). + */ + public abstract void setProgress( double val ); + + /** + * Set the status to be displayed by this logger. + * + * @param status + * the status to display. + */ + public abstract void setStatus( String status ); + + /** + * This logger discard any message. + */ + public static final Logger VOID_LOGGER = new Logger() + { + + @Override + public void setStatus( final String status ) + {} + + @Override + public void setProgress( final double val ) + {} + + @Override + public void log( final String message, final Color color ) + {} + + @Override + public void error( final String message ) + {} + }; + + /** + * This {@link Logger} simply outputs to the standard output and standard + * error. The {@link #setProgress(double)} method is ignored, the + * {@link #setStatus(String)} is sent to the console. + */ + public static Logger DEFAULT_LOGGER = new Logger() + { + + @Override + public void log( final String message, final Color color ) + { + System.out.print( message ); + } + + @Override + public void error( final String message ) + { + System.err.print( message ); + } + + @Override + public void setProgress( final double val ) + {} + + @Override + public void setStatus( final String status ) + { + System.out.println( status ); + } + }; + + /** + * This {@link Logger} outputs to the ImageJ log window, and to the ImageJ + * toolbar to report progress. Colors are ignored. + */ + public static Logger IJ_LOGGER = new Logger() + { + + @Override + public void log( final String message, final Color color ) + { + IJ.log( message ); + } + + @Override + public void error( final String message ) + { + IJ.log( message ); + } + + @Override + public void setProgress( final double val ) + { + IJ.showProgress( val ); + } + + @Override + public void setStatus( final String status ) + { + IJ.showStatus( status ); + } + }; + + /** + * This {@link Logger} outputs everything to the ImageJ toolbar. This is not + * optimal for long messages. Colors are ignored. + */ + public static Logger IJTOOLBAR_LOGGER = new Logger() + { + @Override + public void log( final String message, final Color color ) + { + IJ.showStatus( message ); + } + + @Override + public void error( final String message ) + { + IJ.showStatus( message ); + } + + @Override + public void setProgress( final double val ) + { + IJ.showProgress( val ); + } + + @Override + public void setStatus( final String status ) + { + IJ.showStatus( status ); + } + }; + + /** + * This {@link Logger} outputs to a StringBuilder given at construction. + * Report progress and colors are ignored. + */ + public static class StringBuilderLogger extends Logger + { + + private final StringBuilder sb; + + public StringBuilderLogger( final StringBuilder sb ) + { + this.sb = sb; + } + + public StringBuilderLogger() + { + this( new StringBuilder() ); + } + + /* + * METHODS + */ + + @Override + public void log( final String message, final Color color ) + { + sb.append( message ); + } + + @Override + public void error( final String message ) + { + sb.append( message ); + } + + @Override + public void setProgress( final double val ) + {} + + @Override + public void setStatus( final String status ) + { + sb.append( status ); + } + + @Override + public String toString() + { + return sb.toString(); + } + + }; + + /** + * A logger that wraps a master logger and uses it to echo any received + * message. This class is used to report progress of a sub-process. If it is + * sent to a subprocess, the master logger can show a progress in a range + * and from a starting point that can be specified. + * + * @author Jean-Yves Tinevez - 2014 + * + */ + public static class SlaveLogger extends Logger + { + + private final Logger master; + + private final double progressStart; + + private final double progressRange; + + /** + * Create a new slave logger that will report progress to its master in + * the following way: If a sub-process reports a progress of + * val, then the master logger will receive the progress + * value progressStart + progressRange * val. + * + * @param master + * the master {@link Logger}. + * @param progressStart + * the progress to start with. + * @param progressRange + * the progress range to report. + */ + public SlaveLogger( final Logger master, final double progressStart, final double progressRange ) + { + this.master = master; + this.progressStart = progressStart; + this.progressRange = progressRange; + } + + @Override + public void log( final String message, final Color color ) + { + master.log( message, color ); + } + + @Override + public void error( final String message ) + { + master.error( message ); + } + + @Override + public void setProgress( final double val ) + { + master.setProgress( progressStart + progressRange * val ); + } + + @Override + public void setStatus( final String status ) + { + master.setStatus( status ); + } + + } + + + +} diff --git a/src/main/java/linkers/Model3D.java b/src/main/java/linkers/Model3D.java new file mode 100644 index 0000000..0d47d77 --- /dev/null +++ b/src/main/java/linkers/Model3D.java @@ -0,0 +1,877 @@ +package linkers; + +import java.util.Collection; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import utility.FeatureFilter; +import utility.ModelChangeEvent; +import utility.ModelChangeListener; +import utility.ThreeDRoiobject; +import utility.ThreeDRoiobjectCollection; + + +/** + *

The model for the data managed by TrackMate trackmate.

+ *

+ * This is a relatively large class, with a lot of public methods. This + * complexity arose because this class handles data storage and manipulation, + * through user manual editing and automatic processing. To avoid conflicting + * accesses to the data, some specialized methods had to be created, hopefully + * built in coherent sets. + * + * @author Jean-Yves Tinevez <tinevez@pasteur.fr> - 2010-2013 + * + */ +public class Model3D +{ + + /* + * CONSTANTS + */ + + private static final boolean DEBUG = false; + + /* + * FIELDS + */ + + // FEATURES + + private final FeatureModel3D featureModel; + + // TRACKS + + private final TrackModel3D trackModel; + + // ThreeDRoiobjectS + + /** The ThreeDRoiobjects managed by this model. */ + protected ThreeDRoiobjectCollection ThreeDRoiobjects = new ThreeDRoiobjectCollection(); + + // TRANSACTION MODEL + + /** + * Counter for the depth of nested transactions. Each call to beginUpdate + * increments this counter and each call to endUpdate decrements it. When + * the counter reaches 0, the transaction is closed and the respective + * events are fired. Initial value is 0. + */ + private int updateLevel = 0; + + private final HashSet< ThreeDRoiobject > ThreeDRoiobjectsAdded = new HashSet< >(); + + private final HashSet< ThreeDRoiobject > ThreeDRoiobjectsRemoved = new HashSet< >(); + + private final HashSet< ThreeDRoiobject > ThreeDRoiobjectsMoved = new HashSet< >(); + + private final HashSet< ThreeDRoiobject > ThreeDRoiobjectsUpdated = new HashSet< >(); + + /** + * The event cache. During a transaction, some modifications might trigger + * the need to fire a model change event. We want to fire these events only + * when the transaction closes (when the updateLevel reaches 0), so we store + * the event ID in this cache in the meantime. The event cache contains only + * the int IDs of the events listed in {@link ModelChangeEvent}, namely + *

    + *
  • {@link ModelChangeEvent#ThreeDRoiobjectS_COMPUTED} + *
  • {@link ModelChangeEvent#TRACKS_COMPUTED} + *
  • {@link ModelChangeEvent#TRACKS_VISIBILITY_CHANGED} + *
+ * The {@link ModelChangeEvent#MODEL_MODIFIED} cannot be cached this way, + * for it needs to be configured with modification ThreeDRoiobject and edge targets, so + * it uses a different system (see {@link #flushUpdate()}). + */ + private final HashSet< Integer > eventCache = new HashSet< >(); + + // OTHERS + + /** The logger to append processes messages. */ + private Logger logger = Logger.DEFAULT_LOGGER; + + private String spaceUnits = "pixels"; + + private String timeUnits = "frames"; + + // LISTENERS + + /** + * The list of listeners listening to model content change. + */ + Set< ModelChangeListener > modelChangeListeners = new LinkedHashSet< >(); + + /* + * CONSTRUCTOR + */ + + public Model3D() + { + featureModel = createFeatureModel(); + trackModel = createTrackModel(); + } + + /* + * HOOKS + */ + + /** + * Instantiates a blank {@link TrackModel} to use whithin this model. + *

+ * Subclassers can override this method to have the model work with their + * own subclass of {@link TrackModel}. + * + * @return a new instance of {@link TrackModel}. + */ + protected TrackModel3D createTrackModel() + { + return new TrackModel3D(); + } + + /** + * Instantiates a blank {@link FeatureModel} to use whithin this model. + *

+ * Subclassers can override this method to have the model work with their + * own subclass of {@link FeatureModel}. + * + * @return a new instance of {@link FeatureModel}. + */ + protected FeatureModel3D createFeatureModel() + { + return new FeatureModel3D( this ); + } + + /* + * UTILS METHODS + */ + + @Override + public String toString() + { + final StringBuilder str = new StringBuilder(); + + str.append( '\n' ); + if ( null == ThreeDRoiobjects || ThreeDRoiobjects.keySet().size() == 0 ) + { + str.append( "No ThreeDRoiobjects.\n" ); + } + else + { + str.append( "Contains " + ThreeDRoiobjects.getNThreeDRoiobjects( false ) + " ThreeDRoiobjects in total.\n" ); + } + if ( ThreeDRoiobjects.getNThreeDRoiobjects( true ) == 0 ) + { + str.append( "No filtered ThreeDRoiobjects.\n" ); + } + else + { + str.append( "Contains " + ThreeDRoiobjects.getNThreeDRoiobjects( true ) + " filtered ThreeDRoiobjects.\n" ); + } + + str.append( '\n' ); + if ( trackModel.nTracks( false ) == 0 ) + { + str.append( "No tracks.\n" ); + } + else + { + str.append( "Contains " + trackModel.nTracks( false ) + " tracks in total.\n" ); + } + if ( trackModel.nTracks( true ) == 0 ) + { + str.append( "No filtered tracks.\n" ); + } + else + { + str.append( "Contains " + trackModel.nTracks( true ) + " filtered tracks.\n" ); + } + + str.append( '\n' ); + str.append( "Physical units:\n space units: " + spaceUnits + "\n time units: " + timeUnits + '\n' ); + + str.append( '\n' ); + str.append( featureModel.toString() ); + + return str.toString(); + } + + /* + * DEAL WITH MODEL CHANGE LISTENER + */ + + public void addModelChangeListener( final ModelChangeListener listener ) + { + modelChangeListeners.add( listener ); + } + + public boolean removeModelChangeListener( final ModelChangeListener listener ) + { + return modelChangeListeners.remove( listener ); + } + + public Set< ModelChangeListener > getModelChangeListener() + { + return modelChangeListeners; + } + + /* + * PHYSICAL UNITS + */ + + /** + * Sets the physical units for the quantities stored in this model. + * + * @param spaceUnits + * the spatial units (e.g. μm). + * @param timeUnits + * the time units (e.g. min). + */ + public void setPhysicalUnits( final String spaceUnits, final String timeUnits ) + { + this.spaceUnits = spaceUnits; + this.timeUnits = timeUnits; + } + + /** + * Returns the spatial units for the quantities stored in this model. + * + * @return the spatial units. + */ + public String getSpaceUnits() + { + return spaceUnits; + } + + /** + * Returns the time units for the quantities stored in this model. + * + * @return the time units. + */ + public String getTimeUnits() + { + return timeUnits; + } + + /* + * GRAPH MODIFICATION + */ + + public synchronized void beginUpdate() + { + updateLevel++; + if ( DEBUG ) + System.out.println( "[TrackMateModel] #beginUpdate: increasing update level to " + updateLevel + "." ); + } + + public synchronized void endUpdate() + { + updateLevel--; + if ( DEBUG ) + System.out.println( "[TrackMateModel] #endUpdate: decreasing update level to " + updateLevel + "." ); + if ( updateLevel == 0 ) + { + if ( DEBUG ) + System.out.println( "[TrackMateModel] #endUpdate: update level is 0, calling flushUpdate()." ); + flushUpdate(); + } + } + + /* + * TRACK METHODS: WE DELEGATE TO THE TRACK GRAPH MODEL + */ + + /** + * Removes all the tracks from this model. + * + * @param doNotify + * if true, model listeners will be notified with a + * {@link ModelChangeEvent#TRACKS_COMPUTED} event. + */ + public void clearTracks( final boolean doNotify ) + { + trackModel.clear(); + if ( doNotify ) + { + final ModelChangeEvent event = new ModelChangeEvent( this, ModelChangeEvent.TRACKS_COMPUTED ); + for ( final ModelChangeListener listener : modelChangeListeners ) + listener.modelChanged( event ); + } + } + + /** + * Returns the {@link TrackModel} that manages the tracks for this model. + * + * @return the track model. + */ + public TrackModel3D getTrackModel() + { + return trackModel; + } + + /** + * Sets the tracks stored in this model in bulk. + *

+ * Clears the tracks of this model and replace it by the tracks found by + * inspecting the specified graph. All new tracks found will be made visible + * and will be given a default name. + *

+ * + * @param graph + * the graph to parse for tracks. + * @param doNotify + * if true, model listeners will be notified with a + * {@link ModelChangeEvent#TRACKS_COMPUTED} event. + */ + public void setTracks( final SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > graph, final boolean doNotify ) + { + trackModel.setGraph( graph ); + if ( doNotify ) + { + final ModelChangeEvent event = new ModelChangeEvent( this, ModelChangeEvent.TRACKS_COMPUTED ); + for ( final ModelChangeListener listener : modelChangeListeners ) + listener.modelChanged( event ); + } + } + + /* + * GETTERS / SETTERS FOR ThreeDRoiobjectS + */ + + /** + * Returns the ThreeDRoiobject collection managed by this model. + * + * @return the ThreeDRoiobject collection managed by this model. + */ + public ThreeDRoiobjectCollection getThreeDRoiobjects() + { + return ThreeDRoiobjects; + } + + /** + * Removes all the ThreeDRoiobjects from this model. + * + * @param doNotify + * if true, model listeners will be notified with a + * {@link ModelChangeEvent#ThreeDRoiobjectS_COMPUTED} event. + */ + public void clearThreeDRoiobjects( final boolean doNotify ) + { + ThreeDRoiobjects.clear(); + if ( doNotify ) + { + final ModelChangeEvent event = new ModelChangeEvent( this, ModelChangeEvent.ThreeDRoiobjectS_COMPUTED ); + for ( final ModelChangeListener listener : modelChangeListeners ) + listener.modelChanged( event ); + } + } + + /** + * Set the {@link ThreeDRoiobjectCollection} managed by this model. + * + * @param doNotify + * if true, will file a {@link ModelChangeEvent#ThreeDRoiobjectS_COMPUTED} + * event. + * @param ThreeDRoiobjects + * the {@link ThreeDRoiobjectCollection} to set. + */ + public void setThreeDRoiobjects( final ThreeDRoiobjectCollection ThreeDRoiobjects, final boolean doNotify ) + { + this.ThreeDRoiobjects = ThreeDRoiobjects; + if ( doNotify ) + { + final ModelChangeEvent event = new ModelChangeEvent( this, ModelChangeEvent.ThreeDRoiobjectS_COMPUTED ); + for ( final ModelChangeListener listener : modelChangeListeners ) + listener.modelChanged( event ); + } + } + + /** + * Filters the {@link ThreeDRoiobjectCollection} managed by this model with the + * {@link FeatureFilter}s specified. + * + * @param ThreeDRoiobjectFilters + * the {@link FeatureFilter} collection to use for filtering. + * @param doNotify + * if true, will file a {@link ModelChangeEvent#ThreeDRoiobjectS_FILTERED} + * event. + */ + public void filterThreeDRoiobjects( final Collection< FeatureFilter > ThreeDRoiobjectFilters, final boolean doNotify ) + { + ThreeDRoiobjects.filter( ThreeDRoiobjectFilters ); + if ( doNotify ) + { + final ModelChangeEvent event = new ModelChangeEvent( this, ModelChangeEvent.ThreeDRoiobjectS_FILTERED ); + for ( final ModelChangeListener listener : modelChangeListeners ) + listener.modelChanged( event ); + } + + } + + /* + * LOGGER + */ + + /** + * Set the logger that will receive the messages from the processes + * occurring within this trackmate. + * + * @param logger + * the {@link Logger} to use. + */ + public void setLogger( final Logger logger ) + { + this.logger = logger; + } + + /** + * Return the logger currently set for this model. + * + * @return the {@link Logger} used. + */ + public Logger getLogger() + { + return logger; + } + + /* + * FEATURES + */ + + public FeatureModel3D getFeatureModel() + { + return featureModel; + } + + /* + * MODEL CHANGE METHODS + */ + + /** + * Moves a single ThreeDRoiobject from a frame to another, make it visible if it was + * not, then mark it for feature update. If the source ThreeDRoiobject could not be + * found in the source frame, nothing is done and null is + * returned. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param ThreeDRoiobjectToMove + * the ThreeDRoiobject to move + * @param fromFrame + * the frame the ThreeDRoiobject originated from + * @param toFrame + * the destination frame + * @return the ThreeDRoiobject that was moved, or null if it could not be + * found in the source frame + */ + public synchronized ThreeDRoiobject moveThreeDRoiobjectFrom( final ThreeDRoiobject ThreeDRoiobjectToMove, final Integer fromFrame, final Integer toFrame ) + { + final boolean ok = ThreeDRoiobjects.remove( ThreeDRoiobjectToMove, fromFrame ); + if ( !ok ) + { + if ( DEBUG ) + { + System.err.println( "[TrackMateModel] Could not find ThreeDRoiobject " + ThreeDRoiobjectToMove + " in frame " + fromFrame ); + } + return null; + } + ThreeDRoiobjects.add( ThreeDRoiobjectToMove, toFrame ); + if ( DEBUG ) + { + System.out.println( "[TrackMateModel] Moving " + ThreeDRoiobjectToMove + " from frame " + fromFrame + " to frame " + toFrame ); + } + + // Mark for update ThreeDRoiobject and edges + trackModel.edgesModified.addAll( trackModel.edgesOf( ThreeDRoiobjectToMove ) ); + ThreeDRoiobjectsMoved.add( ThreeDRoiobjectToMove ); + return ThreeDRoiobjectToMove; + } + + /** + * Adds a single ThreeDRoiobject to the collections managed by this model, mark it as + * visible, then update its features. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param ThreeDRoiobjectToAdd + * the ThreeDRoiobject to add. + * @param toFrame + * the frame to add it to. + * + * @return the ThreeDRoiobject just added. + */ + public synchronized ThreeDRoiobject addThreeDRoiobjectTo( final ThreeDRoiobject ThreeDRoiobjectToAdd, final Integer toFrame ) + { + ThreeDRoiobjects.add( ThreeDRoiobjectToAdd, toFrame ); + ThreeDRoiobjectsAdded.add( ThreeDRoiobjectToAdd ); // TRANSACTION + if ( DEBUG ) + { + System.out.println( "[TrackMateModel] Adding ThreeDRoiobject " + ThreeDRoiobjectToAdd + " to frame " + toFrame ); + } + trackModel.addThreeDRoiobject( ThreeDRoiobjectToAdd ); + return ThreeDRoiobjectToAdd; + } + + /** + * Removes a single ThreeDRoiobject from the collections managed by this model. If the + * ThreeDRoiobject cannot be found, nothing is done and null is returned. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param ThreeDRoiobjectToRemove + * the ThreeDRoiobject to remove. + * @return the ThreeDRoiobject removed, or null if it could not be found. + */ + public synchronized ThreeDRoiobject removeThreeDRoiobject( final ThreeDRoiobject ThreeDRoiobjectToRemove ) + { + final int fromFrame = ThreeDRoiobjectToRemove.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( ThreeDRoiobjects.remove( ThreeDRoiobjectToRemove, fromFrame ) ) + { + ThreeDRoiobjectsRemoved.add( ThreeDRoiobjectToRemove ); // TRANSACTION + if ( DEBUG ) + System.out.println( "[TrackMateModel] Removing ThreeDRoiobject " + ThreeDRoiobjectToRemove + " from frame " + fromFrame ); + + trackModel.removeThreeDRoiobject( ThreeDRoiobjectToRemove ); + // changes to edges will be caught automatically by the TrackGraphModel + return ThreeDRoiobjectToRemove; + } + if ( DEBUG ) + System.err.println( "[TrackMateModel] The ThreeDRoiobject " + ThreeDRoiobjectToRemove + " cannot be found in frame " + fromFrame ); + + return null; + } + + /** + * Mark the specified ThreeDRoiobject for update. At the end of the model transaction, + * its features will be recomputed, and other edge and track features that + * depends on it will be as well. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param ThreeDRoiobjectToUpdate + * the ThreeDRoiobject to mark for update + */ + public synchronized void updateFeatures( final ThreeDRoiobject ThreeDRoiobjectToUpdate ) + { + ThreeDRoiobjectsUpdated.add( ThreeDRoiobjectToUpdate ); // Enlist for feature update when + // transaction is marked as finished + final Set< DefaultWeightedEdge > touchingEdges = trackModel.edgesOf( ThreeDRoiobjectToUpdate ); + if ( null != touchingEdges ) + { + trackModel.edgesModified.addAll( touchingEdges ); + } + } + + /** + * Creates a new edge between two ThreeDRoiobjects, with the specified weight. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param source + * the source ThreeDRoiobject. + * @param target + * the target ThreeDRoiobject. + * @param weight + * the weight of the edge. + * @return the edge created. + */ + public synchronized DefaultWeightedEdge addEdge( final ThreeDRoiobject source, final ThreeDRoiobject target, final double weight ) + { + return trackModel.addEdge( source, target, weight ); + } + + /** + * Removes an edge between two ThreeDRoiobjects and returns it. Returns + * null and do nothing to the tracks if the edge did not exist. + * + * @param source + * the source ThreeDRoiobject. + * @param target + * the target ThreeDRoiobject. + * @return the edge between the two ThreeDRoiobjects, if it existed. + */ + public synchronized DefaultWeightedEdge removeEdge( final ThreeDRoiobject source, final ThreeDRoiobject target ) + { + return trackModel.removeEdge( source, target ); + } + + /** + * Removes an existing edge from the model. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param edge + * the edge to remove. + * @return true if the edge existed in the model and was + * successfully, false otherwise. + */ + public synchronized boolean removeEdge( final DefaultWeightedEdge edge ) + { + return trackModel.removeEdge( edge ); + } + + /** + * Sets the weight of the specified edge. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param edge + * the edge. + * @param weight + * the weight to set. + */ + public synchronized void setEdgeWeight( final DefaultWeightedEdge edge, final double weight ) + { + trackModel.setEdgeWeight( edge, weight ); + } + + /** + * Sets the visibility of the specified track. Throws a + * {@link NullPointerException} if the track ID is unknown to the model. + *

+ * For the model update to happen correctly and listeners to be notified + * properly, a call to this method must happen within a transaction, as in: + * + *

+	 * model.beginUpdate();
+	 * try {
+	 * 	... // model modifications here
+	 * } finally {
+	 * 	model.endUpdate();
+	 * }
+	 * 
+ * + * @param trackID + * the track ID. + * @param visible + * the desired visibility. + * @return the specified track visibility prior to calling this method. + */ + public synchronized boolean setTrackVisibility( final Integer trackID, final boolean visible ) + { + final boolean oldvis = trackModel.setVisibility( trackID, visible ); + final boolean modified = oldvis != visible; + if ( modified ) + { + eventCache.add( ModelChangeEvent.TRACKS_VISIBILITY_CHANGED ); + } + return oldvis; + } + + /* + * PRIVATE METHODS + */ + + /** + * Fire events. Regenerate fields derived from the filtered graph. + */ + private void flushUpdate() + { + + if ( DEBUG ) + { + System.out.println( "[TrackMateModel] #flushUpdate()." ); + System.out.println( "[TrackMateModel] #flushUpdate(): Event cache is :" + eventCache ); + System.out.println( "[TrackMateModel] #flushUpdate(): Track content is:\n" + trackModel.echo() ); + } + + /* + * We recompute tracks only if some edges have been added or removed, + * (if some ThreeDRoiobjects have been removed that causes edges to be removes, we + * already know about it). We do NOT recompute tracks if ThreeDRoiobjects have been + * added: they will not result in new tracks made of single ThreeDRoiobjects. + */ + final int nEdgesToSignal = trackModel.edgesAdded.size() + trackModel.edgesRemoved.size() + trackModel.edgesModified.size(); + + // Do we have tracks to update? + final HashSet< Integer > tracksToUpdate = new HashSet< >( trackModel.tracksUpdated ); + + // We also want to update the tracks that have edges that were modified + for ( final DefaultWeightedEdge modifiedEdge : trackModel.edgesModified ) + { + tracksToUpdate.add( trackModel.trackIDOf( modifiedEdge ) ); + } + + // Deal with new or moved ThreeDRoiobjects: we need to update their features. + final int nThreeDRoiobjectsToUpdate = ThreeDRoiobjectsAdded.size() + ThreeDRoiobjectsMoved.size() + ThreeDRoiobjectsUpdated.size(); + if ( nThreeDRoiobjectsToUpdate > 0 ) + { + final HashSet< ThreeDRoiobject > ThreeDRoiobjectsToUpdate = new HashSet< >( nThreeDRoiobjectsToUpdate ); + ThreeDRoiobjectsToUpdate.addAll( ThreeDRoiobjectsAdded ); + ThreeDRoiobjectsToUpdate.addAll( ThreeDRoiobjectsMoved ); + ThreeDRoiobjectsToUpdate.addAll( ThreeDRoiobjectsUpdated ); + } + + // Initialize event + final ModelChangeEvent event = new ModelChangeEvent( this, ModelChangeEvent.MODEL_MODIFIED ); + + // Configure it with ThreeDRoiobjects to signal. + final int nThreeDRoiobjectsToSignal = nThreeDRoiobjectsToUpdate + ThreeDRoiobjectsRemoved.size(); + if ( nThreeDRoiobjectsToSignal > 0 ) + { + event.addAllThreeDRoiobjects( ThreeDRoiobjectsAdded ); + event.addAllThreeDRoiobjects( ThreeDRoiobjectsRemoved ); + event.addAllThreeDRoiobjects( ThreeDRoiobjectsMoved ); + event.addAllThreeDRoiobjects( ThreeDRoiobjectsUpdated ); + + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectsAdded ) + { + event.putThreeDRoiobjectFlag( ThreeDRoiobject, ModelChangeEvent.FLAG_ThreeDRoiobject_ADDED ); + } + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectsRemoved ) + { + event.putThreeDRoiobjectFlag( ThreeDRoiobject, ModelChangeEvent.FLAG_ThreeDRoiobject_REMOVED ); + } + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectsMoved ) + { + event.putThreeDRoiobjectFlag( ThreeDRoiobject, ModelChangeEvent.FLAG_ThreeDRoiobject_FRAME_CHANGED ); + } + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectsUpdated ) + { + event.putThreeDRoiobjectFlag( ThreeDRoiobject, ModelChangeEvent.FLAG_ThreeDRoiobject_MODIFIED ); + } + } + + // Configure it with edges to signal. + if ( nEdgesToSignal > 0 ) + { + event.addAllEdges( trackModel.edgesAdded ); + event.addAllEdges( trackModel.edgesRemoved ); + event.addAllEdges( trackModel.edgesModified ); + + for ( final DefaultWeightedEdge edge : trackModel.edgesAdded ) + { + event.putEdgeFlag( edge, ModelChangeEvent.FLAG_EDGE_ADDED ); + } + for ( final DefaultWeightedEdge edge : trackModel.edgesRemoved ) + { + event.putEdgeFlag( edge, ModelChangeEvent.FLAG_EDGE_REMOVED ); + } + for ( final DefaultWeightedEdge edge : trackModel.edgesModified ) + { + event.putEdgeFlag( edge, ModelChangeEvent.FLAG_EDGE_MODIFIED ); + } + } + + // Configure it with the tracks we found need updating + event.setTracksUpdated( tracksToUpdate ); + + try + { + if ( nEdgesToSignal + nThreeDRoiobjectsToSignal > 0 ) + { + if ( DEBUG ) + { + System.out.println( "[TrackMateModel] #flushUpdate(): firing model modified event" ); + System.out.println( "[TrackMateModel] to " + modelChangeListeners ); + + } + for ( final ModelChangeListener listener : modelChangeListeners ) + { + listener.modelChanged( event ); + } + } + + // Fire events stored in the event cache + for ( final int eventID : eventCache ) + { + if ( DEBUG ) + { + System.out.println( "[TrackMateModel] #flushUpdate(): firing event with ID " + eventID ); + } + final ModelChangeEvent cachedEvent = new ModelChangeEvent( this, eventID ); + for ( final ModelChangeListener listener : modelChangeListeners ) + { + listener.modelChanged( cachedEvent ); + } + } + + } + finally + { + ThreeDRoiobjectsAdded.clear(); + ThreeDRoiobjectsRemoved.clear(); + ThreeDRoiobjectsMoved.clear(); + ThreeDRoiobjectsUpdated.clear(); + trackModel.edgesAdded.clear(); + trackModel.edgesRemoved.clear(); + trackModel.edgesModified.clear(); + trackModel.tracksUpdated.clear(); + eventCache.clear(); + } + + } + +} diff --git a/src/main/java/linkers/NNFlagsearchKDtree.java b/src/main/java/linkers/NNFlagsearchKDtree.java new file mode 100644 index 0000000..d2da429 --- /dev/null +++ b/src/main/java/linkers/NNFlagsearchKDtree.java @@ -0,0 +1,91 @@ +package linkers; + +import net.imglib2.KDTree; +import net.imglib2.KDTreeNode; +import net.imglib2.RealLocalizable; +import net.imglib2.Sampler; +import net.imglib2.neighborsearch.NearestNeighborSearch; + +public class NNFlagsearchKDtree implements NearestNeighborSearch< FlagNode > { + +protected KDTree< FlagNode > tree; + + protected final int n; + protected final double[] pos; + + protected KDTreeNode< FlagNode > bestPoint; + protected double bestSquDistance; + + public NNFlagsearchKDtree( KDTree< FlagNode > tree ) { + n = tree.numDimensions(); + pos = new double[ n ]; + this.tree = tree; + } + + @Override + public int numDimensions() { + return n; + } + + @Override + public void search( RealLocalizable p ) { + p.localize( pos ); + bestSquDistance = Double.MAX_VALUE; + searchNode( tree.getRoot() ); + } + + protected void searchNode( KDTreeNode< FlagNode > current ) { + // consider the current node + final double distance = current.squDistanceTo( pos ); + boolean visited = current.get().isVisited(); + if ( distance < bestSquDistance && !visited ) { + bestSquDistance = distance; + bestPoint = current; + } + + final double axisDiff = pos[ current.getSplitDimension() ] - current.getSplitCoordinate(); + final double axisSquDistance = axisDiff * axisDiff; + final boolean leftIsNearBranch = axisDiff < 0; + + // search the near branch + final KDTreeNode< FlagNode > nearChild = leftIsNearBranch ? current.left : current.right; + final KDTreeNode< FlagNode > awayChild = leftIsNearBranch ? current.right : current.left; + if ( nearChild != null ) + searchNode( nearChild ); + + // search the away branch - maybe + if ( ( axisSquDistance <= bestSquDistance ) && ( awayChild != null ) ) + searchNode( awayChild ); + } + + @Override + public Sampler> getSampler() { + return bestPoint; + } + + @Override + public RealLocalizable getPosition() { + return bestPoint; + } + + @Override + public double getSquareDistance() { + return bestSquDistance; + } + + @Override + public double getDistance() { + return Math.sqrt( bestSquDistance ); + } + + @Override + public NNFlagsearchKDtree copy() { + final NNFlagsearchKDtree copy = new NNFlagsearchKDtree( tree ); + System.arraycopy( pos, 0, copy.pos, 0, pos.length ); + copy.bestPoint = bestPoint; + copy.bestSquDistance = bestSquDistance; + return copy; + } + +} + \ No newline at end of file diff --git a/src/main/java/linkers/PRENNsearch.java b/src/main/java/linkers/PRENNsearch.java new file mode 100644 index 0000000..952e2ab --- /dev/null +++ b/src/main/java/linkers/PRENNsearch.java @@ -0,0 +1,198 @@ +package linkers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.swing.JProgressBar; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + + +import net.imglib2.KDTree; +import net.imglib2.RealPoint; +import utility.PreRoiobject; + +public class PRENNsearch implements BlobTracker { + + private final ConcurrentHashMap> Allblobs; + private final double maxdistance; + private int T; + public JProgressBar jpb; + private SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > graph; + protected Logger logger = Logger.DEFAULT_LOGGER; + protected String errorMessage; + private HashMap AccountedZ; + public PRENNsearch( + final ConcurrentHashMap> Allblobs, final double maxdistance, final int T, + final HashMap AccountedZ, JProgressBar jpb){ + this.Allblobs = Allblobs; + this.maxdistance = maxdistance; + this.T = T; + this.AccountedZ = AccountedZ; + this.jpb = jpb; + + + } + + + + + @Override + public boolean process() { + + reset(); + + Iterator> it = AccountedZ.entrySet().iterator(); + int percent = 0; + while(it.hasNext()) { + + percent++; + + int Z = it.next().getValue(); + + + while(it.hasNext()) { + + + utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, 100 * percent / (AccountedZ.size() - 1), + "Computing Nearest Neighbours for " + " T = " + T + + " Z = " + Z); + int nextZ = it.next().getValue(); + + + String uniqueID = Integer.toString(Z) + Integer.toString(T); + String uniqueIDnext = Integer.toString(nextZ) + Integer.toString(T); + + + ArrayList Spotmaxbase = Allblobs.get(uniqueID); + + ArrayList Spotmaxtarget = Allblobs.get(uniqueIDnext); + + + if (Spotmaxtarget != null && Spotmaxtarget.size() > 0) { + + Iterator baseobjectiterator = Spotmaxbase.iterator(); + + + + final int Targetblobs = Spotmaxtarget.size(); + + final List targetCoords = new ArrayList(Targetblobs); + + final List> targetNodes = new ArrayList>(Targetblobs); + + + + for (int index = 0; index < Spotmaxtarget.size(); ++index){ + + + + targetCoords.add(new RealPoint(Spotmaxtarget.get(index).geometriccenter)); + + targetNodes.add(new FlagNode(Spotmaxtarget.get(index))); + + + } + + if (targetNodes.size() > 0 && targetCoords.size() > 0){ + + final KDTree> Tree = new KDTree>(targetNodes, targetCoords); + + final NNFlagsearchKDtree Search = new NNFlagsearchKDtree(Tree); + + + while(baseobjectiterator.hasNext()){ + + final PreRoiobject source = baseobjectiterator.next(); + final RealPoint sourceCoords = new RealPoint(source.geometriccenter); + Search.search(sourceCoords); + final double squareDist = Search.getSquareDistance(); + + if (squareDist > maxdistance) + continue; + + + final FlagNode targetNode = Search.getSampler().get(); + + + targetNode.setVisited(true); + + synchronized (graph) { + + graph.addVertex(source); + graph.addVertex(targetNode.getValue()); + final DefaultWeightedEdge edge = graph.addEdge(source, targetNode.getValue()); + graph.setEdgeWeight(edge, squareDist); + + + } + + + } + + } + } + + Z = nextZ; + } + } + return true; + + } + + + @Override + public void setLogger( final Logger logger) { + this.logger = logger; + + } + + + @Override + public SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > getResult() + { + return graph; + } + + + + @Override + public boolean checkInput() { + final StringBuilder errrorHolder = new StringBuilder();; + final boolean ok = checkInput(); + if (!ok) { + errorMessage = errrorHolder.toString(); + } + return ok; + } + + + public void reset() { + + graph = new SimpleWeightedGraph(DefaultWeightedEdge.class); + + if (Allblobs!=null && Allblobs.size() > 0) { + + ArrayList firstobject = Allblobs.entrySet().iterator().next().getValue(); + + + + final Iterator it = firstobject.iterator(); + while (it.hasNext()) { + graph.addVertex(it.next()); + } + + } + } + + @Override + public String getErrorMessage() { + + return errorMessage; + } +} diff --git a/src/main/java/linkers/SortedDepthFirstIterator.java b/src/main/java/linkers/SortedDepthFirstIterator.java new file mode 100644 index 0000000..a620d67 --- /dev/null +++ b/src/main/java/linkers/SortedDepthFirstIterator.java @@ -0,0 +1,544 @@ +package linkers; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.jgrapht.DirectedGraph; +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.event.ConnectedComponentTraversalEvent; +import org.jgrapht.event.EdgeTraversalEvent; +import org.jgrapht.event.VertexTraversalEvent; +import org.jgrapht.traverse.AbstractGraphIterator; +import org.jgrapht.traverse.CrossComponentIterator; +import org.jgrapht.util.TypeUtil; + +/** + * A depth-first iterator, that - when branching - chooses the next vertex + * according to a specified comparator. + *

+ * I had to copy-paste whole sections of JGraphT code to make this one: I could + * not extend the desired class, for the interesting method and field were + * private. + * + * @author Jean-Yves Tinevez 2012-2013 + */ +public class SortedDepthFirstIterator< V, E > extends AbstractGraphIterator< V, E > +{ + + // ~ Static fields/initializers + // --------------------------------------------- + + private static final int CCS_BEFORE_COMPONENT = 1; + + private static final int CCS_WITHIN_COMPONENT = 2; + + private static final int CCS_AFTER_COMPONENT = 3; + + /** + * Sentinel object. Unfortunately, we can't use null, because ArrayDeque + * won't accept those. And we don't want to rely on the caller to provide a + * sentinel object for us. So we have to play typecasting games. + */ + private static final Object SENTINEL = new Object(); + + /** + * Standard vertex visit state enumeration. + */ + private static enum VisitColor + { + /** + * Vertex has not been returned via iterator yet. + */ + WHITE, + + /** + * Vertex has been returned via iterator, but we're not done with all of + * its out-edges yet. + */ + GRAY, + + /** + * Vertex has been returned via iterator, and we're done with all of its + * out-edges. + */ + BLACK + } + + // ~ Instance fields + // -------------------------------------------------------- + + private final Deque< Object > stack = new ArrayDeque< Object >(); + + + private final ConnectedComponentTraversalEvent ccFinishedEvent = + new ConnectedComponentTraversalEvent( + this, + ConnectedComponentTraversalEvent.CONNECTED_COMPONENT_FINISHED ); + + private final ConnectedComponentTraversalEvent ccStartedEvent = + new ConnectedComponentTraversalEvent( + this, + ConnectedComponentTraversalEvent.CONNECTED_COMPONENT_STARTED ); + + + private Iterator< V > vertexIterator = null; + + /** + * Stores the vertices that have been seen during iteration and (optionally) + * some additional traversal info regarding each vertex. + */ + protected Map< V, VisitColor > seen = new HashMap< V, VisitColor >(); + + private V startVertex; + + protected Specifics< V, E > specifics; + + protected final Graph< V, E > graph; + + protected final Comparator< V > comparator; + + /** The connected component state */ + private int state = CCS_BEFORE_COMPONENT; + + // ~ Constructors + // ----------------------------------------------------------- + + /** + * Creates a new iterator for the specified graph. Iteration will start at + * the specified start vertex. If the specified start vertex is + * null, Iteration will start at an arbitrary graph vertex. + * + * @param g + * the graph to be iterated. + * @param startVertex + * the vertex iteration to be started. + * + * @throws IllegalArgumentException + * if g==null or does not contain + * startVertex + */ + public SortedDepthFirstIterator( final Graph< V, E > g, final V startVertex, final Comparator< V > comparator ) + { + super(g); + this.comparator = comparator; + this.graph = g; + + specifics = createGraphSpecifics( g ); + vertexIterator = g.vertexSet().iterator(); + setCrossComponentTraversal( startVertex == null ); + + + + if ( startVertex == null ) + { + // pick a start vertex if graph not empty + if ( vertexIterator.hasNext() ) + { + this.startVertex = vertexIterator.next(); + } + else + { + this.startVertex = null; + } + } + else if ( g.containsVertex( startVertex ) ) + { + this.startVertex = startVertex; + } + else + { + throw new IllegalArgumentException( + "graph must contain the start vertex" ); + } + } + + // ~ Methods + // ---------------------------------------------------------------- + + /** + * @see java.util.Iterator#hasNext() + */ + @Override + public boolean hasNext() + { + if ( startVertex != null ) + { + encounterStartVertex(); + } + + if ( isConnectedComponentExhausted() ) + { + if ( state == CCS_WITHIN_COMPONENT ) + { + state = CCS_AFTER_COMPONENT; + if ( nListeners != 0 ) + { + fireConnectedComponentFinished( ccFinishedEvent ); + } + } + + if ( isCrossComponentTraversal() ) + { + while ( vertexIterator.hasNext() ) + { + final V v = vertexIterator.next(); + + if ( !seen.containsKey( v ) ) + { + encounterVertex( v, null ); + state = CCS_BEFORE_COMPONENT; + + return true; + } + } + + return false; + } + else + { + return false; + } + } + else + { + return true; + } + } + + /** + * @see java.util.Iterator#next() + */ + @Override + public V next() + { + + if ( startVertex != null ) + { + encounterStartVertex(); + } + + if ( hasNext() ) + { + if ( state == CCS_BEFORE_COMPONENT ) + { + state = CCS_WITHIN_COMPONENT; + if ( nListeners != 0 ) + { + fireConnectedComponentStarted( ccStartedEvent ); + } + } + + final V nextVertex = provideNextVertex(); + if ( nListeners != 0 ) + { + fireVertexTraversed( createVertexTraversalEvent( nextVertex ) ); + } + + addUnseenChildrenOf( nextVertex ); + + return nextVertex; + } + else + { + throw new NoSuchElementException(); + } + } + + /** + * Called when a vertex has been finished (meaning is dependent on traversal + * represented by subclass). + * + * @param vertex + * vertex which has been finished + */ + private void finishVertex( final V vertex ) + { + if ( nListeners != 0 ) + { + fireVertexFinished( createVertexTraversalEvent( vertex ) ); + } + } + + // ------------------------------------------------------------------------- + + private static < V, E > Specifics< V, E > createGraphSpecifics( final Graph< V, E > g ) + { + if ( g instanceof DirectedGraph ) + { + return new DirectedSpecifics< V, E >( ( DirectedGraph< V, E > ) g ); + } + else + { + return new UndirectedSpecifics< V, E >( g ); + } + } + + /** + * This is where we add the multiple children in proper sorted order. + */ + protected void addUnseenChildrenOf( final V vertex ) + { + + // Retrieve target vertices, and sort them in a list + final List< V > sortedChildren = new ArrayList< V >(); + // Keep a map of matching edges so that we can retrieve them in the same + // order + final Map< V, E > localEdges = new HashMap< V, E >(); + + for ( final E edge : specifics.edgesOf( vertex ) ) + { + final V oppositeV = Graphs.getOppositeVertex( graph, edge, vertex ); + if ( !seen.containsKey( oppositeV ) ) + { + sortedChildren.add( oppositeV ); + } + localEdges.put( oppositeV, edge ); + } + Collections.sort( sortedChildren, Collections.reverseOrder( comparator ) ); + + final Iterator< V > it = sortedChildren.iterator(); + while ( it.hasNext() ) + { + final V child = it.next(); + + if ( nListeners != 0 ) + { + fireEdgeTraversed( createEdgeTraversalEvent( localEdges.get( child ) ) ); + } + + if ( seen.containsKey( child ) ) + { + encounterVertexAgain( child, localEdges.get( child ) ); + } + else + { + encounterVertex( child, localEdges.get( child ) ); + } + } + } + + private void encounterStartVertex() + { + encounterVertex( startVertex, null ); + startVertex = null; + } + + // ~ Depth-first iterator methods + // ---------------------------------------------------------------- + + /** + * @see org.jgrapht.traverse.CrossComponentIterator#isConnectedComponentExhausted() + */ + private boolean isConnectedComponentExhausted() + { + for ( ;; ) + { + if ( stack.isEmpty() ) { return true; } + if ( stack.getLast() != SENTINEL ) + { + // Found a non-sentinel. + return false; + } + + // Found a sentinel: pop it, record the finish time, + // and then loop to check the rest of the stack. + + // Pop null we peeked at above. + stack.removeLast(); + + // This will pop corresponding vertex to be recorded as finished. + recordFinish(); + } + } + + /** + * @see org.jgrapht.traverse.CrossComponentIterator#encounterVertex(Object, + * Object) + */ + protected void encounterVertex( final V vertex, final E edge ) + { + seen.put( vertex, VisitColor.WHITE ); + stack.addLast( vertex ); + } + + /** + * @see org.jgrapht.traverse.CrossComponentIterator#encounterVertexAgain(Object, + * Object) + */ + protected void encounterVertexAgain( final V vertex, final E edge ) + { + final VisitColor color = seen.get( vertex ); + if ( color != VisitColor.WHITE ) + { + // We've already visited this vertex; no need to mess with the + // stack (either it's BLACK and not there at all, or it's GRAY + // and therefore just a sentinel). + return; + } + + // Since we've encountered it before, and it's still WHITE, it + // *must* be on the stack. Use removeLastOccurrence on the + // assumption that for typical topologies and traversals, + // it's likely to be nearer the top of the stack than + // the bottom of the stack. + final boolean found = stack.removeLastOccurrence( vertex ); + assert ( found ); + stack.addLast( vertex ); + } + + /** + * @see CrossComponentIterator#provideNextVertex() + */ + private V provideNextVertex() + { + V v; + for ( ;; ) + { + final Object o = stack.removeLast(); + if ( o == SENTINEL ) + { + // This is a finish-time sentinel we previously pushed. + recordFinish(); + // Now carry on with another pop until we find a non-sentinel + } + else + { + // Got a real vertex to start working on + v = TypeUtil.uncheckedCast( o ); + break; + } + } + + // Push a sentinel for v onto the stack so that we'll know + // when we're done with it. + stack.addLast( v ); + stack.addLast( SENTINEL ); + seen.put( v, VisitColor.GRAY ); + return v; + } + + private void recordFinish() + { + final V v = TypeUtil.uncheckedCast( stack.removeLast() ); + seen.put( v, VisitColor.BLACK ); + finishVertex( v ); + } + + // ~ Inner Classes + // ---------------------------------------------------------- + + /** + * Provides unified interface for operations that are different in directed + * graphs and in undirected graphs. + */ + abstract static class Specifics< VV, EE > + { + /** + * Returns the edges outgoing from the specified vertex in case of + * directed graph, and the edge touching the specified vertex in case of + * undirected graph. + * + * @param vertex + * the vertex whose outgoing edges are to be returned. + * + * @return the edges outgoing from the specified vertex in case of + * directed graph, and the edge touching the specified vertex in + * case of undirected graph. + */ + public abstract Set< ? extends EE > edgesOf( VV vertex ); + } + + + + /** + * A reusable vertex event. + * + * @author Barak Naveh + * @since Aug 11, 2003 + */ + private static class FlyweightVertexEvent< VV > extends VertexTraversalEvent< VV > + { + private static final long serialVersionUID = 3834024753848399924L; + + /** + * @see VertexTraversalEvent#VertexTraversalEvent(Object, Object) + */ + public FlyweightVertexEvent( final Object eventSource, final VV vertex ) + { + super( eventSource, vertex ); + } + + /** + * Sets the vertex of this event. + * + * @param vertex + * the vertex to be set. + */ + protected void setVertex( final VV vertex ) + { + this.vertex = vertex; + } + } + + /** + * An implementation of {@link Specifics} for a directed graph. + */ + private static class DirectedSpecifics< VV, EE > extends Specifics< VV, EE > + { + private final DirectedGraph< VV, EE > graph; + + /** + * Creates a new DirectedSpecifics object. + * + * @param g + * the graph for which this specifics object to be created. + */ + public DirectedSpecifics( final DirectedGraph< VV, EE > g ) + { + graph = g; + } + + @Override + public Set< ? extends EE > edgesOf( final VV vertex ) + { + return graph.outgoingEdgesOf( vertex ); + } + } + + /** + * An implementation of {@link Specifics} in which edge direction (if any) + * is ignored. + */ + private static class UndirectedSpecifics< VV, EE > extends Specifics< VV, EE > + { + private final Graph< VV, EE > graph; + + /** + * Creates a new UndirectedSpecifics object. + * + * @param g + * the graph for which this specifics object to be created. + */ + public UndirectedSpecifics( final Graph< VV, EE > g ) + { + graph = g; + } + + @Override + public Set< EE > edgesOf( final VV vertex ) + { + return graph.edgesOf( vertex ); + } + } + +} diff --git a/src/main/java/linkers/ThreeDBlobTracker.java b/src/main/java/linkers/ThreeDBlobTracker.java new file mode 100644 index 0000000..701c2c7 --- /dev/null +++ b/src/main/java/linkers/ThreeDBlobTracker.java @@ -0,0 +1,39 @@ +package linkers; + + + +import java.util.ArrayList; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import net.imglib2.algorithm.MultiThreaded; +import net.imglib2.algorithm.OutputAlgorithm; +import utility.PreRoiobject; +import utility.ThreeDRoiobject; + +/** + * + * links objects across multiple frames in time-lapse images, Creates a new graph from a list of blobs, the blob properties of the current frame + * are enumerated in the static properties + * @author varunkapoor + * + */ + + +public interface ThreeDBlobTracker extends OutputAlgorithm< SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > > + { + /** + * Sets the {@link Logger} instance that will receive messages from this + * {@link SpotTracker}. + * + * @param logger + * the logger to echo messages to. + */ + public void setLogger( final Logger logger ); + + + + } + + diff --git a/src/main/java/linkers/TimeDirectedDepthFirstIterator.java b/src/main/java/linkers/TimeDirectedDepthFirstIterator.java new file mode 100644 index 0000000..a14a637 --- /dev/null +++ b/src/main/java/linkers/TimeDirectedDepthFirstIterator.java @@ -0,0 +1,41 @@ +package linkers; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.graph.DefaultWeightedEdge; + +import utility.PreRoiobject; + +public class TimeDirectedDepthFirstIterator extends SortedDepthFirstIterator { + + public TimeDirectedDepthFirstIterator(Graph g, PreRoiobject startVertex) { + super(g, startVertex, null); + } + + + + protected void addUnseenChildrenOf(PreRoiobject vertex) { + + int ts = vertex.getFeature(PreRoiobject.Time).intValue(); + for (DefaultWeightedEdge edge : specifics.edgesOf(vertex)) { + if (nListeners != 0) { + fireEdgeTraversed(createEdgeTraversalEvent(edge)); + } + + PreRoiobject oppositeV = Graphs.getOppositeVertex(graph, edge, vertex); + int tt = oppositeV.getFeature(PreRoiobject.Time).intValue(); + if (tt <= ts) { + continue; + } + + if ( seen.containsKey(oppositeV)) { + encounterVertexAgain(oppositeV, edge); + } else { + encounterVertex(oppositeV, edge); + } + } + } + + + +} diff --git a/src/main/java/linkers/TimeDirectedDepthFirstIterator3D.java b/src/main/java/linkers/TimeDirectedDepthFirstIterator3D.java new file mode 100644 index 0000000..54aa23b --- /dev/null +++ b/src/main/java/linkers/TimeDirectedDepthFirstIterator3D.java @@ -0,0 +1,41 @@ +package linkers; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.graph.DefaultWeightedEdge; + +import utility.ThreeDRoiobject; + +public class TimeDirectedDepthFirstIterator3D extends SortedDepthFirstIterator { + + public TimeDirectedDepthFirstIterator3D(Graph g, ThreeDRoiobject startVertex) { + super(g, startVertex, null); + } + + + + protected void addUnseenChildrenOf(ThreeDRoiobject vertex) { + + int ts = vertex.getFeature(ThreeDRoiobject.Time).intValue(); + for (DefaultWeightedEdge edge : specifics.edgesOf(vertex)) { + if (nListeners != 0) { + fireEdgeTraversed(createEdgeTraversalEvent(edge)); + } + + ThreeDRoiobject oppositeV = Graphs.getOppositeVertex(graph, edge, vertex); + int tt = oppositeV.getFeature(ThreeDRoiobject.Time).intValue(); + if (tt <= ts) { + continue; + } + + if ( seen.containsKey(oppositeV)) { + encounterVertexAgain(oppositeV, edge); + } else { + encounterVertex(oppositeV, edge); + } + } + } + + + +} diff --git a/src/main/java/linkers/TimeDirectedNeighborIndex.java b/src/main/java/linkers/TimeDirectedNeighborIndex.java new file mode 100644 index 0000000..7c3064d --- /dev/null +++ b/src/main/java/linkers/TimeDirectedNeighborIndex.java @@ -0,0 +1,306 @@ +package linkers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.alg.NeighborIndex; +import org.jgrapht.event.GraphEdgeChangeEvent; +import org.jgrapht.event.GraphVertexChangeEvent; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.util.ModifiableInteger; + +import utility.PreRoiobject; + + + +public class TimeDirectedNeighborIndex extends NeighborIndex< PreRoiobject, DefaultWeightedEdge > +{ + + // ~ Instance fields + // -------------------------------------------------------- + + Map< PreRoiobject, Neighbors< PreRoiobject, DefaultWeightedEdge > > predecessorMap = new HashMap< PreRoiobject, Neighbors< PreRoiobject, DefaultWeightedEdge > >(); + + Map< PreRoiobject, Neighbors< PreRoiobject, DefaultWeightedEdge > > successorMap = new HashMap< PreRoiobject, Neighbors< PreRoiobject, DefaultWeightedEdge > >(); + + private final Graph< PreRoiobject, DefaultWeightedEdge > graph; + + // ~ Constructors + // ----------------------------------------------------------- + + public TimeDirectedNeighborIndex( final Graph< PreRoiobject, DefaultWeightedEdge > g ) + { + super( g ); + this.graph = g; + } + + // ~ Methods + // ---------------------------------------------------------------- + + /** + * Returns the set of vertices which are the predecessors of a specified + * vertex. The returned set is backed by the index, and will be updated when + * the graph changes as long as the index has been added as a listener to + * the graph. + * + * @param v + * the vertex whose predecessors are desired + * + * @return all unique predecessors of the specified vertex + */ + public Set< PreRoiobject > predecessorsOf( final PreRoiobject v ) + { + return getPredecessors( v ).getNeighbors(); + } + + /** + * Returns the set of vertices which are the predecessors of a specified + * vertex. If the graph is a multigraph, vertices may appear more than once + * in the returned list. Because a list of predecessors can not be + * efficiently maintained, it is reconstructed on every invocation by + * duplicating entries in the neighbor set. It is thus more efficient to use + * {@link #predecessorsOf(PreRoiobject)} unless duplicate neighbors are required. + * + * @param v + * the vertex whose predecessors are desired + * + * @return all predecessors of the specified vertex + */ + public List< PreRoiobject > predecessorListOf( final PreRoiobject v ) + { + return getPredecessors( v ).getNeighborList(); + } + + /** + * Returns the set of vertices which are the successors of a specified + * vertex. The returned set is backed by the index, and will be updated when + * the graph changes as long as the index has been added as a listener to + * the graph. + * + * @param v + * the vertex whose successors are desired + * + * @return all unique successors of the specified vertex + */ + public Set< PreRoiobject > successorsOf( final PreRoiobject v ) + { + return getSuccessors( v ).getNeighbors(); + } + + /** + * Returns the set of vertices which are the successors of a specified + * vertex. If the graph is a multigraph, vertices may appear more than once + * in the returned list. Because a list of successors can not be efficiently + * maintained, it is reconstructed on every invocation by duplicating + * entries in the neighbor set. It is thus more efficient to use + * {@link #successorsOf(PreRoiobject)} unless duplicate neighbors are required. + * + * @param v + * the vertex whose successors are desired + * + * @return all successors of the specified vertex + */ + public List< PreRoiobject > successorListOf( final PreRoiobject v ) + { + return getSuccessors( v ).getNeighborList(); + } + + /** + * @see org.jgrapht.event.GraphListener#edgeAdded(GraphEdgeChangeEvent) + */ + @Override + public void edgeAdded( final GraphEdgeChangeEvent< PreRoiobject, DefaultWeightedEdge > e ) + { + final DefaultWeightedEdge edge = e.getEdge(); + final PreRoiobject source = graph.getEdgeSource( edge ); + final PreRoiobject target = graph.getEdgeTarget( edge ); + + // if a map does not already contain an entry, + // then skip addNeighbor, since instantiating the map + // will take care of processing the edge (which has already + // been added) + + if ( successorMap.containsKey( source ) ) + { + getSuccessors( source ).addNeighbor( target ); + } + else + { + getSuccessors( source ); + } + if ( predecessorMap.containsKey( target ) ) + { + getPredecessors( target ).addNeighbor( source ); + } + else + { + getPredecessors( target ); + } + } + + /** + * @see org.jgrapht.event.GraphListener#edgeRemoved(GraphEdgeChangeEvent) + */ + @Override + public void edgeRemoved( final GraphEdgeChangeEvent< PreRoiobject, DefaultWeightedEdge > e ) + { + final DefaultWeightedEdge edge = e.getEdge(); + final PreRoiobject source = graph.getEdgeSource( edge ); + final PreRoiobject target = graph.getEdgeTarget( edge ); + if ( successorMap.containsKey( source ) ) + { + successorMap.get( source ).removeNeighbor( target ); + } + if ( predecessorMap.containsKey( target ) ) + { + predecessorMap.get( target ).removeNeighbor( source ); + } + } + + /** + * @see org.jgrapht.event.VertexSetListener#vertexAdded(GraphVertexChangeEvent) + */ + @Override + public void vertexAdded( final GraphVertexChangeEvent< PreRoiobject > e ) + { + // nothing to cache until there are edges + } + + /** + * @see org.jgrapht.event.VertexSetListener#vertexRemoved(GraphVertexChangeEvent) + */ + @Override + public void vertexRemoved( final GraphVertexChangeEvent< PreRoiobject > e ) + { + predecessorMap.remove( e.getVertex() ); + successorMap.remove( e.getVertex() ); + } + + private Neighbors< PreRoiobject, DefaultWeightedEdge > getPredecessors( final PreRoiobject v ) + { + Neighbors< PreRoiobject, DefaultWeightedEdge > neighbors = predecessorMap.get( v ); + if ( neighbors == null ) + { + final List< PreRoiobject > nl = Graphs.neighborListOf( graph, v ); + final List< PreRoiobject > bnl = new ArrayList< PreRoiobject >(); + final int ts = v.getFeature( PreRoiobject.Time ).intValue(); + for ( final PreRoiobject PreRoiobject : nl ) + { + final int tt = PreRoiobject.getFeature( PreRoiobject.Time ).intValue(); + if ( tt < ts ) + { + bnl.add( PreRoiobject ); + } + } + neighbors = new Neighbors< PreRoiobject, DefaultWeightedEdge >( v, bnl ); + predecessorMap.put( v, neighbors ); + } + return neighbors; + } + + private Neighbors< PreRoiobject, DefaultWeightedEdge > getSuccessors( final PreRoiobject v ) + { + Neighbors< PreRoiobject, DefaultWeightedEdge > neighbors = successorMap.get( v ); + if ( neighbors == null ) + { + final List< PreRoiobject > nl = Graphs.neighborListOf( graph, v ); + final List< PreRoiobject > bnl = new ArrayList< PreRoiobject >(); + final int ts = v.getFeature( PreRoiobject.Time ).intValue(); + for ( final PreRoiobject PreRoiobject : nl ) + { + final int tt = PreRoiobject.getFeature( PreRoiobject.Time ).intValue(); + if ( tt > ts ) + { + bnl.add( PreRoiobject ); + } + } + neighbors = new Neighbors< PreRoiobject, DefaultWeightedEdge >( v, bnl ); + successorMap.put( v, neighbors ); + } + return neighbors; + } + + // ~ Inner Classes + // ---------------------------------------------------------- + + /** + * Stores cached neighbors for a single vertex. Includes support for live + * neighbor sets and duplicate neighbors. + */ + static class Neighbors< V, E > + { + private final Map< V, ModifiableInteger > neighborCounts = + new LinkedHashMap< V, ModifiableInteger >(); + + // TODO could eventually make neighborSet modifiable, resulting + // in edge removals from the graph + private final Set< V > neighborSet = + Collections.unmodifiableSet( + neighborCounts.keySet() ); + + public Neighbors( final V v, final Collection< V > neighbors ) + { + // add all current neighbors + for ( final V neighbor : neighbors ) + { + addNeighbor( neighbor ); + } + } + + public void addNeighbor( final V v ) + { + ModifiableInteger count = neighborCounts.get( v ); + if ( count == null ) + { + count = new ModifiableInteger( 1 ); + neighborCounts.put( v, count ); + } + else + { + count.increment(); + } + } + + public void removeNeighbor( final V v ) + { + final ModifiableInteger count = neighborCounts.get( v ); + if ( count == null ) { throw new IllegalArgumentException( + "Attempting to remove a neighbor that wasn't present" ); } + + count.decrement(); + if ( count.getValue() == 0 ) + { + neighborCounts.remove( v ); + } + } + + public Set< V > getNeighbors() + { + return neighborSet; + } + + public List< V > getNeighborList() + { + final List< V > neighbors = new ArrayList< V >(); + for ( final Map.Entry< V, ModifiableInteger > entry : neighborCounts.entrySet() ) + { + final V v = entry.getKey(); + final int count = entry.getValue().intValue(); + for ( int i = 0; i < count; i++ ) + { + neighbors.add( v ); + } + } + return neighbors; + } + } + +} diff --git a/src/main/java/linkers/TimeDirectedNeighborIndex3D.java b/src/main/java/linkers/TimeDirectedNeighborIndex3D.java new file mode 100644 index 0000000..d7a3437 --- /dev/null +++ b/src/main/java/linkers/TimeDirectedNeighborIndex3D.java @@ -0,0 +1,306 @@ +package linkers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.alg.NeighborIndex; +import org.jgrapht.event.GraphEdgeChangeEvent; +import org.jgrapht.event.GraphVertexChangeEvent; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.util.ModifiableInteger; + +import utility.ThreeDRoiobject; + + + +public class TimeDirectedNeighborIndex3D extends NeighborIndex< ThreeDRoiobject, DefaultWeightedEdge > +{ + + // ~ Instance fields + // -------------------------------------------------------- + + Map< ThreeDRoiobject, Neighbors< ThreeDRoiobject, DefaultWeightedEdge > > predecessorMap = new HashMap< ThreeDRoiobject, Neighbors< ThreeDRoiobject, DefaultWeightedEdge > >(); + + Map< ThreeDRoiobject, Neighbors< ThreeDRoiobject, DefaultWeightedEdge > > successorMap = new HashMap< ThreeDRoiobject, Neighbors< ThreeDRoiobject, DefaultWeightedEdge > >(); + + private final Graph< ThreeDRoiobject, DefaultWeightedEdge > graph; + + // ~ Constructors + // ----------------------------------------------------------- + + public TimeDirectedNeighborIndex3D( final Graph< ThreeDRoiobject, DefaultWeightedEdge > g ) + { + super( g ); + this.graph = g; + } + + // ~ Methods + // ---------------------------------------------------------------- + + /** + * Returns the set of vertices which are the predecessors of a specified + * vertex. The returned set is backed by the index, and will be updated when + * the graph changes as long as the index has been added as a listener to + * the graph. + * + * @param v + * the vertex whose predecessors are desired + * + * @return all unique predecessors of the specified vertex + */ + public Set< ThreeDRoiobject > predecessorsOf( final ThreeDRoiobject v ) + { + return getPredecessors( v ).getNeighbors(); + } + + /** + * Returns the set of vertices which are the predecessors of a specified + * vertex. If the graph is a multigraph, vertices may appear more than once + * in the returned list. Because a list of predecessors can not be + * efficiently maintained, it is reconstructed on every invocation by + * duplicating entries in the neighbor set. It is thus more efficient to use + * {@link #predecessorsOf(ThreeDRoiobject)} unless duplicate neighbors are required. + * + * @param v + * the vertex whose predecessors are desired + * + * @return all predecessors of the specified vertex + */ + public List< ThreeDRoiobject > predecessorListOf( final ThreeDRoiobject v ) + { + return getPredecessors( v ).getNeighborList(); + } + + /** + * Returns the set of vertices which are the successors of a specified + * vertex. The returned set is backed by the index, and will be updated when + * the graph changes as long as the index has been added as a listener to + * the graph. + * + * @param v + * the vertex whose successors are desired + * + * @return all unique successors of the specified vertex + */ + public Set< ThreeDRoiobject > successorsOf( final ThreeDRoiobject v ) + { + return getSuccessors( v ).getNeighbors(); + } + + /** + * Returns the set of vertices which are the successors of a specified + * vertex. If the graph is a multigraph, vertices may appear more than once + * in the returned list. Because a list of successors can not be efficiently + * maintained, it is reconstructed on every invocation by duplicating + * entries in the neighbor set. It is thus more efficient to use + * {@link #successorsOf(ThreeDRoiobject)} unless duplicate neighbors are required. + * + * @param v + * the vertex whose successors are desired + * + * @return all successors of the specified vertex + */ + public List< ThreeDRoiobject > successorListOf( final ThreeDRoiobject v ) + { + return getSuccessors( v ).getNeighborList(); + } + + /** + * @see org.jgrapht.event.GraphListener#edgeAdded(GraphEdgeChangeEvent) + */ + @Override + public void edgeAdded( final GraphEdgeChangeEvent< ThreeDRoiobject, DefaultWeightedEdge > e ) + { + final DefaultWeightedEdge edge = e.getEdge(); + final ThreeDRoiobject source = graph.getEdgeSource( edge ); + final ThreeDRoiobject target = graph.getEdgeTarget( edge ); + + // if a map does not already contain an entry, + // then skip addNeighbor, since instantiating the map + // will take care of processing the edge (which has already + // been added) + + if ( successorMap.containsKey( source ) ) + { + getSuccessors( source ).addNeighbor( target ); + } + else + { + getSuccessors( source ); + } + if ( predecessorMap.containsKey( target ) ) + { + getPredecessors( target ).addNeighbor( source ); + } + else + { + getPredecessors( target ); + } + } + + /** + * @see org.jgrapht.event.GraphListener#edgeRemoved(GraphEdgeChangeEvent) + */ + @Override + public void edgeRemoved( final GraphEdgeChangeEvent< ThreeDRoiobject, DefaultWeightedEdge > e ) + { + final DefaultWeightedEdge edge = e.getEdge(); + final ThreeDRoiobject source = graph.getEdgeSource( edge ); + final ThreeDRoiobject target = graph.getEdgeTarget( edge ); + if ( successorMap.containsKey( source ) ) + { + successorMap.get( source ).removeNeighbor( target ); + } + if ( predecessorMap.containsKey( target ) ) + { + predecessorMap.get( target ).removeNeighbor( source ); + } + } + + /** + * @see org.jgrapht.event.VertexSetListener#vertexAdded(GraphVertexChangeEvent) + */ + @Override + public void vertexAdded( final GraphVertexChangeEvent< ThreeDRoiobject > e ) + { + // nothing to cache until there are edges + } + + /** + * @see org.jgrapht.event.VertexSetListener#vertexRemoved(GraphVertexChangeEvent) + */ + @Override + public void vertexRemoved( final GraphVertexChangeEvent< ThreeDRoiobject > e ) + { + predecessorMap.remove( e.getVertex() ); + successorMap.remove( e.getVertex() ); + } + + private Neighbors< ThreeDRoiobject, DefaultWeightedEdge > getPredecessors( final ThreeDRoiobject v ) + { + Neighbors< ThreeDRoiobject, DefaultWeightedEdge > neighbors = predecessorMap.get( v ); + if ( neighbors == null ) + { + final List< ThreeDRoiobject > nl = Graphs.neighborListOf( graph, v ); + final List< ThreeDRoiobject > bnl = new ArrayList< ThreeDRoiobject >(); + final int ts = v.getFeature( ThreeDRoiobject.Time ).intValue(); + for ( final ThreeDRoiobject ThreeDRoiobject : nl ) + { + final int tt = ThreeDRoiobject.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( tt < ts ) + { + bnl.add( ThreeDRoiobject ); + } + } + neighbors = new Neighbors< ThreeDRoiobject, DefaultWeightedEdge >( v, bnl ); + predecessorMap.put( v, neighbors ); + } + return neighbors; + } + + private Neighbors< ThreeDRoiobject, DefaultWeightedEdge > getSuccessors( final ThreeDRoiobject v ) + { + Neighbors< ThreeDRoiobject, DefaultWeightedEdge > neighbors = successorMap.get( v ); + if ( neighbors == null ) + { + final List< ThreeDRoiobject > nl = Graphs.neighborListOf( graph, v ); + final List< ThreeDRoiobject > bnl = new ArrayList< ThreeDRoiobject >(); + final int ts = v.getFeature( ThreeDRoiobject.Time ).intValue(); + for ( final ThreeDRoiobject ThreeDRoiobject : nl ) + { + final int tt = ThreeDRoiobject.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( tt > ts ) + { + bnl.add( ThreeDRoiobject ); + } + } + neighbors = new Neighbors< ThreeDRoiobject, DefaultWeightedEdge >( v, bnl ); + successorMap.put( v, neighbors ); + } + return neighbors; + } + + // ~ Inner Classes + // ---------------------------------------------------------- + + /** + * Stores cached neighbors for a single vertex. Includes support for live + * neighbor sets and duplicate neighbors. + */ + static class Neighbors< V, E > + { + private final Map< V, ModifiableInteger > neighborCounts = + new LinkedHashMap< V, ModifiableInteger >(); + + // TODO could eventually make neighborSet modifiable, resulting + // in edge removals from the graph + private final Set< V > neighborSet = + Collections.unmodifiableSet( + neighborCounts.keySet() ); + + public Neighbors( final V v, final Collection< V > neighbors ) + { + // add all current neighbors + for ( final V neighbor : neighbors ) + { + addNeighbor( neighbor ); + } + } + + public void addNeighbor( final V v ) + { + ModifiableInteger count = neighborCounts.get( v ); + if ( count == null ) + { + count = new ModifiableInteger( 1 ); + neighborCounts.put( v, count ); + } + else + { + count.increment(); + } + } + + public void removeNeighbor( final V v ) + { + final ModifiableInteger count = neighborCounts.get( v ); + if ( count == null ) { throw new IllegalArgumentException( + "Attempting to remove a neighbor that wasn't present" ); } + + count.decrement(); + if ( count.getValue() == 0 ) + { + neighborCounts.remove( v ); + } + } + + public Set< V > getNeighbors() + { + return neighborSet; + } + + public List< V > getNeighborList() + { + final List< V > neighbors = new ArrayList< V >(); + for ( final Map.Entry< V, ModifiableInteger > entry : neighborCounts.entrySet() ) + { + final V v = entry.getKey(); + final int count = entry.getValue().intValue(); + for ( int i = 0; i < count; i++ ) + { + neighbors.add( v ); + } + } + return neighbors; + } + } + +} diff --git a/src/main/java/linkers/TimeDirectedSortedDepthFirstIterator.java b/src/main/java/linkers/TimeDirectedSortedDepthFirstIterator.java new file mode 100644 index 0000000..7462bfb --- /dev/null +++ b/src/main/java/linkers/TimeDirectedSortedDepthFirstIterator.java @@ -0,0 +1,68 @@ +package linkers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.graph.DefaultWeightedEdge; + +import utility.PreRoiobject; + + +public class TimeDirectedSortedDepthFirstIterator extends SortedDepthFirstIterator { + + public TimeDirectedSortedDepthFirstIterator(final Graph g, final PreRoiobject startVertex, final Comparator comparator) { + super(g, startVertex, comparator); + } + + + + @Override + protected void addUnseenChildrenOf(final PreRoiobject vertex) { + + // Retrieve target vertices, and sort them in a list + final List< PreRoiobject > sortedChildren = new ArrayList< PreRoiobject >(); + // Keep a map of matching edges so that we can retrieve them in the same order + final Map localEdges = new HashMap(); + + final int ts = vertex.getFeature(PreRoiobject.Time).intValue(); + for (final DefaultWeightedEdge edge : specifics.edgesOf(vertex)) { + + final PreRoiobject oppositeV = Graphs.getOppositeVertex(graph, edge, vertex); + final int tt = oppositeV.getFeature(PreRoiobject.Time).intValue(); + if (tt <= ts) { + continue; + } + + if (!seen.containsKey(oppositeV)) { + sortedChildren.add(oppositeV); + } + localEdges.put(oppositeV, edge); + } + + Collections.sort( sortedChildren, Collections.reverseOrder( comparator ) ); + final Iterator< PreRoiobject > it = sortedChildren.iterator(); + while (it.hasNext()) { + final PreRoiobject child = it.next(); + + if (nListeners != 0) { + fireEdgeTraversed(createEdgeTraversalEvent(localEdges.get(child))); + } + + if (seen.containsKey(child)) { + encounterVertexAgain(child, localEdges.get(child)); + } else { + encounterVertex(child, localEdges.get(child)); + } + } + } + + + +} diff --git a/src/main/java/linkers/TimeDirectedSortedDepthFirstIterator3D.java b/src/main/java/linkers/TimeDirectedSortedDepthFirstIterator3D.java new file mode 100644 index 0000000..46c5386 --- /dev/null +++ b/src/main/java/linkers/TimeDirectedSortedDepthFirstIterator3D.java @@ -0,0 +1,68 @@ +package linkers; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.jgrapht.Graph; +import org.jgrapht.Graphs; +import org.jgrapht.graph.DefaultWeightedEdge; + +import utility.ThreeDRoiobject; + + +public class TimeDirectedSortedDepthFirstIterator3D extends SortedDepthFirstIterator { + + public TimeDirectedSortedDepthFirstIterator3D(final Graph g, final ThreeDRoiobject startVertex, final Comparator comparator) { + super(g, startVertex, comparator); + } + + + + @Override + protected void addUnseenChildrenOf(final ThreeDRoiobject vertex) { + + // Retrieve target vertices, and sort them in a list + final List< ThreeDRoiobject > sortedChildren = new ArrayList< ThreeDRoiobject >(); + // Keep a map of matching edges so that we can retrieve them in the same order + final Map localEdges = new HashMap(); + + final int ts = vertex.getFeature(ThreeDRoiobject.Time).intValue(); + for (final DefaultWeightedEdge edge : specifics.edgesOf(vertex)) { + + final ThreeDRoiobject oppositeV = Graphs.getOppositeVertex(graph, edge, vertex); + final int tt = oppositeV.getFeature(ThreeDRoiobject.Time).intValue(); + if (tt <= ts) { + continue; + } + + if (!seen.containsKey(oppositeV)) { + sortedChildren.add(oppositeV); + } + localEdges.put(oppositeV, edge); + } + + Collections.sort( sortedChildren, Collections.reverseOrder( comparator ) ); + final Iterator< ThreeDRoiobject > it = sortedChildren.iterator(); + while (it.hasNext()) { + final ThreeDRoiobject child = it.next(); + + if (nListeners != 0) { + fireEdgeTraversed(createEdgeTraversalEvent(localEdges.get(child))); + } + + if (seen.containsKey(child)) { + encounterVertexAgain(child, localEdges.get(child)); + } else { + encounterVertex(child, localEdges.get(child)); + } + } + } + + + +} diff --git a/src/main/java/linkers/TrackModel.java b/src/main/java/linkers/TrackModel.java new file mode 100644 index 0000000..1eb6a01 --- /dev/null +++ b/src/main/java/linkers/TrackModel.java @@ -0,0 +1,1396 @@ +package linkers; + + + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.UndirectedGraph; +import org.jgrapht.VertexFactory; +import org.jgrapht.alg.DijkstraShortestPath; +import org.jgrapht.event.ConnectedComponentTraversalEvent; +import org.jgrapht.event.EdgeTraversalEvent; +import org.jgrapht.event.GraphEdgeChangeEvent; +import org.jgrapht.event.GraphListener; +import org.jgrapht.event.GraphVertexChangeEvent; +import org.jgrapht.event.TraversalListener; +import org.jgrapht.event.TraversalListenerAdapter; +import org.jgrapht.event.VertexTraversalEvent; +import org.jgrapht.graph.AsUnweightedGraph; +import org.jgrapht.graph.DefaultListenableGraph; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.ListenableUndirectedGraph; +import org.jgrapht.graph.SimpleDirectedWeightedGraph; +import org.jgrapht.graph.SimpleWeightedGraph; +import org.jgrapht.traverse.BreadthFirstIterator; +import org.jgrapht.traverse.DepthFirstIterator; +import org.jgrapht.traverse.GraphIterator; + +import utility.PreRoiobject; + + + +public class TrackModel +{ +/** + * A component of {@link Model} specialized for tracks. + * + * @author Jean-Yves Tinevez + */ + + + /** + * The mother graph, from which all subsequent fields are calculated. This + * graph is not made accessible to the outside world. Editing it must be + * trough the model methods {@link #addEdge(PreRoiobject, PreRoiobject, double)}, + * {@link #removeEdge(DefaultWeightedEdge)}, {@link #removeEdge(PreRoiobject, PreRoiobject)} + * . + */ + private DefaultListenableGraph< PreRoiobject, DefaultWeightedEdge > graph; + + private final MyGraphListener mgl; + + /* + * TRANSACTION FIELDS + */ + + /** + * The edges that have been added to this model by + * {@link #addEdge(PreRoiobject, PreRoiobject, double)}. + *

+ * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< DefaultWeightedEdge > edgesAdded = new HashSet< DefaultWeightedEdge >(); + + /** + * The edges that have removed from this model by + * {@link #removeEdge(DefaultWeightedEdge)} or + * {@link #removeEdge(PreRoiobject, PreRoiobject)}. + *

+ * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< DefaultWeightedEdge > edgesRemoved = new HashSet< DefaultWeightedEdge >(); + + /** + * The edges that have been modified in this model by changing its cost + * using {@link #setEdgeWeight(DefaultWeightedEdge, double)} or modifying + * the PreRoiobjects it links elsewhere. + *

+ * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< DefaultWeightedEdge > edgesModified = new HashSet< DefaultWeightedEdge >(); + + /** + * The track IDs that have been modified, updated or created, solely + * by removing or adding an edge. Possibly after the removal of a PreRoiobject. + * Tracks having edges that are modified, for instance by modifying a + * PreRoiobject it contains, will not be listed here, but must be sought from the + * {@link #edgesModified} field. + *

+ * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< Integer > tracksUpdated = new HashSet< Integer >(); + + private static final Boolean DEFAULT_VISIBILITY = Boolean.TRUE; + + // ~ Instance fields + // -------------------------------------------------------- + + private int IDcounter = 0; + + private Map< Integer, Set< DefaultWeightedEdge > > connectedEdgeSets; + + Map< DefaultWeightedEdge, Integer > edgeToID; + + private Map< Integer, HashSet< PreRoiobject > > connectedVertexSets; + + Map< PreRoiobject, Integer > vertexToID; + + private Map< Integer, Boolean > visibility; + + private Map< Integer, String > names; + + private final Iterator< String > nameGenerator = new DefaultNameGenerator(); + + /* + * Constructors ----------------------------------------------------------- + */ + + public TrackModel() + { + this( new SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge >( DefaultWeightedEdge.class ) ); + } + + public TrackModel( final SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > graph ) + { + this.mgl = new MyGraphListener(); + setGraph( graph ); + } + + /* + * CREATION METHODS Methods that wipes the current content and replace it in + * bulk. + */ + + /** + * Clears the content of this model and replace it by the tracks found by + * inspecting the specified graph. All new tracks found will be made visible + * and will be given a default name. + * + * @param graph + * the graph to parse for tracks. + */ + void setGraph( final SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > graph ) + { + if ( null != this.graph ) + { + this.graph.removeGraphListener( mgl ); + } + this.graph = new DefaultListenableGraph< PreRoiobject, DefaultWeightedEdge >( graph ); + this.graph.addGraphListener( mgl ); + init( graph ); + } + + /** + * Removes all the tracks from this model. + */ + void clear() + { + setGraph( new SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge >( DefaultWeightedEdge.class ) ); + } + + /** + * This method is meant to help building a model from a serialized source, + * such as a saved file. It allows specifying the exact mapping of track IDs + * to the connected sets. The model content is completely replaced by the + * specified parameters, including the global graph, its connected + * components (both in PreRoiobjects and edges), visibility and naming. + *

+ * It is the caller responsibility to ensure that the graph and provided + * component are coherent. Unexpected behavior might result otherwise. + * + * @param graph + * the mother graph for the model. + * @param trackPreRoiobjects + * the mapping of track IDs vs the connected components as sets + * of PreRoiobjects. + * @param trackEdges + * the mapping of track IDs vs the connected components as sets + * of edges. + * @param trackVisibility + * the track visibility. + * @param trackNames + * the track names. + */ + public void from( final SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > graph, + final Map> trackPreRoiobjects, final Map< Integer, Set< DefaultWeightedEdge > > trackEdges, final Map< Integer, Boolean > trackVisibility, final Map< Integer, String > trackNames ) + { + + if ( null != this.graph ) + { + this.graph.removeGraphListener( mgl ); + } + this.graph = new DefaultListenableGraph< PreRoiobject, DefaultWeightedEdge >( graph ); + this.graph.addGraphListener( mgl ); + + edgesAdded.clear(); + edgesModified.clear(); + edgesRemoved.clear(); + tracksUpdated.clear(); + + visibility = trackVisibility; + names = trackNames; + connectedVertexSets = trackPreRoiobjects; + connectedEdgeSets = trackEdges; + + // Rebuild the id maps + IDcounter = 0; + vertexToID = new HashMap< PreRoiobject, Integer >(); + for ( final Integer id : trackPreRoiobjects.keySet() ) + { + for ( final PreRoiobject PreRoiobject : trackPreRoiobjects.get( id ) ) + { + vertexToID.put( PreRoiobject, id ); + } + if ( id > IDcounter ) + { + IDcounter = id; + } + } + IDcounter++; + + edgeToID = new HashMap< DefaultWeightedEdge, Integer >(); + for ( final Integer id : trackEdges.keySet() ) + { + for ( final DefaultWeightedEdge edge : trackEdges.get( id ) ) + { + edgeToID.put( edge, id ); + } + } + + } + + /* + * DEFAULT VISIBILIT METHODS made to be called from the mother model. + */ + + void addPreRoiobject( final PreRoiobject PreRoiobjectToAdd ) + { + graph.addVertex( PreRoiobjectToAdd ); + } + + void removePreRoiobject( final PreRoiobject PreRoiobjectToRemove ) + { + graph.removeVertex( PreRoiobjectToRemove ); + } + + DefaultWeightedEdge addEdge( final PreRoiobject source, final PreRoiobject target, final double weight ) + { + if ( !graph.containsVertex( source ) ) + { + graph.addVertex( source ); + } + if ( !graph.containsVertex( target ) ) + { + graph.addVertex( target ); + } + final DefaultWeightedEdge edge = graph.addEdge( source, target ); + graph.setEdgeWeight( edge, weight ); + return edge; + } + + DefaultWeightedEdge removeEdge( final PreRoiobject source, final PreRoiobject target ) + { + return graph.removeEdge( source, target ); + } + + boolean removeEdge( final DefaultWeightedEdge edge ) + { + return graph.removeEdge( edge ); + } + + void setEdgeWeight( final DefaultWeightedEdge edge, final double weight ) + { + graph.setEdgeWeight( edge, weight ); + edgesModified.add( edge ); + } + + Boolean setVisibility( final Integer trackID, final boolean visible ) + { + return visibility.put( trackID, Boolean.valueOf( visible ) ); + } + + /* + * PUBLIC METHODS + */ + + /* + * GRAPH + */ + + /** + * Returns a new graph with the same structure as the one wrapped here, and + * with vertices generated by the given {@link Function1}. Edges are copied + * in direction and weight. + * + * @param factory + * the vertex factory used to instantiate new vertices in the new + * graph + * @param function + * the function used to set values of a new vertex in the new + * graph, from the matching PreRoiobject + * @param mappings + * a map that will receive mappings from {@link PreRoiobject} to the new + * vertices. Can be null if you do not want to get + * the mappings + * @param + * the type of the vertices. + * @return a new {@link SimpleDirectedWeightedGraph}. + */ + public < V > SimpleDirectedWeightedGraph< V, DefaultWeightedEdge > copy( final VertexFactory< V > factory, final Function1< PreRoiobject, V > function, final Map< PreRoiobject, V > mappings ) + { + final SimpleDirectedWeightedGraph< V, DefaultWeightedEdge > copy = new SimpleDirectedWeightedGraph< V, DefaultWeightedEdge >( DefaultWeightedEdge.class ); + final Set< PreRoiobject > PreRoiobjects = graph.vertexSet(); + // To store mapping of old graph vs new graph + Map< PreRoiobject, V > map; + if ( null == mappings ) + { + map = new HashMap< PreRoiobject, V >( PreRoiobjects.size() ); + } + else + { + map = mappings; + } + + // Generate new vertices + for ( final PreRoiobject PreRoiobject : Collections.unmodifiableCollection( PreRoiobjects ) ) + { + final V vertex = factory.createVertex(); + function.compute( PreRoiobject, vertex ); + map.put( PreRoiobject, vertex ); + copy.addVertex( vertex ); + } + + // Generate new edges + for ( final DefaultWeightedEdge edge : graph.edgeSet() ) + { + final DefaultWeightedEdge newEdge = copy.addEdge( map.get( graph.getEdgeSource( edge ) ), map.get( graph.getEdgeTarget( edge ) ) ); + copy.setEdgeWeight( newEdge, graph.getEdgeWeight( edge ) ); + } + + return copy; + } + + /** + * Returns true if this model contains the specified edge. + * + * @param source + * the edge source vertex. + * @param target + * the edge target vertex. + * @return true if this model contains the specified edge. + * + * @see org.jgrapht.Graph#containsEdge(Object, Object) + */ + public boolean containsEdge( final PreRoiobject source, final PreRoiobject target ) + { + return graph.containsEdge( source, target ); + } + + /** + * Returns the specified edge. + * + * @param source + * the edge source vertex. + * @param target + * the edge target vertex. + * @return the specified edge, or null if it does not exist. + * + * @see org.jgrapht.Graph#getEdge(Object, Object) + */ + public DefaultWeightedEdge getEdge( final PreRoiobject source, final PreRoiobject target ) + { + return graph.getEdge( source, target ); + } + + /** + * Returns the set of edges of a PreRoiobject. + * + * @param PreRoiobject + * the PreRoiobject. + * @return the set of edges connected to this PreRoiobject. Can be empty if the PreRoiobject + * does not have any edge. + * + * @see org.jgrapht.Graph#edgesOf(Object) + */ + public Set< DefaultWeightedEdge > edgesOf( final PreRoiobject PreRoiobject ) + { + if ( graph.containsVertex( PreRoiobject ) ) + { + return graph.edgesOf( PreRoiobject ); + } + else + { + return Collections.emptySet(); + } + } + + /** + * Returns a set of the edges contained in this model. The set is backed by + * the graph, so changes to the graph are reflected in the set. If the graph + * is modified while an iteration over the set is in progress, the results + * of the iteration are undefined. + * + * @return the edges of this model. + * + * @see org.jgrapht.Graph#edgeSet() + */ + public Set< DefaultWeightedEdge > edgeSet() + { + return graph.edgeSet(); + } + + /** + * Returns a set of the vertices contained in this graph. The set is backed + * by the graph, so changes to the graph are reflected in the set. If the + * graph is modified while an iteration over the set is in progress, the + * results of the iteration are undefined. + * + * @return the vertices of this model. + * + * @see org.jgrapht.Graph#vertexSet() + */ + public Set< PreRoiobject > vertexSet() + { + return graph.vertexSet(); + } + + /** + * Returns the source PreRoiobject of the specified edge. + * + * @param e + * the edge. + * @return the source PreRoiobject of this edge. + * + * @see org.jgrapht.Graph#getEdgeSource(Object) + */ + public PreRoiobject getEdgeSource( final DefaultWeightedEdge e ) + { + return graph.getEdgeSource( e ); + } + + /** + * Returns the target PreRoiobject of the specified edge. + * + * @param e + * the edge. + * @return the target PreRoiobject of this edge. + * + * @see org.jgrapht.Graph#getEdgeTarget(Object) + */ + public PreRoiobject getEdgeTarget( final DefaultWeightedEdge e ) + { + return graph.getEdgeTarget( e ); + } + + /** + * Returns the weight assigned to the specified edge. Its physical meaning + * depend on the particle-linking algorithm used to generate the edge. + * + * @param edge + * the edge. + * @return the edge weight. + * + * @see org.jgrapht.Graph#getEdgeWeight(Object) + */ + public double getEdgeWeight( final DefaultWeightedEdge edge ) + { + return graph.getEdgeWeight( edge ); + } + + /* + * TRACKS + */ + + /** + * Returns true if the track with the specified ID is marked as + * visible, false otherwise. Throws a + * {@link NullPointerException} if the track ID is unknown to this model. + * + * @param ID + * the track ID. + * @return the track visibility. + */ + public boolean isVisible( final Integer ID ) + { + return visibility.get( ID ); + } + /** + * Return a new map sorted by its values. Adapted from + * http://stackoverflow.com + * /questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java + */ + public static < K, V extends Comparable< ? super V > > Map< K, V > sortByValue( final Map< K, V > map, final Comparator< V > comparator ) + { + final List< Map.Entry< K, V > > list = new LinkedList< Map.Entry< K, V > >( map.entrySet() ); + Collections.sort( list, new Comparator< Map.Entry< K, V > >() + { + @Override + public int compare( final Map.Entry< K, V > o1, final Map.Entry< K, V > o2 ) + { + return comparator.compare( o1.getValue(), o2.getValue() ); + } + } ); + + final LinkedHashMap< K, V > result = new LinkedHashMap< K, V >(); + for ( final Map.Entry< K, V > entry : list ) + { + result.put( entry.getKey(), entry.getValue() ); + } + return result; + } + /** + * Returns the set of track IDs managed by this model, ordered by track + * names (alpha-numerically sorted). + * + * @param visibleOnly + * if true, only visible track IDs will be returned. + * @return a new set of track IDs. + */ + public Set< Integer > trackIDs( final boolean visibleOnly ) + { + final Set< Integer > ids = sortByValue( names, AlphanumComparator.instance ).keySet(); + if ( !visibleOnly ) + { + return ids; + } + else + { + final Set< Integer > vids = new LinkedHashSet< Integer >( ids.size() ); + for ( final Integer id : ids ) + { + if ( visibility.get( id ) ) + { + vids.add( id ); + } + } + return vids; + } + } + + /** + * Returns the set of track IDs managed by this model, unsorted. This method + * exists to provide better performance for callers that do not need the IDs + * to be sorted. + * + * @param visibleOnly + * if true, only visible track IDs will be returned. + * @return the set of track IDs. + */ + public Set< Integer > unsortedTrackIDs( final boolean visibleOnly ) + { + if ( !visibleOnly ) + { + return visibility.keySet(); + } + else + { + final Set< Integer > vids = new LinkedHashSet< Integer >( visibility.size() ); + for ( final Integer id : visibility.keySet() ) + { + if ( visibility.get( id ) ) + { + vids.add( id ); + } + } + return vids; + } + } + + /** + * Returns the name of the track with the specified ID. + * + * @param id + * the track ID. + * @return the track name. + */ + public String name( final Integer id ) + { + return names.get( id ); + } + + /** + * Sets the name of the track with the specified ID. + * + * @param id + * the track ID. + * @param name + * the name for the track. + */ + public void setName( final Integer id, final String name ) + { + names.put( id, name ); + } + + /** + * Returns the edges of the track with the specified ID. + * + * @param trackID + * the track ID. + * @return the set of edges. + */ + public Set< DefaultWeightedEdge > trackEdges( final Integer trackID ) + { + return connectedEdgeSets.get( trackID ); + } + + /** + * Returns the PreRoiobjects of the track with the specified ID. + * + * @param trackID + * the track ID. + * @return the set of PreRoiobjects. + */ + public HashSet< PreRoiobject > trackPreRoiobjects( final Integer trackID ) + { + return connectedVertexSets.get( trackID ); + } + + public int nTracks( final boolean visibleOnly ) + { + if ( !visibleOnly ) + { + return connectedEdgeSets.size(); + } + else + { + int ntracks = 0; + for ( final Boolean visible : visibility.values() ) + { + if ( visible ) + { + ntracks++; + } + } + return ntracks; + } + } + + /** + * Returns the track ID the specified edge belong to, or null + * if the specified edge cannot be found in this model. + * + * @param edge + * the edge to search for. + * @return the track ID it belongs to. + */ + public Integer trackIDOf( final DefaultWeightedEdge edge ) + { + return edgeToID.get( edge ); + } + + /** + * Returns the track ID the specified PreRoiobject belong to, or null + * if the specified PreRoiobject cannot be found in this model. + * + * @param PreRoiobject + * the PreRoiobject to search for. + * + * @return the track ID it belongs to. + */ + public Integer trackIDOf( final PreRoiobject PreRoiobject ) + { + return vertexToID.get( PreRoiobject ); + } + + /* + * PRIVATE METHODS + */ + + /** + * Generates initial connected sets in bulk, from a graph. All sets are + * created visible, and are give a default name. + * + * @param graph + * the graph to read edges and vertices from. + */ + private void init( final SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > graph ) + { + vertexToID = new HashMap< PreRoiobject, Integer >(); + edgeToID = new HashMap< DefaultWeightedEdge, Integer >(); + IDcounter = 0; + visibility = new HashMap< Integer, Boolean >(); + names = new HashMap< Integer, String >(); + connectedVertexSets = new HashMap< Integer, HashSet< PreRoiobject > >(); + connectedEdgeSets = new HashMap< Integer, Set< DefaultWeightedEdge > >(); + + edgesAdded.clear(); + edgesModified.clear(); + edgesRemoved.clear(); + tracksUpdated.clear(); + + final Set< PreRoiobject > vertexSet = graph.vertexSet(); + if ( vertexSet.size() > 0 ) + { + final BreadthFirstIterator< PreRoiobject, DefaultWeightedEdge > i = new BreadthFirstIterator< PreRoiobject, DefaultWeightedEdge >( graph ); + i.addTraversalListener( new MyTraversalListener() ); + + while ( i.hasNext() ) + { + i.next(); + } + } + } + + /* + * UTILS + */ + + public String echo() + { + if ( null == connectedVertexSets ) { return "Uninitialized.\n"; } + + final StringBuilder str = new StringBuilder(); + final Set< Integer > vid = connectedVertexSets.keySet(); + final HashSet< Integer > eid = new HashSet< Integer >( connectedEdgeSets.keySet() ); + + for ( final Integer id : vid ) + { + str.append( id + ":\n" ); + str.append( " - " + connectedVertexSets.get( id ) + "\n" ); + final Set< DefaultWeightedEdge > es = connectedEdgeSets.get( id ); + if ( es == null ) + { + str.append( " - no matching edges!\n" ); + } + else + { + str.append( " - " + es + "\n" ); + } + eid.remove( id ); + } + + if ( eid.isEmpty() ) + { + str.append( "No remaining edges ID.\n" ); + } + else + { + str.append( "Found non-matching edge IDs!\n" ); + for ( final Integer id : eid ) + { + str.append( id + ":\n" ); + str.append( " - " + connectedEdgeSets.get( id ) + "\n" ); + } + } + + return str.toString(); + } + + /* + * ITERATORS + */ + + /** + * Returns a new depth first iterator over the PreRoiobjects connected by links in + * this model. A boolean flag allow to set whether the returned iterator + * does take into account the edge direction. If true, the iterator will not + * be able to iterate backward in time. + * + * @param start + * the PreRoiobject to start iteration with. Can be null, + * then the start will be taken randomly and will traverse all + * the links. + * @param directed + * if true returns a directed iterator, undirected if false. + * @return a new depth-first iterator. + */ + public GraphIterator< PreRoiobject, DefaultWeightedEdge > getDepthFirstIterator( final PreRoiobject start, final boolean directed ) + { + if ( directed ) + { + return new TimeDirectedDepthFirstIterator( graph, start ); + } + else + { + return new DepthFirstIterator< PreRoiobject, DefaultWeightedEdge >( graph, start ); + } + } + + /** + * Returns a new depth first iterator over the PreRoiobjects connected by links in + * this model. This iterator is sorted: when branching, it chooses the next + * vertex according to a specified comparator. A boolean flag allow to set + * whether the returned iterator does take into account the edge direction. + * If true, the iterator will not be able to iterate backward in time. + * + * @param start + * the PreRoiobject to start iteration with. Can be null, + * then the start will be taken randomly and will traverse all + * the links. + * @param directed + * if true returns a directed iterator, undirected if false. + * @param comparator + * the comparator to use to pick children in order when + * branching. + * @return a new depth-first iterator. + */ + public SortedDepthFirstIterator< PreRoiobject, DefaultWeightedEdge > getSortedDepthFirstIterator( final PreRoiobject start, final Comparator< PreRoiobject > comparator, final boolean directed ) + { + if ( directed ) + { + return new TimeDirectedSortedDepthFirstIterator( graph, start, comparator ); + } + else + { + return new SortedDepthFirstIterator< PreRoiobject, DefaultWeightedEdge >( graph, start, comparator ); + } + } + + public TimeDirectedNeighborIndex getDirectedNeighborIndex() + { + return new TimeDirectedNeighborIndex( graph ); + } + + /** + * Returns the shortest path between two connected PreRoiobject, using Dijkstra's + * algorithm. The edge weights, if any, are ignored here, meaning that the + * returned path is the shortest in terms of number of edges. + *

+ * Returns null if the two PreRoiobjects are not connected by a track, + * or if one of the PreRoiobject do not belong to the graph, or if the graph field + * is null. + * + * @param source + * the PreRoiobject to start the path with + * @param target + * the PreRoiobject to stop the path with + * @return the shortest path, as a list of edges. + */ + public List< DefaultWeightedEdge > dijkstraShortestPath( final PreRoiobject source, final PreRoiobject target ) + { + if ( null == graph ) { return null; } + final AsUnweightedGraph< PreRoiobject, DefaultWeightedEdge > unWeightedGrah = new AsUnweightedGraph< PreRoiobject, DefaultWeightedEdge >( graph ); + final DijkstraShortestPath< PreRoiobject, DefaultWeightedEdge > pathFinder = new DijkstraShortestPath< PreRoiobject, DefaultWeightedEdge >( unWeightedGrah, source, target ); + final List< DefaultWeightedEdge > path = pathFinder.getPathEdgeList(); + return path; + } + + /* + * Inner Classes + */ + + private class MyTraversalListener implements TraversalListener< PreRoiobject, DefaultWeightedEdge > + { + private HashSet currentConnectedVertexSet; + + private Set< DefaultWeightedEdge > currentConnectedEdgeSet; + + private Integer ID; + + /** + * Called when after traversing a connected set. Stores it, gives it + * default visibility, and a default name. Discard sets made of 1 + * vertices or 0 edges. + */ + @Override + public void connectedComponentFinished( final ConnectedComponentTraversalEvent event ) + { + if ( currentConnectedVertexSet.size() <= 1 || currentConnectedEdgeSet.size() == 0 ) + { + // Forget them + for ( final DefaultWeightedEdge e : currentConnectedEdgeSet ) + { + edgeToID.remove( e ); + } + for ( final PreRoiobject v : currentConnectedVertexSet ) + { + vertexToID.remove( v ); + } + return; + } + // Adds them + connectedVertexSets.put( ID, currentConnectedVertexSet ); + connectedEdgeSets.put( ID, currentConnectedEdgeSet ); + visibility.put( ID, DEFAULT_VISIBILITY ); + names.put( ID, nameGenerator.next() ); + } + + /** + * @see TraversalListenerAdapter#connectedComponentStarted(ConnectedComponentTraversalEvent) + */ + @Override + public void connectedComponentStarted( final ConnectedComponentTraversalEvent e ) + { + currentConnectedVertexSet = new HashSet< PreRoiobject >(); + currentConnectedEdgeSet = new HashSet< DefaultWeightedEdge >(); + ID = IDcounter++; + } + + /** + * @see TraversalListenerAdapter#vertexTraversed(VertexTraversalEvent) + */ + @Override + public void vertexTraversed( final VertexTraversalEvent< PreRoiobject > event ) + { + final PreRoiobject v = event.getVertex(); + currentConnectedVertexSet.add( v ); + vertexToID.put( v, ID ); + } + + @Override + public void edgeTraversed( final EdgeTraversalEvent< DefaultWeightedEdge > event ) + { + final DefaultWeightedEdge e = event.getEdge(); + currentConnectedEdgeSet.add( e ); + edgeToID.put( e, ID ); + } + + @Override + public void vertexFinished( final VertexTraversalEvent< PreRoiobject > e ) + {} + } + + /** + * This listener class is made to deal with complex changes in the track + * graph. + *

+ * By complex change, we mean the changes occurring in the graph caused by + * another change that was initiated manually by the user. For instance, + * imagine we have a simple track branch made of 5 PreRoiobjects that link linearly, + * like this: + * + *

+	 * S1 - S2 - S3 - S4 - S5
+	 * 
+ * + * The user might want to remove the S3 PreRoiobject, in the middle of the track. On + * top of the track rearrangement, that is dealt with elsewhere in the model + * class, this PreRoiobject removal also triggers 2 edges removal: the links S2-S3 + * and S3-S4 disappear. The only way for the {@link TrackModel} to be aware + * of that, and to forward these events to its listener, is to listen itself + * to the {@link #graph} that store links. + *

+ * This is done through this class. This class is notified every time a + * change occur in the {@link #graph}: + *

    + *
  • It ignores events triggered by PreRoiobjects being added or removed, because + * they can't be triggered automatically, and are dealt with in the + * {@link TrackModel#addPreRoiobjectTo(PreRoiobject, Integer)} and + * {@link TrackModel#removePreRoiobject(PreRoiobject, Integer)} methods. + *
  • It catches all events triggered by a link being added or removed in + * the graph, whether they are triggered manually through a call to a model + * method such as {@link TrackModel#addEdge(PreRoiobject, PreRoiobject, double)}, or + * triggered by another call. They are used to build the + * {@link TrackModel#edgesAdded} and {@link TrackModel#edgesRemoved} fields, + * that will be used to notify listeners of the model. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> Aug 12, 2011 + * + */ + private class MyGraphListener implements GraphListener< PreRoiobject, DefaultWeightedEdge > + { + + @Override + public void vertexAdded( final GraphVertexChangeEvent< PreRoiobject > event ) + {} + + @Override + public void vertexRemoved( final GraphVertexChangeEvent< PreRoiobject > event ) + { + if ( null == connectedEdgeSets ) { return; } + + final PreRoiobject v = event.getVertex(); + vertexToID.remove( v ); + final Integer id = vertexToID.get( v ); + if ( id != null ) + { + final Set< PreRoiobject > set = connectedVertexSets.get( id ); + if ( null == set ) { return; // it was removed when removing the + // last edge of a track, most + // likely. + } + set.remove( v ); + + if ( set.isEmpty() ) + { + connectedEdgeSets.remove( id ); + connectedVertexSets.remove( id ); + names.remove( id ); + visibility.remove( id ); + } + } + } + + @Override + public void edgeAdded( final GraphEdgeChangeEvent< PreRoiobject, DefaultWeightedEdge > event ) + { + // To signal to ModelChangeListener + edgesAdded.add( event.getEdge() ); + + // To maintain connected sets coherence: + /* + * This is the tricky part: when we add an edge to our set model, + * first we need to find to what existing set it has been added. + * Then a new edge sometime come with 1 or 2 vertices that might be + * new or belonging to an existing set. + */ + final DefaultWeightedEdge e = event.getEdge(); + + // Was it added to known tracks? + final PreRoiobject sv = graph.getEdgeSource( e ); + final Integer sid = vertexToID.get( sv ); + final PreRoiobject tv = graph.getEdgeTarget( e ); + final Integer tid = vertexToID.get( tv ); + + if ( null != tid && null != sid ) + { + // Case 1: it was added between two existing sets. We connect + // them, therefore + // and take the id of the largest one. The other id, disappear, + // unless they + // belonged to the same set. + + // Did they come from the same set? + if ( tid.equals( sid ) ) + { + // They come from the same set (equals ID). Not much to do. + final Set< DefaultWeightedEdge > ses = connectedEdgeSets.get( sid ); + ses.add( e ); + edgeToID.put( e, sid ); + + } + else + { + // They come from different sets. + + // Edges: + final Set< DefaultWeightedEdge > ses = connectedEdgeSets.get( sid ); + final Set< DefaultWeightedEdge > tes = connectedEdgeSets.get( tid ); + final HashSet< DefaultWeightedEdge > nes = new HashSet< DefaultWeightedEdge >( ses.size() + tes.size() + 1 ); + nes.addAll( ses ); + nes.addAll( tes ); + nes.add( e ); + + // Vertices: + final Set< PreRoiobject > svs = connectedVertexSets.get( sid ); + final Set< PreRoiobject > tvs = connectedVertexSets.get( tid ); + final HashSet< PreRoiobject > nvs = new HashSet< PreRoiobject >( ses.size() + tes.size() ); + nvs.addAll( svs ); + nvs.addAll( tvs ); + + Integer nid, rid; + if ( nvs.size() > tvs.size() ) + { + nid = sid; + rid = tid; + for ( final PreRoiobject v : tvs ) + { + // Vertices of target set change id + vertexToID.put( v, nid ); + } + for ( final DefaultWeightedEdge te : tes ) + { + edgeToID.put( te, nid ); + } + } + else + { + nid = tid; + rid = sid; + for ( final PreRoiobject v : svs ) + { + // Vertices of source set change id + vertexToID.put( v, nid ); + } + for ( final DefaultWeightedEdge se : ses ) + { + edgeToID.put( se, nid ); + } + } + edgeToID.put( e, nid ); + connectedVertexSets.put( nid, nvs ); + connectedVertexSets.remove( rid ); + connectedEdgeSets.put( nid, nes ); + connectedEdgeSets.remove( rid ); + + // Transaction: we signal that the large id is to be + // updated, and forget about the small one + tracksUpdated.add( nid ); + tracksUpdated.remove( rid ); + + // Visibility: if at least one is visible, the new set is + // made visible. + final Boolean targetVisibility = visibility.get( sid ) || visibility.get( tid ); + visibility.put( nid, targetVisibility ); + visibility.remove( rid ); + + // Name: the new set gets the name of the largest one. + names.remove( rid ); // 'nid' already has the right name. + } + + } + else if ( null == sid && null == tid ) + { + // Case 4: the edge was added between two lonely vertices. + // Create a new set id from this + final HashSet< PreRoiobject > nvs = new HashSet< PreRoiobject >( 2 ); + nvs.add( graph.getEdgeSource( e ) ); + nvs.add( graph.getEdgeTarget( e ) ); + + final HashSet< DefaultWeightedEdge > nes = new HashSet< DefaultWeightedEdge >( 1 ); + nes.add( e ); + + final int nid = IDcounter++; + connectedEdgeSets.put( nid, nes ); + connectedVertexSets.put( nid, nvs ); + vertexToID.put( sv, nid ); + vertexToID.put( tv, nid ); + edgeToID.put( e, nid ); + + // Give it visibility + visibility.put( nid, Boolean.TRUE ); + // and a default name. + names.put( nid, nameGenerator.next() ); + // Transaction: we mark the new track as updated + tracksUpdated.add( nid ); + + } + else if ( null == sid ) + { + // Case 2: the edge was added to the target set. No source set, + // but there is a source vertex. + // Add it, with the source vertex, to the target id. + connectedEdgeSets.get( tid ).add( e ); + edgeToID.put( e, tid ); + connectedVertexSets.get( tid ).add( sv ); + vertexToID.put( sv, tid ); + // We do not change the visibility, nor the name. + // Transaction: we mark the mother track as updated + tracksUpdated.add( tid ); + + } + else if ( null == tid ) + { + // Case 3: the edge was added to the source set. No target set, + // but there is a target vertex. + // Add it, with the target vertex, to the source id. + connectedEdgeSets.get( sid ).add( e ); + edgeToID.put( e, sid ); + connectedVertexSets.get( sid ).add( tv ); + vertexToID.put( tv, sid ); + // We do not change the visibility, nor the name. + // Transaction: we mark the mother track as updated + tracksUpdated.add( sid ); + + } + + } + + @Override + public void edgeRemoved( final GraphEdgeChangeEvent< PreRoiobject, DefaultWeightedEdge > event ) + { + // To signal to ModelChangeListeners + edgesRemoved.add( event.getEdge() ); + + // To maintain connected sets coherence + + final DefaultWeightedEdge e = event.getEdge(); + final Integer id = edgeToID.get( e ); + if ( null == id ) { throw new RuntimeException( "Edge is unkown to this model: " + e ); } + final Set< DefaultWeightedEdge > set = connectedEdgeSets.get( id ); + if ( null == set ) { throw new RuntimeException( "Unknown set ID: " + id ); } + + // Remove edge from set. + final boolean removed = set.remove( e ); + if ( !removed ) { throw new RuntimeException( "Could not removed edge " + e + " from set with ID: " + id ); } + // Forget about edge. + edgeToID.remove( e ); + + /* + * Ok the trouble is that now we might be left with 2 sets if the + * edge "was in the middle". Or 1 if it was in the end. Or 0 if it + * was the last edge of the set. + */ + + if ( set.size() == 0 ) + { + // The set is empty, remove it from the map. + connectedEdgeSets.remove( id ); + names.remove( id ); + visibility.remove( id ); + /* We need to remove also the vertices */ + final Set< PreRoiobject > vertexSet = connectedVertexSets.get( id ); + // Forget the vertices were in a set + for ( final PreRoiobject PreRoiobject : vertexSet ) + { + vertexToID.remove( PreRoiobject ); + } + // Forget the vertex set + connectedVertexSets.remove( id ); + /* + * We do not mark it as a track to update, for it disappeared. + * On the other hand, it might *have been* marked as a track to + * update. Since it just joined oblivion, we remove it from the + * list of tracks to update. + */ + tracksUpdated.remove( id ); + + } + else + { + // So there are some edges remaining in the set. + // Look at the connected component of its source and target. + // Source + final HashSet< PreRoiobject > sourceVCS = new HashSet< PreRoiobject >(); + final HashSet< DefaultWeightedEdge > sourceECS = new HashSet< DefaultWeightedEdge >(); + { + final PreRoiobject source = graph.getEdgeSource( e ); + // Get its connected set + final BreadthFirstIterator< PreRoiobject, DefaultWeightedEdge > i = new BreadthFirstIterator< PreRoiobject, DefaultWeightedEdge >( graph, source ); + while ( i.hasNext() ) + { + final PreRoiobject sv = i.next(); + sourceVCS.add( sv ); + sourceECS.addAll( graph.edgesOf( sv ) ); + } + } + // Target + final HashSet< PreRoiobject > targetVCS = new HashSet< PreRoiobject >(); + final HashSet< DefaultWeightedEdge > targetECS = new HashSet< DefaultWeightedEdge >(); + { + final PreRoiobject target = graph.getEdgeTarget( e ); + // Get its connected set + final BreadthFirstIterator< PreRoiobject, DefaultWeightedEdge > i = new BreadthFirstIterator< PreRoiobject, DefaultWeightedEdge >( graph, target ); + while ( i.hasNext() ) + { + final PreRoiobject sv = i.next(); + targetVCS.add( sv ); + targetECS.addAll( graph.edgesOf( sv ) ); + } + } + + /* + * If the two connected components are the same, it means that + * the edge was an "internal" edge: Because there is another + * path that connect its source and target, removing it did NOT + * split the track in 2. We therefore need not to re-attribute + * it. + */ + if ( targetVCS.equals( sourceVCS ) ) + { + tracksUpdated.add( id ); + connectedEdgeSets.get( id ).remove( e ); + return; + } + + /* + * Re-attribute the found connected sets to the model. The + * largest one (in vertices) gets the original id, the other + * gets a new id. As for names: the largest one keeps its name, + * the small one gets a new name. + */ + + if ( targetVCS.size() > sourceVCS.size() ) + { + + connectedEdgeSets.put( id, targetECS ); + connectedVertexSets.put( id, targetVCS ); // they already + // have the + // right id in + // #vertexToId + tracksUpdated.add( id ); // old track has changed + + if ( sourceECS.size() > 0 ) + { + // the smaller part is still a track + final int newid = IDcounter++; + connectedEdgeSets.put( newid, sourceECS ); // otherwise + // forget it + for ( final DefaultWeightedEdge te : sourceECS ) + { + edgeToID.put( te, newid ); + } + connectedVertexSets.put( newid, sourceVCS ); + for ( final PreRoiobject tv : sourceVCS ) + { + vertexToID.put( tv, newid ); + } + final Boolean targetVisibility = visibility.get( id ); + visibility.put( newid, targetVisibility ); + names.put( newid, nameGenerator.next() ); + // Transaction: both children tracks are marked for + // update. + tracksUpdated.add( newid ); + + } + else + { + /* + * Nothing remains from the smallest part. The remaining + * solitary vertex has no right to be called a track. + */ + final PreRoiobject solitary = sourceVCS.iterator().next(); + vertexToID.remove( solitary ); + } + + } + else + { + + if ( sourceECS.size() > 0 ) + { + // There are still some pieces left. It is worth noting + // it. + connectedEdgeSets.put( id, sourceECS ); + connectedVertexSets.put( id, sourceVCS ); + tracksUpdated.add( id ); + + if ( targetECS.size() > 0 ) + { + // the small part is still a track + final int newid = IDcounter++; + connectedEdgeSets.put( newid, targetECS ); + for ( final DefaultWeightedEdge te : targetECS ) + { + edgeToID.put( te, newid ); + } + connectedVertexSets.put( newid, targetVCS ); + for ( final PreRoiobject v : targetVCS ) + { + vertexToID.put( v, newid ); + } + final Boolean targetVisibility = visibility.get( id ); + visibility.put( newid, targetVisibility ); + names.put( newid, nameGenerator.next() ); + // Transaction: both children tracks are marked for + // update. + tracksUpdated.add( newid ); + } + else + { + /* + * Nothing remains from the smallest part. The + * remaining solitary vertex has no right to be + * called a track. + */ + final PreRoiobject solitary = targetVCS.iterator().next(); + vertexToID.remove( solitary ); + } + + } + else + { + // Nothing remains (maybe a solitary vertex) -> forget + // about it all. + connectedEdgeSets.remove( id ); + connectedVertexSets.remove( id ); + names.remove( id ); + visibility.remove( id ); + tracksUpdated.remove( id ); + } + + } + } + } + + } + + private static class DefaultNameGenerator implements Iterator< String > + { + + private int nameID = 0; + + @Override + public boolean hasNext() + { + return true; + } + + @Override + public String next() + { + return "Track_" + nameID++; + } + + @Override + public void remove() + {} + + } + + +} diff --git a/src/main/java/linkers/TrackModel3D.java b/src/main/java/linkers/TrackModel3D.java new file mode 100644 index 0000000..02990aa --- /dev/null +++ b/src/main/java/linkers/TrackModel3D.java @@ -0,0 +1,1396 @@ +package linkers; + + + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.UndirectedGraph; +import org.jgrapht.VertexFactory; +import org.jgrapht.alg.DijkstraShortestPath; +import org.jgrapht.event.ConnectedComponentTraversalEvent; +import org.jgrapht.event.EdgeTraversalEvent; +import org.jgrapht.event.GraphEdgeChangeEvent; +import org.jgrapht.event.GraphListener; +import org.jgrapht.event.GraphVertexChangeEvent; +import org.jgrapht.event.TraversalListener; +import org.jgrapht.event.TraversalListenerAdapter; +import org.jgrapht.event.VertexTraversalEvent; +import org.jgrapht.graph.AsUnweightedGraph; +import org.jgrapht.graph.DefaultListenableGraph; +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.ListenableUndirectedGraph; +import org.jgrapht.graph.SimpleDirectedWeightedGraph; +import org.jgrapht.graph.SimpleWeightedGraph; +import org.jgrapht.traverse.BreadthFirstIterator; +import org.jgrapht.traverse.DepthFirstIterator; +import org.jgrapht.traverse.GraphIterator; + +import utility.ThreeDRoiobject; + + + +public class TrackModel3D +{ +/** + * A component of {@link Model} specialized for tracks. + * + * @author Jean-Yves Tinevez + */ + + + /** + * The mother graph, from which all subsequent fields are calculated. This + * graph is not made accessible to the outside world. Editing it must be + * trough the model methods {@link #addEdge(ThreeDRoiobject, ThreeDRoiobject, double)}, + * {@link #removeEdge(DefaultWeightedEdge)}, {@link #removeEdge(ThreeDRoiobject, ThreeDRoiobject)} + * . + */ + private DefaultListenableGraph< ThreeDRoiobject, DefaultWeightedEdge > graph; + + private final MyGraphListener mgl; + + /* + * TRANSACTION FIELDS + */ + + /** + * The edges that have been added to this model by + * {@link #addEdge(ThreeDRoiobject, ThreeDRoiobject, double)}. + *

    + * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< DefaultWeightedEdge > edgesAdded = new HashSet< DefaultWeightedEdge >(); + + /** + * The edges that have removed from this model by + * {@link #removeEdge(DefaultWeightedEdge)} or + * {@link #removeEdge(ThreeDRoiobject, ThreeDRoiobject)}. + *

    + * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< DefaultWeightedEdge > edgesRemoved = new HashSet< DefaultWeightedEdge >(); + + /** + * The edges that have been modified in this model by changing its cost + * using {@link #setEdgeWeight(DefaultWeightedEdge, double)} or modifying + * the ThreeDRoiobjects it links elsewhere. + *

    + * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< DefaultWeightedEdge > edgesModified = new HashSet< DefaultWeightedEdge >(); + + /** + * The track IDs that have been modified, updated or created, solely + * by removing or adding an edge. Possibly after the removal of a ThreeDRoiobject. + * Tracks having edges that are modified, for instance by modifying a + * ThreeDRoiobject it contains, will not be listed here, but must be sought from the + * {@link #edgesModified} field. + *

    + * It is the parent instance responsibility to clear this field when it is + * fit to do so. + */ + final Set< Integer > tracksUpdated = new HashSet< Integer >(); + + private static final Boolean DEFAULT_VISIBILITY = Boolean.TRUE; + + // ~ Instance fields + // -------------------------------------------------------- + + private int IDcounter = 0; + + private Map< Integer, Set< DefaultWeightedEdge > > connectedEdgeSets; + + Map< DefaultWeightedEdge, Integer > edgeToID; + + private Map< Integer, HashSet< ThreeDRoiobject > > connectedVertexSets; + + Map< ThreeDRoiobject, Integer > vertexToID; + + private Map< Integer, Boolean > visibility; + + private Map< Integer, String > names; + + private final Iterator< String > nameGenerator = new DefaultNameGenerator(); + + /* + * Constructors ----------------------------------------------------------- + */ + + public TrackModel3D() + { + this( new SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge >( DefaultWeightedEdge.class ) ); + } + + public TrackModel3D( final SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > graph ) + { + this.mgl = new MyGraphListener(); + setGraph( graph ); + } + + /* + * CREATION METHODS Methods that wipes the current content and replace it in + * bulk. + */ + + /** + * Clears the content of this model and replace it by the tracks found by + * inspecting the specified graph. All new tracks found will be made visible + * and will be given a default name. + * + * @param graph + * the graph to parse for tracks. + */ + void setGraph( final SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > graph ) + { + if ( null != this.graph ) + { + this.graph.removeGraphListener( mgl ); + } + this.graph = new DefaultListenableGraph< ThreeDRoiobject, DefaultWeightedEdge >( graph ); + this.graph.addGraphListener( mgl ); + init( graph ); + } + + /** + * Removes all the tracks from this model. + */ + void clear() + { + setGraph( new SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge >( DefaultWeightedEdge.class ) ); + } + + /** + * This method is meant to help building a model from a serialized source, + * such as a saved file. It allows specifying the exact mapping of track IDs + * to the connected sets. The model content is completely replaced by the + * specified parameters, including the global graph, its connected + * components (both in ThreeDRoiobjects and edges), visibility and naming. + *

    + * It is the caller responsibility to ensure that the graph and provided + * component are coherent. Unexpected behavior might result otherwise. + * + * @param graph + * the mother graph for the model. + * @param trackThreeDRoiobjects + * the mapping of track IDs vs the connected components as sets + * of ThreeDRoiobjects. + * @param trackEdges + * the mapping of track IDs vs the connected components as sets + * of edges. + * @param trackVisibility + * the track visibility. + * @param trackNames + * the track names. + */ + public void from( final SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > graph, + final Map> trackThreeDRoiobjects, final Map< Integer, Set< DefaultWeightedEdge > > trackEdges, final Map< Integer, Boolean > trackVisibility, final Map< Integer, String > trackNames ) + { + + if ( null != this.graph ) + { + this.graph.removeGraphListener( mgl ); + } + this.graph = new DefaultListenableGraph< ThreeDRoiobject, DefaultWeightedEdge >( graph ); + this.graph.addGraphListener( mgl ); + + edgesAdded.clear(); + edgesModified.clear(); + edgesRemoved.clear(); + tracksUpdated.clear(); + + visibility = trackVisibility; + names = trackNames; + connectedVertexSets = trackThreeDRoiobjects; + connectedEdgeSets = trackEdges; + + // Rebuild the id maps + IDcounter = 0; + vertexToID = new HashMap< ThreeDRoiobject, Integer >(); + for ( final Integer id : trackThreeDRoiobjects.keySet() ) + { + for ( final ThreeDRoiobject ThreeDRoiobject : trackThreeDRoiobjects.get( id ) ) + { + vertexToID.put( ThreeDRoiobject, id ); + } + if ( id > IDcounter ) + { + IDcounter = id; + } + } + IDcounter++; + + edgeToID = new HashMap< DefaultWeightedEdge, Integer >(); + for ( final Integer id : trackEdges.keySet() ) + { + for ( final DefaultWeightedEdge edge : trackEdges.get( id ) ) + { + edgeToID.put( edge, id ); + } + } + + } + + /* + * DEFAULT VISIBILIT METHODS made to be called from the mother model. + */ + + void addThreeDRoiobject( final ThreeDRoiobject ThreeDRoiobjectToAdd ) + { + graph.addVertex( ThreeDRoiobjectToAdd ); + } + + void removeThreeDRoiobject( final ThreeDRoiobject ThreeDRoiobjectToRemove ) + { + graph.removeVertex( ThreeDRoiobjectToRemove ); + } + + DefaultWeightedEdge addEdge( final ThreeDRoiobject source, final ThreeDRoiobject target, final double weight ) + { + if ( !graph.containsVertex( source ) ) + { + graph.addVertex( source ); + } + if ( !graph.containsVertex( target ) ) + { + graph.addVertex( target ); + } + final DefaultWeightedEdge edge = graph.addEdge( source, target ); + graph.setEdgeWeight( edge, weight ); + return edge; + } + + DefaultWeightedEdge removeEdge( final ThreeDRoiobject source, final ThreeDRoiobject target ) + { + return graph.removeEdge( source, target ); + } + + boolean removeEdge( final DefaultWeightedEdge edge ) + { + return graph.removeEdge( edge ); + } + + void setEdgeWeight( final DefaultWeightedEdge edge, final double weight ) + { + graph.setEdgeWeight( edge, weight ); + edgesModified.add( edge ); + } + + Boolean setVisibility( final Integer trackID, final boolean visible ) + { + return visibility.put( trackID, Boolean.valueOf( visible ) ); + } + + /* + * PUBLIC METHODS + */ + + /* + * GRAPH + */ + + /** + * Returns a new graph with the same structure as the one wrapped here, and + * with vertices generated by the given {@link Function1}. Edges are copied + * in direction and weight. + * + * @param factory + * the vertex factory used to instantiate new vertices in the new + * graph + * @param function + * the function used to set values of a new vertex in the new + * graph, from the matching ThreeDRoiobject + * @param mappings + * a map that will receive mappings from {@link ThreeDRoiobject} to the new + * vertices. Can be null if you do not want to get + * the mappings + * @param + * the type of the vertices. + * @return a new {@link SimpleDirectedWeightedGraph}. + */ + public < V > SimpleDirectedWeightedGraph< V, DefaultWeightedEdge > copy( final VertexFactory< V > factory, final Function1< ThreeDRoiobject, V > function, final Map< ThreeDRoiobject, V > mappings ) + { + final SimpleDirectedWeightedGraph< V, DefaultWeightedEdge > copy = new SimpleDirectedWeightedGraph< V, DefaultWeightedEdge >( DefaultWeightedEdge.class ); + final Set< ThreeDRoiobject > ThreeDRoiobjects = graph.vertexSet(); + // To store mapping of old graph vs new graph + Map< ThreeDRoiobject, V > map; + if ( null == mappings ) + { + map = new HashMap< ThreeDRoiobject, V >( ThreeDRoiobjects.size() ); + } + else + { + map = mappings; + } + + // Generate new vertices + for ( final ThreeDRoiobject ThreeDRoiobject : Collections.unmodifiableCollection( ThreeDRoiobjects ) ) + { + final V vertex = factory.createVertex(); + function.compute( ThreeDRoiobject, vertex ); + map.put( ThreeDRoiobject, vertex ); + copy.addVertex( vertex ); + } + + // Generate new edges + for ( final DefaultWeightedEdge edge : graph.edgeSet() ) + { + final DefaultWeightedEdge newEdge = copy.addEdge( map.get( graph.getEdgeSource( edge ) ), map.get( graph.getEdgeTarget( edge ) ) ); + copy.setEdgeWeight( newEdge, graph.getEdgeWeight( edge ) ); + } + + return copy; + } + + /** + * Returns true if this model contains the specified edge. + * + * @param source + * the edge source vertex. + * @param target + * the edge target vertex. + * @return true if this model contains the specified edge. + * + * @see org.jgrapht.Graph#containsEdge(Object, Object) + */ + public boolean containsEdge( final ThreeDRoiobject source, final ThreeDRoiobject target ) + { + return graph.containsEdge( source, target ); + } + + /** + * Returns the specified edge. + * + * @param source + * the edge source vertex. + * @param target + * the edge target vertex. + * @return the specified edge, or null if it does not exist. + * + * @see org.jgrapht.Graph#getEdge(Object, Object) + */ + public DefaultWeightedEdge getEdge( final ThreeDRoiobject source, final ThreeDRoiobject target ) + { + return graph.getEdge( source, target ); + } + + /** + * Returns the set of edges of a ThreeDRoiobject. + * + * @param ThreeDRoiobject + * the ThreeDRoiobject. + * @return the set of edges connected to this ThreeDRoiobject. Can be empty if the ThreeDRoiobject + * does not have any edge. + * + * @see org.jgrapht.Graph#edgesOf(Object) + */ + public Set< DefaultWeightedEdge > edgesOf( final ThreeDRoiobject ThreeDRoiobject ) + { + if ( graph.containsVertex( ThreeDRoiobject ) ) + { + return graph.edgesOf( ThreeDRoiobject ); + } + else + { + return Collections.emptySet(); + } + } + + /** + * Returns a set of the edges contained in this model. The set is backed by + * the graph, so changes to the graph are reflected in the set. If the graph + * is modified while an iteration over the set is in progress, the results + * of the iteration are undefined. + * + * @return the edges of this model. + * + * @see org.jgrapht.Graph#edgeSet() + */ + public Set< DefaultWeightedEdge > edgeSet() + { + return graph.edgeSet(); + } + + /** + * Returns a set of the vertices contained in this graph. The set is backed + * by the graph, so changes to the graph are reflected in the set. If the + * graph is modified while an iteration over the set is in progress, the + * results of the iteration are undefined. + * + * @return the vertices of this model. + * + * @see org.jgrapht.Graph#vertexSet() + */ + public Set< ThreeDRoiobject > vertexSet() + { + return graph.vertexSet(); + } + + /** + * Returns the source ThreeDRoiobject of the specified edge. + * + * @param e + * the edge. + * @return the source ThreeDRoiobject of this edge. + * + * @see org.jgrapht.Graph#getEdgeSource(Object) + */ + public ThreeDRoiobject getEdgeSource( final DefaultWeightedEdge e ) + { + return graph.getEdgeSource( e ); + } + + /** + * Returns the target ThreeDRoiobject of the specified edge. + * + * @param e + * the edge. + * @return the target ThreeDRoiobject of this edge. + * + * @see org.jgrapht.Graph#getEdgeTarget(Object) + */ + public ThreeDRoiobject getEdgeTarget( final DefaultWeightedEdge e ) + { + return graph.getEdgeTarget( e ); + } + + /** + * Returns the weight assigned to the specified edge. Its physical meaning + * depend on the particle-linking algorithm used to generate the edge. + * + * @param edge + * the edge. + * @return the edge weight. + * + * @see org.jgrapht.Graph#getEdgeWeight(Object) + */ + public double getEdgeWeight( final DefaultWeightedEdge edge ) + { + return graph.getEdgeWeight( edge ); + } + + /* + * TRACKS + */ + + /** + * Returns true if the track with the specified ID is marked as + * visible, false otherwise. Throws a + * {@link NullPointerException} if the track ID is unknown to this model. + * + * @param ID + * the track ID. + * @return the track visibility. + */ + public boolean isVisible( final Integer ID ) + { + return visibility.get( ID ); + } + /** + * Return a new map sorted by its values. Adapted from + * http://stackoverflow.com + * /questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java + */ + public static < K, V extends Comparable< ? super V > > Map< K, V > sortByValue( final Map< K, V > map, final Comparator< V > comparator ) + { + final List< Map.Entry< K, V > > list = new LinkedList< Map.Entry< K, V > >( map.entrySet() ); + Collections.sort( list, new Comparator< Map.Entry< K, V > >() + { + @Override + public int compare( final Map.Entry< K, V > o1, final Map.Entry< K, V > o2 ) + { + return comparator.compare( o1.getValue(), o2.getValue() ); + } + } ); + + final LinkedHashMap< K, V > result = new LinkedHashMap< K, V >(); + for ( final Map.Entry< K, V > entry : list ) + { + result.put( entry.getKey(), entry.getValue() ); + } + return result; + } + /** + * Returns the set of track IDs managed by this model, ordered by track + * names (alpha-numerically sorted). + * + * @param visibleOnly + * if true, only visible track IDs will be returned. + * @return a new set of track IDs. + */ + public Set< Integer > trackIDs( final boolean visibleOnly ) + { + final Set< Integer > ids = sortByValue( names, AlphanumComparator.instance ).keySet(); + if ( !visibleOnly ) + { + return ids; + } + else + { + final Set< Integer > vids = new LinkedHashSet< Integer >( ids.size() ); + for ( final Integer id : ids ) + { + if ( visibility.get( id ) ) + { + vids.add( id ); + } + } + return vids; + } + } + + /** + * Returns the set of track IDs managed by this model, unsorted. This method + * exists to provide better performance for callers that do not need the IDs + * to be sorted. + * + * @param visibleOnly + * if true, only visible track IDs will be returned. + * @return the set of track IDs. + */ + public Set< Integer > unsortedTrackIDs( final boolean visibleOnly ) + { + if ( !visibleOnly ) + { + return visibility.keySet(); + } + else + { + final Set< Integer > vids = new LinkedHashSet< Integer >( visibility.size() ); + for ( final Integer id : visibility.keySet() ) + { + if ( visibility.get( id ) ) + { + vids.add( id ); + } + } + return vids; + } + } + + /** + * Returns the name of the track with the specified ID. + * + * @param id + * the track ID. + * @return the track name. + */ + public String name( final Integer id ) + { + return names.get( id ); + } + + /** + * Sets the name of the track with the specified ID. + * + * @param id + * the track ID. + * @param name + * the name for the track. + */ + public void setName( final Integer id, final String name ) + { + names.put( id, name ); + } + + /** + * Returns the edges of the track with the specified ID. + * + * @param trackID + * the track ID. + * @return the set of edges. + */ + public Set< DefaultWeightedEdge > trackEdges( final Integer trackID ) + { + return connectedEdgeSets.get( trackID ); + } + + /** + * Returns the ThreeDRoiobjects of the track with the specified ID. + * + * @param trackID + * the track ID. + * @return the set of ThreeDRoiobjects. + */ + public HashSet< ThreeDRoiobject > trackThreeDRoiobjects( final Integer trackID ) + { + return connectedVertexSets.get( trackID ); + } + + public int nTracks( final boolean visibleOnly ) + { + if ( !visibleOnly ) + { + return connectedEdgeSets.size(); + } + else + { + int ntracks = 0; + for ( final Boolean visible : visibility.values() ) + { + if ( visible ) + { + ntracks++; + } + } + return ntracks; + } + } + + /** + * Returns the track ID the specified edge belong to, or null + * if the specified edge cannot be found in this model. + * + * @param edge + * the edge to search for. + * @return the track ID it belongs to. + */ + public Integer trackIDOf( final DefaultWeightedEdge edge ) + { + return edgeToID.get( edge ); + } + + /** + * Returns the track ID the specified ThreeDRoiobject belong to, or null + * if the specified ThreeDRoiobject cannot be found in this model. + * + * @param ThreeDRoiobject + * the ThreeDRoiobject to search for. + * + * @return the track ID it belongs to. + */ + public Integer trackIDOf( final ThreeDRoiobject ThreeDRoiobject ) + { + return vertexToID.get( ThreeDRoiobject ); + } + + /* + * PRIVATE METHODS + */ + + /** + * Generates initial connected sets in bulk, from a graph. All sets are + * created visible, and are give a default name. + * + * @param graph + * the graph to read edges and vertices from. + */ + private void init( final SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > graph ) + { + vertexToID = new HashMap< ThreeDRoiobject, Integer >(); + edgeToID = new HashMap< DefaultWeightedEdge, Integer >(); + IDcounter = 0; + visibility = new HashMap< Integer, Boolean >(); + names = new HashMap< Integer, String >(); + connectedVertexSets = new HashMap< Integer, HashSet< ThreeDRoiobject > >(); + connectedEdgeSets = new HashMap< Integer, Set< DefaultWeightedEdge > >(); + + edgesAdded.clear(); + edgesModified.clear(); + edgesRemoved.clear(); + tracksUpdated.clear(); + + final Set< ThreeDRoiobject > vertexSet = graph.vertexSet(); + if ( vertexSet.size() > 0 ) + { + final BreadthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge > i = new BreadthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge >( graph ); + i.addTraversalListener( new MyTraversalListener() ); + + while ( i.hasNext() ) + { + i.next(); + } + } + } + + /* + * UTILS + */ + + public String echo() + { + if ( null == connectedVertexSets ) { return "Uninitialized.\n"; } + + final StringBuilder str = new StringBuilder(); + final Set< Integer > vid = connectedVertexSets.keySet(); + final HashSet< Integer > eid = new HashSet< Integer >( connectedEdgeSets.keySet() ); + + for ( final Integer id : vid ) + { + str.append( id + ":\n" ); + str.append( " - " + connectedVertexSets.get( id ) + "\n" ); + final Set< DefaultWeightedEdge > es = connectedEdgeSets.get( id ); + if ( es == null ) + { + str.append( " - no matching edges!\n" ); + } + else + { + str.append( " - " + es + "\n" ); + } + eid.remove( id ); + } + + if ( eid.isEmpty() ) + { + str.append( "No remaining edges ID.\n" ); + } + else + { + str.append( "Found non-matching edge IDs!\n" ); + for ( final Integer id : eid ) + { + str.append( id + ":\n" ); + str.append( " - " + connectedEdgeSets.get( id ) + "\n" ); + } + } + + return str.toString(); + } + + /* + * ITERATORS + */ + + /** + * Returns a new depth first iterator over the ThreeDRoiobjects connected by links in + * this model. A boolean flag allow to set whether the returned iterator + * does take into account the edge direction. If true, the iterator will not + * be able to iterate backward in time. + * + * @param start + * the ThreeDRoiobject to start iteration with. Can be null, + * then the start will be taken randomly and will traverse all + * the links. + * @param directed + * if true returns a directed iterator, undirected if false. + * @return a new depth-first iterator. + */ + public GraphIterator< ThreeDRoiobject, DefaultWeightedEdge > getDepthFirstIterator( final ThreeDRoiobject start, final boolean directed ) + { + if ( directed ) + { + return new TimeDirectedDepthFirstIterator3D( graph, start ); + } + else + { + return new DepthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge >( graph, start ); + } + } + + /** + * Returns a new depth first iterator over the ThreeDRoiobjects connected by links in + * this model. This iterator is sorted: when branching, it chooses the next + * vertex according to a specified comparator. A boolean flag allow to set + * whether the returned iterator does take into account the edge direction. + * If true, the iterator will not be able to iterate backward in time. + * + * @param start + * the ThreeDRoiobject to start iteration with. Can be null, + * then the start will be taken randomly and will traverse all + * the links. + * @param directed + * if true returns a directed iterator, undirected if false. + * @param comparator + * the comparator to use to pick children in order when + * branching. + * @return a new depth-first iterator. + */ + public SortedDepthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge > getSortedDepthFirstIterator( final ThreeDRoiobject start, final Comparator< ThreeDRoiobject > comparator, final boolean directed ) + { + if ( directed ) + { + return new TimeDirectedSortedDepthFirstIterator3D( graph, start, comparator ); + } + else + { + return new SortedDepthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge >( graph, start, comparator ); + } + } + + public TimeDirectedNeighborIndex3D getDirectedNeighborIndex() + { + return new TimeDirectedNeighborIndex3D( graph ); + } + + /** + * Returns the shortest path between two connected ThreeDRoiobject, using Dijkstra's + * algorithm. The edge weights, if any, are ignored here, meaning that the + * returned path is the shortest in terms of number of edges. + *

    + * Returns null if the two ThreeDRoiobjects are not connected by a track, + * or if one of the ThreeDRoiobject do not belong to the graph, or if the graph field + * is null. + * + * @param source + * the ThreeDRoiobject to start the path with + * @param target + * the ThreeDRoiobject to stop the path with + * @return the shortest path, as a list of edges. + */ + public List< DefaultWeightedEdge > dijkstraShortestPath( final ThreeDRoiobject source, final ThreeDRoiobject target ) + { + if ( null == graph ) { return null; } + final AsUnweightedGraph< ThreeDRoiobject, DefaultWeightedEdge > unWeightedGrah = new AsUnweightedGraph< ThreeDRoiobject, DefaultWeightedEdge >( graph ); + final DijkstraShortestPath< ThreeDRoiobject, DefaultWeightedEdge > pathFinder = new DijkstraShortestPath< ThreeDRoiobject, DefaultWeightedEdge >( unWeightedGrah, source, target ); + final List< DefaultWeightedEdge > path = pathFinder.getPathEdgeList(); + return path; + } + + /* + * Inner Classes + */ + + private class MyTraversalListener implements TraversalListener< ThreeDRoiobject, DefaultWeightedEdge > + { + private HashSet currentConnectedVertexSet; + + private Set< DefaultWeightedEdge > currentConnectedEdgeSet; + + private Integer ID; + + /** + * Called when after traversing a connected set. Stores it, gives it + * default visibility, and a default name. Discard sets made of 1 + * vertices or 0 edges. + */ + @Override + public void connectedComponentFinished( final ConnectedComponentTraversalEvent event ) + { + if ( currentConnectedVertexSet.size() <= 1 || currentConnectedEdgeSet.size() == 0 ) + { + // Forget them + for ( final DefaultWeightedEdge e : currentConnectedEdgeSet ) + { + edgeToID.remove( e ); + } + for ( final ThreeDRoiobject v : currentConnectedVertexSet ) + { + vertexToID.remove( v ); + } + return; + } + // Adds them + connectedVertexSets.put( ID, currentConnectedVertexSet ); + connectedEdgeSets.put( ID, currentConnectedEdgeSet ); + visibility.put( ID, DEFAULT_VISIBILITY ); + names.put( ID, nameGenerator.next() ); + } + + /** + * @see TraversalListenerAdapter#connectedComponentStarted(ConnectedComponentTraversalEvent) + */ + @Override + public void connectedComponentStarted( final ConnectedComponentTraversalEvent e ) + { + currentConnectedVertexSet = new HashSet< ThreeDRoiobject >(); + currentConnectedEdgeSet = new HashSet< DefaultWeightedEdge >(); + ID = IDcounter++; + } + + /** + * @see TraversalListenerAdapter#vertexTraversed(VertexTraversalEvent) + */ + @Override + public void vertexTraversed( final VertexTraversalEvent< ThreeDRoiobject > event ) + { + final ThreeDRoiobject v = event.getVertex(); + currentConnectedVertexSet.add( v ); + vertexToID.put( v, ID ); + } + + @Override + public void edgeTraversed( final EdgeTraversalEvent< DefaultWeightedEdge > event ) + { + final DefaultWeightedEdge e = event.getEdge(); + currentConnectedEdgeSet.add( e ); + edgeToID.put( e, ID ); + } + + @Override + public void vertexFinished( final VertexTraversalEvent< ThreeDRoiobject > e ) + {} + } + + /** + * This listener class is made to deal with complex changes in the track + * graph. + *

    + * By complex change, we mean the changes occurring in the graph caused by + * another change that was initiated manually by the user. For instance, + * imagine we have a simple track branch made of 5 ThreeDRoiobjects that link linearly, + * like this: + * + *

    +	 * S1 - S2 - S3 - S4 - S5
    +	 * 
    + * + * The user might want to remove the S3 ThreeDRoiobject, in the middle of the track. On + * top of the track rearrangement, that is dealt with elsewhere in the model + * class, this ThreeDRoiobject removal also triggers 2 edges removal: the links S2-S3 + * and S3-S4 disappear. The only way for the {@link TrackModel} to be aware + * of that, and to forward these events to its listener, is to listen itself + * to the {@link #graph} that store links. + *

    + * This is done through this class. This class is notified every time a + * change occur in the {@link #graph}: + *

      + *
    • It ignores events triggered by ThreeDRoiobjects being added or removed, because + * they can't be triggered automatically, and are dealt with in the + * {@link TrackModel#addThreeDRoiobjectTo(ThreeDRoiobject, Integer)} and + * {@link TrackModel#removeThreeDRoiobject(ThreeDRoiobject, Integer)} methods. + *
    • It catches all events triggered by a link being added or removed in + * the graph, whether they are triggered manually through a call to a model + * method such as {@link TrackModel#addEdge(ThreeDRoiobject, ThreeDRoiobject, double)}, or + * triggered by another call. They are used to build the + * {@link TrackModel#edgesAdded} and {@link TrackModel#edgesRemoved} fields, + * that will be used to notify listeners of the model. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> Aug 12, 2011 + * + */ + private class MyGraphListener implements GraphListener< ThreeDRoiobject, DefaultWeightedEdge > + { + + @Override + public void vertexAdded( final GraphVertexChangeEvent< ThreeDRoiobject > event ) + {} + + @Override + public void vertexRemoved( final GraphVertexChangeEvent< ThreeDRoiobject > event ) + { + if ( null == connectedEdgeSets ) { return; } + + final ThreeDRoiobject v = event.getVertex(); + vertexToID.remove( v ); + final Integer id = vertexToID.get( v ); + if ( id != null ) + { + final Set< ThreeDRoiobject > set = connectedVertexSets.get( id ); + if ( null == set ) { return; // it was removed when removing the + // last edge of a track, most + // likely. + } + set.remove( v ); + + if ( set.isEmpty() ) + { + connectedEdgeSets.remove( id ); + connectedVertexSets.remove( id ); + names.remove( id ); + visibility.remove( id ); + } + } + } + + @Override + public void edgeAdded( final GraphEdgeChangeEvent< ThreeDRoiobject, DefaultWeightedEdge > event ) + { + // To signal to ModelChangeListener + edgesAdded.add( event.getEdge() ); + + // To maintain connected sets coherence: + /* + * This is the tricky part: when we add an edge to our set model, + * first we need to find to what existing set it has been added. + * Then a new edge sometime come with 1 or 2 vertices that might be + * new or belonging to an existing set. + */ + final DefaultWeightedEdge e = event.getEdge(); + + // Was it added to known tracks? + final ThreeDRoiobject sv = graph.getEdgeSource( e ); + final Integer sid = vertexToID.get( sv ); + final ThreeDRoiobject tv = graph.getEdgeTarget( e ); + final Integer tid = vertexToID.get( tv ); + + if ( null != tid && null != sid ) + { + // Case 1: it was added between two existing sets. We connect + // them, therefore + // and take the id of the largest one. The other id, disappear, + // unless they + // belonged to the same set. + + // Did they come from the same set? + if ( tid.equals( sid ) ) + { + // They come from the same set (equals ID). Not much to do. + final Set< DefaultWeightedEdge > ses = connectedEdgeSets.get( sid ); + ses.add( e ); + edgeToID.put( e, sid ); + + } + else + { + // They come from different sets. + + // Edges: + final Set< DefaultWeightedEdge > ses = connectedEdgeSets.get( sid ); + final Set< DefaultWeightedEdge > tes = connectedEdgeSets.get( tid ); + final HashSet< DefaultWeightedEdge > nes = new HashSet< DefaultWeightedEdge >( ses.size() + tes.size() + 1 ); + nes.addAll( ses ); + nes.addAll( tes ); + nes.add( e ); + + // Vertices: + final Set< ThreeDRoiobject > svs = connectedVertexSets.get( sid ); + final Set< ThreeDRoiobject > tvs = connectedVertexSets.get( tid ); + final HashSet< ThreeDRoiobject > nvs = new HashSet< ThreeDRoiobject >( ses.size() + tes.size() ); + nvs.addAll( svs ); + nvs.addAll( tvs ); + + Integer nid, rid; + if ( nvs.size() > tvs.size() ) + { + nid = sid; + rid = tid; + for ( final ThreeDRoiobject v : tvs ) + { + // Vertices of target set change id + vertexToID.put( v, nid ); + } + for ( final DefaultWeightedEdge te : tes ) + { + edgeToID.put( te, nid ); + } + } + else + { + nid = tid; + rid = sid; + for ( final ThreeDRoiobject v : svs ) + { + // Vertices of source set change id + vertexToID.put( v, nid ); + } + for ( final DefaultWeightedEdge se : ses ) + { + edgeToID.put( se, nid ); + } + } + edgeToID.put( e, nid ); + connectedVertexSets.put( nid, nvs ); + connectedVertexSets.remove( rid ); + connectedEdgeSets.put( nid, nes ); + connectedEdgeSets.remove( rid ); + + // Transaction: we signal that the large id is to be + // updated, and forget about the small one + tracksUpdated.add( nid ); + tracksUpdated.remove( rid ); + + // Visibility: if at least one is visible, the new set is + // made visible. + final Boolean targetVisibility = visibility.get( sid ) || visibility.get( tid ); + visibility.put( nid, targetVisibility ); + visibility.remove( rid ); + + // Name: the new set gets the name of the largest one. + names.remove( rid ); // 'nid' already has the right name. + } + + } + else if ( null == sid && null == tid ) + { + // Case 4: the edge was added between two lonely vertices. + // Create a new set id from this + final HashSet< ThreeDRoiobject > nvs = new HashSet< ThreeDRoiobject >( 2 ); + nvs.add( graph.getEdgeSource( e ) ); + nvs.add( graph.getEdgeTarget( e ) ); + + final HashSet< DefaultWeightedEdge > nes = new HashSet< DefaultWeightedEdge >( 1 ); + nes.add( e ); + + final int nid = IDcounter++; + connectedEdgeSets.put( nid, nes ); + connectedVertexSets.put( nid, nvs ); + vertexToID.put( sv, nid ); + vertexToID.put( tv, nid ); + edgeToID.put( e, nid ); + + // Give it visibility + visibility.put( nid, Boolean.TRUE ); + // and a default name. + names.put( nid, nameGenerator.next() ); + // Transaction: we mark the new track as updated + tracksUpdated.add( nid ); + + } + else if ( null == sid ) + { + // Case 2: the edge was added to the target set. No source set, + // but there is a source vertex. + // Add it, with the source vertex, to the target id. + connectedEdgeSets.get( tid ).add( e ); + edgeToID.put( e, tid ); + connectedVertexSets.get( tid ).add( sv ); + vertexToID.put( sv, tid ); + // We do not change the visibility, nor the name. + // Transaction: we mark the mother track as updated + tracksUpdated.add( tid ); + + } + else if ( null == tid ) + { + // Case 3: the edge was added to the source set. No target set, + // but there is a target vertex. + // Add it, with the target vertex, to the source id. + connectedEdgeSets.get( sid ).add( e ); + edgeToID.put( e, sid ); + connectedVertexSets.get( sid ).add( tv ); + vertexToID.put( tv, sid ); + // We do not change the visibility, nor the name. + // Transaction: we mark the mother track as updated + tracksUpdated.add( sid ); + + } + + } + + @Override + public void edgeRemoved( final GraphEdgeChangeEvent< ThreeDRoiobject, DefaultWeightedEdge > event ) + { + // To signal to ModelChangeListeners + edgesRemoved.add( event.getEdge() ); + + // To maintain connected sets coherence + + final DefaultWeightedEdge e = event.getEdge(); + final Integer id = edgeToID.get( e ); + if ( null == id ) { throw new RuntimeException( "Edge is unkown to this model: " + e ); } + final Set< DefaultWeightedEdge > set = connectedEdgeSets.get( id ); + if ( null == set ) { throw new RuntimeException( "Unknown set ID: " + id ); } + + // Remove edge from set. + final boolean removed = set.remove( e ); + if ( !removed ) { throw new RuntimeException( "Could not removed edge " + e + " from set with ID: " + id ); } + // Forget about edge. + edgeToID.remove( e ); + + /* + * Ok the trouble is that now we might be left with 2 sets if the + * edge "was in the middle". Or 1 if it was in the end. Or 0 if it + * was the last edge of the set. + */ + + if ( set.size() == 0 ) + { + // The set is empty, remove it from the map. + connectedEdgeSets.remove( id ); + names.remove( id ); + visibility.remove( id ); + /* We need to remove also the vertices */ + final Set< ThreeDRoiobject > vertexSet = connectedVertexSets.get( id ); + // Forget the vertices were in a set + for ( final ThreeDRoiobject ThreeDRoiobject : vertexSet ) + { + vertexToID.remove( ThreeDRoiobject ); + } + // Forget the vertex set + connectedVertexSets.remove( id ); + /* + * We do not mark it as a track to update, for it disappeared. + * On the other hand, it might *have been* marked as a track to + * update. Since it just joined oblivion, we remove it from the + * list of tracks to update. + */ + tracksUpdated.remove( id ); + + } + else + { + // So there are some edges remaining in the set. + // Look at the connected component of its source and target. + // Source + final HashSet< ThreeDRoiobject > sourceVCS = new HashSet< ThreeDRoiobject >(); + final HashSet< DefaultWeightedEdge > sourceECS = new HashSet< DefaultWeightedEdge >(); + { + final ThreeDRoiobject source = graph.getEdgeSource( e ); + // Get its connected set + final BreadthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge > i = new BreadthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge >( graph, source ); + while ( i.hasNext() ) + { + final ThreeDRoiobject sv = i.next(); + sourceVCS.add( sv ); + sourceECS.addAll( graph.edgesOf( sv ) ); + } + } + // Target + final HashSet< ThreeDRoiobject > targetVCS = new HashSet< ThreeDRoiobject >(); + final HashSet< DefaultWeightedEdge > targetECS = new HashSet< DefaultWeightedEdge >(); + { + final ThreeDRoiobject target = graph.getEdgeTarget( e ); + // Get its connected set + final BreadthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge > i = new BreadthFirstIterator< ThreeDRoiobject, DefaultWeightedEdge >( graph, target ); + while ( i.hasNext() ) + { + final ThreeDRoiobject sv = i.next(); + targetVCS.add( sv ); + targetECS.addAll( graph.edgesOf( sv ) ); + } + } + + /* + * If the two connected components are the same, it means that + * the edge was an "internal" edge: Because there is another + * path that connect its source and target, removing it did NOT + * split the track in 2. We therefore need not to re-attribute + * it. + */ + if ( targetVCS.equals( sourceVCS ) ) + { + tracksUpdated.add( id ); + connectedEdgeSets.get( id ).remove( e ); + return; + } + + /* + * Re-attribute the found connected sets to the model. The + * largest one (in vertices) gets the original id, the other + * gets a new id. As for names: the largest one keeps its name, + * the small one gets a new name. + */ + + if ( targetVCS.size() > sourceVCS.size() ) + { + + connectedEdgeSets.put( id, targetECS ); + connectedVertexSets.put( id, targetVCS ); // they already + // have the + // right id in + // #vertexToId + tracksUpdated.add( id ); // old track has changed + + if ( sourceECS.size() > 0 ) + { + // the smaller part is still a track + final int newid = IDcounter++; + connectedEdgeSets.put( newid, sourceECS ); // otherwise + // forget it + for ( final DefaultWeightedEdge te : sourceECS ) + { + edgeToID.put( te, newid ); + } + connectedVertexSets.put( newid, sourceVCS ); + for ( final ThreeDRoiobject tv : sourceVCS ) + { + vertexToID.put( tv, newid ); + } + final Boolean targetVisibility = visibility.get( id ); + visibility.put( newid, targetVisibility ); + names.put( newid, nameGenerator.next() ); + // Transaction: both children tracks are marked for + // update. + tracksUpdated.add( newid ); + + } + else + { + /* + * Nothing remains from the smallest part. The remaining + * solitary vertex has no right to be called a track. + */ + final ThreeDRoiobject solitary = sourceVCS.iterator().next(); + vertexToID.remove( solitary ); + } + + } + else + { + + if ( sourceECS.size() > 0 ) + { + // There are still some pieces left. It is worth noting + // it. + connectedEdgeSets.put( id, sourceECS ); + connectedVertexSets.put( id, sourceVCS ); + tracksUpdated.add( id ); + + if ( targetECS.size() > 0 ) + { + // the small part is still a track + final int newid = IDcounter++; + connectedEdgeSets.put( newid, targetECS ); + for ( final DefaultWeightedEdge te : targetECS ) + { + edgeToID.put( te, newid ); + } + connectedVertexSets.put( newid, targetVCS ); + for ( final ThreeDRoiobject v : targetVCS ) + { + vertexToID.put( v, newid ); + } + final Boolean targetVisibility = visibility.get( id ); + visibility.put( newid, targetVisibility ); + names.put( newid, nameGenerator.next() ); + // Transaction: both children tracks are marked for + // update. + tracksUpdated.add( newid ); + } + else + { + /* + * Nothing remains from the smallest part. The + * remaining solitary vertex has no right to be + * called a track. + */ + final ThreeDRoiobject solitary = targetVCS.iterator().next(); + vertexToID.remove( solitary ); + } + + } + else + { + // Nothing remains (maybe a solitary vertex) -> forget + // about it all. + connectedEdgeSets.remove( id ); + connectedVertexSets.remove( id ); + names.remove( id ); + visibility.remove( id ); + tracksUpdated.remove( id ); + } + + } + } + } + + } + + private static class DefaultNameGenerator implements Iterator< String > + { + + private int nameID = 0; + + @Override + public boolean hasNext() + { + return true; + } + + @Override + public String next() + { + return "Track_" + nameID++; + } + + @Override + public void remove() + {} + + } + + +} diff --git a/src/main/java/listeners/AngleListener.java b/src/main/java/listeners/AngleListener.java deleted file mode 100644 index 6ff9e3b..0000000 --- a/src/main/java/listeners/AngleListener.java +++ /dev/null @@ -1,107 +0,0 @@ -package listeners; - -import java.awt.Rectangle; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.SimpleWeightedGraph; - -import ellipsoidDetector.Intersectionobject; -import ij.gui.Overlay; -import ij.gui.Roi; -import net.imglib2.Cursor; -import net.imglib2.KDTree; -import net.imglib2.Point; -import net.imglib2.RandomAccess; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.RealPoint; -import net.imglib2.RealPointSampleList; -import net.imglib2.img.ImgFactory; -import net.imglib2.img.array.ArrayImgFactory; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; -import net.imglib2.type.logic.BitType; -import net.imglib2.type.numeric.integer.IntType; -import net.imglib2.type.numeric.integer.UnsignedByteType; -import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.util.Util; -import net.imglib2.util.ValuePair; -import net.imglib2.view.Views; -import pluginTools.InteractiveSimpleEllipseFit; -import pluginTools.InteractiveSimpleEllipseFit.ValueChange; -import utility.Roiobject; -import varun_algorithm_stats.Normalize; - -public class AngleListener implements ActionListener { - - final InteractiveSimpleEllipseFit parent; - - public AngleListener(final InteractiveSimpleEllipseFit parent) { - - this.parent = parent; - } - - @Override - public void actionPerformed(ActionEvent e) { - - // For computing angles - parent.superReducedSamples.clear(); - parent.table.removeAll(); - parent.table.repaint(); - parent.Tracklist.clear(); - parent.overlay.clear(); - parent.imp.getCanvas().removeMouseListener(parent.mvl); - parent.imp.getCanvas().removeMouseMotionListener(parent.ml); - if (parent.supermode) { - - - parent.empty = utility.Binarization.CreateBinaryBit(parent.originalimg, parent.lowprob, parent.highprob); - - - - - parent.parentgraph = new SimpleWeightedGraph(DefaultWeightedEdge.class); - parent.parentgraphZ = new - HashMap>(); - parent.StartComputing(); - - } - - if (parent.automode) { - - - parent.emptysmooth = utility.Binarization.CreateBinaryBit(parent.originalimgsmooth, parent.lowprob, parent.highprob); - parent.empty = utility.Binarization.CreateBinaryBit(parent.originalimg, parent.lowprob, parent.highprob); - - - parent.parentgraph = new SimpleWeightedGraph(DefaultWeightedEdge.class); - parent.parentgraphZ = new - HashMap>(); - parent.StartComputing(); - - } - - - else if(!parent.automode && !parent.supermode) { - - parent.parentgraph = new SimpleWeightedGraph(DefaultWeightedEdge.class); - parent.parentgraphZ = new - HashMap>(); - parent.StartComputing(); - System.out.println("Starting computing manual mode"); - - } - - - - - - } - - - -} diff --git a/src/main/java/listeners/ManualInterventionListener.java b/src/main/java/listeners/ManualInterventionListener.java deleted file mode 100644 index 5e72ae2..0000000 --- a/src/main/java/listeners/ManualInterventionListener.java +++ /dev/null @@ -1,25 +0,0 @@ -package listeners; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; - -import pluginTools.InteractiveSimpleEllipseFit; - -public class ManualInterventionListener implements ActionListener { - - final InteractiveSimpleEllipseFit parent; - - public ManualInterventionListener(final InteractiveSimpleEllipseFit parent) { - - this.parent = parent; - } - - @Override - public void actionPerformed(ActionEvent e) { - - - parent.StartManualIntervention(); - - } - -} diff --git a/src/main/java/listeners/RedoListener.java b/src/main/java/listeners/RedoListener.java deleted file mode 100644 index 76595f6..0000000 --- a/src/main/java/listeners/RedoListener.java +++ /dev/null @@ -1,63 +0,0 @@ -package listeners; - -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; -import java.util.HashMap; - -import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.SimpleWeightedGraph; - -import ellipsoidDetector.Intersectionobject; -import ij.ImageStack; -import pluginTools.InteractiveSimpleEllipseFit; -import pluginTools.InteractiveSimpleEllipseFit.ValueChange; - -public class RedoListener implements ActionListener { - - final InteractiveSimpleEllipseFit parent; - - public RedoListener(final InteractiveSimpleEllipseFit parent) { - - this.parent = parent; - } - - @Override - public void actionPerformed(ActionEvent e) { - - parent.superReducedSamples.clear(); - - if (parent.supermode) { - - parent.empty = utility.Binarization.CreateBinaryBit(parent.originalimg, parent.lowprob, parent.highprob); - - parent.parentgraph = new SimpleWeightedGraph( - DefaultWeightedEdge.class); - parent.parentgraphZ = new HashMap>(); - parent.StartComputingCurrent(); - - } - - if (parent.automode) { - - parent.emptysmooth = utility.Binarization.CreateBinaryBit(parent.originalimgsmooth, parent.lowprob, - parent.highprob); - parent.empty = utility.Binarization.CreateBinaryBit(parent.originalimg, parent.lowprob, parent.highprob); - - parent.parentgraph = new SimpleWeightedGraph( - DefaultWeightedEdge.class); - parent.parentgraphZ = new HashMap>(); - parent.StartComputingCurrent(); - - } - - else if (!parent.automode && !parent.supermode) { - parent.parentgraph = new SimpleWeightedGraph( - DefaultWeightedEdge.class); - parent.parentgraphZ = new HashMap>(); - parent.StartComputingCurrent(); - - } - - } - -} diff --git a/src/main/java/loaddirectory/LeftRightDirectoryLoader.java b/src/main/java/loaddirectory/LeftRightDirectoryLoader.java new file mode 100644 index 0000000..7d56f63 --- /dev/null +++ b/src/main/java/loaddirectory/LeftRightDirectoryLoader.java @@ -0,0 +1,54 @@ +package loaddirectory; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.io.File; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class LeftRightDirectoryLoader { + + final File[] leftwings; + final File[] rightwings; + final String bordertitle; + + public LeftRightDirectoryLoader( final String bordertitle, final File[] leftwings, final File[] rightwings) { + + this.bordertitle = bordertitle; + + this.leftwings = leftwings; + + this.rightwings = rightwings; + + } + + public JPanel DirectoryLoader = new JPanel(); + public JButton LoadLeftWingDirectory; + public JButton LoadRightWingDirectory; + + public JPanel LoadDirectory() { + + layoutManager.Setlayout.LayoutSetter(DirectoryLoader); + + LoadLeftWingDirectory = new JButton("Load Left Wing Directory"); + LoadRightWingDirectory = new JButton("Load Right Wing Directory"); + + Border directorychooser = new CompoundBorder(new TitledBorder(bordertitle), new EmptyBorder(layoutManager.Setlayout.c.insets)); + + DirectoryLoader.add(LoadLeftWingDirectory, new GridBagConstraints(0, 0, 3, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10,10,0,10), 0,0)); + + DirectoryLoader.add(LoadRightWingDirectory, new GridBagConstraints(3, 0, 3, 1, 0, 0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(10,10,0,10), 0,0)); + + DirectoryLoader.setBorder(directorychooser); + + + return DirectoryLoader; + + } + +} diff --git a/src/main/java/loadfile/CovistoFourChFileLoader.java b/src/main/java/loadfile/CovistoFourChFileLoader.java new file mode 100644 index 0000000..278773b --- /dev/null +++ b/src/main/java/loadfile/CovistoFourChFileLoader.java @@ -0,0 +1,64 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoFourChFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoFourChFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelFourChannel = new JPanel(); + + public JComboBox ChooseImage; + public JComboBox ChoosesecImage; + public JComboBox ChoosethirdImage; + public JComboBox ChoosefourthImage; + public JPanel FourChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelFourChannel); + ChooseImage = new JComboBox(blankimageNames); + ChoosesecImage = new JComboBox(blankimageNames); + ChoosethirdImage = new JComboBox(blankimageNames); + ChoosefourthImage = new JComboBox(blankimageNames); + + + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelFourChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelFourChannel.add(ChoosesecImage, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelFourChannel.add(ChoosethirdImage, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelFourChannel.add(ChoosefourthImage, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + panelFourChannel.setBorder(chooseoriginalfile); + + + + return panelFourChannel; + + } + +} diff --git a/src/main/java/loadfile/CovistoOneChFileLoader.java b/src/main/java/loadfile/CovistoOneChFileLoader.java new file mode 100644 index 0000000..ff131c8 --- /dev/null +++ b/src/main/java/loadfile/CovistoOneChFileLoader.java @@ -0,0 +1,48 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoOneChFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoOneChFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelSingleChannel = new JPanel(); + + public JComboBox ChooseImage; + public JPanel SingleChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelSingleChannel); + ChooseImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + panelSingleChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + panelSingleChannel.setBorder(chooseoriginalfile); + + + + return panelSingleChannel; + + } + +} diff --git a/src/main/java/loadfile/CovistoThreeChForceFileLoader.java b/src/main/java/loadfile/CovistoThreeChForceFileLoader.java new file mode 100644 index 0000000..efb2bab --- /dev/null +++ b/src/main/java/loadfile/CovistoThreeChForceFileLoader.java @@ -0,0 +1,57 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoThreeChForceFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoThreeChForceFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelThreeChannel = new JPanel(); + + public JComboBox ChooseImage; + public JComboBox ChoosesecImage; + public JComboBox ChoosethirdImage; + public JPanel ThreeChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelThreeChannel); + ChooseImage = new JComboBox(blankimageNames); + ChoosesecImage = new JComboBox(blankimageNames); + ChoosethirdImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelThreeChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelThreeChannel.add(ChoosesecImage, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelThreeChannel.add(ChoosethirdImage, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelThreeChannel.setBorder(chooseoriginalfile); + + + + return panelThreeChannel; + + } + +} diff --git a/src/main/java/loadfile/CovistoTwoChDropbox.java b/src/main/java/loadfile/CovistoTwoChDropbox.java new file mode 100644 index 0000000..9f6a6a0 --- /dev/null +++ b/src/main/java/loadfile/CovistoTwoChDropbox.java @@ -0,0 +1,62 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTwoChDropbox { + + + + final String bordertitle; + final String[] blankimageNames; + + public CovistoTwoChDropbox(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelTwoChannel = new JPanel(); + + public JComboBox ChooseImage; + public JButton ChooseFile; + public JPanel TwoChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelTwoChannel); + ChooseImage = new JComboBox(blankimageNames); + ChooseFile = new JButton("Load CSV File"); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelTwoChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.add(ChooseFile, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.setBorder(chooseoriginalfile); + + + + return panelTwoChannel; + + } + + + + + + +} diff --git a/src/main/java/loadfile/CovistoTwoChForceFileLoader.java b/src/main/java/loadfile/CovistoTwoChForceFileLoader.java new file mode 100644 index 0000000..bd4c371 --- /dev/null +++ b/src/main/java/loadfile/CovistoTwoChForceFileLoader.java @@ -0,0 +1,53 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTwoChForceFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoTwoChForceFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelTwoChannel = new JPanel(); + + public JComboBox ChooseImage; + public JComboBox ChoosesecImage; + public JPanel TwoChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelTwoChannel); + ChooseImage = new JComboBox(blankimageNames); + ChoosesecImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + + panelTwoChannel.add(ChooseImage, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.add(ChoosesecImage, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.setBorder(chooseoriginalfile); + + + + return panelTwoChannel; + + } + +} diff --git a/src/main/java/loadfile/CovistoTwoChOptionFileLoader.java b/src/main/java/loadfile/CovistoTwoChOptionFileLoader.java new file mode 100644 index 0000000..f86d722 --- /dev/null +++ b/src/main/java/loadfile/CovistoTwoChOptionFileLoader.java @@ -0,0 +1,53 @@ +package loadfile; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; + +import javax.swing.JComboBox; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTwoChOptionFileLoader { + + final String bordertitle; + final String[] blankimageNames; + + public CovistoTwoChOptionFileLoader(final String bordertitle, final String[] blankimageNames) { + + this.bordertitle = bordertitle; + this.blankimageNames = blankimageNames; + } + + public JPanel panelTwoChannel = new JPanel(); + public boolean twochannel = false; + public Checkbox Godouble = new Checkbox("Load a second channel", twochannel); + + public JComboBox ChooseImage; + + public JPanel TwoChannelOption() { + + + + layoutManager.Setlayout.LayoutSetter(panelTwoChannel); + ChooseImage = new JComboBox(blankimageNames); + Border chooseoriginalfile = new CompoundBorder(new TitledBorder(bordertitle), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + panelTwoChannel.add(Godouble, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.add(ChooseImage, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + panelTwoChannel.setBorder(chooseoriginalfile); + + + + return panelTwoChannel; + + } + +} diff --git a/src/main/java/mser3D/ComputeCompTree.java b/src/main/java/mser3D/ComputeCompTree.java new file mode 100644 index 0000000..5fd0d06 --- /dev/null +++ b/src/main/java/mser3D/ComputeCompTree.java @@ -0,0 +1,114 @@ +package mser3D; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.concurrent.ExecutionException; + +import javax.swing.JProgressBar; +import javax.swing.SwingWorker; + +import ij.IJ; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import mserGUI.CovistoMserPanel; +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.componenttree.mser.MserTree; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.Util; +import net.imglib2.view.Views; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class ComputeCompTree & NativeType> { + + final InteractiveMethods parent; + final JProgressBar jpb; + public final RandomAccessibleInterval source; + + public RandomAccessibleInterval bitimg; + public boolean apply3D; + public int z; + public int t; + + public ComputeCompTree(final InteractiveMethods parent, final RandomAccessibleInterval source, + final JProgressBar jpb, boolean apply3D, int z, int t) { + + this.parent = parent; + this.source = source; + this.jpb = jpb; + this.apply3D = apply3D; + this.z = z; + this.t = t; + + bitimg = new ArrayImgFactory().create(source, new BitType()); + } + + public void execute() { + + MserTree newtree; + // Compute the component tree + if (CovistoMserPanel.darktobright) + + newtree = MserTree.buildMserTree(source, CovistoMserPanel.delta, CovistoMserPanel.minSize, CovistoMserPanel.maxSize, + CovistoMserPanel.Unstability_Score, CovistoMserPanel.minDiversity, true); + + else + + newtree = MserTree.buildMserTree(source, CovistoMserPanel.delta, CovistoMserPanel.minSize, CovistoMserPanel.maxSize, + CovistoMserPanel.Unstability_Score, CovistoMserPanel.minDiversity, false); + + parent.Rois = utility.FinderUtils.getcurrentRois(newtree); + parent.CurrentPreRoiobject = new ArrayList(); + + for (Roi currentroi : parent.Rois) { + + final double[] geocenter = currentroi.getContourCentroid(); + final Pair Intensityandpixels = PreRoiobject.getIntensity(currentroi, source); + final double intensity = Intensityandpixels.getA(); + final double numberofpixels = Intensityandpixels.getB(); + final double averageintensity = intensity / numberofpixels; + PreRoiobject currentobject = new PreRoiobject(currentroi, + new double[] { geocenter[0], geocenter[1], CovistoZselectPanel.thirdDimension }, numberofpixels, intensity, + averageintensity, CovistoZselectPanel.thirdDimension, CovistoTimeselectPanel.fourthDimension); + parent.CurrentPreRoiobject.add(currentobject); + } + + String uniqueID = Integer.toString(z) + Integer.toString(t); + parent.ZTRois.put(uniqueID, parent.CurrentPreRoiobject); + common3D.BinaryCreation.CreateBinaryRoi(parent, source, bitimg,parent.Rois, z, t); + } + + + + public RandomAccessibleInterval getBinaryimg() { + + return bitimg; + } + + + + +} diff --git a/src/main/java/mser3D/MserAll.java b/src/main/java/mser3D/MserAll.java new file mode 100644 index 0000000..3abf247 --- /dev/null +++ b/src/main/java/mser3D/MserAll.java @@ -0,0 +1,213 @@ +package mser3D; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import javax.swing.SwingWorker; + +import distanceTransform.DistWatershed; +import dog3D.DogAll.ParallelCalls; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; +import preProcessing.GlobalThresholding; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class MserAll extends SwingWorker { + + final InteractiveMethods parent; + + public MserAll(final InteractiveMethods parent) { + + this.parent = parent; + + } +public class ParallelCalls implements Callable { + + + public final InteractiveMethods parent; + public final int z; + public final int t; + public RandomAccessibleInterval currentbitimg; + + + public ParallelCalls(InteractiveMethods parent,RandomAccessibleInterval currentbitimg, int z, int t) { + + + this.parent = parent; + this.currentbitimg = currentbitimg; + + this.z = z; + this.t = t; + + } + + + @Override + public Void call() throws Exception { + + double percent = t +z; + RandomAccessibleInterval CurrentView = utility.CovistoSlicer.getCurrentView(parent.originalimg, z, + CovistoZselectPanel.thirdDimensionSize, t, CovistoTimeselectPanel.fourthDimensionSize); + utility.CovsitoProgressBar.CovistoSetProgressBar(parent.jpb, 100 *(percent / (CovistoTimeselectPanel.fourthDimensionSize + CovistoZselectPanel.thirdDimensionSize + 1 )) ,"Computing"); + // UnsignedByteType image created here + parent.updatePreview(ValueChange.THIRDDIMmouse); + RandomAccessibleInterval newimg = utility.CovistoSlicer.PREcopytoByteImage(CurrentView); + + + + processParallelSlice(newimg, currentbitimg, z, t); + + return null; + } + + + + } + @Override + protected Void doInBackground() throws Exception { + + + + parent.apply3D = true; + + + RandomAccessibleInterval bitimg = new ArrayImgFactory().create(parent.originalimg, new BitType()); + + List> list = new ArrayList>(); + int nThreads = Runtime.getRuntime().availableProcessors(); + final ExecutorService taskExecutor = Executors.newFixedThreadPool(nThreads); + for (int t = CovistoTimeselectPanel.fourthDimensionsliderInit; t <= CovistoTimeselectPanel.fourthDimensionSize; ++t) { + + + for (int z = CovistoZselectPanel.thirdDimensionsliderInit; z <= CovistoZselectPanel.thirdDimensionSize; ++z) { + + CovistoZselectPanel.thirdDimension = z; + CovistoTimeselectPanel.fourthDimension = t; + + RandomAccessibleInterval currentbitimg = utility.CovistoSlicer.getCurrentView(bitimg, z, CovistoZselectPanel.thirdDimensionSize, t, + CovistoTimeselectPanel.fourthDimensionSize); + + + ParallelCalls call = new ParallelCalls(parent, currentbitimg, z, t); + + Future Futureresult = taskExecutor.submit(call); + list.add(Futureresult); + + } + + + } + + for (Future fut : list) { + + + fut.get(); + + } + ImageJFunctions.show(bitimg).setTitle("Binary Image"); + + + + + + + + + + + return null; + } + + + protected void processParallelSlice(RandomAccessibleInterval< UnsignedByteType > slice, RandomAccessibleInterval< BitType > bitoutputslice, int z, int t) { + + + + + + ComputeCompTree ComputeMSER = new ComputeCompTree(parent, slice, parent.jpb, parent.apply3D, z, t); + ComputeMSER.execute(); + + RandomAccessibleInterval bitimg = ComputeMSER.getBinaryimg(); + Cursor< BitType > bitcursor = Views.iterable(bitoutputslice).localizingCursor(); + + RandomAccess ranac = bitimg.randomAccess(); + + while(bitcursor.hasNext()) { + + bitcursor.fwd(); + + ranac.setPosition(bitcursor); + + bitcursor.get().set(ranac.get()); + + + } + + + + + } + + + protected void processSlice(RandomAccessibleInterval< UnsignedByteType > slice, RandomAccessibleInterval< BitType > bitoutputslice, int z, int t) { + + + + parent.CurrentPreRoiobject = new ArrayList(); + + ComputeCompTree ComputeMSER = new ComputeCompTree(parent, slice, parent.jpb, parent.apply3D, z, t); + ComputeMSER.execute(); + + RandomAccessibleInterval bitimg = ComputeMSER.getBinaryimg(); + Cursor< BitType > bitcursor = Views.iterable(bitoutputslice).localizingCursor(); + + RandomAccess ranac = bitimg.randomAccess(); + + while(bitcursor.hasNext()) { + + bitcursor.fwd(); + + ranac.setPosition(bitcursor); + + bitcursor.get().set(ranac.get()); + + + } + + + + + } + + @Override + protected void done() { + try { + + parent.apply3D = false; + get(); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/mserGUI/CovistoMserPanel.java b/src/main/java/mserGUI/CovistoMserPanel.java new file mode 100644 index 0000000..0563628 --- /dev/null +++ b/src/main/java/mserGUI/CovistoMserPanel.java @@ -0,0 +1,209 @@ +package mserGUI; + +import java.awt.Checkbox; +import java.awt.CheckboxGroup; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + + + +public class CovistoMserPanel { + + public static JPanel MserPanel = new JPanel(); + + public static int deltaInit = 10; + public static float delta = deltaInit; + public static int Unstability_ScoreInit = 1; + public static float Unstability_Score = Unstability_ScoreInit; + + public static int minSizeInit = 0; + + public static long maxSize = 1; + public static long minSize = minSizeInit; + public static long minSizemin = 0; + public static long minSizemax = 3000; + public static int maxSizeInit = 5; + + public static float deltaMin = 0; + public static float minDiversityMin = 0; + public static float minDiversityMax = 1; + public static int minDiversityInit = 1; + public static float minDiversity = minDiversityInit; + public static final int scrollbarSize = 1000; + public static float Unstability_ScoreMin = 0f; + public static float Unstability_ScoreMax = 1f; + public static float deltaMax = 255f; + + public final static String deltastring = "Stride in intensiy threshold space"; + public final static String Unstability_Scorestring = "Unstability score"; + public final static String minDivstring = "Minimum diversity b/w components of tree"; + public final static String minSizestring = "Minimum size of MSER ellipses"; + public final static String maxSizestring = "Maximum size of MSER ellipses"; + public final static Label deltaText = new Label(deltastring + " = " + deltaInit, Label.CENTER); + public final static Label Unstability_ScoreText = new Label(Unstability_Scorestring + " = " + Unstability_ScoreInit, + Label.CENTER); + public final static Label minDivText = new Label(minDivstring + " = " + minDiversityInit, Label.CENTER); + public final static Label minSizeText = new Label(minSizestring + " = " + minSize, Label.CENTER); + public final static Label maxSizeText = new Label(maxSizestring + " = " + maxSize, Label.CENTER); + + public final static JScrollBar deltaS = new JScrollBar(Scrollbar.HORIZONTAL, deltaInit, 10, 0, 10 + scrollbarSize); + public final static JScrollBar Unstability_ScoreS = new JScrollBar(Scrollbar.HORIZONTAL, Unstability_ScoreInit, 10, 0, + 10 + scrollbarSize); + public final static JScrollBar minDiversityS = new JScrollBar(Scrollbar.HORIZONTAL, minDiversityInit, 10, 0, 10 + scrollbarSize); + + public final static JScrollBar minSizeS = new JScrollBar(Scrollbar.HORIZONTAL, minSizeInit, 10, 0, 10 + scrollbarSize); + public final static JScrollBar maxSizeS = new JScrollBar(Scrollbar.HORIZONTAL, maxSizeInit, 10, 0, 10 + scrollbarSize); + + + + public static boolean darktobright = false; + public static boolean brighttodark = true; + public static CheckboxGroup minormaxMser = new CheckboxGroup(); + public final static Checkbox findminimaMser = new Checkbox("Locate Maxima", minormaxMser, brighttodark); + public final static Checkbox findmaximaMser = new Checkbox("Locate Minima", minormaxMser, darktobright); + + + public static JButton AllMser = new JButton("MSER in 3D/4D"); + + + + public static void setInitialDelta(final float value) { + delta = value; + deltaInit = scrollbar.Utility.computeScrollbarPositionFromValue(delta, deltaMin, deltaMax, scrollbarSize); + } + + public static void setInitialmaxSize(final int value) { + maxSize = value; + maxSizeInit = scrollbar.Utility.computeScrollbarPositionFromValue(maxSize,minSizemin, minSizemax, scrollbarSize); + } + public static void setInitialminDiversity(final float value) { + minDiversity = value; + minDiversityInit = scrollbar.Utility.computeScrollbarPositionFromValue(minDiversity, minDiversityMin, minDiversityMax, + scrollbarSize); + } + + public static double getInitialminDiversity(final float value) { + + return minDiversity; + + } + + public static void setInitialminSize(final int value) { + minSize = value; + minSizeInit = scrollbar.Utility.computeScrollbarPositionFromValue(minSize, minSizemin, minSizemax, scrollbarSize); + } + public static void setInitialUnstability_Score(final float value) { + Unstability_Score = value; + Unstability_ScoreInit = scrollbar.Utility.computeScrollbarPositionFromValue(Unstability_Score, Unstability_ScoreMin, + Unstability_ScoreMax, scrollbarSize); + } + public static double getInitialminSize(final int value) { + + return minSize; + + } + + public double getInitialDelta(final float value) { + + return delta; + + } + + public static double getInitialmaxSize(final int value) { + + return maxSize; + + } + public static JPanel MserPanel() { + + layoutManager.Setlayout.LayoutSetter(MserPanel); + + Border mserborder = new CompoundBorder(new TitledBorder("MSER detection"), new EmptyBorder(layoutManager.Setlayout.c.insets)); + Unstability_ScoreS.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(Unstability_ScoreInit, + Unstability_ScoreMin, Unstability_ScoreMax, scrollbarSize)); + + minDiversityS.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(minDiversityInit, + minDiversityMin, minDiversityMax, scrollbarSize)); + + maxSizeS.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(maxSize, minSizemin, minSizemax, + scrollbarSize)); + + minSizeS.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(minSize, minSizemin, minSizemax, + scrollbarSize)); + + deltaS.setValue( + scrollbar.Utility.computeScrollbarPositionFromValue(deltaInit, deltaMin, deltaMax, scrollbarSize)); + + delta = scrollbar.Utility.computeValueFromScrollbarPosition(deltaS.getValue(), deltaMin, deltaMax, + scrollbarSize); + minDiversity = scrollbar.Utility.computeValueFromScrollbarPosition(minDiversityS.getValue(), + minDiversityMin, minDiversityMax, scrollbarSize); + Unstability_Score = scrollbar.Utility.computeValueFromScrollbarPosition(Unstability_ScoreS.getValue(), + Unstability_ScoreMin, Unstability_ScoreMax, scrollbarSize); + minSize = (long) scrollbar.Utility.computeValueFromScrollbarPosition(minSizeS.getValue(), minSizemin, + minSizemax, scrollbarSize); + + maxSize = (long) scrollbar.Utility.computeValueFromScrollbarPosition(maxSizeS.getValue(), minSizemin, + minSizemax, scrollbarSize); + + + MserPanel.add(deltaText, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(deltaS, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + + MserPanel.add(Unstability_ScoreText, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(Unstability_ScoreS, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(minDivText, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(minDiversityS, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(minSizeText, new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(minSizeS, new GridBagConstraints(0, 7, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + MserPanel.add(maxSizeText, new GridBagConstraints(0, 8, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + MserPanel.add(maxSizeS, new GridBagConstraints(0, 9, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + MserPanel.add(findminimaMser, new GridBagConstraints(0, 10, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + MserPanel.add(findmaximaMser, new GridBagConstraints(1, 10, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + MserPanel.add(AllMser, new GridBagConstraints(1, 12, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + MserPanel.setBorder(mserborder); + + + + return MserPanel; + + } + + +} diff --git a/src/main/java/mserMethods/MSERSeg.java b/src/main/java/mserMethods/MSERSeg.java new file mode 100644 index 0000000..9178845 --- /dev/null +++ b/src/main/java/mserMethods/MSERSeg.java @@ -0,0 +1,119 @@ +package mserMethods; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import javax.swing.JProgressBar; +import javax.swing.SwingWorker; + +import ij.gui.Roi; +import ij.process.ColorProcessor; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import mserGUI.CovistoMserPanel; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.componenttree.mser.MserTree; +import net.imglib2.type.NativeType; +import net.imglib2.util.Pair; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import zGUI.CovistoZselectPanel; + +public class MSERSeg extends SwingWorker { + + final InteractiveMethods parent; + final JProgressBar jpb; + + public MSERSeg(final InteractiveMethods parent, final JProgressBar jpb) { + + this.parent = parent; + this.jpb = jpb; + + } + + @Override + protected Void doInBackground() throws Exception { + if(!parent.snakeongoing) { + if (CovistoMserPanel.darktobright) + + parent.newtree = MserTree.buildMserTree(parent.newimg, CovistoMserPanel.delta, CovistoMserPanel.minSize, CovistoMserPanel.maxSize, + CovistoMserPanel.Unstability_Score, CovistoMserPanel.minDiversity, true); + + else + + parent.newtree = MserTree.buildMserTree(parent.newimg, CovistoMserPanel.delta, CovistoMserPanel.minSize, CovistoMserPanel.maxSize, + CovistoMserPanel.Unstability_Score, CovistoMserPanel.minDiversity, false); + parent.overlay.clear(); + parent.Rois = utility.FinderUtils.getcurrentRois(parent.newtree); + + parent.CurrentPreRoiobject = new ArrayList(); + ArrayList centerRoi = utility.FinderUtils.getRoiMean(parent.newtree); + + + + + for (int index = 0; index < centerRoi.size(); ++index) { + + Roi or = parent.Rois.get(index); + + or.setStrokeColor(parent.colorDrawMser); + parent.overlay.add(or); + + + } + for (Roi currentroi: parent.Rois) { + + final double[] geocenter = currentroi.getContourCentroid(); + final Pair Intensityandpixels = PreRoiobject.getIntensity(currentroi, parent.CurrentView); + final double intensity = Intensityandpixels.getA(); + final double numberofpixels = Intensityandpixels.getB(); + final double averageintensity = intensity / numberofpixels; + PreRoiobject currentobject = new PreRoiobject(currentroi, new double[] {geocenter[0], geocenter[1], CovistoZselectPanel.thirdDimension}, + numberofpixels, intensity, averageintensity, CovistoZselectPanel.thirdDimension, CovistoTimeselectPanel.fourthDimension); + parent.CurrentPreRoiobject.add(currentobject); + } + + for (Map.Entry> entry : parent.ZTRois.entrySet()) { + + ArrayList current = entry.getValue(); + for (PreRoiobject currentroi : current) { + + if (currentroi.fourthDimension == CovistoTimeselectPanel.fourthDimension && currentroi.thirdDimension == CovistoZselectPanel.thirdDimension) { + + currentroi.rois.setStrokeColor(parent.colorSnake); + parent.overlay.add(currentroi.rois); + + } + + } + } + parent.imp.setOverlay(parent.overlay); + parent.imp.updateAndDraw(); + + + + utility.CovsitoProgressBar.CovistoSetProgressBar(jpb, "Done"); + } + if(parent.snakeongoing) + parent.updatePreview(ValueChange.SNAKE); + return null; + } + + @Override + protected void done() { + + + + try { + get(); + } catch (InterruptedException e) { + + } catch (ExecutionException e) { + + } + + } + +} diff --git a/src/main/java/nearestNeighbourGUI/CovistoNearestNPanel.java b/src/main/java/nearestNeighbourGUI/CovistoNearestNPanel.java new file mode 100644 index 0000000..ed6c12f --- /dev/null +++ b/src/main/java/nearestNeighbourGUI/CovistoNearestNPanel.java @@ -0,0 +1,51 @@ +package nearestNeighbourGUI; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.Scrollbar; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoNearestNPanel { + + public static final int scrollbarSize = 1000; + public static JPanel NearestNPanel = new JPanel(); + public static final String maxSearchstringNearest = "Maximum search radius"; + public static int maxSearchInit = 100; + public static float maxSearchradiusNearest = 15; + public static Label maxSearchTextNearest = new Label(maxSearchstringNearest + " = " + maxSearchInit, Label.CENTER); + public static final JScrollBar maxSearchNearest = new JScrollBar(Scrollbar.HORIZONTAL, maxSearchInit, 10, 0, 10 + scrollbarSize); + + public static float maxSearchradiusMinNearest = 1; + public static float maxSearchradiusMaxNearest = 1000; + public static final JButton AllthreeD = new JButton("Track in Z"); + + public static JPanel NearestNPanel() { + + layoutManager.Setlayout.LayoutSetter(NearestNPanel); + Border NNborder = new CompoundBorder(new TitledBorder("NearestNeighbour Search in Z"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + maxSearchTextNearest = new Label(maxSearchstringNearest + " = " + maxSearchradiusNearest, Label.CENTER); + + NearestNPanel.add(maxSearchTextNearest, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + NearestNPanel.add(maxSearchNearest, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + NearestNPanel.add(AllthreeD, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + NearestNPanel.setBorder(NNborder); + + return NearestNPanel; + + } + +} diff --git a/src/main/java/pluginTools/ComputeAngles.java b/src/main/java/pluginTools/ComputeAngles.java deleted file mode 100644 index e29a158..0000000 --- a/src/main/java/pluginTools/ComputeAngles.java +++ /dev/null @@ -1,283 +0,0 @@ -package pluginTools; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ExecutionException; - -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.SwingWorker; - -import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.SimpleWeightedGraph; - -import costMatrix.PixelratiowDistCostFunction; -import ellipsoidDetector.Intersectionobject; -import ij.ImageStack; -import kalmanTracker.ETrackCostFunction; -import kalmanTracker.IntersectionobjectCollection; -import kalmanTracker.KFsearch; -import kalmanTracker.NearestNeighbourSearch; -import kalmanTracker.NearestNeighbourSearch2D; -import kalmanTracker.TrackModel; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.util.Pair; -import net.imglib2.util.ValuePair; -import pluginTools.InteractiveSimpleEllipseFit.ValueChange; -import track.TrackingFunctions; -import utility.CreateTable; -import utility.ThreeDRoiobject; - -public class ComputeAngles extends SwingWorker { - - final InteractiveSimpleEllipseFit parent; - final JProgressBar jpb; - - public ComputeAngles(final InteractiveSimpleEllipseFit parent, final JProgressBar jpb) { - - this.parent = parent; - - this.jpb = jpb; - } - - @Override - protected Void doInBackground() throws Exception { - - System.out.println("Invoking Intersection" + " " + parent.Accountedframes.size() + " " + parent.AccountedZ.size()); - - HashMap map = sortByValues(parent.Accountedframes); - parent.Accountedframes = map; - HashMap mapZ = sortByValues(parent.AccountedZ); - parent.AccountedZ = mapZ; - - EllipseTrack newtrack = new EllipseTrack(parent, jpb); - newtrack.IntersectandTrack(); - System.out.println("Invoked Intersection"); - return null; - - } - - private static HashMap sortByValues(HashMap map) { - List> list = new LinkedList>(map.entrySet()); - - // Defined Custom Comparator here - Collections.sort(list, new Comparator>() { - - @Override - public int compare(Entry o1, Entry o2) { - - return (o1.getValue()).compareTo(o2.getValue()); - - } - }); - - // Here I am copying the sorted list in HashMap - // using LinkedHashMap to preserve the insertion order - HashMap sortedHashMap = new LinkedHashMap(); - - for (Iterator> it = list.iterator(); it.hasNext();) { - - Map.Entry entry = (Map.Entry) it.next(); - - sortedHashMap.put(entry.getKey(), entry.getValue()); - } - return sortedHashMap; - } - - @Override - protected void done() { - - parent.jpb.setIndeterminate(false); - - parent.Cardframe.validate(); - - parent.prestack = new ImageStack((int) parent.originalimg.dimension(0), (int) parent.originalimg.dimension(1), - java.awt.image.ColorModel.getRGBdefault()); - - parent.resultDraw.clear(); - - parent.Tracklist.clear(); - - TrackingFunctions track = new TrackingFunctions(parent); - if (parent.ndims > 3) { - - Iterator> itZ = parent.AccountedZ.entrySet().iterator(); - - while (itZ.hasNext()) { - - int z = itZ.next().getValue(); - - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = track.Trackfunction(); - - parent.parentgraphZ.put(Integer.toString(z), simplegraph); - } - Lineage(); - } - - else { - - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = track.Trackfunction(); - - - - parent.parentgraphZ.put(Integer.toString(1), simplegraph); - - Lineage(); - } - - try { - get(); - } catch (InterruptedException e) { - - } catch (ExecutionException e) { - - } - - } - - public void Lineage() { - - for (Map.Entry> entryZ : parent.parentgraphZ - .entrySet()) { - - TrackModel model = new TrackModel(entryZ.getValue()); - - int minid = Integer.MAX_VALUE; - int maxid = Integer.MIN_VALUE; - - for (final Integer id : model.trackIDs(true)) { - - if (id > maxid) - maxid = id; - - if (id < minid) - minid = id; - - } - - - if (minid != Integer.MAX_VALUE) { - - for (final Integer id : model.trackIDs(true)) { - - Comparator> ThirdDimcomparison = new Comparator>() { - - @Override - public int compare(final Pair A, - final Pair B) { - - return A.getB().z - B.getB().z; - - } - - }; - - Comparator> FourthDimcomparison = new Comparator>() { - - @Override - public int compare(final Pair A, - final Pair B) { - - return A.getB().t - B.getB().t; - - } - - }; - - model.setName(id, "Track" + id + entryZ.getKey()); - - final HashSet Angleset = model.trackIntersectionobjects(id); - - Iterator Angleiter = Angleset.iterator(); - - while (Angleiter.hasNext()) { - - Intersectionobject currentangle = Angleiter.next(); - parent.Tracklist.add(new ValuePair( - Integer.toString(id) + entryZ.getKey(), currentangle)); - } - Collections.sort(parent.Tracklist, ThirdDimcomparison); - if (parent.fourthDimensionSize > 1) - Collections.sort(parent.Tracklist, FourthDimcomparison); - - } - - for (int id = minid; id <= maxid; ++id) { - Intersectionobject bestangle = null; - if (model.trackIntersectionobjects(id) != null) { - - List sortedList = new ArrayList( - model.trackIntersectionobjects(id)); - - Collections.sort(sortedList, new Comparator() { - - @Override - public int compare(Intersectionobject o1, Intersectionobject o2) { - - return o1.t - o2.t; - } - - }); - - Iterator iterator = sortedList.iterator(); - - int count = 0; - while (iterator.hasNext()) { - - Intersectionobject currentangle = iterator.next(); - - if (count == 0) - bestangle = currentangle; - if(parent.originalimg.numDimensions() > 3) { - if (currentangle.t == parent.fourthDimension) { - bestangle = currentangle; - count++; - break; - } - } - else if (parent.originalimg.numDimensions()<= 3){ - if (currentangle.z == parent.thirdDimension) { - bestangle = currentangle; - count++; - break; - - } - - - } - - - } - parent.Finalresult.put(Integer.toString(id) + entryZ.getKey(), bestangle); - - } - - } - } - } - CreateTable.CreateTableView(parent); - - } - - - -} \ No newline at end of file diff --git a/src/main/java/pluginTools/ComputeAnglesCurrent.java b/src/main/java/pluginTools/ComputeAnglesCurrent.java deleted file mode 100644 index 398c5e1..0000000 --- a/src/main/java/pluginTools/ComputeAnglesCurrent.java +++ /dev/null @@ -1,301 +0,0 @@ -package pluginTools; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.GridBagConstraints; -import java.awt.Insets; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.text.DecimalFormat; -import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.concurrent.ExecutionException; - -import javax.swing.JProgressBar; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.SwingWorker; - -import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.SimpleWeightedGraph; - -import ellipsoidDetector.Intersectionobject; -import ij.ImageStack; -import kalmanTracker.ETrackCostFunction; -import kalmanTracker.IntersectionobjectCollection; -import kalmanTracker.KFsearch; -import kalmanTracker.NearestNeighbourSearch; -import kalmanTracker.NearestNeighbourSearch2D; -import kalmanTracker.TrackModel; -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.util.Pair; -import net.imglib2.util.ValuePair; -import utility.CreateTable; - -public class ComputeAnglesCurrent extends SwingWorker { - - final InteractiveSimpleEllipseFit parent; - final JProgressBar jpb; - - public ComputeAnglesCurrent(final InteractiveSimpleEllipseFit parent, final JProgressBar jpb) { - - this.parent = parent; - - this.jpb = jpb; - } - - @Override - protected Void doInBackground() throws Exception { - - parent.table.removeAll(); - HashMap map = sortByValues(parent.Accountedframes); - parent.Accountedframes = map; - - HashMap mapZ = sortByValues(parent.AccountedZ); - parent.AccountedZ = mapZ; - - EllipseTrack newtrack = new EllipseTrack(parent, jpb); - newtrack.IntersectandTrackCurrent(); - - return null; - - } - - private static HashMap sortByValues(HashMap map) { - List> list = new LinkedList>(map.entrySet()); - - // Defined Custom Comparator here - Collections.sort(list, new Comparator>() { - - @Override - public int compare(Entry o1, Entry o2) { - - return (o1.getValue()).compareTo(o2.getValue()); - - } - }); - - // Here I am copying the sorted list in HashMap - // using LinkedHashMap to preserve the insertion order - HashMap sortedHashMap = new LinkedHashMap(); - - for (Iterator> it = list.iterator(); it.hasNext();) { - - Map.Entry entry = (Map.Entry) it.next(); - - sortedHashMap.put(entry.getKey(), entry.getValue()); - } - return sortedHashMap; - } - - @Override - protected void done() { - - parent.jpb.setIndeterminate(false); - - parent.Cardframe.validate(); - - parent.prestack = new ImageStack((int) parent.originalimg.dimension(0), (int) parent.originalimg.dimension(1), - java.awt.image.ColorModel.getRGBdefault()); - - parent.resultDraw.clear(); - - parent.Tracklist.clear(); - - if (parent.ndims > 3) { - - Iterator> itZ = parent.AccountedZ.entrySet().iterator(); - - while (itZ.hasNext()) { - - int z = itZ.next().getValue(); - - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = Trackfunction(); - - parent.parentgraphZ.put(Integer.toString(z), simplegraph); - } - Lineage(); - } - - else { - - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = Trackfunction(); - - - - parent.parentgraphZ.put(Integer.toString(1), simplegraph); - - Lineage(); - } - - try { - get(); - } catch (InterruptedException e) { - - } catch (ExecutionException e) { - - } - - } - - public void Lineage() { - - for (Map.Entry> entryZ : parent.parentgraphZ - .entrySet()) { - - TrackModel model = new TrackModel(entryZ.getValue()); - - int minid = Integer.MAX_VALUE; - int maxid = Integer.MIN_VALUE; - - for (final Integer id : model.trackIDs(true)) { - - if (id > maxid) - maxid = id; - - if (id < minid) - minid = id; - - } - - - if (minid != Integer.MAX_VALUE) { - - for (final Integer id : model.trackIDs(true)) { - - Comparator> ThirdDimcomparison = new Comparator>() { - - @Override - public int compare(final Pair A, - final Pair B) { - - return A.getB().z - B.getB().z; - - } - - }; - - Comparator> FourthDimcomparison = new Comparator>() { - - @Override - public int compare(final Pair A, - final Pair B) { - - return A.getB().t - B.getB().t; - - } - - }; - - model.setName(id, "Track" + id + entryZ.getKey()); - - final HashSet Angleset = model.trackIntersectionobjects(id); - - Iterator Angleiter = Angleset.iterator(); - - while (Angleiter.hasNext()) { - - Intersectionobject currentangle = Angleiter.next(); - parent.Tracklist.add(new ValuePair( - Integer.toString(id) + entryZ.getKey(), currentangle)); - } - Collections.sort(parent.Tracklist, ThirdDimcomparison); - if (parent.fourthDimensionSize > 1) - Collections.sort(parent.Tracklist, FourthDimcomparison); - - } - - for (int id = minid; id <= maxid; ++id) { - Intersectionobject bestangle = null; - if (model.trackIntersectionobjects(id) != null) { - - List sortedList = new ArrayList( - model.trackIntersectionobjects(id)); - - Collections.sort(sortedList, new Comparator() { - - @Override - public int compare(Intersectionobject o1, Intersectionobject o2) { - - return o1.t - o2.t; - } - - }); - - Iterator iterator = sortedList.iterator(); - - int count = 0; - while (iterator.hasNext()) { - - Intersectionobject currentangle = iterator.next(); - - if (count == 0) - bestangle = currentangle; - if(parent.originalimg.numDimensions() > 3) { - if (currentangle.t == parent.fourthDimension) { - bestangle = currentangle; - count++; - break; - } - } - else if (parent.originalimg.numDimensions()<= 3){ - if (currentangle.z == parent.thirdDimension) { - bestangle = currentangle; - count++; - break; - - } - - - } - - - } - parent.Finalresult.put(Integer.toString(id) + entryZ.getKey(), bestangle); - - } - - } - } - } - CreateTable.CreateTableView(parent); - - } - - public SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > Trackfunction() { - - parent.UserchosenCostFunction = new ETrackCostFunction(1, 0); - - ArrayList> colllist = new ArrayList>(); - for(Map.Entry> entry : parent.ALLIntersections.entrySet()) { - - ArrayList bloblist = entry.getValue(); - colllist.add(bloblist); - - - - } - - KFsearch Tsearch = new KFsearch(colllist, parent.UserchosenCostFunction, parent.maxSearchradius, parent.maxSearchradius, parent.maxframegap, parent.AccountedZ, parent.jpb); - Tsearch.process(); - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = Tsearch.getResult(); - - return simplegraph; - - - } - - - -} \ No newline at end of file diff --git a/src/main/java/pluginTools/ComputeManual.java b/src/main/java/pluginTools/ComputeManual.java deleted file mode 100644 index 22e36fd..0000000 --- a/src/main/java/pluginTools/ComputeManual.java +++ /dev/null @@ -1,247 +0,0 @@ -package pluginTools; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutionException; - -import javax.swing.JProgressBar; -import javax.swing.SwingWorker; - -import org.jgrapht.graph.DefaultWeightedEdge; -import org.jgrapht.graph.SimpleWeightedGraph; - -import ellipsoidDetector.Intersectionobject; -import ij.ImageStack; -import kalmanTracker.ETrackCostFunction; -import kalmanTracker.IntersectionobjectCollection; -import kalmanTracker.KFsearch; -import kalmanTracker.TrackModel; -import net.imglib2.util.Pair; -import net.imglib2.util.ValuePair; -import utility.CreateTable; - -public class ComputeManual extends SwingWorker { - - final InteractiveSimpleEllipseFit parent; - final JProgressBar jpb; - - public ComputeManual(final InteractiveSimpleEllipseFit parent, final JProgressBar jpb) { - - this.parent = parent; - - this.jpb = jpb; - - } - - @Override - protected Void doInBackground() throws Exception { - - - EllipseTrack newtrack = new EllipseTrack(parent, jpb); - newtrack.ManualIntervention(); - - return null; - - } - - @Override - protected void done() { - - parent.jpb.setIndeterminate(false); - - parent.Cardframe.validate(); - - parent.prestack = new ImageStack((int) parent.originalimg.dimension(0), (int) parent.originalimg.dimension(1), - java.awt.image.ColorModel.getRGBdefault()); - - parent.resultDraw.clear(); - - parent.Tracklist.clear(); - - if (parent.ndims > 3) { - - Iterator> itZ = parent.AccountedZ.entrySet().iterator(); - - while (itZ.hasNext()) { - - int z = itZ.next().getValue(); - - SimpleWeightedGraph simplegraph = Trackfunction(); - - parent.parentgraphZ.put(Integer.toString(z), simplegraph); - } - Lineage(); - } - - else { - - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = Trackfunction(); - - - - parent.parentgraphZ.put(Integer.toString(1), simplegraph); - - Lineage(); - } - - try { - get(); - } catch (InterruptedException e) { - - } catch (ExecutionException e) { - - } - - } - - public void Lineage() { - - for (Map.Entry> entryZ : parent.parentgraphZ - .entrySet()) { - - TrackModel model = new TrackModel(entryZ.getValue()); - - int minid = Integer.MAX_VALUE; - int maxid = Integer.MIN_VALUE; - - for (final Integer id : model.trackIDs(true)) { - - if (id > maxid) - maxid = id; - - if (id < minid) - minid = id; - - } - - - if (minid != Integer.MAX_VALUE) { - - for (final Integer id : model.trackIDs(true)) { - - Comparator> ThirdDimcomparison = new Comparator>() { - - @Override - public int compare(final Pair A, - final Pair B) { - - return A.getB().z - B.getB().z; - - } - - }; - - Comparator> FourthDimcomparison = new Comparator>() { - - @Override - public int compare(final Pair A, - final Pair B) { - - return A.getB().t - B.getB().t; - - } - - }; - - model.setName(id, "Track" + id + entryZ.getKey()); - - final HashSet Angleset = model.trackIntersectionobjects(id); - - Iterator Angleiter = Angleset.iterator(); - - while (Angleiter.hasNext()) { - - Intersectionobject currentangle = Angleiter.next(); - parent.Tracklist.add(new ValuePair( - Integer.toString(id) + entryZ.getKey(), currentangle)); - } - Collections.sort(parent.Tracklist, ThirdDimcomparison); - if (parent.fourthDimensionSize > 1) - Collections.sort(parent.Tracklist, FourthDimcomparison); - - } - - for (int id = minid; id <= maxid; ++id) { - Intersectionobject bestangle = null; - if (model.trackIntersectionobjects(id) != null) { - - List sortedList = new ArrayList( - model.trackIntersectionobjects(id)); - - Collections.sort(sortedList, new Comparator() { - - @Override - public int compare(Intersectionobject o1, Intersectionobject o2) { - - return o1.t - o2.t; - } - - }); - - Iterator iterator = sortedList.iterator(); - - int count = 0; - while (iterator.hasNext()) { - - Intersectionobject currentangle = iterator.next(); - - if (count == 0) - bestangle = currentangle; - if(parent.originalimg.numDimensions() > 3) { - if (currentangle.t == parent.fourthDimension) { - bestangle = currentangle; - count++; - break; - } - } - else if (parent.originalimg.numDimensions()<= 3){ - if (currentangle.z == parent.thirdDimension) { - bestangle = currentangle; - count++; - break; - - } - - - } - - - } - parent.Finalresult.put(Integer.toString(id) + entryZ.getKey(), bestangle); - - } - - } - } - } - CreateTable.CreateTableView(parent); - - } - - public SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > Trackfunction() { - - parent.UserchosenCostFunction = new ETrackCostFunction(1, 0); - - ArrayList> colllist = new ArrayList>(); - for(Map.Entry> entry : parent.ALLIntersections.entrySet()) { - - ArrayList bloblist = entry.getValue(); - colllist.add(bloblist); - - - } - - KFsearch Tsearch = new KFsearch(colllist, parent.UserchosenCostFunction, parent.maxSearchradius, parent.maxSearchradius, parent.maxframegap, parent.AccountedZ, parent.jpb); - Tsearch.process(); - SimpleWeightedGraph< Intersectionobject, DefaultWeightedEdge > simplegraph = Tsearch.getResult(); - - return simplegraph; - - - } -} diff --git a/src/main/java/pluginTools/Computeinwater.java b/src/main/java/pluginTools/Computeinwater.java index 2ef9470..ee4bc3c 100644 --- a/src/main/java/pluginTools/Computeinwater.java +++ b/src/main/java/pluginTools/Computeinwater.java @@ -46,10 +46,7 @@ import pluginTools.InteractiveSimpleEllipseFit.ValueChange; import utility.DisplayAuto; import utility.LabelCurvature; -import utility.LabelRansac; -import utility.NormalIntersection; import utility.Roiobject; -import utility.SuperIntersection; import utility.Watershedobject; import varun_algorithm_ransac_Ransac.Ellipsoid; @@ -88,84 +85,7 @@ public Computeinwater(final InteractiveSimpleEllipseFit parent, final RandomAcce this.percent = percent; } - public void ParallelRansac() { - - int nThreads = Runtime.getRuntime().availableProcessors(); - // set up executor service - final ExecutorService taskExecutor = Executors.newFixedThreadPool(nThreads); - List> tasks = new ArrayList>(); - - ArrayList resultroi = new ArrayList(); - ArrayList resultovalroi = new ArrayList(); - ArrayList resultlineroi = new ArrayList(); - // Obtain the points of intersections - - ArrayList AllPointsofIntersect = new ArrayList(); - ArrayList Allintersection = new ArrayList(); - - ArrayList> fitmapspecial = new ArrayList>(); - - Iterator setiter = parent.pixellist.iterator(); - - parent.superReducedSamples = new ArrayList>>>(); - while (setiter.hasNext()) { - percent++; - - int label = setiter.next(); - - Watershedobject current; - - if (parent.supermode || parent.automode) { - current = utility.Watershedobject.CurrentLabelBinaryImage(CurrentViewInt, label); - - System.out.println("Should open in only super or auto mode"); - } - else { - current = utility.Watershedobject.CurrentLabelImage(CurrentViewInt, label); - - System.out.println("Should open in manual mode"); - } - - // Neglect the small watershed regions by choosing only those regions which have - // more than 9 candidate points for ellipse fitting - List> truths = new ArrayList>(); - - - - if (current.Size > parent.minperimeter / 3 * parent.minperimeter / 3 - && current.Size < parent.maxperimeter / 3 * parent.maxperimeter / 3 - && current.meanIntensity > parent.minellipsepoints) { - tasks.add(Executors.callable(new LabelRansac(parent, current.source, truths, t, z, resultroi, - resultovalroi, resultlineroi, AllPointsofIntersect, Allintersection, fitmapspecial, parent.jpb, - percent, parent.supermode))); - - - } - } - - try { - - System.out.println("Executing tasks"); - taskExecutor.invokeAll(tasks); - - - // Get superintersection - - - SuperIntersection newintersect = new SuperIntersection(parent); - AllPointsofIntersect = new ArrayList(); - Allintersection = new ArrayList(); - newintersect.Getsuperintersection(resultroi, resultovalroi, resultlineroi, AllPointsofIntersect, - Allintersection, t, z); - - } catch (InterruptedException e1) { - - System.out.println(e1 + " Task not executed"); - - } - - } - + public void ParallelRansacCurve() throws Exception { diff --git a/src/main/java/pluginTools/EllipseTrack.java b/src/main/java/pluginTools/EllipseTrack.java index da70635..93b71b3 100644 --- a/src/main/java/pluginTools/EllipseTrack.java +++ b/src/main/java/pluginTools/EllipseTrack.java @@ -65,36 +65,7 @@ public EllipseTrack(final InteractiveSimpleEllipseFit parent, final JProgressBar - public void BlockRepeat(double percent, int z, int t) { - parent.updatePreview(ValueChange.THIRDDIMmouse); - - percent++; - if(jpb!=null) - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.fourthDimensionSize + 1), - "Fitting ellipses and computing angles T = " + t + "/" + parent.fourthDimensionSize + " Z = " + z + "/" - + parent.thirdDimensionSize); - - RandomAccessibleInterval CurrentView = utility.Slicer.getCurrentViewBit(parent.empty, z, - parent.thirdDimensionSize, t, parent.fourthDimensionSize); - RandomAccessibleInterval CurrentViewInt = utility.Slicer.getCurrentViewInt(parent.originalimgsuper, z, - parent.thirdDimensionSize, t, parent.fourthDimensionSize); - - IntType min = new IntType(); - IntType max = new IntType(); - computeMinMax(Views.iterable(CurrentViewInt), min, max); - // Neglect the background class label - int currentLabel = min.get(); - - - - RandomAccessibleInterval expanededtotalimg = ExpandBorder.extendBorder(parent, CurrentViewInt, currentLabel); - //RandomAccessibleInterval CurrentViewthin = getThin(CurrentView); - GetPixelList(expanededtotalimg); - Computeinwater compute = new Computeinwater(parent, CurrentView, expanededtotalimg, t, z, (int) percent); - compute.ParallelRansac(); - } - public void BlockRepeatCurve(double percent, int z, int t) throws Exception { @@ -144,45 +115,7 @@ public void TestAuto(int z, int t) { } } - public void BlockRepeatAuto(double percent, int z, int t) { - - parent.updatePreview(ValueChange.THIRDDIMmouse); - - percent++; - if(jpb!=null) - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.fourthDimensionSize + 1), - "Fitting ellipses and computing angles T = " + t + "/" + parent.fourthDimensionSize + " Z = " + z + "/" - + parent.thirdDimensionSize); - - RandomAccessibleInterval CurrentView = utility.Slicer.getCurrentViewBit(parent.empty, z, - parent.thirdDimensionSize, t, parent.fourthDimensionSize); - RandomAccessibleInterval CurrentViewSmooth = utility.Slicer.getCurrentViewBit(parent.emptysmooth, z, - parent.thirdDimensionSize, t, parent.fourthDimensionSize); - - // Use smoothed image for segmentation and non smooth image for getting the - // candidate points for fitting ellipses - RandomAccessibleInterval CurrentInt = getSeg(CurrentViewSmooth); - //RandomAccessibleInterval CurrentViewthin = getThin(CurrentView); - if (parent.showWater) { - - Watershow(CurrentInt); - - } - - IntType min = new IntType(); - IntType max = new IntType(); - computeMinMax(Views.iterable(CurrentInt), min, max); - // Neglect the background class label - int currentLabel = max.get(); - - RandomAccessibleInterval expanededtotalimg = ExpandBorder.extendBorder(parent, CurrentInt, currentLabel); - //RandomAccessibleInterval CurrentViewthin = getThin(CurrentView); - GetPixelList(expanededtotalimg); - Computeinwater compute = new Computeinwater(parent, CurrentView, expanededtotalimg, t, z, (int) percent); - compute.ParallelRansac(); - - } public void BlockRepeatAutoCurve(double percent, int z, int t) throws Exception { @@ -252,179 +185,10 @@ public void Watershow(RandomAccessibleInterval CurrentInt) { } - public void BlockRepeatManual(double percent, int z, int t) { - - parent.updatePreview(ValueChange.THIRDDIMmouse); - percent++; - if (parent.fourthDimensionSize != 0) - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.Accountedframes.entrySet().size()), - "Fitting ellipses and computing angles T = " + t + "/" + parent.fourthDimensionSize + " Z = " + z - + "/" + parent.thirdDimensionSize); - else - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.AccountedZ.entrySet().size()), - "Fitting ellipses and computing angles T/Z = " + z + "/" + parent.thirdDimensionSize); - - RandomAccessibleInterval CurrentView = utility.Slicer.getCurrentViewBit(parent.empty, z, - parent.thirdDimensionSize, t, parent.fourthDimensionSize); - RandomAccessibleInterval CurrentViewInt = utility.Slicer.getCurrentViewInt(parent.emptyWater, z, - parent.thirdDimensionSize, t, parent.fourthDimensionSize); - - IntType min = new IntType(); - IntType max = new IntType(); - computeMinMax(Views.iterable(CurrentViewInt), min, max); - // Neglect the background class label - int currentLabel = min.get(); - - RandomAccessibleInterval expanededtotalimg = ExpandBorder.extendBorder(parent, CurrentViewInt, currentLabel); - - //RandomAccessibleInterval CurrentViewthin = getThin(CurrentView); - GetPixelList(expanededtotalimg); - Computeinwater compute = new Computeinwater(parent, CurrentView, expanededtotalimg, t, z, (int) percent); - compute.ParallelRansac(); - - } - - public void ManualIntervention() { - - double percent = 0; - int z = parent.thirdDimension; - int t = parent.fourthDimension; - - BlockRepeatManual(percent, z, t); - - } - public void IntersectandTrack() { - - // Main method for computing intersections and tangents and angles between - // tangents - double percent = 0; - - if (parent.supermode) { - - if (parent.originalimg.numDimensions() > 3) { - - for (int t = parent.AutostartTime; t <= parent.AutoendTime; ++t) { - parent.TID = Integer.toString(t); - parent.Accountedframes.put(parent.TID, t); - for (int z = 1; z <= parent.thirdDimensionSize; ++z) { - - parent.thirdDimension = z; - parent.fourthDimension = t; - - BlockRepeat(percent, z, t); - - } - } - - } - - else if (parent.originalimg.numDimensions() > 2) { - - for (int z = parent.AutostartTime; z <= parent.AutoendTime; ++z) { - - parent.thirdDimension = z; - parent.ZID = Integer.toString(z); - parent.AccountedZ.put(parent.ZID, z); - BlockRepeat(percent, z, 1); - - } - - } else { - int z = parent.thirdDimension; - int t = parent.fourthDimension; - - BlockRepeat(percent, z, t); - - } - - } - - if (parent.automode) { - - if (parent.originalimg.numDimensions() > 3) { - for (int t = parent.AutostartTime; t <= parent.AutoendTime; ++t) { - parent.TID = Integer.toString(t); - parent.Accountedframes.put(parent.TID, t); - for (int z = 1; z <= parent.thirdDimensionSize; ++z) { - - parent.thirdDimension = z; - parent.fourthDimension = t; - - BlockRepeatAuto(percent, z, t); - - } - } - - } - - else if (parent.originalimg.numDimensions() > 2) { - - for (int z = parent.AutostartTime; z <= parent.AutoendTime; ++z) { - - parent.thirdDimension = z; - parent.ZID = Integer.toString(z); - parent.AccountedZ.put(parent.ZID, z); - BlockRepeatAuto(percent, z, 1); - - } - - } else { - int z = parent.thirdDimension; - int t = parent.fourthDimension; - BlockRepeatAuto(percent, z, t); - - } - } - - else if (!parent.automode && !parent.supermode) { - - if (parent.originalimg.numDimensions() > 3) { - - for (Map.Entry entry : parent.Accountedframes.entrySet()) { - - int t = entry.getValue(); - - for (Map.Entry entryZ : parent.AccountedZ.entrySet()) { - - int z = entryZ.getValue(); - - BlockRepeatManual(percent, z, t); - } - } - - } else if (parent.originalimg.numDimensions() > 2 && parent.originalimg.numDimensions() <= 3) { - - int t = parent.fourthDimension; - - for (Map.Entry entryZ : parent.AccountedZ.entrySet()) { - - int z = entryZ.getValue(); - - - BlockRepeatManual(percent, z, t); - - } - - } - - else { - - int z = parent.thirdDimension; - int t = parent.fourthDimension; - BlockRepeatManual(percent, z, t); - - } - - } - - parent.updatePreview(ValueChange.THIRDDIMmouse); - - } - public void ComputeCurvature() throws Exception { // Main method for computing intersections and tangents and angles between @@ -747,37 +511,7 @@ public & Type> void computeMinMax(final Iterable } } - public void IntersectandTrackCurrent() { - - double percent = 0; - - int z = parent.thirdDimension; - int t = parent.fourthDimension; - if (parent.supermode) { - - - - BlockRepeat(percent, z, t); - - } - - if (parent.automode) { - - - - BlockRepeatAuto(percent, z, t); - - } - - else if (!parent.automode && !parent.supermode) { - - BlockRepeatManual(percent, z, t); - - } - - parent.updatePreview(ValueChange.THIRDDIMmouse); - - } + public void ComputeCurvatureCurrent() throws Exception { double percent = 0; diff --git a/src/main/java/pluginTools/InteractiveSimpleEllipseFit.java b/src/main/java/pluginTools/InteractiveSimpleEllipseFit.java index 1b2c16a..3c1747b 100644 --- a/src/main/java/pluginTools/InteractiveSimpleEllipseFit.java +++ b/src/main/java/pluginTools/InteractiveSimpleEllipseFit.java @@ -1,11 +1,13 @@ package pluginTools; + import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Checkbox; import java.awt.CheckboxGroup; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; import java.awt.Frame; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -77,7 +79,6 @@ import ij.process.ColorProcessor; import kalmanForSegments.Segmentobject; import kalmanTracker.TrackModel; -import listeners.AngleListener; import listeners.AutoEndListener; import listeners.AutoStartListener; import listeners.BackGroundListener; @@ -106,7 +107,6 @@ import listeners.LinescanradiusListener; import listeners.LostFrameListener; import listeners.LowProbListener; -import listeners.ManualInterventionListener; import listeners.MaxTryListener; import listeners.MaxperimeterListener; import listeners.MaxsizeListener; @@ -117,7 +117,6 @@ import listeners.MinsizeListener; import listeners.OutsideCutoffListener; import listeners.RListener; -import listeners.RedoListener; import listeners.RegionInteriorListener; import listeners.ResolutionListener; import listeners.RimLineSelectionListener; @@ -1281,17 +1280,7 @@ public void updatePreview(final ValueChange change) { clockimp.setOverlay(clockoverlay); } - if (change == ValueChange.INSIDE || change == ValueChange.OUTSIDE) { - - if (automode) { - - empty = CreateBinaryBit(originalimg, lowprob, highprob); - - } - - StartComputing(); - - } + if (change == ValueChange.SEG) { @@ -1480,13 +1469,7 @@ public void updatePreview(final ValueChange change) { } if (change == ValueChange.THIRDDIMmouse || change == ValueChange.FOURTHDIMmouse) { - if (Tracklist.size() > 0 && (automode || supermode)) { - - ComputeAngles current = new ComputeAngles(this, null); - - current.Lineage(); - - } + if (Tracklist.size() > 0 && (curveautomode || curvesupermode)) { ComputeCurvature.CurvedLineage(this); @@ -1607,21 +1590,7 @@ public void updatePreview(final ValueChange change) { } - public void StartComputing() { - ComputeAngles compute = new ComputeAngles(this, jpb); - - compute.execute(); - - } - - public void StartManualIntervention() { - - ComputeManual compute = new ComputeManual(this, jpb); - - compute.execute(); - - } public void StartCurvatureComputing(File savefile) { if (!batchmode) { @@ -1676,13 +1645,7 @@ public RandomAccessibleInterval CreateBinaryBit(RandomAccessibleInterva } - public void StartComputingCurrent() { - redoing = true; - ComputeAnglesCurrent compute = new ComputeAnglesCurrent(this, jpb); - - compute.execute(); - } public void StartCurvatureComputingCurrent() { @@ -1861,7 +1824,7 @@ public void DisplayDefault() { } } - public JFrame Cardframe = new JFrame("Intersection angle or curvature measurment"); + public JFrame Cardframe = new JFrame("Deformation measurment"); public JPanel panelCont = new JPanel(); public JPanel panelFirst = new JPanel(); public JPanel panelSecond = new JPanel(); @@ -1916,6 +1879,13 @@ public void DisplayDefault() { public String smoothsliderstring = "Ratio of functions "; public String mininlierstring = "Box Size(um)"; + public JLabel lblCitation = new JLabel("" + "Wizard of Oz is a plugin to compute " + + "local deformation by fitting circles and measuring distance of the boundary .\n" + "

      " + + "points from the center of the cell. \n" + + "

      " + "Made for OOzge Ozguc and Heon Leon Maitre by Varun Kapoor, " + + ""); + + public Label timeText = new Label("Current T = " + 1, Label.CENTER); public Label zText = new Label("Current Z = " + 1, Label.CENTER); public Label zgenText = new Label("Current Z / T = " + 1, Label.CENTER); @@ -2032,7 +2002,8 @@ public void DisplayDefault() { new EmptyBorder(c.insets)); int textwidth = 5; - + public static final Font FONT = new Font( "Arial", Font.PLAIN, 10 ); + public static final Font SMALL_FONT = FONT.deriveFont( 8 ); public void Card(boolean hide) { minInlierText = new Label(mininlierstring + " = " + minNumInliers, Label.CENTER); @@ -2194,7 +2165,14 @@ public void Card(boolean hide) { c.gridheight = 10; c.gridy = 1; c.gridx = 0; - + lblCitation.setFont(SMALL_FONT); + + final GridBagConstraints gbcLblCitation = new GridBagConstraints(); + gbcLblCitation.fill = GridBagConstraints.BOTH; + gbcLblCitation.insets = new Insets(5, 5, 5, 5); + gbcLblCitation.gridwidth = 4; + gbcLblCitation.gridx = 0; + gbcLblCitation.gridy = 0; // Put time slider Timeselect.add(timeText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, @@ -2644,10 +2622,8 @@ public void actionPerformed(ActionEvent e) { CurrentCurvaturebutton.addActionListener(new CurrentCurvatureListener(this)); Curvaturebutton.addActionListener(new CurvatureListener(this)); Displaybutton.addActionListener(new DisplayVisualListener(this, true)); - Anglebutton.addActionListener(new AngleListener(this)); startT.addTextListener(new AutoStartListener(this)); endT.addTextListener(new AutoEndListener(this)); - Redobutton.addActionListener(new RedoListener(this)); Roibutton.addActionListener(new RoiListener(this)); inputFieldZ.addTextListener(new ZlocListener(this, false)); cutoffField.addTextListener(new InsideLocListener(this, false)); @@ -2676,7 +2652,6 @@ public void actionPerformed(ActionEvent e) { highprobmax, scrollbarSize, highprobslider)); Clearmanual.addActionListener(new ClearforManual(this)); - ManualCompute.addActionListener(new ManualInterventionListener(this)); maxSearchS.addAdjustmentListener(new ETrackMaxSearchListener(this, maxSearchText, maxSearchstring, maxSearchradiusMin, maxSearchradiusMax, scrollbarSize, maxSearchS)); diff --git a/src/main/java/plugins.config b/src/main/java/plugins.config index fff38ca..3e9ff45 100644 --- a/src/main/java/plugins.config +++ b/src/main/java/plugins.config @@ -1 +1 @@ -Plugins>ETrack, " Automated Angle and Curvature measurement ", pluginTools.IlastikEllipseFileChooser \ No newline at end of file +Plugins>Wizard_of_OZ, " Local Deformation & Intensity Tracking ", pluginTools.IlastikEllipseFileChooser \ No newline at end of file diff --git a/src/main/java/preProcessing/AddGaussian.java b/src/main/java/preProcessing/AddGaussian.java new file mode 100644 index 0000000..5968394 --- /dev/null +++ b/src/main/java/preProcessing/AddGaussian.java @@ -0,0 +1,146 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import ij.IJ; +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +public class AddGaussian { + + + final public static void addGaussian( final RandomAccessibleInterval< FloatType > image, final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + final double[] two_sq_sigma = new double[ numDimensions ]; + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = 2 * getSuggestedKernelDiameter( sigma[ d ] ); + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ] / 2; + max[ d ] = min[ d ] + size[ d ] - 1; + two_sq_sigma[ d ] = sigma[ d ] * sigma[ d ]; + } + + final RandomAccessible< FloatType > infinite = Views.extendZero( image ); + final RandomAccessibleInterval< FloatType > interval = Views.interval( infinite, min, max ); + final IterableInterval< FloatType > iterable = Views.iterable( interval ); + final Cursor< FloatType > cursor = iterable.localizingCursor(); + + + + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = 1; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getDoublePosition( d ); + value *= Math.exp( -(x * x) / two_sq_sigma[ d ] ); + + + } + + + cursor.get().set( cursor.get().get() + (float)value ); + + + } + + + + + } + + + + + + + final public static void addGaussian( final IterableInterval< FloatType > image, final double Amplitude, + final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = getSuggestedKernelDiameter( sigma[ d ] ) * 2; + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ]/2; + max[ d ] = min[ d ] + size[ d ] - 1; + + } + + + final Cursor< FloatType > cursor = image.localizingCursor(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = Amplitude; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getIntPosition( d ); + value *= Math.exp( -(x * x) / (sigma[ d ] * sigma[ d ] ) ); + } + + + cursor.get().set( cursor.get().get() + (float)value ); + + + } + + } + + public static int getSuggestedKernelDiameter( final double sigma ) + { + int size = 0; + int cutoff = 5; // This number means cutoff is chosen to be cutoff times sigma. + if ( sigma > 0 ) + size = Math.max( cutoff, ( 2 * ( int ) ( cutoff * sigma + 0.5 ) + 1 ) ); + + return size; + } + + + + + +} diff --git a/src/main/java/preProcessing/FlatFieldCorrection.java b/src/main/java/preProcessing/FlatFieldCorrection.java new file mode 100644 index 0000000..f45c1a2 --- /dev/null +++ b/src/main/java/preProcessing/FlatFieldCorrection.java @@ -0,0 +1,252 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import java.util.Arrays; + +import javax.swing.JProgressBar; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.gauss3.Gauss3; +import net.imglib2.algorithm.neighborhood.DiamondShape.NeighborhoodsAccessible; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +/** + * A simple flat field and median filter that operates on 1D, 2D or 3D images. + *

      + * For 3D images, the filtering is done only in 2D XY slices. Removes shot noise, + * corrects for uneven illumination + * + * @author V. Kapoor - 2017, Jean-Yves Tinevez - 2015 + * + * @param + * the type of the source image. + */ +public class FlatFieldCorrection & NativeType> extends BenchmarkAlgorithm implements OutputAlgorithm< RandomAccessibleInterval< T >> +{ + private static final String BASE_ERROR_MSG = "[FlatFieldAndMedianFilter2D] "; + + private final RandomAccessibleInterval< T > source; + + private RandomAccessibleInterval< T > output; + + private final int radius; + + private final double[] psf; + + private JProgressBar jpb; + + /** + * Instantiate a new Flat field correction filter that will operate on the specified + * source. + * + * @param currentPreprocessedimg + * the source to operate on. + * @param radius + * determines the size of the neighborhood. In 2D or 3D, a radius + * of 1 will generate a 3x3 neighborhood. + */ + public FlatFieldCorrection( final RandomAccessibleInterval source, final int radius, final double[] psf ) + { + this.source = source; + this.radius = radius; + this.psf = psf; + } + + + public FlatFieldCorrection( final RandomAccessibleInterval source, final int radius, final JProgressBar jpb, final double[] psf ) + { + this.source = source; + this.radius = radius; + this.psf = psf; + this.jpb = jpb; + } + + @Override + public boolean checkInput() + { + if ( source.numDimensions() > 3 ) + { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D or 3D images. Got " + source.numDimensions() + "D."; + return false; + } + if ( radius < 1 ) + { + errorMessage = BASE_ERROR_MSG + "Radius cannot be smaller than 1. Got " + radius + "."; + return false; + } + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final T type = source.randomAccess().get().createVariable(); + final ImgFactory< T > factory = Util.getArrayOrCellImgFactory( source, type ); + this.output = factory.create( source, type ); + + + + if ( source.numDimensions() > 2 ) + { + final long nz = source.dimension( 2 ); + + double percent = 0; + for ( long z = 0; z < nz; z++ ) + { + + percent++; + final IntervalView< T > slice = Views.hyperSlice( source, 2, z ); + final IntervalView< T > outputSlice = Views.hyperSlice( output, 2, z ); + + if(jpb!=null) + scrollbar.Utility.SetProgressBar(jpb, 100 * percent/nz, "PreProcessing, please wait.."); + + processSlice( slice, outputSlice ); + + + + } + } + else + { + processSlice( source, Views.iterable(output) ); + } + + this.processingTime = System.currentTimeMillis() - start; + return true; + } + + + private void subtract(RandomAccessibleInterval target, RandomAccessibleInterval darkfield){ + + + Cursor cursor = Views.iterable(target).localizingCursor(); + RandomAccess Gaussran = darkfield.randomAccess(); + double value; + while(cursor.hasNext()){ + + cursor.fwd(); + + Gaussran.setPosition(cursor); + + value = cursor.get().getRealDouble() - Gaussran.get().getRealDouble(); + cursor.get().setReal(Math.max(value,0)); + + + } + + + + + } + + /** + * + * @param in + * + * The randomaccessible interval on which we apply a gaussian filter (big radius = image dim / n pixels) and then + * subtract it from the original image to create a background corrected image + * + * @param out + * + * Median filter is applied on the background corrected image generated from the previous step and is the pre-processed image to be + * used by the Line finders of the MTV tracker + * + */ + private void processSlice( final RandomAccessibleInterval< T > in, final IterableInterval< T > out ) + { + + double[] sigma = new double[in.numDimensions()]; + for (int d = 0; d < in.numDimensions(); ++d) { + sigma[d] = (int) Math.round((in.realMax(d) - in.realMin(d)) / 20.0); + } + + RandomAccessibleInterval gaussimg= Utils.copyImage(in); + RandomAccessibleInterval correctedgaussimg = Utils.copyImage(in); + + + try { + + Gauss3.gauss(sigma, Views.extendBorder(gaussimg), gaussimg); + + } catch (IncompatibleTypeException e) { + + e.printStackTrace(); + } + + // Subtract the darkfield from the image + subtract(correctedgaussimg, gaussimg); + + + + final Cursor< T > cursor = out.localizingCursor(); + + final RectangleShape shape = new RectangleShape( radius, false ); + final net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible nracessible = shape.neighborhoodsRandomAccessible( Views.extendZero( correctedgaussimg ) ); + final RandomAccess< Neighborhood< T >> nra = nracessible.randomAccess( correctedgaussimg ); + + final int size = ( int ) nra.get().size(); + final double[] values = new double[ size ]; + + // Fill buffer with median values. + while ( cursor.hasNext() ) + { + cursor.fwd(); + nra.setPosition( cursor ); + + + int index = 0; + for ( final T pixel : nra.get() ) + { + values[ index++ ] = pixel.getRealDouble(); + } + + Arrays.sort( values, 0, index ); + cursor.get().setReal( values[ ( index - 1 ) / 2 ] ); + } + } + + @Override + public RandomAccessibleInterval getResult() + { + return output; + } +} diff --git a/src/main/java/preProcessing/FlatFieldOnly.java b/src/main/java/preProcessing/FlatFieldOnly.java new file mode 100644 index 0000000..a9925f5 --- /dev/null +++ b/src/main/java/preProcessing/FlatFieldOnly.java @@ -0,0 +1,237 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import java.util.Arrays; + +import javax.swing.JProgressBar; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.gauss3.Gauss3; +import net.imglib2.algorithm.neighborhood.DiamondShape.NeighborhoodsAccessible; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +/** + * A simple flat field and median filter that operates on 1D, 2D or 3D images. + *

      + * For 3D images, the filtering is done only in 2D XY slices. Removes shot noise, + * corrects for uneven illumination + * + * @author V. Kapoor - 2017, Jean-Yves Tinevez - 2015 + * + * @param + * the type of the source image. + */ +public class FlatFieldOnly & NativeType> extends BenchmarkAlgorithm implements OutputAlgorithm< RandomAccessibleInterval< T >> +{ + private static final String BASE_ERROR_MSG = "[FlatField2D] "; + + private final RandomAccessibleInterval< T > source; + + private RandomAccessibleInterval< T > output; + + private final int flatfieldradius; + + + private JProgressBar jpb; + + /** + * Instantiate a new Flat field correction filter that will operate on the specified + * source. + * + * @param currentPreprocessedimg + * the source to operate on. + * @param radius + * determines the size of the neighborhood. In 2D or 3D, a radius + * of 1 will generate a 3x3 neighborhood. + */ + public FlatFieldOnly( final RandomAccessibleInterval source, final int flatfieldradius ) + { + this.source = source; + this.flatfieldradius = flatfieldradius; + } + + + public FlatFieldOnly( final RandomAccessibleInterval source, final int flatfieldradius, final JProgressBar jpb ) + { + this.source = source; + this.flatfieldradius = flatfieldradius; + this.jpb = jpb; + } + + @Override + public boolean checkInput() + { + if ( source.numDimensions() > 3 ) + { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D or 3D images. Got " + source.numDimensions() + "D."; + return false; + } + + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final T type = source.randomAccess().get().createVariable(); + final ImgFactory< T > factory = Util.getArrayOrCellImgFactory( source, type ); + this.output = factory.create( source, type ); + + + + if ( source.numDimensions() > 2 ) + { + final long nz = source.dimension( 2 ); + + double percent = 0; + for ( long z = 0; z < nz; z++ ) + { + + percent++; + final IntervalView< T > slice = Views.hyperSlice( source, 2, z ); + final IntervalView< T > outputSlice = Views.hyperSlice( output, 2, z ); + + if(jpb!=null) + scrollbar.Utility.SetProgressBar(jpb, 100 * percent/nz, "Doing Flat field correction, please wait.."); + + processSlice( slice, outputSlice ); + + + + } + } + else + { + processSlice( source, Views.iterable(output) ); + } + + this.processingTime = System.currentTimeMillis() - start; + return true; + } + + + private void subtract(RandomAccessibleInterval target, RandomAccessibleInterval darkfield){ + + + Cursor cursor = Views.iterable(target).localizingCursor(); + RandomAccess Gaussran = darkfield.randomAccess(); + double value; + while(cursor.hasNext()){ + + cursor.fwd(); + + Gaussran.setPosition(cursor); + + value = cursor.get().getRealDouble() - Gaussran.get().getRealDouble(); + cursor.get().setReal(Math.max(value,0)); + + + } + + + + + } + + /** + * + * @param in + * + * The randomaccessible interval on which we apply a gaussian filter (big radius = image dim / n pixels) and then + * subtract it from the original image to create a background corrected image + * + * @param out + * + * Median filter is applied on the background corrected image generated from the previous step and is the pre-processed image to be + * used by the Line finders of the MTV tracker + * + */ + private void processSlice( final RandomAccessibleInterval< T > in, final IterableInterval< T > out ) + { + + double[] sigma = new double[in.numDimensions()]; + for (int d = 0; d < in.numDimensions(); ++d) { + sigma[d] = (int) Math.round((in.realMax(d) - in.realMin(d)) / flatfieldradius); + } + + RandomAccessibleInterval gaussimg = Utils.copyImage(in); + RandomAccessibleInterval correctedgaussimg = Utils.copyImage(in); + + + try { + + Gauss3.gauss(sigma, Views.extendBorder(gaussimg), gaussimg); + + } catch (IncompatibleTypeException e) { + + e.printStackTrace(); + } + + // Subtract the darkfield from the image + subtract(correctedgaussimg, gaussimg); + + + final Cursor< T > cursor = out.localizingCursor(); + + + + final RandomAccess nra = correctedgaussimg.randomAccess(); + + + // Fill buffer with median values. + while ( cursor.hasNext() ) + { + cursor.fwd(); + nra.setPosition( cursor ); + + + cursor.get().setReal( nra.get().getPowerDouble()); + } + + + } + + @Override + public RandomAccessibleInterval getResult() + { + return output; + } +} diff --git a/src/main/java/preProcessing/GenericFilters.java b/src/main/java/preProcessing/GenericFilters.java new file mode 100644 index 0000000..7884922 --- /dev/null +++ b/src/main/java/preProcessing/GenericFilters.java @@ -0,0 +1,189 @@ +package preProcessing; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.algorithm.region.hypersphere.HyperSphere; +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.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Intervals; +import net.imglib2.view.Views; + +public class GenericFilters { + + + /** + * + * Gets the boundary between different labels and make a binary image of the current label + * + * @param inputimg + * @param label + * @return + */ + public static RandomAccessibleInterval BinaryBoundaryImage(RandomAccessibleInterval inputimg){ + + RandomAccessibleInterval gradientimg = new ArrayImgFactory().create(inputimg, + new BitType()); + RandomAccess gradran = gradientimg.randomAccess(); + Interval interval = Intervals.expand( inputimg, -1 ); + final Cursor< BitType > center = Views.iterable( inputimg ).cursor(); + final RectangleShape shape = new RectangleShape( 1, true ); + inputimg = Views.interval( inputimg, interval ); + for ( final Neighborhood< BitType > localNeighborhood : shape.neighborhoods( inputimg ) ) + { + final BitType centerValue = center.next(); + + boolean isSimilar = true; + + for ( final BitType value : localNeighborhood ) + { + if ( centerValue.compareTo( value ) < 0 ) + { + isSimilar = false; + break; + } + } + + if ( isSimilar ) + { + + gradran.setPosition(center); + gradran.get().set(centerValue); + } + } + + + + + return gradientimg; + + } + + + public static RandomAccessibleInterval CurrentLabelImage(RandomAccessibleInterval Intimg, int currentLabel) { + int n = Intimg.numDimensions(); + long[] position = new long[n]; + + Cursor intCursor = Views.iterable(Intimg).cursor(); + + RandomAccessibleInterval outimg = new ArrayImgFactory().create(Intimg, new BitType()); + + RandomAccess imageRA = outimg.randomAccess(); + + // Go through the whole image and add every pixel, that belongs to + // the currently processed label + long[] minVal = { Intimg.max(0), Intimg.max(1) }; + long[] maxVal = { Intimg.min(0), Intimg.min(1) }; + + while (intCursor.hasNext()) { + intCursor.fwd(); + imageRA.setPosition(intCursor); + int i = intCursor.get().get(); + if (i == currentLabel) { + intCursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + + imageRA.get().setOne(); + } else + imageRA.get().setZero(); + + } + + + return outimg; + + } + + public static & NativeType> RandomAccessibleInterval GradientmagnitudeImage( + RandomAccessibleInterval inputimg) { + + + final T type = inputimg.randomAccess().get().createVariable(); + RandomAccessibleInterval gradientimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + Cursor cursor = Views.iterable(gradientimg).localizingCursor(); + RandomAccessible view = Views.extendBorder(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + // iterate over all pixels + while (cursor.hasNext()) { + // move the cursor to the next pixel + cursor.fwd(); + + // compute gradient and its direction in each dimension + double gradient = 0; + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + // set the randomaccess to the location of the cursor + randomAccess.setPosition(cursor); + + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + } + + cursor.get().setReal(Math.sqrt(gradient)); + + } + + return gradientimg; + } + + public static & NativeType> RandomAccessibleInterval AutoThreshold(RandomAccessibleInterval inputimg){ + + final T type = inputimg.randomAccess().get().createVariable(); + RandomAccessibleInterval Threshimg = new ArrayImgFactory().create(inputimg, + type); + //Supress values below the low threshold + int n = inputimg.numDimensions(); + double[] position = new double[n]; + final double Lowthreshold = GlobalThresholding.AutomaticThresholdingSec(inputimg); + double threshold = Lowthreshold; + Cursor inputcursor = Views.iterable(inputimg).localizingCursor(); + RandomAccess outputran = Threshimg.randomAccess(); + while(inputcursor.hasNext()){ + inputcursor.fwd(); + inputcursor.localize(position); + outputran.setPosition(inputcursor); + if (inputcursor.get().getRealDouble()<= threshold) + outputran.get().setZero(); + else + outputran.get().set(inputcursor.get()); + } + return Threshimg; + + + } + + +} diff --git a/src/main/java/preProcessing/GetLocalmaxminMT.java b/src/main/java/preProcessing/GetLocalmaxminMT.java new file mode 100644 index 0000000..02e7680 --- /dev/null +++ b/src/main/java/preProcessing/GetLocalmaxminMT.java @@ -0,0 +1,891 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.Iterator; + + +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.ImageStack; +import ij.gui.Line; +import ij.gui.Overlay; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.PointSampleList; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.RealRandomAccess; +import net.imglib2.algorithm.dog.DogDetection; +import net.imglib2.algorithm.dog.DogDetection.ExtremaType; +import net.imglib2.algorithm.localextrema.RefinedPeak; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.algorithm.region.hypersphere.HyperSphere; +import net.imglib2.algorithm.region.hypersphere.HyperSphereCursor; +import net.imglib2.algorithm.stats.Normalize; +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.interpolation.randomaccess.NLinearInterpolatorFactory; +import net.imglib2.Cursor; +import net.imglib2.Point; +import net.imglib2.PointSampleList; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.type.NativeType; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Intervals; +import net.imglib2.util.Pair; +import net.imglib2.util.RealSum; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; + +public class GetLocalmaxminMT { + + public static enum IntensityType { + Gaussian + } + + protected IntensityType intensityType; + + // Thresholding a FloatType to set values below the threshold to 0 intensity + public static< T extends RealType< T > & NativeType< T >> void ThresholdingMT(RandomAccessibleInterval img, RandomAccessibleInterval imgout, + double ThresholdValue, final IntensityType setintensity, double[] sigma) { + + final double[] backpos = new double[imgout.numDimensions()]; + final Cursor bound = Views.iterable(img).localizingCursor(); + + final RandomAccess outbound = imgout.randomAccess(); + + while (bound.hasNext()) { + + + bound.fwd(); + + outbound.setPosition(bound); + + if (bound.get().getPowerDouble() > (ThresholdValue)) { + + bound.localize(backpos); + switch (setintensity) { + + + + case Gaussian: + Utils.addGaussian(imgout, backpos, sigma); + break; + + default: + Utils.addGaussian(imgout, backpos, sigma); + break; + + } + + } + + else { + + outbound.get().setZero(); + + } + } + } + + // Thresholding a FlotType to convert to BitType + + + // Finds and displays Local Maxima by constructing a 3*3*3.. local + // neighbourhood + public static< T extends RealType< T > & NativeType< T >> RandomAccessibleInterval FindandDisplayLocalMaxima(RandomAccessibleInterval img, + final IntensityType setintensity, double[] sigma) { + final T type = img.randomAccess().get().createVariable(); + // Create a new image for the output + RandomAccessibleInterval output = new ArrayImgFactory().create(img, type); + + // define an interval that is span number of pixel smaller on each side + // in each dimension + int span = 1; + + Interval interval = Intervals.expand(img, -span); + + // create a view on the source with this interval + img = Views.interval(img, interval); + + // create a Cursor that iterates over the source and checks in a + // 8-neighborhood + // if it is a maxima + final Cursor center = Views.iterable(img).cursor(); + + // instantiate a RectangleShape to access rectangular local + // neighborhoods + + final RectangleShape shape = new RectangleShape(span, true); + + // iterate over the set of neighborhoods in the image + for (final Neighborhood localNeighborhood : shape.neighborhoods(img)) { + final T centerValue = center.next(); + + // keep this boolean true as long as no other value in the local + // neighborhood + // is smaller + boolean isMaximum = true; + + // check if all pixels in the local neighborhood that are smaller + for (final T value : localNeighborhood) { + // test if the center is smaller than the current pixel value + if (centerValue.compareTo(value) < 0) { + isMaximum = false; + break; + } + } + int n = img.numDimensions(); + double[] position = new double[n]; + if (isMaximum) { + final RandomAccess outbound = output.randomAccess(); + outbound.setPosition(center); + + center.localize(position); + switch (setintensity) { + + + case Gaussian: + Utils.addGaussian(output, position, sigma); + break; + + + } + + } + } + + return output; + } + + + + + // Finds and displays Local Minima by constructing a 3*3*3.. local + // neighbourhood + public static< T extends RealType< T > & NativeType< T >> RandomAccessibleInterval FindandDisplayLocalMinima(RandomAccessibleInterval img, + final IntensityType setintensity, double[] sigma) { + + final T type = img.randomAccess().get().createVariable(); + // Create a new image for the output + Img output = new ArrayImgFactory().create(img, type); + + // define an interval that is span number of pixel smaller on each side + // in each dimension + int span = 1; + + Interval interval = Intervals.expand(img, -span); + + // create a view on the source with this interval + img = Views.interval(img, interval); + + // create a Cursor that iterates over the source and checks in a + // 8-neighborhood + // if it is a maxima + final Cursor center = Views.iterable(img).cursor(); + + // instantiate a RectangleShape to access rectangular local + // neighborhoods + + final RectangleShape shape = new RectangleShape(span, true); + + // iterate over the set of neighborhoods in the image + for (final Neighborhood localNeighborhood : shape.neighborhoods(img)) { + final T centerValue = center.next(); + + // keep this boolean true as long as no other value in the local + // neighborhood + // is smaller + boolean isMinimum = true; + + // check if all pixels in the local neighborhood that are smaller + for (final T value : localNeighborhood) { + // test if the center is smaller than the current pixel value + if (centerValue.compareTo(value) >= 0) { + isMinimum = false; + break; + } + } + double[] position = new double[img.numDimensions()]; + if (isMinimum) { + + final RandomAccess outbound = output.randomAccess(); + outbound.setPosition(center); + center.localize(position); + switch (setintensity) { + + + + case Gaussian: + Utils.addGaussian(output, position, sigma); + break; + default: + Utils.addGaussian(output, position, sigma); + break; + + } + + } + } + + return output; + } + + // Write Local minima to an ArrayList + public static< T extends RealType< T > & NativeType< T >> ArrayList FindLocalMinima(RandomAccessibleInterval img) { + + int n = img.numDimensions(); + + ArrayList Minlist = new ArrayList(n); + + int span = 1; + + Interval interval = Intervals.expand(img, -span); + + img = Views.interval(img, interval); + + final Cursor center = Views.iterable(img).cursor(); + + final RectangleShape shape = new RectangleShape(span, true); + + for (final Neighborhood localNeighborhood : shape.neighborhoods(img)) { + + final T centerValue = center.next(); + + boolean isMinimum = true; + + for (final T value : localNeighborhood) { + if (centerValue.compareTo(value) >= 0) { + isMinimum = false; + break; + } + } + + if (isMinimum) { + RealPoint Minpoints = new RealPoint(center); + Minlist.add(Minpoints); + } + } + + return Minlist; + } + + // Write Local maxima to an ArrayList + public static< T extends RealType< T > & NativeType< T >> ArrayList FindLocalMaxima(RandomAccessibleInterval img) { + + int n = img.numDimensions(); + + ArrayList Maxlist = new ArrayList(n); + + int span = 1; + + Interval interval = Intervals.expand(img, -span); + + img = Views.interval(img, interval); + + final Cursor center = Views.iterable(img).cursor(); + + final RectangleShape shape = new RectangleShape(span, true); + + for (final Neighborhood localNeighborhood : shape.neighborhoods(img)) { + final T centerValue = center.next(); + + boolean isMaximum = true; + + for (final T value : localNeighborhood) { + + if (centerValue.compareTo(value) < 0) { + isMaximum = false; + break; + } + } + + if (isMaximum) { + RealPoint Minpoints = new RealPoint(center); + Maxlist.add(Minpoints); + } + } + + return Maxlist; + } + + + // Detect minima in Scale space write it as an ArrayList> + public static ArrayList> HoughspaceMaxima(RandomAccessibleInterval houghimage, + FinalInterval interval, double[] sizes, double thetaPerPixel, double rhoPerPixel) { + final Float houghval = GlobalThresholding.AutomaticThresholding(houghimage); + ArrayList> SubpixelMinlist = new ArrayList>(houghimage.numDimensions()); + + // Get local Minima in scale space to get Max rho-theta points + double minPeakValue = 0.5 * houghval; + double smallsigma = 1; + double bigsigma = 1.1; + SubpixelMinlist = GetLocalmaxminMT.ScalespaceMinima(houghimage, interval, thetaPerPixel, rhoPerPixel, + minPeakValue, smallsigma, bigsigma); + + return SubpixelMinlist; + } + + + // Detect minima in Scale space write it as an ArrayList> + public static ArrayList> ScalespaceMinima(RandomAccessibleInterval inputimg, + FinalInterval interval, double thetaPerPixel, double rhoPerPixel, double minPeakValue, double smallsigma, + double bigsigma) { + + ArrayList> SubpixelMinlist = new ArrayList>(inputimg.numDimensions()); + // Create a Dog Detection object in Hough space + DogDetection newdog = new DogDetection(Views.extendMirrorSingle(inputimg), interval, + new double[] { thetaPerPixel, rhoPerPixel }, smallsigma, bigsigma, DogDetection.ExtremaType.MINIMA, + minPeakValue, true); + + // Detect minima in Scale space + SubpixelMinlist = newdog.getSubpixelPeaks(); + + return SubpixelMinlist; + } + + // Detect minima in Scale space write it as an ArrayList<> + public static ArrayList ScalespaceMinimaSimple(RandomAccessibleInterval inputimg, + FinalInterval interval, double thetaPerPixel, double rhoPerPixel, double minPeakValue, double smallsigma, + double bigsigma) { + + ArrayList SubpixelMinlist = new ArrayList(inputimg.numDimensions()); + // Create a Dog Detection object in Hough spacex + DogDetection newdog = new DogDetection(Views.extendMirrorSingle(inputimg), interval, + new double[] { thetaPerPixel, rhoPerPixel }, smallsigma, bigsigma, DogDetection.ExtremaType.MINIMA, + minPeakValue, true); + + // Detect minima in Scale space + SubpixelMinlist = newdog.getPeaks(); + + return SubpixelMinlist; + } + + // Detect minima in Scale space write it as an ArrayList<> + public static ArrayList ScalespaceMaximaSimple(RandomAccessibleInterval inputimg, + FinalInterval interval, double thetaPerPixel, double rhoPerPixel, double minPeakValue, double smallsigma, + double bigsigma) { + + ArrayList SubpixelMaxlist = new ArrayList(inputimg.numDimensions()); + // Create a Dog Detection object in Hough space + DogDetection newdog = new DogDetection(Views.extendMirrorSingle(inputimg), interval, + new double[] { thetaPerPixel, rhoPerPixel }, smallsigma, bigsigma, DogDetection.ExtremaType.MAXIMA, + minPeakValue, true); + + // Detect minima in Scale space + SubpixelMaxlist = newdog.getPeaks(); + + return SubpixelMaxlist; + } + + public static< T extends RealType< T > & NativeType< T >> Pair computeMinMaxIntensity(final RandomAccessibleInterval inputimg) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + + T type = inputimg.randomAccess().get().createVariable(); + // initialize min and max with the first image value + T min = type.copy(); + T max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(min) < 0) { + min.set(type); + + } + + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + Pair pair = new ValuePair(min, max); + return pair; + } + public static< T extends RealType< T > & NativeType< T >> Pair computesecondMinMaxIntensity(final RandomAccessibleInterval inputimg) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + + + T type = inputimg.randomAccess().get().createVariable(); + // initialize min and max with the first image value + + T min = type.copy(); + T secondmin = type.copy(); + T max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(min) < 0) { + min.set(type); + + } + if (type.compareTo(secondmin) < 0 && secondmin.compareTo(min)>0) { + secondmin.set(type); + + } + + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + Pair pair = new ValuePair(secondmin, max); + return pair; + } + + public static double computeMaxIntensity(final RandomAccessibleInterval inputimg) { + final int ndims = inputimg.numDimensions(); + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + // initialize min and max with the first image value + double maxVal = Double.MIN_VALUE; + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + cursor.fwd(); + + + + if (cursor.get().get() > maxVal) { + maxVal = cursor.get().get(); + + } + } + + return maxVal; + } + + public static double computeMinIntensity(final RandomAccessibleInterval inputimg) { + final int ndims = inputimg.numDimensions(); + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + // initialize min and max with the first image value + double minVal = Double.MAX_VALUE; + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + cursor.fwd(); + + + + if (cursor.get().get() < minVal) { + minVal = cursor.get().get(); + + } + } + + return minVal; + } + + public < T extends Comparable< T > & Type< T > > void computeMinMax( + final Iterable< T > input, final T min, final T max ) + { + // create a cursor for the image (the order does not matter) + final Iterator< T > iterator = input.iterator(); + + // initialize min and max with the first image value + T type = iterator.next(); + + min.set( type ); + max.set( type ); + + // loop over the rest of the data and determine min and max value + while ( iterator.hasNext() ) + { + // we need this type more than once + type = iterator.next(); + + if ( type.compareTo( min ) < 0 ) + min.set( type ); + + if ( type.compareTo( max ) > 0 ) + max.set( type ); + } + } + + public static double computeMaxIntensityinlabel(final RandomAccessibleInterval inputimg, + final RandomAccessibleInterval intimg, final int label ) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + final RandomAccess intranac = intimg.randomAccess(); + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + intranac.setPosition(cursor); + int i = intranac.get().get(); + + + if (i == label){ + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + } + final double maxintensity = max.getRealDouble(); + return maxintensity; + } + + public static double computeMaxIntensityalongline(final RandomAccessibleInterval inputimg, + final RandomAccessibleInterval intimg, final int label, final double slope, final double intercept ) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + final RandomAccess intranac = intimg.randomAccess(); + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + intranac.setPosition(cursor); + int i = intranac.get().get(); + + + long pointonline = (long) (cursor.getDoublePosition(1) - slope * cursor.getDoublePosition(1) - intercept); + if (i == label && pointonline == 0){ + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + } + final double maxintensity = max.getRealDouble(); + return maxintensity; + } + public static long[] computeMaxinLabel( + final RandomAccessibleInterval inputimg, + final RandomAccessibleInterval intimg, + final int label, + boolean ignorebright) { + + final Cursor intcursor = Views.iterable(intimg).localizingCursor(); + final RandomAccess ranac = inputimg.randomAccess(); + + Pair pair = GetLocalmaxminMT.computeMinMaxIntensity(inputimg); + + // Neglect bright beads + + final Float threshold = (pair.getB().get() - pair.getA().get())/4; + + // initialize min and max with the first image value + double max = Double.MIN_VALUE; + long[] pos = new long[inputimg.numDimensions()]; + while(intcursor.hasNext()){ + intcursor.fwd(); + + final int i = intcursor.get().get(); + + if (i == label){ + ranac.setPosition(intcursor); + if (ranac.get().get() > max){ + max = ranac.get().getRealDouble(); + + ranac.localize(pos); + + } + + } + + + } + + if (ignorebright == true){ + + if (max<= threshold) + + return pos; + } + if (ignorebright == false) + return pos; + + else + return null; + } + + + + + // Find maxima only if the pixel intensity is higher than a certain + // threshold value + public static RandomAccessibleInterval FindConditionalLocalMaxima( + RandomAccessibleInterval img, + final IntensityType setintensity, double[] sigma, Float val) { + + RandomAccessibleInterval output = new ArrayImgFactory().create(img, new FloatType()); + // Construct a 5*5*5... local neighbourhood + int span = 2; + + Interval interval = Intervals.expand(img, -span); + + img = Views.interval(img, interval); + + final Cursor center = Views.iterable(img).cursor(); + + final RectangleShape shape = new RectangleShape(span, true); + + for (final Neighborhood localNeighborhood : shape.neighborhoods(img)) { + final FloatType centerValue = center.next(); + + boolean isMaximum = true; + + for (final FloatType value : localNeighborhood) { + if (centerValue.compareTo(value) < 0 && centerValue.get() < val) { + isMaximum = false; + break; + } + } + + int n = img.numDimensions(); + double[] position = new double[n]; + if (isMaximum) { + final RandomAccess outbound = output.randomAccess(); + outbound.setPosition(center); + + center.localize(position); + switch (setintensity) { + + + + case Gaussian: + Utils.addGaussian(output, position, sigma); + break; + + + default: + Utils.addGaussian(output, position, sigma); + break; + + } + + } + + } + + return output; + } + + // Find maxima only if the pixel intensity is higher than a certain + // threshold value + public static RandomAccessibleInterval FindDirectionalLocalMaxima( + RandomAccessibleInterval img, ImgFactory imageFactory, + final IntensityType setintensity, double[] sigma, Float val) { + + RandomAccessibleInterval output = imageFactory.create(img, new FloatType()); + // Construct a 5*5*5... local neighbourhood + int span = 2; + + Interval interval = Intervals.expand(img, -span); + + img = Views.interval(img, interval); + + final Cursor center = Views.iterable(img).cursor(); + + final RectangleShape shape = new RectangleShape(span, true); + + for (final Neighborhood localNeighborhood : shape.neighborhoods(img)) { + final FloatType centerValue = center.next(); + + boolean isMaximum = true; + + Cursor localcursor = localNeighborhood.localizingCursor(); + + while (localcursor.hasNext()) { + localcursor.fwd(); + if (centerValue.compareTo(localcursor.get()) < 0){ + isMaximum = false; + break; + } + } + + + int n = img.numDimensions(); + double[] position = new double[n]; + if (isMaximum) { + final RandomAccess outbound = output.randomAccess(); + outbound.setPosition(center); + + center.localize(position); + switch (setintensity) { + + + + case Gaussian: + Utils.addGaussian(output, position, sigma); + break; + + + + default: + Utils.addGaussian(output, position, sigma); + break; + + } + + } + + } + + return output; + } + + public static ArrayList> Removesimilar(ArrayList> SubpixelMinlist, + double thetatolerance, double rhotolerance) { + /******** + * The part below removes the close values in theta and rho coordinate + * (keeps only one of the multiple values) + ********/ + + int j = 0; + + for (int i = 0; i < SubpixelMinlist.size(); ++i) { + + j = i + 1; + while (j < SubpixelMinlist.size()) { + + if (Math.abs(SubpixelMinlist.get(i).getDoublePosition(0) + - SubpixelMinlist.get(j).getDoublePosition(0)) <= thetatolerance + && Math.abs(SubpixelMinlist.get(i).getDoublePosition(1) + - SubpixelMinlist.get(j).getDoublePosition(1)) <= rhotolerance) { + + SubpixelMinlist.remove(j); + + } + + else { + ++j; + } + + } + + } + + return SubpixelMinlist; + } + public static void ThresholdingMTBit( final RandomAccessibleInterval currentPreprocessedimg, + final RandomAccessibleInterval< BitType > out, + final Float threshold ) + { + final Cursor< FloatType > cIn = Views.iterable( currentPreprocessedimg ).localizingCursor(); + final RandomAccess< BitType > rOut = out.randomAccess(); + + final double[] position = new double[out.numDimensions()]; + while ( cIn.hasNext() ) + { + cIn.fwd(); + rOut.setPosition( cIn ); + + rOut.localize(position); + if ( cIn.get().get() >= ( threshold ) ){ + + rOut.get().setOne(); + + } + else{ + rOut.get().setZero(); + + } + } + + } + public static void ThresholdingMTBit(RandomAccessibleInterval img, + RandomAccessibleInterval imgout, FloatType thresholdHough) { + Normalize.normalize(Views.iterable(img), new FloatType(0), new FloatType(255)); + Normalize.normalize(Views.iterable(img), new FloatType(0), new FloatType(255)); + final double[] backpos = new double[imgout.numDimensions()]; + + final Cursor bound = Views.iterable(img).localizingCursor(); + + final RandomAccess outbound = imgout.randomAccess(); + + while (bound.hasNext()) { + + bound.fwd(); + + outbound.setPosition(bound); + { + + if (bound.get().compareTo( thresholdHough ) < 0){ + + bound.localize(backpos); + + outbound.get().setReal(0); + + } + + else { + + outbound.get().setReal(255); + + } + + + + } + + } + } + + + +} diff --git a/src/main/java/preProcessing/GlobalThresholding.java b/src/main/java/preProcessing/GlobalThresholding.java new file mode 100644 index 0000000..7fb658a --- /dev/null +++ b/src/main/java/preProcessing/GlobalThresholding.java @@ -0,0 +1,166 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + + + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.Point; +import net.imglib2.PointSampleList; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.RealSum; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; + +public class GlobalThresholding { + + // Automatic thresholding done on the Normalized input image + // Algorithm: Get max and min intensity for an image and choose an initial + // threshold value, T = (max-min)/2. This threshold value + // segments image into two regions, get the mean pixel value for both the + // regions (x1, x2) + // then set the new threshold T_N = (x1 +x2)/2, segment initial image by + // this value and repeat the process + // till (T_N - T_{N+1} & NativeType< T >> Float AutomaticThresholding(RandomAccessibleInterval inputimg) { + + + final T type = inputimg.randomAccess().get().createVariable(); + T min = type; + T max = type; + + Float ThresholdNew, Thresholdupdate; + + Pair pair = new ValuePair(min, max); + pair = GetLocalmaxminMT.computeMinMaxIntensity(inputimg); + + ThresholdNew = (float) ((pair.getB().getRealDouble() - pair.getA().getRealDouble()) / 2); + + // Get the new threshold value after segmenting the inputimage with thresholdnew + Thresholdupdate = (float) SegmentbyThresholding(Views.iterable(inputimg), ThresholdNew); + + while (true) { + + ThresholdNew = (float) SegmentbyThresholding(Views.iterable(inputimg), Thresholdupdate); + + // Check if the new threshold value is close to the previous value + if (Math.abs(Thresholdupdate - ThresholdNew) < 1.0E-2) + break; + Thresholdupdate = ThresholdNew; + } + + + return ThresholdNew; + + } + + +public static< T extends RealType< T > & NativeType< T >> double AutomaticThresholdingSec(RandomAccessibleInterval inputimg) { + + final T type = inputimg.randomAccess().get().createVariable(); + T min = type; + T max = type; + + Float ThresholdNew, Thresholdupdate; + + Pair pair = new ValuePair(min, max); + pair = GetLocalmaxminMT.computesecondMinMaxIntensity(inputimg); + + ThresholdNew = (float) ((pair.getB().getRealDouble() - pair.getA().getRealDouble()) / 2); + + // Get the new threshold value after segmenting the inputimage with thresholdnew + Thresholdupdate = (float) SegmentbyThresholding(Views.iterable(inputimg), ThresholdNew); + while (true) { + + ThresholdNew = (float) SegmentbyThresholding(Views.iterable(inputimg), Thresholdupdate); + + // Check if the new threshold value is close to the previous value + if (Math.abs(Thresholdupdate - ThresholdNew) < 1.0E-2) + break; + Thresholdupdate = ThresholdNew; + } + + + return ThresholdNew; + + } + + // Segment image by thresholding, used to determine automatic thresholding + // level + public static< T extends RealType< T > & NativeType< T >> double SegmentbyThresholding(IterableInterval inputimg, Float Threshold) { + + int n = inputimg.numDimensions(); + double ThresholdNew; + PointSampleList listA = new PointSampleList(n); + PointSampleList listB = new PointSampleList(n); + Cursor cursor = inputimg.localizingCursor(); + while (cursor.hasNext()) { + cursor.fwd(); + + if (cursor.get().getRealDouble() > 0 && cursor.get().getRealDouble() < Threshold) { + Point newpointA = new Point(n); + newpointA.setPosition(cursor); + listA.add(newpointA, cursor.get().copy()); + } else if (cursor.get().getRealDouble() > 0 && cursor.get().getRealDouble() >= Threshold ) { + Point newpointB = new Point(n); + newpointB.setPosition(cursor); + listB.add(newpointB, cursor.get().copy()); + } + } + final RealSum realSumA = new RealSum(); + long countA = 1; + + for (final T type : listA) { + realSumA.add(type.getRealDouble()); + ++countA; + } + + final double sumA = realSumA.getSum() / countA; + + final RealSum realSumB = new RealSum(); + long countB = 1; + + for (final T type : listB) { + realSumB.add(type.getRealDouble()); + ++countB; + } + + final double sumB = realSumB.getSum() / countB; + + ThresholdNew = (float) (sumA + sumB) / 2; + + return ThresholdNew; + + } + public static void InvertInensityMap(RandomAccessibleInterval inputimg, FloatType minval, FloatType maxval){ + // Normalize the input image + Normalize.normalize(Views.iterable(inputimg), minval, maxval); + // Now invert the normalization scale to get intensity inversion + Normalize.normalize(Views.iterable(inputimg), maxval, minval); + } +} diff --git a/src/main/java/preProcessing/Kernels.java b/src/main/java/preProcessing/Kernels.java new file mode 100644 index 0000000..f71676d --- /dev/null +++ b/src/main/java/preProcessing/Kernels.java @@ -0,0 +1,888 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +package preProcessing; + +import java.util.Arrays; +import java.util.Random; + +import mpicbg.imglib.algorithm.math.ComputeMinMax; +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.gauss3.Gauss3; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.algorithm.region.hypersphere.HyperSphere; +import net.imglib2.algorithm.region.hypersphere.HyperSphereCursor; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.NativeType; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.complex.ComplexFloatType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; + +public class Kernels { + + public static enum ProcessingType { + Gradientmag, NaiveEdge, SupressThresh, CannyEdge + } + // Any preprocessing + + public static RandomAccessibleInterval Preprocess(final RandomAccessibleInterval inputimg, + final ProcessingType edge) { + + RandomAccessibleInterval imgout = new ArrayImgFactory().create(inputimg, new FloatType()); + + switch (edge) { + + case NaiveEdge: + imgout = NaiveEdge(inputimg, new double[] { 1, 1 }); + break; + case Gradientmag: + imgout = GradientmagnitudeImage(inputimg); + break; + + case SupressThresh: + imgout = Supressthresh(inputimg); + break; + case CannyEdge: + imgout = CannyEdge(inputimg); + break; + default: + imgout = Supressthresh(inputimg); + break; + + } + + return imgout; + } + + // Do mean filtering on the inputimage + public static void MeanFilter(RandomAccessibleInterval inputimage, + RandomAccessibleInterval outimage, double sigma) { + // Mean filtering for a given sigma + Cursor cursorInput = Views.iterable(inputimage).cursor(); + Cursor cursorOutput = Views.iterable(outimage).cursor(); + FloatType mean = Views.iterable(inputimage).firstElement().createVariable(); + while (cursorInput.hasNext()) { + cursorInput.fwd(); + cursorOutput.fwd(); + HyperSphere hyperSphere = new HyperSphere(Views.extendMirrorSingle(inputimage), + cursorInput, (long) sigma); + HyperSphereCursor cursorsphere = hyperSphere.cursor(); + cursorsphere.fwd(); + mean.set(cursorsphere.get()); + int n = 1; + while (cursorsphere.hasNext()) { + cursorsphere.fwd(); + n++; + mean.add(cursorsphere.get()); + } + mean.div(new FloatType(n)); + cursorOutput.get().set(mean); + } + + } + + // Do mean filtering on the inputimage + public static void MeanFilterBit(RandomAccessibleInterval inputimage, + RandomAccessibleInterval outimage, double sigma) { + // Mean filtering for a given sigma + Cursor cursorInput = Views.iterable(inputimage).cursor(); + Cursor cursorOutput = Views.iterable(outimage).cursor(); + BitType mean = Views.iterable(inputimage).firstElement().createVariable(); + + while (cursorInput.hasNext()) { + cursorInput.fwd(); + cursorOutput.fwd(); + HyperSphere hyperSphere = new HyperSphere(Views.extendMirrorSingle(inputimage), + cursorInput, (long) sigma); + HyperSphereCursor cursorsphere = hyperSphere.cursor(); + cursorsphere.fwd(); + mean.set(cursorsphere.get()); + boolean isbright = false; + while (cursorsphere.hasNext()) { + cursorsphere.fwd(); + if (cursorsphere.get().get()) { + isbright = true; + break; + } + } + + if (isbright) + + cursorOutput.get().set(isbright); + } + + } + + // Naive Edge detector, first get the gradient of the image, then do local + // supression + + public static RandomAccessibleInterval NaiveEdge(RandomAccessibleInterval inputimg, + double[] sigma) { + RandomAccessibleInterval premaximgout = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval maximgout = new ArrayImgFactory().create(inputimg, + new FloatType()); + // Compute gradient of the image + premaximgout = GradientmagnitudeImage(inputimg); + + // Compute global threshold for the premaximgout + final Float Lowthreshold = GlobalThresholding.AutomaticThresholding(premaximgout); + Cursor precursor = Views.iterable(premaximgout).localizingCursor(); + RandomAccess outputran = maximgout.randomAccess(); + while (precursor.hasNext()) { + precursor.fwd(); + outputran.setPosition(precursor); + if (precursor.get().get() <= Lowthreshold) + outputran.get().setZero(); + else + outputran.get().set(precursor.get()); + ; + } + return maximgout; + + } + + public static void addBackground(final IterableInterval iterable, final double value) { + for (final FloatType t : iterable) + t.setReal(t.get() + value); + } + + public static RandomAccessibleInterval CannyEdge(RandomAccessibleInterval inputimg) { + int n = inputimg.numDimensions(); + RandomAccessibleInterval cannyimage = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval gradientimage = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval Threshcannyimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + + // We will create local neighbourhood on this image + gradientimage = GradientmagnitudeImage(inputimg); + + // This is the intended output image so set up a cursor on it + Cursor cursor = Views.iterable(cannyimage).localizingCursor(); + + // Extend the input image for gradient computation + RandomAccessible view = Views.extendMirrorSingle(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + RandomAccessible gradientview = Views.extendMirrorSingle(gradientimage); + + final double[] direction = new double[n]; + final double[] left = new double[n]; + final double[] right = new double[n]; + + // iterate over all pixels + while (cursor.hasNext()) { + // Initialize a point + cursor.fwd(); + // compute gradient and its direction in each dimension and move + // along the direction + double gradient = 0; + for (int d = 0; d < inputimg.numDimensions(); ++d) { + randomAccess.setPosition(cursor); + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + direction[d] = (Fwd - Back) / 2; + + } + // Normalize the gradient direction + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + if (gradient != 0) + direction[d] = direction[d] / gradient; + else + direction[d] = Double.MAX_VALUE; + } + + cursor.get().setReal(Math.sqrt(gradient)); + + // A 5*5*5.. neighbourhood for span = 2, a 3*3*3.. neighbourhood for span = 1. + final int span = 1; + // Create a hypersphere at the current point in the gradient image + final HyperSphere localsphere = new HyperSphere(gradientview, cursor, span); + // To get only the points which are along the gradient direction create left and + // right in d dimensions + Cursor localcursor = localsphere.localizingCursor(); + for (int d = 0; d < n; ++d) { + left[d] = cursor.getDoublePosition(d) - direction[d]; + right[d] = cursor.getDoublePosition(d) + direction[d]; + } + boolean isMaximum = true; + final double tolerance = 20; + final RandomAccess outbound = Threshcannyimg.randomAccess(); + while (localcursor.hasNext()) { + localcursor.fwd(); + + for (int d = 0; d < n; ++d) + // Before computing maxima check if it is along the gradient direction + if (localcursor.getDoublePosition(d) - left[d] == 0 + || localcursor.getDoublePosition(d) - right[d] == 0) { + if (cursor.get().compareTo(localcursor.get()) < 0) { + isMaximum = false; + + break; + } + } + + if (cursor.get().compareTo(localcursor.get()) >= 0) { + for (int d = 0; d < n; ++d) + // If it is a maxima but not near the gradient direction, reject it + if (Math.abs(localcursor.getDoublePosition(d) - left[d]) > tolerance + || Math.abs(localcursor.getDoublePosition(d) - right[d]) > tolerance) { + + isMaximum = false; + + break; + } + } + + if (isMaximum) { + + outbound.setPosition(cursor); + outbound.get().set(cursor.get()); + + } + } + } + + // Supress values below the low threshold + final Float Lowthreshold = GlobalThresholding.AutomaticThresholding(Threshcannyimg); + Cursor cannycursor = Views.iterable(Threshcannyimg).localizingCursor(); + Float threshold = Lowthreshold; + + while (cannycursor.hasNext()) { + cannycursor.fwd(); + if (cannycursor.get().get() <= threshold) + cannycursor.get().setZero(); + else + cannycursor.get().set(cannycursor.get()); + } + + return Threshcannyimg; + } + + public static RandomAccessibleInterval CannyEdgenosupress(RandomAccessibleInterval inputimg) { + int n = inputimg.numDimensions(); + RandomAccessibleInterval cannyimage = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval gradientimage = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval Threshcannyimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + + // We will create local neighbourhood on this image + gradientimage = GradientmagnitudeImage(inputimg); + + // This is the intended output image so set up a cursor on it + Cursor cursor = Views.iterable(cannyimage).localizingCursor(); + + // Extend the input image for gradient computation + RandomAccessible view = Views.extendMirrorSingle(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + RandomAccessible gradientview = Views.extendMirrorSingle(gradientimage); + + final double[] direction = new double[n]; + final double[] left = new double[n]; + final double[] right = new double[n]; + + // iterate over all pixels + while (cursor.hasNext()) { + // Initialize a point + cursor.fwd(); + // compute gradient and its direction in each dimension and move + // along the direction + double gradient = 0; + for (int d = 0; d < inputimg.numDimensions(); ++d) { + randomAccess.setPosition(cursor); + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + direction[d] = (Fwd - Back) / 2; + + } + // Normalize the gradient direction + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + if (gradient != 0) + direction[d] = direction[d] / gradient; + else + direction[d] = Double.MAX_VALUE; + } + + cursor.get().setReal(Math.sqrt(gradient)); + + // A 5*5*5.. neighbourhood for span = 2, a 3*3*3.. neighbourhood for span = 1. + final int span = 1; + // Create a hypersphere at the current point in the gradient image + final HyperSphere localsphere = new HyperSphere(gradientview, cursor, span); + // To get only the points which are along the gradient direction create left and + // right in d dimensions + Cursor localcursor = localsphere.localizingCursor(); + for (int d = 0; d < n; ++d) { + left[d] = cursor.getDoublePosition(d) - direction[d]; + right[d] = cursor.getDoublePosition(d) + direction[d]; + } + boolean isMaximum = true; + final RandomAccess outbound = Threshcannyimg.randomAccess(); + while (localcursor.hasNext()) { + localcursor.fwd(); + + if (cursor.get().compareTo(localcursor.get()) < 0) { + isMaximum = false; + + break; + + } + + if (isMaximum) { + + outbound.setPosition(cursor); + outbound.get().set(cursor.get()); + + } + } + } + + return Threshcannyimg; + } + + public static RandomAccessibleInterval CannyEdgeandMean(RandomAccessibleInterval inputimg, + final double sigma) { + int n = inputimg.numDimensions(); + RandomAccessibleInterval cannyimage = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval gradientimage = new ArrayImgFactory().create(inputimg, + new FloatType()); + RandomAccessibleInterval Threshcannyimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + + // We will create local neighbourhood on this image + gradientimage = GradientmagnitudeImage(inputimg); + + // This is the intended output image so set up a cursor on it + Cursor cursor = Views.iterable(cannyimage).localizingCursor(); + + // Extend the input image for gradient computation + RandomAccessible view = Views.extendMirrorSingle(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + RandomAccessible gradientview = Views.extendMirrorSingle(gradientimage); + + final double[] direction = new double[n]; + final double[] left = new double[n]; + final double[] right = new double[n]; + + // iterate over all pixels + while (cursor.hasNext()) { + // Initialize a point + cursor.fwd(); + // compute gradient and its direction in each dimension and move + // along the direction + double gradient = 0; + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + randomAccess.setPosition(cursor); + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + direction[d] = (Fwd - Back) / 2; + + } + // Normalize the gradient direction + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + if (gradient != 0) + direction[d] = direction[d] / gradient; + else + direction[d] = Double.MAX_VALUE; + } + + cursor.get().setReal(Math.sqrt(gradient)); + + // A 5*5*5.. neighbourhood for span = 2, a 3*3*3.. neighbourhood for span = 1. + final int span = 1; + // Create a hypersphere at the current point in the gradient image + final HyperSphere localsphere = new HyperSphere(gradientview, cursor, span); + // To get only the points which are along the gradient direction create left and + // right in d dimensions + Cursor localcursor = localsphere.localizingCursor(); + for (int d = 0; d < n; ++d) { + left[d] = cursor.getDoublePosition(d) - direction[d]; + right[d] = cursor.getDoublePosition(d) + direction[d]; + } + boolean isMaximum = true; + final RandomAccess outbound = Threshcannyimg.randomAccess(); + while (localcursor.hasNext()) { + localcursor.fwd(); + + if (cursor.get().compareTo(localcursor.get()) < 0) { + isMaximum = false; + + break; + } + + if (isMaximum) { + + outbound.setPosition(cursor); + outbound.get().set(cursor.get()); + + } + } + } + + // Supress values below the low threshold + final Float Lowthreshold = GlobalThresholding.AutomaticThresholding(Threshcannyimg); + Cursor cannycursor = Views.iterable(Threshcannyimg).localizingCursor(); + Float threshold = Lowthreshold; + + while (cannycursor.hasNext()) { + cannycursor.fwd(); + if (cannycursor.get().get() <= threshold) + cannycursor.get().setZero(); + else + cannycursor.get().set(cannycursor.get()); + } + + RandomAccessibleInterval meanimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + MeanFilter(Threshcannyimg, meanimg, sigma); + + return meanimg; + } + + public static RandomAccessibleInterval CannyEdgeandMeanBit(RandomAccessibleInterval inputimg, + final double sigma) { + int n = inputimg.numDimensions(); + RandomAccessibleInterval cannyimage = new ArrayImgFactory().create(inputimg, new BitType()); + RandomAccessibleInterval gradientimage = new ArrayImgFactory().create(inputimg, + new BitType()); + RandomAccessibleInterval Threshcannyimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + + // We will create local neighbourhood on this image + gradientimage = GradientmagnitudeImageBit(inputimg); + + // This is the intended output image so set up a cursor on it + Cursor cursor = Views.iterable(cannyimage).localizingCursor(); + + // Extend the input image for gradient computation + RandomAccessible view = Views.extendMirrorSingle(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + RandomAccessible gradientview = Views.extendMirrorSingle(gradientimage); + + final double[] direction = new double[n]; + final double[] left = new double[n]; + final double[] right = new double[n]; + + // iterate over all pixels + while (cursor.hasNext()) { + // Initialize a point + cursor.fwd(); + // compute gradient and its direction in each dimension and move + // along the direction + double gradient = 0; + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + randomAccess.setPosition(cursor); + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + direction[d] = (Fwd - Back) / 2; + + } + // Normalize the gradient direction + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + if (gradient != 0) + direction[d] = direction[d] / gradient; + else + direction[d] = Double.MAX_VALUE; + } + + cursor.get().setReal(Math.sqrt(gradient)); + + // A 5*5*5.. neighbourhood for span = 2, a 3*3*3.. neighbourhood for span = 1. + final int span = 1; + // Create a hypersphere at the current point in the gradient image + final HyperSphere localsphere = new HyperSphere(gradientview, cursor, span); + // To get only the points which are along the gradient direction create left and + // right in d dimensions + Cursor localcursor = localsphere.localizingCursor(); + for (int d = 0; d < n; ++d) { + left[d] = cursor.getDoublePosition(d) - direction[d]; + right[d] = cursor.getDoublePosition(d) + direction[d]; + } + boolean isMaximum = true; + final RandomAccess outbound = Threshcannyimg.randomAccess(); + while (localcursor.hasNext()) { + localcursor.fwd(); + + if (cursor.get().compareTo(localcursor.get()) < 0) { + isMaximum = false; + + break; + } + + if (isMaximum) { + + outbound.setPosition(cursor); + FloatType setfloat = new FloatType(cursor.get().getRealFloat()); + outbound.get().set(setfloat); + + } + } + } + + RandomAccessibleInterval meanimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + meanimg = MedianFilter(Threshcannyimg, sigma); + + Pair minmax = computeMinMax(meanimg); + + RandomAccessibleInterval copyoriginal = CreateBinary(meanimg, minmax.getA(), minmax.getB()); + + return copyoriginal; + } + + public static RandomAccessibleInterval CreateBinary(RandomAccessibleInterval source, + double lowprob, double highprob) { + + RandomAccessibleInterval copyoriginal = new ArrayImgFactory().create(source, new BitType()); + + final RandomAccess ranac = copyoriginal.randomAccess(); + final Cursor cursor = Views.iterable(source).localizingCursor(); + + while (cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + if (cursor.get().getRealDouble() > (lowprob)) { + + ranac.get().setOne(); + } else { + ranac.get().setZero(); + } + + } + + return copyoriginal; + + } + + public static & NativeType> RandomAccessibleInterval MedianFilter( + RandomAccessibleInterval inputimg, double sigma) { + // Mean filtering for a given sigma + + RandomAccessibleInterval outimg = inputimg; + Cursor cursorOutput = Views.iterable(outimg).cursor(); + + final RectangleShape shape = new RectangleShape((int) sigma, false); + final net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible nracessible = shape + .neighborhoodsRandomAccessible(Views.extendZero(inputimg)); + final RandomAccess> nra = nracessible.randomAccess(inputimg); + + final int size = (int) nra.get().size(); + final double[] values = new double[size]; + + double mean = 0; + // Fill buffer with median values. + while (cursorOutput.hasNext()) { + cursorOutput.fwd(); + nra.setPosition(cursorOutput); + int index = 0; + for (final T pixel : nra.get()) { + values[index++] = pixel.getRealDouble(); + mean += pixel.getRealDouble(); + } + // mean = mean / index; + + // Median values + Arrays.sort(values, 0, index); + cursorOutput.get().setReal(values[(index - 1) / 2]); + + // Mean values + // cursorOutput.get().setReal( mean ); + + } + return outimg; + + } + + public static RandomAccessibleInterval Supressthresh(RandomAccessibleInterval inputimg) { + RandomAccessibleInterval Threshimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + // Supress values below the low threshold + int n = inputimg.numDimensions(); + double[] position = new double[n]; + final double Lowthreshold = GlobalThresholding.AutomaticThresholdingSec(inputimg); + double threshold = Lowthreshold; + Cursor inputcursor = Views.iterable(inputimg).localizingCursor(); + RandomAccess outputran = Threshimg.randomAccess(); + while (inputcursor.hasNext()) { + inputcursor.fwd(); + inputcursor.localize(position); + outputran.setPosition(inputcursor); + if (inputcursor.get().get() <= threshold) + outputran.get().setZero(); + else + outputran.get().set(inputcursor.get()); + } + return Threshimg; + + } + + public static RandomAccessibleInterval SupressLowthresh(RandomAccessibleInterval inputimg) { + RandomAccessibleInterval Threshimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + // Supress values below the low threshold + int n = inputimg.numDimensions(); + double[] position = new double[n]; + final double Lowthreshold = GlobalThresholding.AutomaticThresholdingSec(inputimg); + double threshold = Lowthreshold; + Cursor inputcursor = Views.iterable(inputimg).localizingCursor(); + RandomAccess outputran = Threshimg.randomAccess(); + while (inputcursor.hasNext()) { + inputcursor.fwd(); + inputcursor.localize(position); + outputran.setPosition(inputcursor); + if (inputcursor.get().get() <= 0.2 * threshold) + outputran.get().setZero(); + else + outputran.get().set(inputcursor.get()); + } + return Threshimg; + + } + + public static RandomAccessibleInterval SupressHeavythresh(RandomAccessibleInterval inputimg) { + RandomAccessibleInterval Threshimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + // Supress values below the low threshold + int n = inputimg.numDimensions(); + double[] position = new double[n]; + final double Lowthreshold = GlobalThresholding.AutomaticThresholdingSec(inputimg); + double threshold = Lowthreshold; + Float Highthreshold = new Float(2.0 * threshold); + Cursor inputcursor = Views.iterable(inputimg).localizingCursor(); + RandomAccess outputran = Threshimg.randomAccess(); + while (inputcursor.hasNext()) { + inputcursor.fwd(); + inputcursor.localize(position); + outputran.setPosition(inputcursor); + if (inputcursor.get().get() <= Highthreshold) + outputran.get().setZero(); + else + outputran.get().set(inputcursor.get()); + } + return Threshimg; + + } + + public static RandomAccessibleInterval GradientmagnitudeImage( + RandomAccessibleInterval inputimg) { + + RandomAccessibleInterval gradientimg = new ArrayImgFactory().create(inputimg, + new FloatType()); + Cursor cursor = Views.iterable(gradientimg).localizingCursor(); + RandomAccessible view = Views.extendBorder(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + // iterate over all pixels + while (cursor.hasNext()) { + // move the cursor to the next pixel + cursor.fwd(); + + // compute gradient and its direction in each dimension + double gradient = 0; + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + // set the randomaccess to the location of the cursor + randomAccess.setPosition(cursor); + + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + } + + cursor.get().setReal(Math.sqrt(gradient)); + + } + + return gradientimg; + } + + public static RandomAccessibleInterval GradientmagnitudeImageBit( + RandomAccessibleInterval inputimg) { + + RandomAccessibleInterval gradientimg = new ArrayImgFactory().create(inputimg, new BitType()); + Cursor cursor = Views.iterable(gradientimg).localizingCursor(); + RandomAccessible view = Views.extendBorder(inputimg); + RandomAccess randomAccess = view.randomAccess(); + + // iterate over all pixels + while (cursor.hasNext()) { + // move the cursor to the next pixel + cursor.fwd(); + + // compute gradient and its direction in each dimension + double gradient = 0; + + for (int d = 0; d < inputimg.numDimensions(); ++d) { + // set the randomaccess to the location of the cursor + randomAccess.setPosition(cursor); + + // move one pixel back in dimension d + randomAccess.bck(d); + + // get the value + double Back = randomAccess.get().getRealDouble(); + + // move twice forward in dimension d, i.e. + // one pixel above the location of the cursor + randomAccess.fwd(d); + randomAccess.fwd(d); + + // get the value + double Fwd = randomAccess.get().getRealDouble(); + + gradient += ((Fwd - Back) * (Fwd - Back)) / 4; + + } + + cursor.get().setReal(Math.sqrt(gradient)); + + } + + return gradientimg; + } + + public static Pair computeMinMax(RandomAccessibleInterval input) { + + final Cursor cursor = Views.iterable(input).cursor(); + + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType min = type.copy(); + FloatType max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(min) < 0) { + min.set(type); + } + + if (type.compareTo(max) > 0) { + max.set(type); + } + } + + return new ValuePair(min.getRealDouble(), max.getRealDouble()); + + } + +} diff --git a/src/main/java/preProcessing/MedianFilter2D.java b/src/main/java/preProcessing/MedianFilter2D.java new file mode 100644 index 0000000..3033479 --- /dev/null +++ b/src/main/java/preProcessing/MedianFilter2D.java @@ -0,0 +1,159 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import java.util.Arrays; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.neighborhood.DiamondShape.NeighborhoodsAccessible; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +/** + * A simple median filter that operates on 1D, 2D or 3D images. + *

      + * For 3D images, the filtering is done only in 2D XY slices. Indeed, this + * filter is made mainly to remove shot noise on cameras, for which the median + * calculated on a simple 2D neighborhood is enough. + * + * @author Jean-Yves Tinevez - 2015 + * + * @param + * the type of the source image. + */ +public class MedianFilter2D< T extends RealType< T > & NativeType< T >> extends BenchmarkAlgorithm implements OutputAlgorithm< RandomAccessibleInterval< T >> +{ + private static final String BASE_ERROR_MSG = "[MedianFiler2D] "; + + private final RandomAccessibleInterval< T > source; + + private RandomAccessibleInterval< T > output; + + private final int radius; + + /** + * Instantiate a new median filter that will operate on the specified + * source. + * + * @param currentPreprocessedimg + * the source to operate on. + * @param radius + * determines the size of the neighborhood. In 2D or 3D, a radius + * of 1 will generate a 3x3 neighborhood. + */ + public MedianFilter2D( final RandomAccessibleInterval source, final int radius ) + { + this.source = source; + this.radius = radius; + } + + @Override + public boolean checkInput() + { + if ( source.numDimensions() > 3 ) + { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D or 3D images. Got " + source.numDimensions() + "D."; + return false; + } + if ( radius < 1 ) + { + errorMessage = BASE_ERROR_MSG + "Radius cannot be smaller than 1. Got " + radius + "."; + return false; + } + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final T type = source.randomAccess().get().createVariable(); + final ImgFactory< T > factory = Util.getArrayOrCellImgFactory( source, type ); + this.output = factory.create( source, type ); + + if ( source.numDimensions() > 2 ) + { + final long nz = source.dimension( 2 ); + for ( long z = 0; z < nz; z++ ) + { + final IntervalView< T > slice = Views.hyperSlice( source, 2, z ); + final IntervalView< T > outputSlice = Views.hyperSlice( output, 2, z ); + + processSlice( slice, outputSlice ); + } + } + else + { + processSlice( source, Views.iterable(output) ); + } + + this.processingTime = System.currentTimeMillis() - start; + return true; + } + + private void processSlice( final RandomAccessibleInterval< T > in, final IterableInterval< T > out ) + { + final Cursor< T > cursor = out.localizingCursor(); + + final RectangleShape shape = new RectangleShape( radius, false ); + final net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible nracessible = shape.neighborhoodsRandomAccessible( Views.extendZero( in ) ); + final RandomAccess< Neighborhood< T >> nra = nracessible.randomAccess( in ); + + final int size = ( int ) nra.get().size(); + final double[] values = new double[ size ]; + + // Fill buffer with median values. + while ( cursor.hasNext() ) + { + cursor.fwd(); + nra.setPosition( cursor ); + int index = 0; + for ( final T pixel : nra.get() ) + { + values[ index++ ] = pixel.getRealDouble(); + } + + Arrays.sort( values, 0, index ); + cursor.get().setReal( values[ ( index - 1 ) / 2 ] ); + } + } + + @Override + public RandomAccessibleInterval getResult() + { + return output; + } +} diff --git a/src/main/java/preProcessing/MedianFilterImg2D.java b/src/main/java/preProcessing/MedianFilterImg2D.java new file mode 100644 index 0000000..baf49f9 --- /dev/null +++ b/src/main/java/preProcessing/MedianFilterImg2D.java @@ -0,0 +1,159 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import java.util.Arrays; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.neighborhood.DiamondShape.NeighborhoodsAccessible; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +/** + * A simple median filter that operates on 1D, 2D or 3D images. + *

      + * For 3D images, the filtering is done only in 2D XY slices. Indeed, this + * filter is made mainly to remove shot noise on cameras, for which the median + * calculated on a simple 2D neighborhood is enough. + * + * @author Jean-Yves Tinevez - 2015 + * + * @param + * the type of the source image. + */ +public class MedianFilterImg2D< T extends RealType< T > & NativeType< T >> extends BenchmarkAlgorithm implements OutputAlgorithm< Img< T >> +{ + private static final String BASE_ERROR_MSG = "[MedianFiler2D] "; + + private final Img< T > source; + + private Img< T > output; + + private final int radius; + + /** + * Instantiate a new median filter that will operate on the specified + * source. + * + * @param currentPreprocessedimg + * the source to operate on. + * @param radius + * determines the size of the neighborhood. In 2D or 3D, a radius + * of 1 will generate a 3x3 neighborhood. + */ + public MedianFilterImg2D( final Img source, final int radius ) + { + this.source = source; + this.radius = radius; + } + + @Override + public boolean checkInput() + { + if ( source.numDimensions() > 3 ) + { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D or 3D images. Got " + source.numDimensions() + "D."; + return false; + } + if ( radius < 1 ) + { + errorMessage = BASE_ERROR_MSG + "Radius cannot be smaller than 1. Got " + radius + "."; + return false; + } + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final T type = source.randomAccess().get().createVariable(); + final ImgFactory< T > factory = Util.getArrayOrCellImgFactory( source, type ); + this.output = factory.create( source, type ); + + if ( source.numDimensions() > 2 ) + { + final long nz = source.dimension( 2 ); + for ( long z = 0; z < nz; z++ ) + { + final IntervalView< T > slice = Views.hyperSlice( source, 2, z ); + final IntervalView< T > outputSlice = Views.hyperSlice( output, 2, z ); + + processSlice( slice, outputSlice ); + } + } + else + { + processSlice( source, Views.iterable(output) ); + } + + this.processingTime = System.currentTimeMillis() - start; + return true; + } + + private void processSlice( final RandomAccessibleInterval< T > in, final IterableInterval< T > out ) + { + final Cursor< T > cursor = out.localizingCursor(); + + final RectangleShape shape = new RectangleShape( radius, false ); + final net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible nracessible = shape.neighborhoodsRandomAccessible( Views.extendZero( in ) ); + final RandomAccess< Neighborhood< T >> nra = nracessible.randomAccess( in ); + + final int size = ( int ) nra.get().size(); + final double[] values = new double[ size ]; + + // Fill buffer with median values. + while ( cursor.hasNext() ) + { + cursor.fwd(); + nra.setPosition( cursor ); + int index = 0; + for ( final T pixel : nra.get() ) + { + values[ index++ ] = pixel.getRealDouble(); + } + + Arrays.sort( values, 0, index ); + cursor.get().setReal( values[ ( index - 1 ) / 2 ] ); + } + } + + @Override + public Img getResult() + { + return output; + } +} diff --git a/src/main/java/preProcessing/MedianFilterOnly.java b/src/main/java/preProcessing/MedianFilterOnly.java new file mode 100644 index 0000000..7ee0d63 --- /dev/null +++ b/src/main/java/preProcessing/MedianFilterOnly.java @@ -0,0 +1,213 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import java.util.Arrays; + +import javax.swing.JProgressBar; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.BenchmarkAlgorithm; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.algorithm.gauss3.Gauss3; +import net.imglib2.algorithm.neighborhood.DiamondShape.NeighborhoodsAccessible; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +/** + * A simple flat field and median filter that operates on 1D, 2D or 3D images. + *

      + * For 3D images, the filtering is done only in 2D XY slices. Removes shot noise, + * corrects for uneven illumination + * + * @author V. Kapoor - 2017, Jean-Yves Tinevez - 2015 + * + * @param + * the type of the source image. + */ +public class MedianFilterOnly extends BenchmarkAlgorithm implements OutputAlgorithm< RandomAccessibleInterval< FloatType >> +{ + private static final String BASE_ERROR_MSG = "[FlatField2D] "; + + private final RandomAccessibleInterval< FloatType > source; + + private RandomAccessibleInterval< FloatType > output; + + private final int radius; + + + private JProgressBar jpb; + + /** + * Instantiate a new Flat field correction filter that will operate on the specified + * source. + * + * @param currentPreprocessedimg + * the source to operate on. + * @param radius + * determines the size of the neighborhood. In 2D or 3D, a radius + * of 1 will generate a 3x3 neighborhood. + */ + public MedianFilterOnly( final RandomAccessibleInterval source, final int radius ) + { + this.source = source; + this.radius = radius; + } + + + public MedianFilterOnly( final RandomAccessibleInterval source, final int radius, final JProgressBar jpb ) + { + this.source = source; + this.radius = radius; + this.jpb = jpb; + } + + @Override + public boolean checkInput() + { + if ( source.numDimensions() > 3 ) + { + errorMessage = BASE_ERROR_MSG + " Can only operate on 1D, 2D or 3D images. Got " + source.numDimensions() + "D."; + return false; + } + if ( radius < 1 ) + { + errorMessage = BASE_ERROR_MSG + "Radius cannot be smaller than 1. Got " + radius + "."; + return false; + } + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + + final FloatType type = source.randomAccess().get().createVariable(); + final ImgFactory< FloatType > factory = Util.getArrayOrCellImgFactory( source, type ); + this.output = factory.create( source, type ); + + + + if ( source.numDimensions() > 2 ) + { + final long nz = source.dimension( 2 ); + + double percent = 0; + for ( long z = 0; z < nz; z++ ) + { + + percent++; + final IntervalView< FloatType > slice = Views.hyperSlice( source, 2, z ); + final IntervalView< FloatType > outputSlice = Views.hyperSlice( output, 2, z ); + + if(jpb!=null) + scrollbar.Utility.SetProgressBar(jpb, 100 * percent/nz, "Doing Median Filtering, please wait.."); + + processSlice( slice, outputSlice ); + + + + } + } + else + { + processSlice( source, Views.iterable(output) ); + } + + this.processingTime = System.currentTimeMillis() - start; + return true; + } + + + + + /** + * + * @param in + * + * The randomaccessible interval on which we apply a gaussian filter (big radius = image dim / n pixels) and then + * subtract it from the original image to create a background corrected image + * + * @param out + * + * Median filter is applied on the background corrected image generated from the previous step and is the pre-processed image to be + * used by the Line finders of the MTV tracker + * + */ + private void processSlice( final RandomAccessibleInterval< FloatType > in, final IterableInterval< FloatType > out ) + { + + double[] sigma = new double[in.numDimensions()]; + for (int d = 0; d < in.numDimensions(); ++d) { + sigma[d] = (int) Math.round((in.realMax(d) - in.realMin(d)) / 20.0); + } + + + + final Cursor< FloatType > cursor = out.localizingCursor(); + + final RectangleShape shape = new RectangleShape( radius, false ); + final net.imglib2.algorithm.neighborhood.RectangleShape.NeighborhoodsAccessible nracessible = shape.neighborhoodsRandomAccessible( Views.extendZero( in ) ); + final RandomAccess< Neighborhood< FloatType >> nra = nracessible.randomAccess( in ); + + final int size = ( int ) nra.get().size(); + final double[] values = new double[ size ]; + + // Fill buffer with median values. + while ( cursor.hasNext() ) + { + cursor.fwd(); + nra.setPosition( cursor ); + + + int index = 0; + for ( final FloatType pixel : nra.get() ) + { + values[ index++ ] = pixel.getRealDouble(); + } + + Arrays.sort( values, 0, index ); + cursor.get().setReal( values[ ( index - 1 ) / 2 ] ); + } + + } + + @Override + public RandomAccessibleInterval getResult() + { + return output; + } +} diff --git a/src/main/java/preProcessing/MinMaxIntensity.java b/src/main/java/preProcessing/MinMaxIntensity.java new file mode 100644 index 0000000..7e66d98 --- /dev/null +++ b/src/main/java/preProcessing/MinMaxIntensity.java @@ -0,0 +1,57 @@ +package preProcessing; + +import java.util.Iterator; + +import net.imglib2.IterableInterval; +import net.imglib2.img.Img; +import net.imglib2.type.Type; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; + +public class MinMaxIntensity { + + + /** + * Compute the min and max for any {@link Iterable}, like an {@link Img}. + * + * The only functionality we need for that is to iterate. Therefore we need no {@link Cursor} + * that can localize itself, neither do we need a {@link RandomAccess}. So we simply use the + * most simple interface in the hierarchy. + * + * @param input - the input that has to just be {@link Iterable} + * @param min - the type that will have min + * @param max - the type that will have max + */ + public static < T extends Comparable< T > & Type< T > > Pair computeMinMax( + final IterableInterval< T > input) + { + // create a cursor for the image (the order does not matter) + final Iterator< T > iterator = input.iterator(); + // create two empty variables + T min = input.firstElement().createVariable(); + T max = input.firstElement().createVariable(); + // initialize min and max with the first image value + T type = iterator.next(); + + min.set( type ); + max.set( type ); + + // loop over the rest of the data and determine min and max value + while ( iterator.hasNext() ) + { + // we need this type more than once + type = iterator.next(); + + if ( type.compareTo( min ) < 0 ) + min.set( type ); + + if ( type.compareTo( max ) > 0 ) + max.set( type ); + } + + return new ValuePair(min, max); + } + + + +} diff --git a/src/main/java/preProcessing/Normalization.java b/src/main/java/preProcessing/Normalization.java new file mode 100644 index 0000000..0c8f8cc --- /dev/null +++ b/src/main/java/preProcessing/Normalization.java @@ -0,0 +1,93 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package preProcessing; + +import ij.ImageJ; +import ij.ImagePlus; +import ij.io.FileSaver; +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +public class Normalization { + + + + public static void main(String[] args){ + + + new ImageJ(); + + String filepath = "/Users/aimachine/Documents/Tea_Data/Test.tif"; + ImagePlus impA = new ImagePlus( filepath ); + RandomAccessibleInterval img = ImageJFunctions.convertFloat(impA); + new Normalize(); + FloatType minval = new FloatType(0); + FloatType maxval = new FloatType(1); + + int boundarylabel = 1; + int outterlabel = 2; + int innerlabel = 3; + int backgroundlabel = 4; + + // Change the label here for creating a lebelled image + int label = backgroundlabel; + + Normalize.normalize(Views.iterable(img), minval, maxval); + + // Make Labelled image + + + RandomAccessibleInterval Intimg = new ArrayImgFactory().create(img, + new IntType()); + + RandomAccess intran = Intimg.randomAccess(); + Cursor cursor = Views.iterable(img).localizingCursor(); + + while(cursor.hasNext()){ + + cursor.next(); + + intran.setPosition(cursor); + + if (cursor.get().get() > 0) { + + intran.get().set(outterlabel); + } + + + + } + + + ImageJFunctions.show(Intimg); + + + } + +} diff --git a/src/main/java/preProcessing/Otsu.java b/src/main/java/preProcessing/Otsu.java new file mode 100644 index 0000000..559bc9d --- /dev/null +++ b/src/main/java/preProcessing/Otsu.java @@ -0,0 +1,305 @@ +package preProcessing; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.Point; +import net.imglib2.PointSampleList; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.RealSum; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; + +public class Otsu { + + + + + + public static RandomAccessibleInterval convertBittoFloat(final Img bitinputimg){ + + + final FloatType type = new FloatType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(bitinputimg, type); + final RandomAccessibleInterval output = factory.create(bitinputimg, type); + + Cursor cursor = bitinputimg.localizingCursor(); + + RandomAccess ranac = output.randomAccess(); + + while(cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + ranac.get().setReal(cursor.get().getRealDouble()); + + + } + + return output; + + } + + + + public static RandomAccessibleInterval Getlabelledimage(final RandomAccessibleInterval inputimg, final int label ){ + + final FloatType type = new FloatType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimg, type); + final Img output = factory.create(inputimg, type); + + Cursor cursor = Views.iterable(inputimg).localizingCursor(); + + RandomAccess ranac = output.randomAccess(); + + while(cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + if (cursor.get().get() == label) + ranac.get().setOne(); + else + ranac.get().setZero(); + + + + + } + + + + + return output; + + + } + + public static Img Getbinaryimage(final RandomAccessibleInterval inputimg, final float threshold){ + + + final BitType type = new BitType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimg, type); + final Img output = factory.create(inputimg, type); + + Cursor cursor = Views.iterable(inputimg).localizingCursor(); + + RandomAccess ranac = output.randomAccess(); + + while(cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + if (cursor.get().get() > threshold) + ranac.get().setOne(); + else + ranac.get().setZero(); + + + + + } + + + + + return output; + } + + + + // Automatic thresholding done on the Normalized input image + // Algorithm: Get max and min intensity for an image and choose an initial + // threshold value, T = (max-min)/2. This threshold value + // segments image into two regions, get the mean pixel value for both the + // regions (x1, x2) + // then set the new threshold T_N = (x1 +x2)/2, segment initial image by + // this value and repeat the process + // till (T_N - T_{N+1} inputimg) { + + FloatType min = new FloatType(); + FloatType max = new FloatType(); + + Float ThresholdNew, Thresholdupdate; + + Pair pair = new ValuePair(min, max); + pair = computeMinMaxIntensity(inputimg); + + ThresholdNew = (pair.getB().get() - pair.getA().get()) / 2; + + // Get the new threshold value after segmenting the inputimage with thresholdnew + Thresholdupdate = SegmentbyThresholding(Views.iterable(inputimg), ThresholdNew); + + while (true) { + + ThresholdNew = SegmentbyThresholding(Views.iterable(inputimg), Thresholdupdate); + // Check if the new threshold value is close to the previous value + if (Math.abs(Thresholdupdate - ThresholdNew) < 1.0E-2) + break; + Thresholdupdate = ThresholdNew; + } + + + return ThresholdNew; + + } + + + public static Float AutomaticThresholdingSec(RandomAccessibleInterval inputimg) { + + FloatType min = new FloatType(); + FloatType max = new FloatType(); + + Float ThresholdNew, Thresholdupdate; + + Pair pair = new ValuePair(min, max); + pair = computesecondMinMaxIntensity(inputimg); + + ThresholdNew = (pair.getB().get() - pair.getA().get()) / 2; + + // Get the new threshold value after segmenting the inputimage with thresholdnew + Thresholdupdate = SegmentbyThresholding(Views.iterable(inputimg), ThresholdNew); + + while (true) { + + ThresholdNew = SegmentbyThresholding(Views.iterable(inputimg), Thresholdupdate); + + // Check if the new threshold value is close to the previous value + if (Math.abs(Thresholdupdate - ThresholdNew) < 1.0E-2) + break; + Thresholdupdate = ThresholdNew; + } + + + return ThresholdNew; + + } + + // Segment image by thresholding, used to determine automatic thresholding + // level + public static Float SegmentbyThresholding(IterableInterval inputimg, Float Threshold) { + + int n = inputimg.numDimensions(); + Float ThresholdNew; + PointSampleList listA = new PointSampleList(n); + PointSampleList listB = new PointSampleList(n); + Cursor cursor = inputimg.localizingCursor(); + while (cursor.hasNext()) { + cursor.fwd(); + + if (cursor.get().get() >= 0 && cursor.get().get() < Threshold) { + Point newpointA = new Point(n); + newpointA.setPosition(cursor); + listA.add(newpointA, cursor.get().copy()); + } else if (cursor.get().get() >= 0 && cursor.get().get() >= Threshold ) { + Point newpointB = new Point(n); + newpointB.setPosition(cursor); + listB.add(newpointB, cursor.get().copy()); + } + } + final RealSum realSumA = new RealSum(); + long countA = 0; + + for (final FloatType type : listA) { + realSumA.add(type.getRealDouble()); + ++countA; + } + + final double sumA = realSumA.getSum() / countA; + + final RealSum realSumB = new RealSum(); + long countB = 0; + + for (final FloatType type : listB) { + realSumB.add(type.getRealDouble()); + ++countB; + } + + final double sumB = realSumB.getSum() / countB; + + ThresholdNew = (float) (sumA + sumB) / 2; + + return ThresholdNew; + + } + public static void InvertInensityMap(RandomAccessibleInterval inputimg, FloatType minval, FloatType maxval){ + // Normalize the input image + Normalize.normalize(Views.iterable(inputimg), minval, maxval); + // Now invert the normalization scale to get intensity inversion + Normalize.normalize(Views.iterable(inputimg), maxval, minval); + } + public static Pair computeMinMaxIntensity(final RandomAccessibleInterval inputimg) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType min = type.copy(); + FloatType max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(min) < 0) { + min.set(type); + + } + + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + Pair pair = new ValuePair(min, max); + return pair; + } + public static Pair computesecondMinMaxIntensity(final RandomAccessibleInterval inputimg) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType min = type.copy(); + FloatType secondmin = type.copy(); + FloatType max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(min) < 0) { + min.set(type); + + } + if (type.compareTo(secondmin) < 0 && secondmin.compareTo(min)>0) { + secondmin.set(type); + + } + + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + Pair pair = new ValuePair(secondmin, max); + return pair; + } + + +} diff --git a/src/main/java/preProcessing/PrePipeline.java b/src/main/java/preProcessing/PrePipeline.java new file mode 100644 index 0000000..31ff362 --- /dev/null +++ b/src/main/java/preProcessing/PrePipeline.java @@ -0,0 +1,63 @@ +package preProcessing; + + +import mpicbg.imglib.image.display.imagej.ImageJFunctions; +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.morphology.table2d.Branchpoints; +import net.imglib2.algorithm.morphology.table2d.Clean; +import net.imglib2.algorithm.morphology.table2d.Spur; +import net.imglib2.algorithm.morphology.table2d.Thin; +import net.imglib2.algorithm.morphology.table2d.Thin1; +import net.imglib2.algorithm.morphology.table2d.Thin2; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.BooleanType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.ExtendedRandomAccessibleInterval; +import net.imglib2.view.Views; + +public class PrePipeline { + + public static RandomAccessibleInterval Dothinning(final RandomAccessibleInterval inputimage, final int FlatFieldRadi){ + + + //PreProcessing Step + RandomAccessibleInterval inputimagePRE = Utils.FlatFieldOnly(inputimage, FlatFieldRadi); + + float threshold = Otsu.AutomaticThresholding(inputimagePRE); + Img Bitinuputimage = Otsu.Getbinaryimage(inputimagePRE,threshold); + + + RandomAccessibleInterval output = Otsu.convertBittoFloat(Bitinuputimage); + + + + return output; + } + + public static RandomAccessibleInterval SelectClassLabel(final RandomAccessibleInterval inputimage, final int label){ + + // From Ilastik the label for image + RandomAccessibleInterval preoutput = Otsu.Getlabelledimage(inputimage, label); + float threshold = Otsu.AutomaticThresholding(preoutput); + Img Bitinuputimage = Otsu.Getbinaryimage(preoutput,threshold); + + RandomAccessibleInterval output = Otsu.convertBittoFloat(Thin.thin(Thin.thin(Bitinuputimage))); + + + return output; + } + + + + +} diff --git a/src/main/java/preProcessing/Utils.java b/src/main/java/preProcessing/Utils.java new file mode 100644 index 0000000..4ba5bfa --- /dev/null +++ b/src/main/java/preProcessing/Utils.java @@ -0,0 +1,479 @@ +package preProcessing; + + + +import java.awt.Rectangle; +import java.util.ArrayList; + +import javax.swing.JProgressBar; + +import fiji.util.DistanceComparator; +import ij.gui.EllipseRoi; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.IterableInterval; +import net.imglib2.Point; +import net.imglib2.PointSampleList; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.algorithm.stats.Normalize; +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.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; + +public class Utils { + + + + + public static & NativeType>RandomAccessibleInterval FlatFieldMedian(RandomAccessibleInterval originalimg, + final double[] flatfieldradius) { + + + final FlatFieldCorrection flatfilter = new FlatFieldCorrection(originalimg, 1, flatfieldradius); + flatfilter.process(); + RandomAccessibleInterval ProgramPreprocessedimg = flatfilter.getResult(); + return ProgramPreprocessedimg; + + } + public static & NativeType> RandomAccessibleInterval FlatFieldOnly(RandomAccessibleInterval originalimg, final int flatfieldradius) { + + + final FlatFieldOnly flatfilter = new FlatFieldOnly(originalimg, flatfieldradius); + flatfilter.process(); + RandomAccessibleInterval ProgramPreprocessedimg = flatfilter.getResult(); + return ProgramPreprocessedimg; + + } + + public static RandomAccessibleInterval MedianOnly(RandomAccessibleInterval originalimg, final int radius) { + + + final MedianFilterOnly flatfilter = new MedianFilterOnly(originalimg, radius); + flatfilter.process(); + RandomAccessibleInterval ProgramPreprocessedimg = flatfilter.getResult(); + return ProgramPreprocessedimg; + + } + + + public static & NativeType> RandomAccessibleInterval Preprocess(RandomAccessibleInterval originalimg, final double[] psf) { + + + final FlatFieldCorrection flatfilter = new FlatFieldCorrection(originalimg, 1, psf); + flatfilter.process(); + RandomAccessibleInterval ProgramPreprocessedimg = flatfilter.getResult(); + return ProgramPreprocessedimg; + + } + public static & NativeType> Img< T > copyImage(final RandomAccessibleInterval< T > input) { + // create a new Image with the same dimensions but the other imgFactory + // note that the input provides the size for the new image by + // implementing the Interval interface + Img output = new ArrayImgFactory().create(input, Views.iterable(input).firstElement()); + + // create a cursor that automatically localizes itself on every move + Cursor cursorInput = Views.iterable(input).localizingCursor(); + RandomAccess randomAccess = output.randomAccess(); + + // iterate over the input cursor + while (cursorInput.hasNext()) { + // move input cursor forward + cursorInput.fwd(); + + // set the output cursor to the position of the input cursor + randomAccess.setPosition(cursorInput); + + // set the value of this pixel of the output image, every Type + // supports T.set( T type ) + randomAccess.get().set(cursorInput.get()); + } + + // return the copy + return output; + } + + + + /** + * Generic, type-agnostic method to create an identical copy of an Img + * + * @param currentPreprocessedimg2 + * - the Img to copy + * @return - the copy of the Img + */ + public static Img copytoByteImage(final RandomAccessibleInterval input, final Rectangle standardRectangle) { + // create a new Image with the same properties + // note that the input provides the size for the new image as it + // implements + // the Interval interface + final RandomAccessibleInterval inputcopy = copyImage(input); + Normalize.normalize(Views.iterable(inputcopy), new FloatType(0), new FloatType(255)); + final UnsignedByteType type = new UnsignedByteType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(input, type); + final Img output = factory.create(input, type); + // create a cursor for both images + RandomAccess ranac = inputcopy.randomAccess(); + Cursor cursorOutput = output.cursor(); + + // iterate over the input + while (cursorOutput.hasNext()) { + // move both cursors forward by one pixel + cursorOutput.fwd(); + + int x = cursorOutput.getIntPosition(0); + int y = cursorOutput.getIntPosition(1); + + if (standardRectangle.contains(x, y)) { + + ranac.setPosition(cursorOutput); + + // set the value of this pixel of the output image to the same + // as + // the input, + // every Type supports T.set( T type ) + cursorOutput.get().set((int) Math.round(ranac.get().getRealFloat())); + } + } + + // return the copy + return output; + } + + + + public static Img copytoByteImage(final RandomAccessibleInterval input, final FinalInterval standardInterval) { + // create a new Image with the same properties + // note that the input provides the size for the new image as it + // implements + // the Interval interface + RandomAccessibleInterval inputcopy = copyImage(input); + Normalize.normalize(Views.iterable(inputcopy), new FloatType(0), new FloatType(255)); + + inputcopy = Views.interval(input, standardInterval); + final UnsignedByteType type = new UnsignedByteType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(input, type); + final Img output = factory.create(input, type); + // create a cursor for both images + RandomAccess ranac = inputcopy.randomAccess(); + + + Cursor cursorOutput = output.cursor(); + + // iterate over the input + while (cursorOutput.hasNext()) { + // move both cursors forward by one pixel + cursorOutput.fwd(); + + + + + ranac.setPosition(cursorOutput); + + // set the value of this pixel of the output image to the same + // as + // the input, + // every Type supports T.set( T type ) + cursorOutput.get().set((int) ranac.get().get()); + } + + // return the copy + return output; + } + + public static Img copytoByteImage(final RandomAccessibleInterval input, final RandomAccessibleInterval intimg, + final Rectangle standardRectangle, int label) { + // create a new Image with the same properties + // note that the input provides the size for the new image as it + // implements + // the Interval interface + RandomAccess intran = intimg.randomAccess(); + final RandomAccessibleInterval inputcopy = copyImage(input); + Normalize.normalize(Views.iterable(inputcopy), new FloatType(0), new FloatType(255)); + final UnsignedByteType type = new UnsignedByteType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(input, type); + final Img output = factory.create(input, type); + // create a cursor for both images + RandomAccess ranac = inputcopy.randomAccess(); + Cursor cursorOutput = output.cursor(); + + // iterate over the input + while (cursorOutput.hasNext()) { + // move both cursors forward by one pixel + cursorOutput.fwd(); + + int x = cursorOutput.getIntPosition(0); + int y = cursorOutput.getIntPosition(1); + + if (standardRectangle.contains(x, y)) { + + intran.setPosition(cursorOutput); + if(intran.get().get() == label){ + ranac.setPosition(cursorOutput); + + // set the value of this pixel of the output image to the same + // asintfra + // the input, + // every Type supports T.set( T type ) + cursorOutput.get().set((int) ranac.get().get()); + } + } + } + + // return the copy + return output; + } + + + + public static double[] Transformback(double[] location, double[] size, double[] min, + double[] max) { + + int n = location.length; + + double[] delta = new double[n]; + + final double[] realpos = new double[n]; + + for (int d = 0; d < n; ++d){ + + delta[d] = (max[d] - min[d]) / size[d]; + + realpos[d] = (location[d] - min[d]) / delta[d]; + } + return realpos; + + } + + + public static & NativeType> RandomAccessibleInterval extractImage(final RandomAccessibleInterval intervalView, final FinalInterval interval) { + + return intervalView; + } + + + public static & NativeType> RandomAccessibleInterval oldextractImage(final RandomAccessibleInterval intervalView, final FinalInterval interval) { + + final T type = intervalView.randomAccess().get().createVariable(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(intervalView, type); + RandomAccessibleInterval totalimg = factory.create(intervalView, type); + final RandomAccessibleInterval img = Views.interval(intervalView, interval); + + double[] newmin = Transformback(new double[] { img.min(0), img.min(1) }, + new double[] { totalimg.dimension(0), totalimg.dimension(1) }, new double[] { img.min(0), img.min(1) }, + new double[] { img.max(0), img.max(1) }); + + double[] newmax = Transformback(new double[] { img.max(0), img.max(1) }, + new double[] { totalimg.dimension(0), totalimg.dimension(1) }, + new double[] { totalimg.min(0), totalimg.min(1) }, new double[] { totalimg.max(0), totalimg.max(1) }); + long[] newminlong = new long[] { Math.round(newmin[0]), Math.round(newmin[1]) }; + long[] newmaxlong = new long[] { Math.round(newmax[0]), Math.round(newmax[1]) }; + + RandomAccessibleInterval outimg = factory.create(new FinalInterval(newminlong, newmaxlong), type); + RandomAccess ranac = outimg.randomAccess(); + final Cursor cursor = Views.iterable(img).localizingCursor(); + + while (cursor.hasNext()) { + + cursor.fwd(); + + double[] newlocation = Transformback( + new double[] { cursor.getDoublePosition(0), cursor.getDoublePosition(1) }, + new double[] { totalimg.dimension(0), totalimg.dimension(1) }, + new double[] { totalimg.min(0), totalimg.min(1) }, + new double[] { totalimg.max(0), totalimg.max(1) }); + long[] newlocationlong = new long[] { Math.round(newlocation[0]), Math.round(newlocation[1]) }; + ranac.setPosition(newlocationlong); + ranac.get().set(cursor.get()); + + } + + return intervalView; + } + + + + + + public static & NativeType> RandomAccessibleInterval getCurrentView(RandomAccessibleInterval originalimg, int thirdDimension, int thirdDimensionSize) { + + final T type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + if (thirdDimensionSize == 0) { + + totalimg = originalimg; + } + + if (thirdDimensionSize > 0) { + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + } + + return totalimg; + + } + public static RandomAccessibleInterval copytoByteImageBit(RandomAccessibleInterval input, + RandomAccessibleInterval intimg, Rectangle standardRectangle, int label) { + // create a new Image with the same properties + // note that the input provides the size for the new image as it + // implements + // the Interval interface + RandomAccessibleInterval inputcopy = copyImage(input); + + final UnsignedByteType type = new UnsignedByteType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(input, type); + final Img output = factory.create(input, type); + // create a cursor for both images + RandomAccess ranac = inputcopy.randomAccess(); + + + Cursor cursorOutput = output.cursor(); + + // iterate over the input + while (cursorOutput.hasNext()) { + // move both cursors forward by one pixel + cursorOutput.fwd(); + + + ranac.setPosition(cursorOutput); + + // set the value of this pixel of the output image to the same + // as + // the input, + // every Type supports T.set( T type ) + + if (ranac.get().get()) + cursorOutput.get().set(255); + else + cursorOutput.get().set(0); + + } + + // return the copy + return output; + } + + final public static< T extends RealType< T > & NativeType< T >> void addGaussian( final RandomAccessibleInterval< T > image, final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + final double[] two_sq_sigma = new double[ numDimensions ]; + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = 2 * getSuggestedKernelDiameter( sigma[ d ] ); + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ] / 2; + max[ d ] = min[ d ] + size[ d ] - 1; + two_sq_sigma[ d ] = sigma[ d ] * sigma[ d ]; + } + + final RandomAccessible< T > infinite = Views.extendZero( image ); + final RandomAccessibleInterval< T > interval = Views.interval( infinite, min, max ); + final IterableInterval< T > iterable = Views.iterable( interval ); + final Cursor< T > cursor = iterable.localizingCursor(); + + + + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = 1; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getDoublePosition( d ); + value *= Math.exp( -(x * x) / two_sq_sigma[ d ] ); + + + } + + + cursor.get().setReal( cursor.get().getPowerFloat() + (float)value ); + + + } + + + + + } + + + + + + + final public static void addGaussian( final IterableInterval< FloatType > image, final double Amplitude, + final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = getSuggestedKernelDiameter( sigma[ d ] ) * 2; + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ]/2; + max[ d ] = min[ d ] + size[ d ] - 1; + + } + + + final Cursor< FloatType > cursor = image.localizingCursor(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = Amplitude; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getIntPosition( d ); + value *= Math.exp( -(x * x) / (sigma[ d ] * sigma[ d ] ) ); + } + + + cursor.get().set( cursor.get().get() + (float)value ); + + + } + + } + + public static int getSuggestedKernelDiameter( final double sigma ) + { + int size = 0; + int cutoff = 5; // This number means cutoff is chosen to be cutoff times sigma. + if ( sigma > 0 ) + size = Math.max( cutoff, ( 2 * ( int ) ( cutoff * sigma + 0.5 ) + 1 ) ); + + return size; + } + + + +} diff --git a/src/main/java/ransac/Catastrophy/DetectCatastrophy.java b/src/main/java/ransac/Catastrophy/DetectCatastrophy.java new file mode 100644 index 0000000..22326ae --- /dev/null +++ b/src/main/java/ransac/Catastrophy/DetectCatastrophy.java @@ -0,0 +1,111 @@ +package ransac.Catastrophy; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +import org.jfree.chart.JFreeChart; +import org.jfree.chart.util.ShapeUtils; +import org.jfree.data.xy.XYSeriesCollection; + +import ij.measure.ResultsTable; +import mpicbg.models.Point; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import ransac.Rateobject; +import ransac.PointFunctionMatch.PointFunctionMatch; +import ransac.loadFiles.Tracking; +import ransacPoly.AbstractFunction2D; +import ransacPoly.LinearFunction; +import ransacPoly.Polynomial; + +public class DetectCatastrophy { + + @SuppressWarnings("rawtypes") + public List> ManualCat( + ArrayList>> segments, ArrayList allrates, + double shrinkrate, ResultsTable rt, double minDistanceCatastrophe, XYSeriesCollection dataset, + JFreeChart chart, double[] calibrations, double negtimediff, double averageshrink, int i , int catindex, int negcount, int segment) { + + + List> catstarttimerates = new ArrayList>(); + + + System.out.println("Overriding Ransac, Detecting without fiting a function"); + + + for (int catastrophy = 0; catastrophy < segments.size() - 1; ++catastrophy) { + + final Pair> start = segments.get(catastrophy); + final Pair> end = segments.get(catastrophy + 1); + + double tStart = start.getB().get(start.getB().size() - 1).getP1().getL()[0]; + double tEnd = end.getB().get(0).getP1().getL()[0]; + + final double lStart = start.getB().get(start.getB().size() - 1).getP1().getL()[1]; + final double lEnd = end.getB().get(0).getP1().getL()[1]; + + if (Math.abs(lStart - lEnd) >= minDistanceCatastrophe) { + + final double slope = (lEnd - lStart) / (tEnd - tStart); + final double intercept = lEnd - slope * tEnd; + + LinearFunction linearfunc = new LinearFunction(slope, intercept); + ++i; + dataset.addSeries(Tracking.drawFunction((Polynomial) linearfunc, tStart, tEnd, 0.1, lStart, lEnd, + "CManual " + catindex + catastrophy)); + Tracking.setColor(chart, i, new Color(255, 192, 255)); + Tracking.setDisplayType(chart, i, true, false); + Tracking.setStroke(chart, i, 2f); + ++i; + + double startX = tStart; + double endX = tEnd; + + double linearrate = linearfunc.getCoefficient(1); + + if (linearrate < 0) { + + negcount++; + negtimediff += endX - startX; + + shrinkrate = linearrate; + averageshrink += linearrate; + + rt.incrementCounter(); + rt.addValue("Start time", startX * calibrations[2]); + rt.addValue("End time", endX * calibrations[2]); + rt.addValue("Growth Rate", linearrate * calibrations[0] / calibrations[2]); + Pair startrate = new ValuePair((float) startX, (float) linearrate); + + catstarttimerates.add(startrate); + + ArrayList p = new ArrayList(); + + p.add(new PointFunctionMatch(new Point(new double[] { tStart, lStart }))); + p.add(new PointFunctionMatch(new Point(new double[] { tEnd, lEnd }))); + + Rateobject rate = new Rateobject(linearrate * calibrations[0] / calibrations[2], + (int) (startX * calibrations[2]), (int) (endX * calibrations[2])); + allrates.add(rate); + + dataset.addSeries(Tracking.drawPoints(Tracking.toPairList(p), calibrations, + "CManual(points) " + catindex + catastrophy)); + + Tracking.setColor(chart, i, new Color(255, 192, 255)); + Tracking.setDisplayType(chart, i, false, true); + Tracking.setShape(chart, i, ShapeUtils.createDiamond(4f)); + + ++i; + ++segment; + } + + } + } + return catstarttimerates; + + } + + + +} diff --git a/src/main/java/ransac/Display/DisplayHistogram.java b/src/main/java/ransac/Display/DisplayHistogram.java new file mode 100644 index 0000000..d74dc52 --- /dev/null +++ b/src/main/java/ransac/Display/DisplayHistogram.java @@ -0,0 +1,119 @@ +package ransac.Display; + +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.axis.NumberAxis; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.StandardXYBarPainter; +import org.jfree.chart.renderer.xy.XYBarRenderer; +import org.jfree.data.xy.IntervalXYDataset; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import net.imglib2.util.ValuePair; + +public class DisplayHistogram { + + + protected static double min; + protected static double max; + public static List> binData(final List data, final double min, final double max, + final int numBins) { + // avoid the one value that is exactly 100% + final double size = max - min + 0.000001; + + // bin and count the entries + final int[] bins = new int[numBins]; + + for (final double v : data) + ++bins[(int) Math.floor(((v - min) / size) * numBins)]; + + // make the list of bins + final ArrayList> hist = new ArrayList>(); + + final double binSize = size / numBins; + for (int bin = 0; bin < numBins; ++bin) + hist.add(new ValuePair(min + binSize / 2 + binSize * bin, bins[bin])); + + return hist; + } + + public static double getMin() { + return min; + } + + public static double getMax() { + return max; + } + + public static ValuePair getMinMax(final List data) { + // compute min/max/size + double min = data.get(0); + double max = data.get(0); + + for (final double v : data) { + min = Math.min(min, v); + max = Math.max(max, v); + } + + return new ValuePair(min, max); + } + + public static IntervalXYDataset createDataset(final List values, final int numBins, final String title) { + final XYSeries series = new XYSeries(title); + + final ValuePair minmax = getMinMax(values); + min = minmax.getA(); + max = minmax.getB(); + + final List> hist = binData(values, min, max, numBins); + + for (final ValuePair pair : hist) + series.add(pair.getA(), pair.getB()); + + final XYSeriesCollection dataset = new XYSeriesCollection(series); + dataset.setAutoWidth(true); + + return dataset; + } + + public static JFreeChart makehistXChart(final List Xdataset, final int numBins) { + return makehistXChart(Xdataset, "MT Lifetime Distribution", "px", "Count", numBins); + } + + public static JFreeChart makehistXChart(final List Xdataset, final String title, final String x, final String y, + final int numBins) { + + final IntervalXYDataset SigmaXdataset = createDataset(Xdataset, numBins, title); + + final JFreeChart sigmaXchart = createChart(SigmaXdataset, title, x); + + return sigmaXchart; + } + + protected static JFreeChart createChart(final IntervalXYDataset dataset, final String title, final String units) { + final JFreeChart chart = ChartFactory.createXYBarChart(title, "Lifetime [" + units + "]", false, "Count", + dataset, PlotOrientation.VERTICAL, false, // legend + false, false); + + NumberAxis range = (NumberAxis) chart.getXYPlot().getDomainAxis(); + range.setRange(getMin(), getMax()); + + XYPlot plot = chart.getXYPlot(); + XYBarRenderer renderer = (XYBarRenderer) plot.getRenderer(); + + renderer.setSeriesPaint(0, Color.red); + renderer.setDrawBarOutline(true); + renderer.setSeriesOutlinePaint(0, Color.black); + renderer.setBarPainter(new StandardXYBarPainter()); + + return chart; + } + + +} diff --git a/src/main/java/ransac/Display/DisplayPoints.java b/src/main/java/ransac/Display/DisplayPoints.java new file mode 100644 index 0000000..bfafb61 --- /dev/null +++ b/src/main/java/ransac/Display/DisplayPoints.java @@ -0,0 +1,41 @@ +package ransac.Display; + +import java.awt.Dimension; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; + +public class DisplayPoints { + public static JFrame display( final JFreeChart chart ) { return display( chart, new Dimension( 800, 500 ) ); } + public static JFrame display( final JFreeChart chart, final Dimension d ) + { + final JPanel panel = new JPanel(); + final ChartPanel chartPanel = new ChartPanel( + chart, + d.width - 10, + d.height - 35, + ChartPanel.DEFAULT_MINIMUM_DRAW_WIDTH, + ChartPanel.DEFAULT_MINIMUM_DRAW_HEIGHT, + ChartPanel.DEFAULT_MAXIMUM_DRAW_WIDTH, + ChartPanel.DEFAULT_MAXIMUM_DRAW_HEIGHT, + ChartPanel.DEFAULT_BUFFER_USED, + true, // properties + true, // save + true, // print + true, // zoom + true // tooltips + ); + panel.add( chartPanel ); + + final JFrame frame = new JFrame(); + frame.setContentPane( panel ); + frame.validate(); + frame.setSize( d ); + + frame.setVisible( true ); + return frame; + } +} \ No newline at end of file diff --git a/src/main/java/ransac/FLSobject.java b/src/main/java/ransac/FLSobject.java new file mode 100644 index 0000000..2718ac0 --- /dev/null +++ b/src/main/java/ransac/FLSobject.java @@ -0,0 +1,18 @@ +package ransac; + +public class FLSobject { + + + public final int Framenumber; + public final int seedID; + public final double length; + + + public FLSobject(final int Framenumber, final int seedID, final double length){ + + this.Framenumber = Framenumber; + this.seedID = seedID; + this.length = length; + + } +} diff --git a/src/main/java/ransac/Lengthdistro/LengthDistribution.java b/src/main/java/ransac/Lengthdistro/LengthDistribution.java new file mode 100644 index 0000000..81ba0db --- /dev/null +++ b/src/main/java/ransac/Lengthdistro/LengthDistribution.java @@ -0,0 +1,357 @@ +package ransac.Lengthdistro; + +import java.awt.Dimension; +import java.io.File; +import java.text.NumberFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.title.TextTitle; +import org.jfree.chart.ui.RectangleEdge; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import ransac.FLSobject; +import ransac.Display.DisplayPoints; +import ransac.loadFiles.Tracking; +import ransacPoly.InterpolatedPolynomial; + +/** + * + * Compute the length distribution at a certain time point or give an averaged + * distribution over time Take logarithm of distribution and fit a straight line + * to determine the fit parameters + * + * @author aimachine + * + */ + +public class LengthDistribution { + + /** + * Get the maximum length in the file + * + * @param file + * @return + */ + + public static double Lengthdistro(File file) { + + ArrayList currentobject = Tracking.loadMTStat(file); + + double maxlength = 0; + + if (currentobject != null) { + for (int index = 0; index < currentobject.size(); ++index) { + + for (int secindex = 0; secindex < currentobject.size(); ++secindex) { + + maxlength = Math.max(currentobject.get(index).length, currentobject.get(secindex).length); + + } + } + + } + + return maxlength; + + } + + /** + * Returns a length list at a certain time point + * + * @param file + * @param framenumber + * @return + */ + + public static ArrayList> LengthdistroatTime(File file, final int framenumber) { + + ArrayList currentobject = Tracking.loadMTStat(file); + + ArrayList> lengthlist = new ArrayList>(); + + if (currentobject != null) { + for (int index = 0; index < currentobject.size(); ++index) { + + if (currentobject.get(index).Framenumber == framenumber) { + lengthlist.add(new ValuePair(currentobject.get(index).seedID, + currentobject.get(index).length)); + + } + + } + + } + + return lengthlist; + + } + + /** + * + * Compute average length distribution from all the movies + * + * + * @param AllMovies + * @param calibration + */ + + public static void GetLengthDistributionArray(ArrayList AllMovies, double[] calibration) { + + ArrayList maxlist = new ArrayList(); + for (int i = 0; i < AllMovies.size(); ++i) { + + double maxlength = LengthDistribution.Lengthdistro(AllMovies.get(i)); + + if (maxlength != Double.NaN && maxlength > 0) + maxlist.add(maxlength); + + } + Collections.sort(maxlist); + + int max = (int) Math.round(maxlist.get(maxlist.size() - 1)) + 1; + XYSeries counterseries = new XYSeries("MT length distribution"); + XYSeries Logcounterseries = new XYSeries("MT Log length distribution"); + final ArrayList points = new ArrayList(); + for (int length = 0; length < max; ++length) { + + HashMap frameseed = new HashMap(); + + for (int i = 0; i < AllMovies.size(); ++i) { + + File file = AllMovies.get(i); + + double currentlength = LengthDistribution.Lengthdistro(file); + + ArrayList currentobject = Tracking.loadMTStat(file); + + if (currentlength > length) { + + MakeSeed(currentobject, frameseed, length); + } + + } + + // Get maxima length, count + int maxvalue = Integer.MIN_VALUE; + + for (int key : frameseed.keySet()) { + + int Count = frameseed.get(key); + + if (Count >= maxvalue) + maxvalue = Count; + } + + if (maxvalue != Integer.MIN_VALUE) { + counterseries.add(length, maxvalue); + + if (maxvalue > 0) { + Logcounterseries.add((length), Math.log(maxvalue)); + points.add(new Point(new double[] { length, Math.log(maxvalue) })); + } + + } + } + + final XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(counterseries); + final XYSeriesCollection Logdataset = new XYSeriesCollection(); + Logdataset.addSeries(Logcounterseries); + + final JFreeChart chart = ChartFactory.createScatterPlot("MT length distribution", "Length (micrometer)", + "Number of MT", dataset); + + // Fitting line to log of the length distribution + InterpolatedPolynomial poly = new InterpolatedPolynomial(1); + try { + + poly.fitFunction(points); + + } catch (NotEnoughDataPointsException e) { + + } + dataset.addSeries(Tracking.drawexpFunction(poly, counterseries.getMinX(), counterseries.getMaxX(), 0.5, + "Exponential fit")); + NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMaximumFractionDigits(3); + TextTitle legendText = new TextTitle("Mean Length" + " : " + nf.format(-1.0 / poly.getCoefficients(1)) + " " + + "Standard Deviation" + " : " + nf.format(poly.SSE)); + legendText.setPosition(RectangleEdge.RIGHT); + + DisplayPoints.display(chart, new Dimension(800, 500)); + chart.addSubtitle(legendText); + + } + + /** + * + * Compute length distribution at a certain time point over all files + * + * @param AllMovies + * @param calibration + * @param framenumber + */ + + public static void GetLengthDistributionArrayatTime(ArrayList AllMovies, double[] calibration, + final int framenumber) { + + ArrayList maxlist = new ArrayList(); + for (int i = 0; i < AllMovies.size(); ++i) { + + ArrayList> lengthlist = LengthDistribution.LengthdistroatTime(AllMovies.get(i), + framenumber); + + for (int index = 0; index < lengthlist.size(); ++index) { + if (lengthlist.get(index).getB() != Double.NaN && lengthlist.get(index).getB() > 0) + maxlist.add(lengthlist.get(index).getB()); + + } + } + Collections.sort(maxlist); + + int max = 0; + if (maxlist.size() > 0) + max = (int) Math.round(maxlist.get(maxlist.size() - 1)) + 1; + XYSeries counterseries = new XYSeries("MT length distribution"); + XYSeries Logcounterseries = new XYSeries("MT Log length distribution"); + final ArrayList points = new ArrayList(); + for (int length = 0; length < max; ++length) { + + HashMap frameseed = new HashMap(); + + for (int i = 0; i < AllMovies.size(); ++i) { + + File file = AllMovies.get(i); + + ArrayList currentobject = Tracking.loadMTStat(file); + + MakeSeed(currentobject, frameseed, length, framenumber); + + } + + // Get maxima length, count + int maxvalue = Integer.MIN_VALUE; + + for (int key : frameseed.keySet()) { + + int Count = frameseed.get(key); + + if (Count >= maxvalue) + maxvalue = Count; + } + + if (maxvalue != Integer.MIN_VALUE) { + counterseries.add(length, maxvalue); + + if (maxvalue > 0) { + + Logcounterseries.add((length), Math.log(maxvalue)); + points.add(new Point(new double[] { length, Math.log(maxvalue) })); + } + + } + } + + final XYSeriesCollection dataset = new XYSeriesCollection(); + dataset.addSeries(counterseries); + final XYSeriesCollection Logdataset = new XYSeriesCollection(); + Logdataset.addSeries(Logcounterseries); + + final JFreeChart chart = ChartFactory.createScatterPlot("MT length distribution", "Length (micrometer)", + "Number of MT", dataset); + + // Fitting line to log of the length distribution + InterpolatedPolynomial poly = new InterpolatedPolynomial(1); + try { + + poly.fitFunction(points); + + } catch (NotEnoughDataPointsException e) { + + } + dataset.addSeries(Tracking.drawexpFunction(poly, counterseries.getMinX(), counterseries.getMaxX(), 0.5, + "Exponential fit")); + NumberFormat nf = NumberFormat.getInstance(Locale.ENGLISH); + nf.setMaximumFractionDigits(3); + TextTitle legendText = new TextTitle("Mean Length" + " : " + nf.format(-1.0 / poly.getCoefficients(1)) + " " + + "Standard Deviation" + " : " + nf.format(poly.SSE)); + legendText.setPosition(RectangleEdge.RIGHT); + + DisplayPoints.display(chart, new Dimension(800, 500)); + chart.addSubtitle(legendText); + + } + + + /** + * + * Put frame number and count the microtubules having a certain length at a given time in all movies + * + * @param currentobject + * @param frameseed + * @param length + * @param framenumber + */ + public static void MakeSeed(final ArrayList currentobject, final HashMap frameseed, + final int length, final int framenumber) { + + for (int index = 0; index < currentobject.size(); ++index) { + ArrayList seedlist = new ArrayList(); + if (currentobject.get(index).length >= length && currentobject.get(index).Framenumber == framenumber) { + seedlist.add(currentobject.get(index).seedID); + if (frameseed.get(currentobject.get(index).Framenumber) != null + && frameseed.get(currentobject.get(index).Framenumber) != Double.NaN) { + + int currentcount = frameseed.get(currentobject.get(index).Framenumber); + frameseed.put(currentobject.get(index).Framenumber, seedlist.size() + currentcount); + } else if (currentobject.get(index) != null) + frameseed.put(currentobject.get(index).Framenumber, seedlist.size()); + + } + + } + + } + + /** + * + * Put frame number and count the microtubules having a certain length at any given time in all movies + * + * @param currentobject + * @param frameseed + * @param length + */ + + public static void MakeSeed(final ArrayList currentobject, final HashMap frameseed, + final int length) { + + for (int index = 0; index < currentobject.size(); ++index) { + ArrayList seedlist = new ArrayList(); + if (currentobject.get(index).length >= length) { + seedlist.add(currentobject.get(index).seedID); + if (frameseed.get(currentobject.get(index).Framenumber) != null + && frameseed.get(currentobject.get(index).Framenumber) != Double.NaN) { + + int currentcount = frameseed.get(currentobject.get(index).Framenumber); + frameseed.put(currentobject.get(index).Framenumber, seedlist.size() + currentcount); + } else if (currentobject.get(index) != null) + frameseed.put(currentobject.get(index).Framenumber, seedlist.size()); + + } + + } + + } + +} diff --git a/src/main/java/ransac/PointFunctionMatch/Function.java b/src/main/java/ransac/PointFunctionMatch/Function.java new file mode 100644 index 0000000..d6c1500 --- /dev/null +++ b/src/main/java/ransac/PointFunctionMatch/Function.java @@ -0,0 +1,41 @@ +package ransac.PointFunctionMatch; + +import java.util.Collection; + +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +/** + * Interface for a {@link Function} that can be fit to {@link Point}s + * + * @author Stephan Preibisch + */ +public interface Function< F extends Function< F, P >, P extends Point > +{ + /** + * @return - how many points are at least necessary to fit the function + */ + public int getMinNumPoints(); + + /** + * Fits this Function to the set of {@link Point}s. + + * @param points - {@link Collection} of {@link Point}s + * @throws NotEnoughDataPointsException - thrown if not enough {@link Point}s are in the {@link Collection} + */ + public void fitFunction( final Collection

      points ) throws NotEnoughDataPointsException, IllDefinedDataPointsException; + + /** + * Computes the minimal distance of a {@link Point} to this function + * + * @param point - the {@link Point} + * @return - distance to the {@link Function} + */ + public double distanceTo( final P point ); + + /** + * @return - a copy of the function object + */ + public F copy(); +} \ No newline at end of file diff --git a/src/main/java/ransac/PointFunctionMatch/PointFunctionMatch.java b/src/main/java/ransac/PointFunctionMatch/PointFunctionMatch.java new file mode 100644 index 0000000..14a699f --- /dev/null +++ b/src/main/java/ransac/PointFunctionMatch/PointFunctionMatch.java @@ -0,0 +1,50 @@ +package ransac.PointFunctionMatch; + +import mpicbg.models.CoordinateTransform; +import mpicbg.models.Point; +import mpicbg.models.PointMatch; + +/** + * Replaces the PointMatch fitting a function to a set of Point instead of a set of point to a set of point + * + * @author Stephan Preibisch (stephan.preibisch@gmx.de) & Timothee Lionnet + */ +public class PointFunctionMatch extends PointMatch +{ + private static final long serialVersionUID = -8070932126418631690L; + + //final protected Function function; + + double distance = 0; + + public PointFunctionMatch( final Point p1 ) + { + super( p1, null ); + } + + //public Function getFunction() { return function; } + + /** + * Here one could compute and return the closest point on the function to p1, + * but it is not well defined as there could be more than one... + */ + @Deprecated + @Override + public Point getP2() { return null; } + + @SuppressWarnings("unchecked") + public void apply( final CoordinateTransform t ) + { + distance = (float)((Function)t).distanceTo( p1 ); + } + + @SuppressWarnings("unchecked") + public void apply( final CoordinateTransform t, final float amount ) + { + distance = (float)((Function)t).distanceTo( p1 ); + } + + @Override + public double getDistance() { return distance; } + +} \ No newline at end of file diff --git a/src/main/java/ransac/Rateobject.java b/src/main/java/ransac/Rateobject.java new file mode 100644 index 0000000..06f7d0b --- /dev/null +++ b/src/main/java/ransac/Rateobject.java @@ -0,0 +1,21 @@ +package ransac; + +public class Rateobject { + + + public final double rate; + public final int starttime; + public final int endtime; + + + + public Rateobject(final double rate, final int starttime,final int endtime ){ + + this.rate = rate; + this.starttime = starttime; + this.endtime = endtime; + + + } + +} diff --git a/src/main/java/ransac/loadFiles/Filereader.java b/src/main/java/ransac/loadFiles/Filereader.java new file mode 100644 index 0000000..16a5ac6 --- /dev/null +++ b/src/main/java/ransac/loadFiles/Filereader.java @@ -0,0 +1,92 @@ +package ransac.loadFiles; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.PrintWriter; + +public class Filereader { + + + public static BufferedReader openFileRead(final File file) + { + BufferedReader inputFile; + try + { + inputFile = new BufferedReader(new FileReader(file)); + } + catch (IOException e) + { + System.out.println("TextFileAccess.openFileRead(): " + e); + inputFile = null; + } + return (inputFile); + } + + public static BufferedReader openFileRead(final String fileName) + { + BufferedReader inputFile; + try + { + inputFile = new BufferedReader(new FileReader(fileName)); + } + catch (IOException e) + { + System.out.println("TextFileAccess.openFileRead(): " + e); + inputFile = null; + } + return (inputFile); + } + + public static PrintWriter openFileWrite(final File file) + { + PrintWriter outputFile; + try + { + outputFile = new PrintWriter(new FileWriter(file)); + } + catch (IOException e) + { + System.out.println("TextFileAccess.openFileWrite(): " + e); + outputFile = null; + } + return (outputFile); + } + + public static PrintWriter openFileWrite(final String fileName) + { + PrintWriter outputFile; + try + { + outputFile = new PrintWriter(new FileWriter(fileName)); + } + catch (IOException e) + { + System.out.println("TextFileAccess.openFileWrite(): " + e); + outputFile = null; + } + return (outputFile); + } + + public static PrintWriter openFileWriteEx(final File file) throws IOException + { + return new PrintWriter(new FileWriter(file)); + } + + public static BufferedReader openFileReadEx(final File file) throws IOException + { + return new BufferedReader(new FileReader(file)); + } + + public static PrintWriter openFileWriteEx(final String fileName) throws IOException + { + return new PrintWriter(new FileWriter(fileName)); + } + + public static BufferedReader openFileReadEx(final String fileName) throws IOException + { + return new BufferedReader(new FileReader(fileName)); + } +} diff --git a/src/main/java/ransac/loadFiles/Tracking.java b/src/main/java/ransac/loadFiles/Tracking.java new file mode 100644 index 0000000..b41a147 --- /dev/null +++ b/src/main/java/ransac/loadFiles/Tracking.java @@ -0,0 +1,667 @@ +package ransac.loadFiles; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Shape; +import java.io.BufferedReader; +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.JFrame; +import javax.swing.JPanel; + +import org.jfree.chart.ChartFactory; +import org.jfree.chart.ChartPanel; +import org.jfree.chart.JFreeChart; +import org.jfree.chart.plot.PlotOrientation; +import org.jfree.chart.plot.XYPlot; +import org.jfree.chart.renderer.xy.XYItemRenderer; +import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer; +import org.jfree.chart.util.ShapeUtils; +import org.jfree.data.xy.XYSeries; +import org.jfree.data.xy.XYSeriesCollection; + +import mpicbg.models.Point; + +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import ransac.FLSobject; +import ransac.PointFunctionMatch.*; +import ransacPoly.AbstractFunction2D; +import ransacPoly.HigherOrderPolynomialFunction; +import ransacPoly.InterpolatedPolynomial; +import ransacPoly.LinearFunction; +import ransacPoly.MixedPolynomial; +import ransacPoly.MixedPolynomialFunction; +import ransacPoly.Polynomial; +import ransacPoly.QuadraticFunction; +import ransacPoly.RansacFunction; + +public class Tracking { + + + + public static ArrayList< FLSobject > loadMTStat( final File file ) + { + final ArrayList< FLSobject > points = new ArrayList< FLSobject >(); + + try + { + BufferedReader in = Filereader.openFileRead( file ); + + while( in.ready() ) + { + String line = in.readLine().trim(); + + while ( line.contains( "\t\t" ) ) + line = line.replaceAll( "\t\t", "\t" ); + + if ( line.length() >= 3 && line.matches( "[0-9].*" ) ) + { + final String[] split = line.trim().split( "\t" ); + + final int frame = (int)Double.parseDouble( split[ 0 ] ); + final double length = Double.parseDouble( split[ 2 ] ); + final int seedID = (int)Double.parseDouble( split[ 3 ] ); + + FLSobject statobject = new FLSobject(frame, seedID, length); + points.add( statobject); + } + } + } + catch ( Exception e ) + { + e.printStackTrace(); + return null; + } + + Collections.sort( points, new Comparator< FLSobject >() + { + @Override + public int compare( final FLSobject o1, final FLSobject o2 ) + { + + final int t1 = o1.Framenumber; + final int t2 = o2.Framenumber; + + + if (t1 < t2) + return -1; + else if (t1 == t2) + return 0; + else + return 1; + } + } ); + + if (points.size() > 0) + return points; + + else return null; + } + + public static double[] loadCalibration(final File file) + { + + double[] calibrations = new double[3]; + double calibrationX = 1, calibrationY = 1, calibrationT = 1; + try + { + BufferedReader in = Filereader.openFileRead( file ); + + + while( in.ready() ) + { + String line = in.readLine().trim(); + + while ( line.contains( "\t\t" ) ) + line = line.replaceAll( "\t\t", "\t" ); + + if ( line.length() >= 3 && line.matches( "[0-9].*" ) ) + { + final String[] split = line.trim().split( "\t" ); + + calibrationX = Double.parseDouble( split[ 10 ] ); + calibrationY = Double.parseDouble( split[ 11 ] ); + calibrationT = Double.parseDouble( split[ 12 ] ); + + } + } + } + catch ( Exception e ) + { + e.printStackTrace(); + return null; + } + + calibrations[0] = calibrationX; + calibrations[1] = calibrationY; + calibrations[2] = calibrationT; + + return calibrations; + } + + public static ArrayList< Pair< Integer, Double > > loadMT( final File file ) + { + final ArrayList< Pair< Integer, Double > > points = new ArrayList< Pair< Integer, Double > >(); + + try + { + BufferedReader in = Filereader.openFileRead( file ); + + while( in.ready() ) + { + String line = in.readLine().trim(); + + while ( line.contains( "\t\t" ) ) + line = line.replaceAll( "\t\t", "\t" ); + + if ( line.length() >= 3 && line.matches( "[0-9].*" ) ) + { + final String[] split = line.trim().split( "\t" ); + + final int frame = (int)Double.parseDouble( split[ 0 ] ); + final double length = Double.parseDouble( split[ 1 ] ); + + points.add( new ValuePair< Integer, Double >( frame, length ) ); + } + } + } + catch ( Exception e ) + { + e.printStackTrace(); + return null; + } + + Collections.sort( points, new Comparator< Pair< Integer, Double > >() + { + @Override + public int compare( final Pair< Integer, Double > o1, final Pair< Integer, Double > o2 ) + { + return o1.getA().compareTo( o2.getA() ); + } + } ); + + if (points.size() > 0) + return points; + + else return null; + } + + + public static ArrayList< Pair< Integer, Double > > loadsimple( final File file ) + { + final ArrayList< Pair< Integer, Double > > points = new ArrayList< Pair< Integer, Double > >(); + final ArrayList< Pair< Integer, Double > > normalpoints = new ArrayList< Pair< Integer, Double > >(); + try + { + BufferedReader in = Filereader.openFileRead( file ); + + while( in.ready() ) + { + String line = in.readLine().trim(); + + while ( line.contains( "\t\t" ) ) + line = line.replaceAll( "\t\t", "\t" ); + + if ( line.length() >= 3 && line.matches( "[0-9].*" ) ) + { + final String[] split = line.trim().split( "\t" ); + + final int frame = (int)Double.parseDouble( split[ 0 ] ); + final double length = Double.parseDouble( split[ 1 ] ); + + points.add( new ValuePair< Integer, Double >( frame, length ) ); + } + } + } + catch ( Exception e ) + { + e.printStackTrace(); + return null; + } + + double maxlength = Double.MIN_VALUE; + for( Pair< Integer, Double> point:points){ + + double length = point.getB(); + if ( length > maxlength) + maxlength = length; + } + for( Pair< Integer, Double> point:points){ + Pair< Integer, Double> newpoint = new ValuePair< Integer, Double >(point.getA()/ 10 , point.getB() / maxlength ); + normalpoints.add(newpoint); + } + + return normalpoints; + } + + public static XYSeries drawPoints( final List< Pair< Integer, Double > > mts, double[] calibrations ) { return drawPoints( mts, calibrations, "MT Length" ); } + public static XYSeries drawPoints( final List< Pair< Integer, Double > > mts, double[] calibrations, final String name ) + { + XYSeries series = new XYSeries( name ); + + if (mts!=null){ + for ( final Pair< Integer, Double > mt : mts ) + series.add( mt.getA(), mt.getB() ); + } + return series; + } + + public static XYSeries drawFunction( final Polynomial< ?, Point > polynomial, final double from, final double to, final double step, final String name ) + { + XYSeries series = new XYSeries( name ); + + for ( double x = from; x <= to; x = x + step ) + series.add( x, polynomial.predict( x ) ); + + return series; + } + + public static XYSeries drawFunction( final InterpolatedPolynomial polynomial, final double from, final double to, final double step, final String name ) + { + XYSeries series = new XYSeries( name ); + + for ( double x = from; x <= to; x = x + step ) + series.add( x, polynomial.predict( x ) ); + + return series; + } + + + public static XYSeries drawexpFunction( final InterpolatedPolynomial polynomial, final double from, final double to, final double step, final String name ) + { + XYSeries series = new XYSeries( name ); + + for ( double x = from; x <= to; x = x + step ) + series.add( x, Math.exp(polynomial.predict( x )) ); + + return series; + } + + + + public static XYSeries drawFunction( final Polynomial< ?, Point > polynomial, final double from, final double to, final double step, final double minY, final double maxY, final String name ) + { + XYSeries series = new XYSeries( name ); + + for ( double x = from; x <= to; x = x + step ) + { + final double v = polynomial.predict( x ); + + if ( v >= minY && v <= maxY ) + series.add( x, v ); + } + + return series; + } + + public static JFreeChart makeChart( final XYSeriesCollection dataset ) { return makeChart( dataset, "XY Chart", "x-axis", "y-axis" ); } + public static JFreeChart makeChart( final XYSeriesCollection dataset, final String title, final String x, final String y ) + { + final JFreeChart chart = ChartFactory.createXYLineChart( + title, x, y, + dataset, + PlotOrientation.VERTICAL, + true, + true, + false + ); + + return chart; + } + + public static void setColor( final JFreeChart chart, final int seriesIndex, final Color col ) + { + final XYPlot plot = chart.getXYPlot(); + final XYItemRenderer renderer = plot.getRenderer(); + renderer.setSeriesPaint( seriesIndex, col ); + } + + public static void setStroke( final JFreeChart chart, final int seriesIndex, final float stroke ) + { + final XYPlot plot = chart.getXYPlot(); + final XYItemRenderer renderer = plot.getRenderer(); + renderer.setSeriesStroke( seriesIndex, new BasicStroke( stroke ) ); + } + + public static void setShape( final JFreeChart chart, final int seriesIndex, final Shape shape ) + { + final XYPlot plot = chart.getXYPlot(); + final XYItemRenderer renderer = plot.getRenderer(); + renderer.setSeriesShape( seriesIndex, shape ); + } + + public static void setSmallUpTriangleShape( final JFreeChart chart, final int seriesIndex ) + { + final XYPlot plot = chart.getXYPlot(); + final XYItemRenderer renderer = plot.getRenderer(); + renderer.setSeriesShape( seriesIndex, ShapeUtils.createUpTriangle( 0.5f ) ); + } + + public static void setDisplayType( final JFreeChart chart, final int seriesIndex, final boolean line, final boolean shape ) + { + final XYPlot plot = chart.getXYPlot(); + final XYLineAndShapeRenderer renderer = (XYLineAndShapeRenderer)plot.getRenderer(); + renderer.setSeriesLinesVisible( seriesIndex, line ); + renderer.setSeriesShapesVisible( seriesIndex, shape ); + } + + public static JFrame display( final JFreeChart chart ) { return display( chart, new Dimension( 800, 500 ) ); } + public static JFrame display( final JFreeChart chart, final Dimension d ) + { + final JPanel panel = new JPanel(); + final ChartPanel chartPanel = new ChartPanel( + chart, + d.width - 10, + d.height - 35, + ChartPanel.DEFAULT_MINIMUM_DRAW_WIDTH, + ChartPanel.DEFAULT_MINIMUM_DRAW_HEIGHT, + ChartPanel.DEFAULT_MAXIMUM_DRAW_WIDTH, + ChartPanel.DEFAULT_MAXIMUM_DRAW_HEIGHT, + ChartPanel.DEFAULT_BUFFER_USED, + true, // properties + true, // save + true, // print + true, // zoom + true // tooltips + ); + panel.add( chartPanel ); + + final JFrame frame = new JFrame(); + frame.setContentPane( panel ); + frame.validate(); + frame.setSize( d ); + + frame.setVisible( true ); + return frame; + } + + public static ArrayList< Point > toPoints( final ArrayList< Pair< Integer, Double > > mts ) + { + final ArrayList< Point > points = new ArrayList< Point >(); + + + for ( final Pair< Integer, Double > mt : mts ) + points.add( new Point( new double[]{ mt.getA(), mt.getB() } ) ); + + return points; + } + + public static ArrayList< Pair< Integer, Double > > toPairList( final ArrayList< PointFunctionMatch > points ) + { + final ArrayList< Pair< Integer, Double > > mts = new ArrayList< Pair< Integer, Double > >(); + + for ( final PointFunctionMatch p : points ) + mts.add( new ValuePair< Integer, Double >( (int)Math.round( p.getP1().getW()[ 0 ] ), p.getP1().getW()[ 1 ] ) ); + + return mts; + } + + public static < P extends AbstractFunction2D< P > > Pair< P, ArrayList< PointFunctionMatch > > findFunction( final ArrayList< Point > mts, final P function ) + { + return findFunction( mts, function, 3.0, function.getMinNumPoints(), 6 ); + } + + public static < P extends AbstractFunction2D< P > > Pair< P, ArrayList< PointFunctionMatch > > findFunction( + final ArrayList< Point > mts, + final P function, + final double maxError, + final int minNumInliers ) + { + final ArrayList< PointFunctionMatch > candidates = new ArrayList(); + final ArrayList< PointFunctionMatch > inliers = new ArrayList(); + + for ( final Point p : mts ) + candidates.add( new PointFunctionMatch( p ) ); + + try + { + function.ransac( candidates, inliers, 100, maxError, 0, minNumInliers ); + + if (inliers!=null){ + + if ( inliers.size() >= function.getMinNumPoints() ) + { + function.fit( inliers ); + + //System.out.println( inliers.size() + "/" + candidates.size() ); + //System.out.println( function ); + } + + else + { + //System.out.println( "0/" + candidates.size() ); + return null; + } + } + } + catch ( Exception e ) + { + // TODO Auto-generated catch block + return null; + } + + return new ValuePair< P, ArrayList< PointFunctionMatch > >( function, inliers ); + } + + /* + * if ( LinearFunction.slopeFits( result.getB(), linear, minSlope, maxSlope ) ) + */ + public static < P extends AbstractFunction2D< P > > ArrayList< Pair< P, ArrayList< PointFunctionMatch > > > findAllFunctions( + final ArrayList< Point > mts, + final P function, + final double maxError, + final int minNumInliers ) + { + boolean fitted; + + final ArrayList< Point > remainingPoints = new ArrayList< Point >(); + if (mts!=null){ + remainingPoints.addAll( mts ); + + final ArrayList< Pair< P, ArrayList< PointFunctionMatch > > > segments = new ArrayList< Pair> >(); + + + do + { + fitted = false; + + final Pair< P, ArrayList< PointFunctionMatch > > f = findFunction( remainingPoints, function.copy(), maxError, minNumInliers ); + + if ( f != null && f.getB().size() > 0 ) + { + fitted = true; + segments.add( f ); + + final ArrayList< Point > inlierPoints = new ArrayList< Point >(); + for ( final PointFunctionMatch p : f.getB() ) + inlierPoints.add( p.getP1() ); + + remainingPoints.removeAll( inlierPoints ); + } + } + + while ( fitted ); + + return segments; + } + + else return null; + } + + + @SuppressWarnings("deprecation") + public static Pair< LinearFunction, ArrayList< PointFunctionMatch > > findLinearFunction( + final ArrayList< Point > mts, + final double maxError, + final int minNumInliers, + + final double minSlope, + final double maxSlope ) + { + final ArrayList< PointFunctionMatch > candidates = new ArrayList(); + final ArrayList< PointFunctionMatch > inliers = new ArrayList(); + + for ( final Point p : mts ) + candidates.add( new PointFunctionMatch( p ) ); + + final LinearFunction function = new LinearFunction(); + + try + { + function.ransac( candidates, inliers, 1000, maxError, 0, minNumInliers, minSlope, maxSlope ); + + if ( inliers.size() >= function.getMinNumPoints() ) + { + function.fit( inliers ); + + System.out.println( inliers.size() + "/" + candidates.size() ); + System.out.println( function ); + } + else + { + System.out.println( "0/" + candidates.size() ); + return null; + } + } + catch ( Exception e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + return null; + } + + return new ValuePair< LinearFunction, ArrayList< PointFunctionMatch > >( function, inliers ); + } + @SuppressWarnings("deprecation") + public static < P extends AbstractFunction2D< P > > Pair> findFunction( + final ArrayList< Point > mts, + final P function, + final double maxError, + final int minNumInliers, + final double maxDist ) + { + final ArrayList< PointFunctionMatch > candidates = new ArrayList(); + final ArrayList< PointFunctionMatch > inliers = new ArrayList(); + + for ( final Point p : mts ) + candidates.add( new PointFunctionMatch( p ) ); + + try + { + function.ransac( candidates, inliers, 100, maxError, 0.01, minNumInliers ); + + if ( inliers.size() >= function.getMinNumPoints() ) + { + function.fit( inliers ); + + //System.out.println( inliers.size() + "/" + candidates.size() ); + //System.out.println( function ); + } + else + { + //System.out.println( "0/" + candidates.size() ); + return null; + } + } + catch ( Exception e ) + { + System.out.println( "Couldn't fit function: " + e ); + // TODO Auto-generated catch block + //e.printStackTrace(); + return null; + } + + return new ValuePair< P, ArrayList< PointFunctionMatch > >( function, inliers ); + } + + + + + + @SuppressWarnings("deprecation") + public static RansacFunction findQuadLinearFunction( + final ArrayList< Point > mts, + final MixedPolynomialFunction > mixedfunction, + + final double maxError, + final int minNumInliers) + { + final ArrayList< PointFunctionMatch > candidates = new ArrayList(); + final ArrayList< PointFunctionMatch > inliers = new ArrayList(); + for ( final Point p : mts ) + candidates.add( new PointFunctionMatch( p ) ); + try + { + + + // Find Inliers using quadratic function, then fit a higher order polynomial to it + mixedfunction.ransacN( candidates, inliers, 100, maxError, 0, mixedfunction.getMinNumPoints() ); + + if(inliers.size() > mixedfunction.getMinNumPoints()) { + + mixedfunction.fit( inliers ); + RansacFunction returnfunction = new RansacFunction(mixedfunction, inliers, candidates); + + return returnfunction; + } + else { + + /* + System.out.println("Ransac Failed, falling over to Brute force regression"); + mixedfunction.fit( candidates ); + RansacFunction returnfunction = new RansacFunction(mixedfunction, candidates, candidates); + */ + return null; + } + + + } + catch ( Exception e ) + { + System.out.println( "Couldn't fit function: " + e ); + + return null; + } + + + } + + public static Pair< Double, Double > fromTo( final ArrayList< PointFunctionMatch > points ) + { + double min = points.get( 0 ).getP1().getW()[ 0 ]; + double max = min; + + for ( final PointFunctionMatch p : points ) + { + final double value = p.getP1().getW()[ 0 ]; + min = Math.min( min, value ); + max = Math.max( max, value ); + } + + return new ValuePair< Double, Double >( min, max ); + } + public static Pair< Double, Double > fromToY( final ArrayList< PointFunctionMatch > points ) + { + double min = points.get( 1 ).getP1().getW()[ 1 ]; + double max = min; + + for ( final PointFunctionMatch p : points ) + { + final double value = p.getP1().getW()[ 1 ]; + min = Math.min( min, value ); + max = Math.max( max, value ); + } + + return new ValuePair< Double, Double >( min, max ); + } + + + + +} diff --git a/src/main/java/ransacPoly/AbstractFunction.java b/src/main/java/ransacPoly/AbstractFunction.java new file mode 100644 index 0000000..acc91ee --- /dev/null +++ b/src/main/java/ransacPoly/AbstractFunction.java @@ -0,0 +1,56 @@ +package ransacPoly; + +import java.util.ArrayList; +import java.util.Collection; + +import mpicbg.models.AbstractModel; +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; +import mpicbg.models.PointMatch; + +/** + * Radial Symmetry Package + * + * This software is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. If not, see . + * + * @author Stephan Preibisch (stephan.preibisch@gmx.de) & Timothee Lionnet + */ +public abstract class AbstractFunction< M extends AbstractFunction< M > > extends AbstractModel< M > implements Function< Point > +{ + @Override + public int getMinNumMatches() { return getMinNumPoints(); } + + @Deprecated + @Override + public

      void fit( final Collection

      matches ) throws NotEnoughDataPointsException, IllDefinedDataPointsException + { + final ArrayList list = new ArrayList(); + + for ( final P pm : matches ) + list.add( pm.getP1() ); + + fitFunction( list ); + } + + + + + + @Override + public double[] apply( final double[] location ) { return null; } + + @Override + public void applyInPlace( final double[] location ) {} +} diff --git a/src/main/java/ransacPoly/AbstractFunction2D.java b/src/main/java/ransacPoly/AbstractFunction2D.java new file mode 100644 index 0000000..939eea8 --- /dev/null +++ b/src/main/java/ransacPoly/AbstractFunction2D.java @@ -0,0 +1,199 @@ +package ransacPoly; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + + +import mpicbg.models.AbstractModel; +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.PointMatch; +import ransac.PointFunctionMatch.PointFunctionMatch; + +/** + * Adds a RANSAC with a specifc max gap between points in 2d on the x-axis + * + * @author Stephan Saalfeld and Stephan Preibisch + * + * @param + */ +public abstract class AbstractFunction2D< M extends AbstractFunction2D< M > > extends AbstractFunction< M > +{ + private static final long serialVersionUID = 9102425001575237374L; + + /** + * Find the {@link AbstractModel} of a set of {@link PointMatch} candidates + * containing a high number of outliers using + * {@link #ransac(List, Collection, int, double, double, int) RANSAC} + * \citet[{FischlerB81}. + * + * @param modelClass class of the model to be estimated + * @param candidates candidate data points inluding (many) outliers + * @param inliers remaining candidates after RANSAC + * @param iterations number of iterations + * @param epsilon maximal allowed transfer error + * @param minInlierRatio minimal number of inliers to number of + * candidates + * @param minNumInliers minimally required absolute number of inliers + * @param maxGapDim0 max distance between points on the x-axis (will keep the larger set of points) + * + * @return true if {@link AbstractModel} could be estimated and inliers is not + * empty, false otherwise. If false, {@link AbstractModel} remains unchanged. + */ + @SuppressWarnings("deprecation") + final public < P extends PointFunctionMatch >boolean ransacN( + final List< P > candidates, + final Collection< P > inliers, + final int iterations, + final double epsilon, + final double minInlierRatio, + final int minNumInliers) + throws NotEnoughDataPointsException + { + if ( candidates.size() < getMinNumMatches() ) + throw new NotEnoughDataPointsException( candidates.size() + " data points are not enough to solve the Model, at least " + getMinNumMatches() + " data points required." ); + cost = Double.MAX_VALUE; + + final M copy = copy(); + final M m = copy(); + + inliers.clear(); + + int i = 0; + final HashSet< P > minMatches = new HashSet< P >(); + +A: while ( i < iterations ) + { + // choose model.MIN_SET_SIZE disjunctive matches randomly + minMatches.clear(); + for ( int j = 0; j < getMinNumMatches(); ++j ) + { + P p; + do + { + p = candidates.get( ( int )( rnd.nextDouble() * candidates.size() ) ); + } + while ( minMatches.contains( p ) ); + minMatches.add( p ); + } + try { m.fit( minMatches ); } + catch ( final IllDefinedDataPointsException e ) + { + ++i; + continue; + } + + final ArrayList< P > tempInliers = new ArrayList< P >(); + + int numInliers = 0; + boolean isGood = m.test( candidates, tempInliers, epsilon, minInlierRatio, minNumInliers ); + while ( isGood && numInliers < tempInliers.size() ) + { + numInliers = tempInliers.size(); + try { m.fit( tempInliers ); } + catch ( final IllDefinedDataPointsException e ) + { + ++i; + continue A; + } + isGood = m.test( candidates, tempInliers, epsilon, minInlierRatio, minNumInliers ); + } + if ( + isGood && + m.betterThan( copy ) && + tempInliers.size() >= minNumInliers ) + { + copy.set( m ); + inliers.clear(); + inliers.addAll( tempInliers ); + } + ++i; + } + if ( inliers.size() == 0 ) + return false; + set( copy ); + return true; + } + + /** + * Test the {@link AbstractModel} for a set of {@link PointMatch} candidates. + * Return true if the number of inliers / number of candidates is larger + * than or equal to min_inlier_ratio, otherwise false. + * + * Clears inliers and fills it with the fitting subset of candidates. + * + * Sets {@link #getCost() cost} = 1.0 - |inliers| / |candidates|. + * + * @param candidates set of point correspondence candidates + * @param inliers set of point correspondences that fit the model + * @param epsilon maximal allowed transfer error + * @param minInlierRatio minimal ratio |inliers| / |candidates| (0.0 => 0%, 1.0 => 100%) + * @param minNumInliers minimally required absolute number of inliers + */ + public < P extends PointFunctionMatch > boolean test( + final Collection< P > candidates, + final List< P > inliers, + final double epsilon, + final double minInlierRatio, + final int minNumInliers, + final double maxGapDim0 ) + { + inliers.clear(); + + for ( final P m : candidates ) + { + m.apply( this ); + if ( m.getDistance() < epsilon ) inliers.add( m ); + } + + if ( inliers.size() > 1 ) + { + Collections.sort( inliers, new Comparator< P >() + { + @Override + public int compare( final P o1, final P o2 ) + { + if ( o1.getP1().getW()[ 0 ] < o2.getP1().getW()[ 0 ] ) + return -1; + else if ( o1.getP1().getW()[ 0 ] == o2.getP1().getW()[ 0 ] ) + return 0; + else + return 1; + } + } ); + + final ArrayList< P > maxInliers = new ArrayList< P >(); + final ArrayList< P > tmpInliers = new ArrayList< P >(); + + tmpInliers.add( inliers.get( 0 ) ); + + for ( int i = 1; i < inliers.size(); ++i ) + { + + + + // distance between the points <= maxGapDim0, then just keep adding the points + tmpInliers.add( inliers.get( i ) ); + + } + + inliers.clear(); + + // is the latest set of points larger than the biggest set so far? + if ( tmpInliers.size() > maxInliers.size() ) + inliers.addAll( tmpInliers ); + else + inliers.addAll( maxInliers ); + } + + final double ir = ( double )inliers.size() / ( double )candidates.size(); + setCost( Math.max( 0.0, Math.min( 1.0, 1.0 - ir ) ) ); + + return ( inliers.size() >= minNumInliers && ir > minInlierRatio ); + } +} + diff --git a/src/main/java/ransacPoly/Function.java b/src/main/java/ransacPoly/Function.java new file mode 100644 index 0000000..9524331 --- /dev/null +++ b/src/main/java/ransacPoly/Function.java @@ -0,0 +1,61 @@ +package ransacPoly; + +import java.util.Collection; + +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +/** + * Interface for a {@link Function} that can be fit to {@link Point}s + * + * @author Stephan Preibisch + * + * @param

      - if a special extension of {@link Point} is necessary, otherwise just implement Function + */ +/** + * Radial Symmetry Package + * + * This software is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. If not, see . + * + * @author Stephan Preibisch (stephan.preibisch@gmx.de) & Timothee Lionnet + */ +public interface Function< P extends Point > +{ + /** + * @return - how many points are at least necessary to fit the function + */ + public int getMinNumPoints(); + + /** + * Fits this Function to the set of {@link Point}s. + * @param points - {@link Collection} of {@link Point}s + * @throws NotEnoughDataPointsException - thrown if not enough {@link Point}s are in the {@link Collection} + * @throws IllDefinedDataPointsException + */ + public void fitFunction( final Collection

      points ) throws NotEnoughDataPointsException, IllDefinedDataPointsException; + + + + + + /** + * Computes the minimal distance of a {@link Point} to this function + * + * @param point - the {@link Point} + * @return - distance to the {@link Function} + */ + public double distanceTo( final P point ); + +} diff --git a/src/main/java/ransacPoly/HigherOrderPolynomialFunction.java b/src/main/java/ransacPoly/HigherOrderPolynomialFunction.java new file mode 100644 index 0000000..82fc9e4 --- /dev/null +++ b/src/main/java/ransacPoly/HigherOrderPolynomialFunction.java @@ -0,0 +1,208 @@ +package ransacPoly; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; + +import Jama.Matrix; +import Jama.QRDecomposition; + +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +/** + * @author Varun Kapoor, Stephan Preibisch + */ +public class HigherOrderPolynomialFunction extends AbstractFunction2D< HigherOrderPolynomialFunction > implements Polynomial< HigherOrderPolynomialFunction, Point > +{ + private static final long serialVersionUID = 5010369758205651325L; + + // For initial guesses for Newton Raphson + final Random rndx = new Random( 43583458 ); + + final int minNumPoints; + final int degree; + + public final double[] coeff; + + public HigherOrderPolynomialFunction(final int degree) + { + this.degree = degree; + this.minNumPoints = degree + 1; + this.coeff = new double[degree + 1]; + } + + /** + * @return - the coefficients of the polynomial in x + */ + @Override + public double getCoefficient( final int j ) { return coeff[j]; } + + @Override + public int getMinNumPoints() { return minNumPoints; } + + /* + * This is a fit function for the polynomial of user chosen degree + */ + public void fitFunction2( final Collection< Point > points ) throws NotEnoughDataPointsException + { + final int nPoints = points.size(); + + if ( nPoints < minNumPoints ) + throw new NotEnoughDataPointsException("Not enough points, at least " + minNumPoints + " are necessary, available are " + nPoints ); + + // Vandermonde matrix + final double[][] vandermonde = new double[ nPoints ][ degree + 1 ]; + final double[] y = new double[ nPoints ]; + + int i = 0; + for ( final Point p : points ) + { + final double x = p.getW()[ 0 ]; + + for ( int j = 0; j <= degree; ++j ) + vandermonde[ i ][ j ] = NewtonRaphson.pow( x, j ); + + y[ i++ ] = p.getW()[ 1 ]; + } + + final Matrix X = new Matrix(vandermonde); + + // create matrix from vector + final Matrix Y = new Matrix(y, nPoints); + + // find least squares solution + final QRDecomposition qr = new QRDecomposition(X); + final Matrix coefficients = qr.solve(Y); + + for ( int j = degree; j >= 0; --j ) + this.coeff[ j ] = coefficients.get( j, 0 ); + } + + public void fitFunction(final Collection< Point > points ) throws NotEnoughDataPointsException, IllDefinedDataPointsException + { + final int numPoints = points.size(); + + if ( numPoints < minNumPoints ) + throw new NotEnoughDataPointsException( "Not enough points, at least " + minNumPoints + " are necessary and available are: " + numPoints ); + + // compute matrices + final double[][] delta = new double[ degree + 1 ][ degree + 1 ]; + final double[] tetha = new double[ degree + 1 ]; + + final double[] powCache = new double[ degree * 2 + 1 ]; + powCache[ powCache.length - 1 ] = 1; + + for ( final Point p : points ) + { + final double x = p.getW()[ 0 ]; + final double y = p.getW()[ 1 ]; + + double power = 1; + for ( int d = 1; d < powCache.length; ++d ) + { + power *= x; + powCache[ powCache.length - 1 - d ] = power; + } + + for ( int r = 0; r <= degree; ++r ) + for ( int c = 0; c <= degree; ++c ) + delta[ r ][ c ] += powCache[ r + c ]; + + double mulY = y; + + for ( int d = 0; d <= degree; ++d ) + { + tetha[ degree - d ] += mulY; + mulY *= x; + } + } + + // invert matrix + final Matrix deltaInv = MatrixFunctions.computePseudoInverseMatrix( new Matrix( delta ), 0.00001 ); + + for ( int d = degree; d >= 0; --d ) + { + this.coeff[ d ] = 0; + + for ( int i = 0; i <= degree; ++i ) + this.coeff[ d ] += deltaInv.get( degree - d, i ) * tetha[ i ]; + } + } + + // Distance of a point from a polynomial + @Override + public double distanceTo( final Point point ) + { + final double x1 = point.getW()[0]; + final double y1 = point.getW()[1]; + + + return new NewtonRaphson( rndx, degree ).run( x1, y1, coeff ); + } + + @Override + public void set( final HigherOrderPolynomialFunction p ) + { + for (int j = degree; j >= 0; j--) + this.coeff[j] = p.getCoefficient(j); + + this.setCost(p.getCost()); + } + + @Override + public HigherOrderPolynomialFunction copy() + { + final HigherOrderPolynomialFunction c = new HigherOrderPolynomialFunction( degree ); + + for (int j = degree; j >= 0; j--) + c.coeff[j] = getCoefficient(j); + + c.setCost( getCost() ); + + return c; + } + + @Override + public int degree() { return degree; } + + // Horner's method to get y values correspoing to x + @Override + public double predict( final double x ) + { + // horner's method + double y = 0.0; + for (int j = degree; j >= 0; j--) + y = getCoefficient(j) + (x * y); + return y; + } + + // Horner's method to get y First derivative values correspoing to x + @Override + public double predictFirstderivative( final double x ) + { + double y = 0.0; + for (int j = 1; j <= degree; j++) { + + y += j * getCoefficient(j) * Math.pow(x, j - 1); + } + + return y; + } + + // Horner's method to get y First derivative values correspoing to x + @Override + public double predictSecondderivative( final double x ) + { + double y = 0.0; + + for (int j = 2; j <= degree; j++) { + + y += j * ( j - 1) * getCoefficient(j) * Math.pow(x, j - 2); + } + return y; + } + +} \ No newline at end of file diff --git a/src/main/java/ransacPoly/InterpolatedPolynomial.java b/src/main/java/ransacPoly/InterpolatedPolynomial.java new file mode 100644 index 0000000..4a3bf7e --- /dev/null +++ b/src/main/java/ransacPoly/InterpolatedPolynomial.java @@ -0,0 +1,454 @@ +package ransacPoly; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; + +import Jama.Matrix; +import Jama.QRDecomposition; +import ransac.PointFunctionMatch.PointFunctionMatch; +import utils.DistanceMetrics; +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +public class InterpolatedPolynomial extends AbstractFunction { + + /** + * + */ + private static final long serialVersionUID = 5010369758205651325L; + final int minNumPoints; + int degree; + + public double SSE; + public double SST; + public final double[] coeff; + + public InterpolatedPolynomial(final int degree) { + + this.degree = degree; + this.minNumPoints = degree + 1; + this.coeff = new double[degree + 1]; + } + + /** + * @return - the coefficients of the polynomial in x + */ + public double getCoefficients(final int j) { + return coeff[j]; + } + + @Override + public int getMinNumPoints() { + return minNumPoints; + } + + /* + * + * This is a fit function for the polynomial of user chosen degree + * + */ + public void fitFunction(final Collection points) throws NotEnoughDataPointsException { + Matrix coefficients; + final int Npoints = points.size(); + if (Npoints < minNumPoints) + throw new NotEnoughDataPointsException("Not enough points, at least " + minNumPoints + " are necessary."); + double[] y = new double[Npoints]; + double[] x = new double[Npoints]; + + int count = 0; + for (final Point p : points) { + x[count] = p.getW()[0]; + y[count] = p.getW()[1]; + count++; + } + + // Vandermonde matrix + double[][] vandermonde = new double[Npoints][degree + 1]; + for (int i = 0; i < Npoints; i++) { + for (int j = 0; j <= degree; j++) { + vandermonde[i][j] = Math.pow(x[i], j); + } + } + Matrix X = new Matrix(vandermonde); + + // create matrix from vector + Matrix Y = new Matrix(y, Npoints); + + // find least squares solution + QRDecomposition qr = new QRDecomposition(X); + coefficients = qr.solve(Y); + + // mean of y[] values + double sum = 0.0; + for (int i = 0; i < Npoints; i++) + sum += y[i]; + double mean = sum / Npoints; + + // total variation to be accounted for + for (int i = 0; i < Npoints; i++) { + double dev = y[i] - mean; + SST += dev * dev; + } + + // variation not accounted for + Matrix residuals = X.times(coefficients).minus(Y); + SSE = residuals.norm2() * residuals.norm2(); + + for (int j = degree; j >= 0; j--) { + this.coeff[j] = coefficients.get(j, 0); + + } + + } + static double asinh(double x) + { + return Math.log(x + Math.sqrt(x*x + 1.0)); + } + + static double acosh(double x) + { + return Math.log(x + Math.sqrt(x*x - 1.0)); + } + + static double atanh(double x) + { + return 0.5*Math.log( (x + 1.0) / (x - 1.0) ); + } + // Distance of a point from a polynomial + @Override + public double distanceTo(final Point point) { + final double x1 = point.getW()[0]; + final double y1 = point.getW()[1]; + + + if (degree == 1) + return Math.abs(y1 - coeff[1]*x1 - coeff[0]) /(Math.sqrt( 1 + coeff[1] * coeff[1])); + + if (degree == 2){ + + double a3 , a2, a1, a0, Abar, Bbar, Phi, p, q, xc; + double xc1 = 0, xc2 = 0, xc3 = 0, yc1 = 0, yc2 = 0, yc3 = 0 ; + + a3 = 2 * coeff[2] * coeff[2] ; + a2 = 3 * coeff[1] * coeff[2] / a3 ; + a1 = (2 * coeff[0] * coeff[2] - 2 * coeff[2] * y1 + 1 + coeff[1] *coeff[1]) / a3 ; + a0 = (coeff[0] *coeff[1] - y1 * coeff[1] - x1) / a3 ; + + p = (3 * a1 - a2 * a2) / 3 ; + q = (-9 * a1 * a2 + 27 * a0 + 2 * a2 * a2 * a2) / 27 ; + + + + if ((q * q / 4 + p * p * p / 27) > 0){ + Abar = Math.cbrt(-q/2 + Math.sqrt( q * q / 4 + p * p * p / 27)); + Bbar = Math.cbrt(-q/2 - Math.sqrt( q * q / 4 + p * p * p / 27)); + + xc = Abar + Bbar - a2 / 3; + xc1 = xc; + xc2 = xc; + xc3 = xc; + + } + if ((q * q / 4 + p * p * p / 27) == 0){ + + if (q > 0){ + xc1 = -2 * Math.sqrt(-p / 3); + xc2 = Math.sqrt(-p / 3); + xc3 = xc2; + } + + if (q < 0){ + xc1 = 2 * Math.sqrt(-p / 3); + xc2 = -Math.sqrt(-p / 3); + xc3 = xc2; + } + + if (q == 0){ + + xc1 = 0; + xc2 = 0; + xc3 = 0; + + } + + } + + if ((q * q / 4 + p * p * p / 27) < 0){ + + if ( q >= 0) + + Phi = Math.acos(- Math.sqrt( q * q * 0.25 / (-p * p * p / 27))); + + else + + Phi = Math.acos(Math.sqrt( q * q * 0.25 / (-p * p * p / 27))); + + + xc1 = 2 * Math.sqrt( -p / 3) * Math.cos(Phi / 3) - a2 / 3; + xc2 = 2 * Math.sqrt( -p / 3) * Math.cos((Phi + 2 * Math.PI) / 3) - a2 / 3; + xc3 = 2 * Math.sqrt( -p / 3) * Math.cos((Phi + 4 * Math.PI) / 3) - a2 / 3; + + + } + + + for (int j = degree; j >= 0; j--) { + + yc1 += coeff[j] * Math.pow(xc1, j); + + yc2 += coeff[j] * Math.pow(xc2, j); + + yc3 += coeff[j] * Math.pow(xc3, j); + } + + + + double returndistA = DistanceMetrics.StraightLineDistance(new double[] { x1, y1 }, new double[] { xc1, yc1 }); + + double returndistB = DistanceMetrics.StraightLineDistance(new double[] { x1, y1 }, new double[] { xc2, yc2 }); + + double returndistC = DistanceMetrics.StraightLineDistance(new double[] { x1, y1 }, new double[] { xc3, yc3 }); + + double returndist = Math.min(returndistA, Math.min(returndistB, returndistC)); + + + return returndist; + + } + + else{ + // Initial guesses for Newton Raphson + final Random rndx = new Random(); + double xc = rndx.nextFloat(); + + double polyfunc = 0; + double polyfuncdiff = 0; + double delpolyfuncdiff = 0; + double Dmin = 0; + double Dmindiff = 0; + double xcNew = rndx.nextFloat() * rndx.nextFloat() ; + double secdelpolyfuncdiff = 0; + double Dminsecdiff = 0; + + /** + * Newton Raphson routine to get the shortest distance of a point from a + * curve. + */ + for (int j = degree; j >= 0; j--) + + polyfunc += coeff[j] * Math.pow(xc, j); + + + for (int j = degree; j >= 1; j--) + + polyfuncdiff += j * coeff[j] * Math.pow(xc, j - 1); + + + + for (int j = degree; j >= 2; j--) + delpolyfuncdiff += j * (j - 1) * coeff[j] * Math.pow(xc, j - 2); + + for (int j = degree; j >= 3; j--) + secdelpolyfuncdiff += j * (j - 1) * (j - 2) * coeff[j] * Math.pow(xc, j - 3); + + do { + + xc = xcNew; + + Dmin = (polyfunc - y1) * polyfuncdiff + (xc - x1); + + Dmindiff = polyfuncdiff * polyfuncdiff + (polyfunc - y1)* delpolyfuncdiff + 1; + + Dminsecdiff = (polyfunc - y1)*secdelpolyfuncdiff + delpolyfuncdiff * polyfuncdiff + 2 * polyfuncdiff * delpolyfuncdiff ; + + + + // Compute the first iteration of the new point + xcNew = NewtonRaphson(xc, Dmin, Dmindiff, Dminsecdiff); + + if (xcNew == Double.NaN) + xcNew = xc; + + + // System.out.println(xcNew); + + // Compute the functions and the required derivates at the new point + delpolyfuncdiff = 0; + polyfunc = 0; + polyfuncdiff = 0; + secdelpolyfuncdiff = 0; + for (int j = degree; j >= 0; j--) { + + polyfunc += coeff[j] * Math.pow(xcNew, j); + + } + for (int j = degree; j >= 1; j--) { + + polyfuncdiff += j * coeff[j] * Math.pow(xcNew, j - 1); + + } + + for (int j = degree; j >= 2; j--) + delpolyfuncdiff += j * (j - 1) * coeff[j] * Math.pow(xcNew, j - 2); + + for (int j = degree; j >= 3; j--) + secdelpolyfuncdiff += j * (j - 1) * (j - 2) * coeff[j] * Math.pow(xcNew, j - 3); + + + + + + + + } while (Math.abs((xcNew - xc)) > 1.0E-3); + + // After the solution is found compute the y co-oordinate of the point + // on the curve + polyfunc = 0; + for (int j = degree; j >= 0; j--) { + + polyfunc += coeff[j] * Math.pow(xc, j); + + } + + // Get the distance of (x1, y1) point from the curve and return the + // value + + double returndist = DistanceMetrics.StraightLineDistance(new double[] { x1, y1 }, new double[] { xc, polyfunc }); + return returndist; + } + } + + public double NewtonRaphson(final double oldpoint, final double Function, final double Functionderiv, final double Functionsecderiv) { + + + double secondordertermA = -(Functionderiv - oldpoint*Functionsecderiv) ; + double secondordertermSQRT = Math.pow((Functionderiv - oldpoint*Functionsecderiv),2) - 2 * Functionsecderiv * (Function + Functionsecderiv*0.5*oldpoint*oldpoint - Functionderiv*oldpoint) ; + double newpoint = 0 ; + + + /* This part used the actual second order Taylor expansion, however the paper is VLSI Design doing the same suggested using binomial theorem after that + /* + if (secondordertermSQRT > 0 && Functionsecderiv!= 0) + newpoint = (secondordertermA + Math.sqrt(secondordertermSQRT)) / (Functionsecderiv); + else + newpoint = oldpoint - Function / Functionderiv; + + */ + newpoint = oldpoint - (Function / Functionderiv) * (1 + 0.5 * Function * Functionsecderiv / (Functionderiv * Functionderiv) ); + + + return newpoint; + + } + + public static int i = 0; + + @Override + public void set(final InterpolatedPolynomial p) { + + for (int j = degree; j >= 0; j--) { + + this.coeff[j] = p.getCoefficients(j); + } + + this.setCost(p.getCost()); + } + + @Override + public InterpolatedPolynomial copy() { + InterpolatedPolynomial c = new InterpolatedPolynomial(degree); + + for (int j = degree; j >= 0; j--) { + c.coeff[j] = getCoefficients(j); + } + + c.setCost(getCost()); + + return c; + } + + public int degree() { + return degree; + } + + public double R2() { + return 1.0 - SSE / SST; + } + + // Horner's method to get y values correspoing to x + public double predict( final double x ) + { + // horner's method + double y = 0.0; + for (int j = degree; j >= 0; j--) + y = getCoefficients(j) + (x * y); + return y; + } + + // Horner's method to get y values correspoing to x + public double predictderivative(double x) { + // horner's method for derivative of a function + double y = 0; + for (int j = degree; j >= 0; j--) + if(degree >= 1) + y += j * getCoefficients(j) * Math.pow(x, degree - 1); + return y; + } + + // Horner's method to get y values correspoing to x + public double predictsecderivative(double x) { + // horner's method for derivative of a function + double y = 0; + for (int j = degree; j >= 0; j--) + if(degree >= 2) + y += j * ( j - 1) * getCoefficients(j) * Math.pow(x, degree - 2); + + return y; + } + public static void main(String[] args) throws NotEnoughDataPointsException, IllDefinedDataPointsException { + final ArrayList points = new ArrayList(); + + points.add(new Point(new double[] { 1f, -3.95132f })); + points.add(new Point(new double[] { 2f, 6.51205f })); + points.add(new Point(new double[] { 3f, 18.03612f })); + points.add(new Point(new double[] { 4f, 28.65245f })); + points.add(new Point(new double[] { 5f, 42.05581f })); + points.add(new Point(new double[] { 6f, 54.01327f })); + points.add(new Point(new double[] { 7f, 64.58747f })); + points.add(new Point(new double[] { 8f, 76.48754f })); + points.add(new Point(new double[] { 9f, 89.00033f })); + + final ArrayList candidates = new ArrayList(); + final ArrayList inliersPoly = new ArrayList(); + long startTime = System.nanoTime(); + for (final Point p : points) + candidates.add(new PointFunctionMatch(p)); + + final int degree = 2; + // Using the polynomial model to do the fitting + final InterpolatedPolynomial regression = new InterpolatedPolynomial(degree); + + regression.ransac(candidates, inliersPoly, 100, 0.1, 0.5); + + System.out.println("inliers: " + inliersPoly.size()); + for ( final PointFunctionMatch p : inliersPoly ) + System.out.println( regression.distanceTo( p.getP1() ) ); + + + regression.fit(inliersPoly); + + + + System.out.println(" y = " ); + for (int i = degree; i >= 0; --i) + System.out.println(regression.getCoefficients(i) + " " + "x" + " X to the power of " + i ); + long totalTime = System.nanoTime()- startTime; + System.out.println("Time: " + totalTime); + + } + + +} diff --git a/src/main/java/ransacPoly/LinearFunction.java b/src/main/java/ransacPoly/LinearFunction.java new file mode 100644 index 0000000..e708ccb --- /dev/null +++ b/src/main/java/ransacPoly/LinearFunction.java @@ -0,0 +1,382 @@ +package ransacPoly; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; + + +import mpicbg.models.AbstractModel; +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; +import mpicbg.models.PointMatch; +import ransac.PointFunctionMatch.PointFunctionMatch; + +/** + * @author Stephan Preibisch (stephan.preibisch@gmx.de) & Timothee Lionnet + */ +public class LinearFunction extends AbstractFunction2D< LinearFunction > implements Polynomial< LinearFunction, Point > +{ + private static final long serialVersionUID = 5289346951323596267L; + + final int minNumPoints = 2; + + double m, n; // m*x + n + + public LinearFunction() { this( 0,0 ); } + public LinearFunction( final double m, final double n ) + { + this.m = m; + this.n = n; + } + + /** + * @return - the center of the circle in x + */ + public double getN() { return n; } + + /** + * @return - the center of the circle in y + */ + public double getM() { return m; } + + @Override + public int degree() { return 1; } + + @Override + public double getCoefficient( final int j ) + { + if ( j == 0 ) + return n; + else if ( j == 1 ) + return m; + else + return 0; + } + + @Override + public int getMinNumPoints() { return minNumPoints; } + + public void fitFunction( final Collection points ) throws NotEnoughDataPointsException + { + final int numPoints = points.size(); + + if ( numPoints < minNumPoints ) + throw new NotEnoughDataPointsException( "Not enough points, at least " + minNumPoints + " are necessary and available are: " + numPoints ); + + // compute matrices + final double[] delta = new double[ 4 ]; + final double[] tetha = new double[ 2 ]; + + for ( final Point p : points ) + { + final double x = p.getW()[ 0 ]; + final double y = p.getW()[ 1 ]; + + final double xx = x*x; + final double xy = x*y; + + delta[ 0 ] += xx; + delta[ 1 ] += x; + delta[ 2 ] += x; + delta[ 3 ] += 1; + + tetha[ 0 ] += xy; + tetha[ 1 ] += y; + } + + // invert matrix + MatrixFunctions.invert2x2( delta ); + + this.m = delta[ 0 ] * tetha[ 0 ] + delta[ 1 ] * tetha[ 1 ]; + this.n = delta[ 2 ] * tetha[ 0 ] + delta[ 3 ] * tetha[ 1 ]; + } + + @Override + public double distanceTo( final Point point ) + { + final double x1 = point.getW()[ 0 ]; + final double y1 = point.getW()[ 1 ]; + + return Math.abs( y1 - m*x1 - n ) / ( Math.sqrt( m*m + 1 ) ); + } + + @Override + public void set( final LinearFunction m ) + { + this.n = m.getN(); + this.m = m.getM(); + this.setCost( m.getCost() ); + } + + @Override + public double predict( final double x ) { return m*x + n; } + + @Override + public double predictFirstderivative( final double x ) { return m; } + + @Override + public double predictSecondderivative( final double x ) { return 0; } + + + @Override + public LinearFunction copy() + { + LinearFunction c = new LinearFunction(); + + c.n = getN(); + c.m = getM(); + c.setCost( getCost() ); + + return c; + } + + @Override + public String toString() { return "f(x)=" + getM() + "*x + " + getN(); } + + /** + * Find the {@link AbstractModel} of a set of {@link PointMatch} candidates + * containing a high number of outliers using + * {@link #ransac(List, Collection, int, double, double, int) RANSAC} + * \citet[{FischlerB81}. + * + * @param modelClass class of the model to be estimated + * @param candidates candidate data points inluding (many) outliers + * @param inliers remaining candidates after RANSAC + * @param iterations number of iterations + * @param epsilon maximal allowed transfer error + * @param minInlierRatio minimal number of inliers to number of + * candidates + * @param minNumInliers minimally required absolute number of inliers + * @param maxGapDim0 max distance between points on the x-axis (will keep the larger set of points) + * + * @return true if {@link AbstractModel} could be estimated and inliers is not + * empty, false otherwise. If false, {@link AbstractModel} remains unchanged. + */ + @SuppressWarnings("deprecation") + final public < P extends PointFunctionMatch >boolean ransac( + final List< P > candidates, + final Collection< P > inliers, + final int iterations, + final double epsilon, + final double minInlierRatio, + final int minNumInliers, + + final double minSlope, + final double maxSlope ) + throws NotEnoughDataPointsException + { + if ( candidates.size() < getMinNumMatches() ) + throw new NotEnoughDataPointsException( candidates.size() + " data points are not enough to solve the Model, at least " + getMinNumMatches() + " data points required." ); + + cost = Double.MAX_VALUE; + + final LinearFunction copy = copy(); + final LinearFunction m = copy(); + + inliers.clear(); + + int i = 0; + final HashSet< P > minMatches = new HashSet< P >(); + +A: while ( i < iterations ) + { + // choose model.MIN_SET_SIZE disjunctive matches randomly + minMatches.clear(); + for ( int j = 0; j < getMinNumMatches(); ++j ) + { + P p; + do + { + p = candidates.get( ( int )( rnd.nextDouble() * candidates.size() ) ); + } + while ( minMatches.contains( p ) ); + minMatches.add( p ); + } + try { m.fit( minMatches ); } + catch ( final IllDefinedDataPointsException e ) + { + ++i; + continue; + } + + final ArrayList< P > tempInliers = new ArrayList< P >(); + + int numInliers = 0; + boolean isGood = m.test( candidates, tempInliers, epsilon, minInlierRatio, minNumInliers, minSlope, maxSlope ); + while ( isGood && numInliers < tempInliers.size() ) + { + numInliers = tempInliers.size(); + try { m.fit( tempInliers ); } + catch ( final IllDefinedDataPointsException e ) + { + ++i; + continue A; + } + isGood = m.test( candidates, tempInliers, epsilon, minInlierRatio, minNumInliers, minSlope, maxSlope ); + } + if ( + isGood && + m.betterThan( copy ) && + tempInliers.size() >= minNumInliers ) + { + copy.set( m ); + inliers.clear(); + inliers.addAll( tempInliers ); + } + ++i; + } + if ( inliers.size() == 0 ) + return false; + + set( copy ); + return true; + } + + /** + * Test the {@link AbstractModel} for a set of {@link PointMatch} candidates. + * Return true if the number of inliers / number of candidates is larger + * than or equal to min_inlier_ratio, otherwise false. + * + * Clears inliers and fills it with the fitting subset of candidates. + * + * Sets {@link #getCost() cost} = 1.0 - |inliers| / |candidates|. + * + * @param candidates set of point correspondence candidates + * @param inliers set of point correspondences that fit the model + * @param epsilon maximal allowed transfer error + * @param minInlierRatio minimal ratio |inliers| / |candidates| (0.0 => 0%, 1.0 => 100%) + * @param minNumInliers minimally required absolute number of inliers + */ + public < P extends PointFunctionMatch > boolean test( + final Collection< P > candidates, + final List< P > inliers, + final double epsilon, + final double minInlierRatio, + final int minNumInliers, + + final double minSlope, + final double maxSlope ) + { + final LinearFunction tmp = new LinearFunction(); + inliers.clear(); + + for ( final P m : candidates ) + { + m.apply( this ); + if ( m.getDistance() < epsilon ) inliers.add( m ); + } + + if ( inliers.size() > 1 ) + { + Collections.sort( inliers, new Comparator< P >() + { + @Override + public int compare( final P o1, final P o2 ) + { + if ( o1.getP1().getW()[ 0 ] < o2.getP1().getW()[ 0 ] ) + return -1; + else if ( o1.getP1().getW()[ 0 ] == o2.getP1().getW()[ 0 ] ) + return 0; + else + return 1; + } + } ); + + final ArrayList< P > maxInliers = new ArrayList< P >(); + final ArrayList< P > tmpInliers = new ArrayList< P >(); + + tmpInliers.add( inliers.get( 0 ) ); + + for ( int i = 1; i < inliers.size(); ++i ) + { + final P current = inliers.get( i ); + + + // distance between two points on the x > maxGapDim0 + + // if this was the largest chunk of data so far, keep it + if ( tmpInliers.size() > maxInliers.size() && slopeFits( tmpInliers, tmp, minSlope, maxSlope ) ) + { + maxInliers.clear(); + maxInliers.addAll( tmpInliers ); + } + + // clear tmpInliers, add the current one for a new start + tmpInliers.clear(); + tmpInliers.add( current ); + + } + + inliers.clear(); + + // is the latest set of points larger than the biggest set so far? + if ( tmpInliers.size() > maxInliers.size() && slopeFits( tmpInliers, tmp, minSlope, maxSlope ) ) + inliers.addAll( tmpInliers ); + else + inliers.addAll( maxInliers ); + } + + final double ir = ( double )inliers.size() / ( double )candidates.size(); + setCost( Math.max( 0.0, Math.min( 1.0, 1.0 - ir ) ) ); + + return ( inliers.size() >= minNumInliers && ir > minInlierRatio ); + } + + @SuppressWarnings("deprecation") + public static < P extends PointFunctionMatch > boolean slopeFits( final List< P > inliers, final LinearFunction function, final double minSlope, final double maxSlope ) + { + try + { + function.fit( inliers ); + + if ( function.getM() >= minSlope && function.getM() <= maxSlope ) + return true; + } + catch ( Exception e ) {} + + return false; + } + + + + @SuppressWarnings("deprecation") + public static void main( String[] args ) throws NotEnoughDataPointsException, IllDefinedDataPointsException + { + final ArrayList< Point > points = new ArrayList(); + + points.add( new Point( new double[]{ 1f, -3.95132f } ) ); + points.add( new Point( new double[]{ 2f, 6.51205f } ) ); + points.add( new Point( new double[]{ 3f, 18.03612f } ) ); + points.add( new Point( new double[]{ 4f, 28.65245f } ) ); + points.add( new Point( new double[]{ 5f, 42.05581f } ) ); + points.add( new Point( new double[]{ 6f, 54.01327f } ) ); + points.add( new Point( new double[]{ 7f, 64.58747f } ) ); + points.add( new Point( new double[]{ 8f, 76.48754f } ) ); + points.add( new Point( new double[]{ 9f, 89.00033f } ) ); + + final ArrayList< PointFunctionMatch > candidates = new ArrayList(); + final ArrayList< PointFunctionMatch > inliers = new ArrayList(); + + for ( final Point p : points ) + candidates.add( new PointFunctionMatch( p ) ); + + final LinearFunction l = new LinearFunction(); + + l.ransac( candidates, inliers, 100, 0.1, 0.5 ); + + System.out.println( inliers.size() ); + + l.fit( inliers ); + + System.out.println( "y = " + l.m + " x + " + l.n ); + for ( final PointFunctionMatch p : inliers ) + System.out.println( l.distanceTo( p.getP1() ) ); + + //System.out.println( l.distanceTo( new Point( new float[]{ 1f, 0f } ) ) ); + } + +} diff --git a/src/main/java/ransacPoly/MatrixFunctions.java b/src/main/java/ransacPoly/MatrixFunctions.java new file mode 100644 index 0000000..7415fe6 --- /dev/null +++ b/src/main/java/ransacPoly/MatrixFunctions.java @@ -0,0 +1,140 @@ +package ransacPoly; + +import Jama.Matrix; +import Jama.SingularValueDecomposition; +import mpicbg.models.NoninvertibleModelException; + +/** + * @author Stephan Preibisch (stephan.preibisch@gmx.de) & Timothee Lionnet + */ +public class MatrixFunctions +{ + /** + * Cannot instantiate this class + */ + private MatrixFunctions() {} + + final public static void invert2x2( final float[] matrix ) + { + final float a = matrix[ 0 ]; + final float b = matrix[ 1 ]; + final float c = matrix[ 2 ]; + final float d = matrix[ 3 ]; + + final float det = 1.0f / ( a*d - b*c ); + + matrix[ 0 ] = d * det; + matrix[ 1 ] = -b * det; + matrix[ 2 ] = -c * det; + matrix[ 3 ] = a * det; + } + + final public static void invert2x2( final double[] matrix ) + { + final double a = matrix[ 0 ]; + final double b = matrix[ 1 ]; + final double c = matrix[ 2 ]; + final double d = matrix[ 3 ]; + + final double det = 1.0 / ( a*d - b*c ); + + matrix[ 0 ] = d * det; + matrix[ 1 ] = -b * det; + matrix[ 2 ] = -c * det; + matrix[ 3 ] = a * det; + } + + final static public void invert3x3( final double[] m ) throws NoninvertibleModelException + { + assert m.length == 9 : "Matrix3x3 supports 3x3 double[][] only."; + + final double det = det3x3( m ); + if ( det == 0 ) throw new NoninvertibleModelException( "Matrix not invertible." ); + + final double i00 = ( m[ 4 ] * m[ 8 ] - m[ 5 ] * m[ 7 ] ) / det; + final double i01 = ( m[ 2 ] * m[ 7 ] - m[ 1 ] * m[ 8 ] ) / det; + final double i02 = ( m[ 1 ] * m[ 5 ] - m[ 2 ] * m[ 4 ] ) / det; + + final double i10 = ( m[ 5 ] * m[ 6 ] - m[ 3 ] * m[ 8 ] ) / det; + final double i11 = ( m[ 0 ] * m[ 8 ] - m[ 2 ] * m[ 6 ] ) / det; + final double i12 = ( m[ 2 ] * m[ 3 ] - m[ 0 ] * m[ 5 ] ) / det; + + final double i20 = ( m[ 3 ] * m[ 7 ] - m[ 4 ] * m[ 6 ] ) / det; + final double i21 = ( m[ 1 ] * m[ 6 ] - m[ 0 ] * m[ 7 ] ) / det; + final double i22 = ( m[ 0 ] * m[ 4 ] - m[ 1 ] * m[ 3 ] ) / det; + + m[ 0 ] = i00; + m[ 1 ] = i01; + m[ 2 ] = i02; + + m[ 3 ] = i10; + m[ 4 ] = i11; + m[ 5 ] = i12; + + m[ 6 ] = i20; + m[ 7 ] = i21; + m[ 8 ] = i22; + } + + + /** + * Calculate the determinant of a matrix given as a float[] (row after row). + * + * @author Stephan Saalfeld + * + * @param a matrix given row by row + * + * @return determinant + */ + final static public double det3x3( final double[] a ) + { + assert a.length == 9 : "Matrix3x3 supports 3x3 float[][] only."; + + return + a[ 0 ] * a[ 4 ] * a[ 8 ] + + a[ 3 ] * a[ 7 ] * a[ 2 ] + + a[ 6 ] * a[ 1 ] * a[ 5 ] - + a[ 2 ] * a[ 4 ] * a[ 6 ] - + a[ 5 ] * a[ 7 ] * a[ 0 ] - + a[ 8 ] * a[ 1 ] * a[ 3 ]; + } + + /** + * Computes the pseudo-inverse of a matrix using Singular Value Decomposition + * + * @param M - the input {@link Matrix} + * @param threshold - the threshold for inverting diagonal elements (suggested 0.001) + * @return the inverted {@link Matrix} or an approximation with lowest possible squared error + */ + final public static Matrix computePseudoInverseMatrix( final Matrix M, final double threshold ) + { + final SingularValueDecomposition svd = new SingularValueDecomposition( M ); + + Matrix U = svd.getU(); // U Left Matrix + final Matrix S = svd.getS(); // W + final Matrix V = svd.getV(); // VT Right Matrix + + double temp; + + // invert S + for ( int j = 0; j < S.getRowDimension(); ++j ) + { + temp = S.get( j, j ); + + if ( temp < threshold ) // this is an inaccurate inverting of the matrix + temp = 1.0 / threshold; + else + temp = 1.0 / temp; + + S.set( j, j, temp ); + } + + // transponse U + U = U.transpose(); + + // + // compute result + // + return ((V.times(S)).times(U)); + } +} diff --git a/src/main/java/ransacPoly/MixedPolynomial.java b/src/main/java/ransacPoly/MixedPolynomial.java new file mode 100644 index 0000000..2d65073 --- /dev/null +++ b/src/main/java/ransacPoly/MixedPolynomial.java @@ -0,0 +1,79 @@ +package ransacPoly; + +import java.util.ArrayList; +import java.util.Collection; + + +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +public class MixedPolynomial < +A extends AbstractFunction< A > & Polynomial< A, Point >, +B extends AbstractFunction< B > & Polynomial< B, Point > > +extends MixedPolynomialFunction< A, B, MixedPolynomial< A, B > > +implements Polynomial< MixedPolynomial< A, B >, Point > +{ +private static final long serialVersionUID = 6929934343495578299L; + +public Polynomial< ?, Point > interpolatedFunction; + +public MixedPolynomial( final A a, final B b, double lambda ) +{ +super( a, b, lambda ); + +// use the higher-order polynom to fit a function to interpolated points +if ( a.degree() > b.degree() ) + interpolatedFunction = a.copy(); +else + interpolatedFunction = b.copy(); +} + +@Override +protected void interpolate( final Collection< Point > points ) throws NotEnoughDataPointsException, IllDefinedDataPointsException +{ +final ArrayList< Point > interpolatedPoints = new ArrayList< Point >(); + +for ( final Point p : points ) +{ + final double x = p.getW()[ 0 ]; + + final double y1 = a.predict( x ); + final double y2 = b.predict( x ); + + interpolatedPoints.add( new Point( new double[]{ x, l1 * y1 + lambda * y2 } ) ); +} + +interpolatedFunction.fitFunction( interpolatedPoints ); +} + +@Override +public double predict( final double x ) { return interpolatedFunction.predict( x ); } + +@Override +public double predictFirstderivative( final double x ) { return interpolatedFunction.predictFirstderivative( x ); } + +@Override +public double predictSecondderivative( final double x ) { return interpolatedFunction.predictSecondderivative( x ); } +@Override +public double distanceTo( final Point point ) { return interpolatedFunction.distanceTo( point ); } + +@Override +public int degree() { return interpolatedFunction.degree(); } + +@Override +public double getCoefficient( final int j ) { return interpolatedFunction.getCoefficient( j ); } + +@Override +public MixedPolynomial< A, B > copy() +{ +final MixedPolynomial< A, B > copy = new MixedPolynomial< A, B >( a.copy(), b.copy(), lambda ); + +// it must be and AbstractFunction since it is A or B +copy.interpolatedFunction = interpolatedFunction.copy(); + +copy.setCost( getCost() ); + +return copy; +} +} diff --git a/src/main/java/ransacPoly/MixedPolynomialFunction.java b/src/main/java/ransacPoly/MixedPolynomialFunction.java new file mode 100644 index 0000000..6350217 --- /dev/null +++ b/src/main/java/ransacPoly/MixedPolynomialFunction.java @@ -0,0 +1,91 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package ransacPoly; + +import java.util.Collection; + +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; + +/** + * Interpolation of two AbstractFunctions, inspired by Interpolated Models by Stephan Saalfeld + * + * @author Stephan Preibisch + */ +public abstract class MixedPolynomialFunction< A extends AbstractFunction< A >, B extends AbstractFunction< B >, M extends MixedPolynomialFunction< A, B, M > > extends AbstractFunction2D< M > +{ + private static final long serialVersionUID = -8524786898599474286L; + + final protected A a; + final protected B b; + protected double lambda; + protected double l1; + + public MixedPolynomialFunction( final A a, final B b, final double lambda ) + { + this.a = a; + this.b = b; + + this.lambda = lambda; + l1 = 1.0 - lambda; + } + + public A getA() { return a; } + public B getB() { return b; } + public double getLambda() { return lambda; } + + public void setLambda( final double lambda ) + { + this.lambda = lambda; + this.l1 = 1.0f - lambda; + } + + + + @Override + public int getMinNumPoints() { return Math.max( a.getMinNumPoints(), b.getMinNumPoints() ); } + + @Override + public void set( final M m ) + { + a.set( m.a ); + b.set( m.b ); + lambda = m.lambda; + l1 = m.l1; + cost = m.cost; + } + + @Override + public void fitFunction( final Collection< Point > points ) + throws NotEnoughDataPointsException, IllDefinedDataPointsException + { + a.fitFunction( points ); + b.fitFunction( points ); + + interpolate( points ); + } + + + protected abstract void interpolate( final Collection< Point > points ) + throws NotEnoughDataPointsException, IllDefinedDataPointsException; +} diff --git a/src/main/java/ransacPoly/NewtonRaphson.java b/src/main/java/ransacPoly/NewtonRaphson.java new file mode 100644 index 0000000..3ad83b8 --- /dev/null +++ b/src/main/java/ransacPoly/NewtonRaphson.java @@ -0,0 +1,159 @@ +package ransacPoly; + + +import java.util.Random; + +/** + * Newton Raphson routine to get the shortest distance of a point from a + * curve. + * + * @author Varun Kapoor, Stephan Preibisch + * + */ +public class NewtonRaphson +{ + public static int MAX_ITER = 1000000; + public static double MIN_CHANGE = 1.0E-3; + + public double xc, xcNew, polyfunc, polyfuncdiff, delpolyfuncdiff, dmin, dMinDiff, secdelpolyfuncdiff, dminsecdiff; + final int degree; + final double[] powCache; + double damp = 1; + final Random rndx; + + public NewtonRaphson( final Random rndx, final int degree ) + { + // Initial guesses for Newton Raphson + this.rndx = rndx; + this.xc = rndx.nextFloat(); + this.xcNew = rndx.nextFloat() * rndx.nextFloat(); + this.degree = degree; + this.powCache = new double[ degree + 4 ]; + } + + public double run( final double x, final double y, final double[] coeff ) + { + updatePowCache( xc ); + computeFunctions( coeff ); + + int iteration = 0; + + do + { + xc = xcNew; + dmin = (polyfunc - y) * polyfuncdiff + (xc - x); + dMinDiff = polyfuncdiff * polyfuncdiff + (polyfunc - y)* delpolyfuncdiff + 1; + dminsecdiff = (polyfunc - y)*secdelpolyfuncdiff + delpolyfuncdiff * polyfuncdiff + 2 * polyfuncdiff * delpolyfuncdiff ; + + // Compute the first iteration of the new point + ++iteration; + + if ( iteration % 1000 == 0 ) + { + damp = rndx.nextDouble(); + iterate(); + damp = 1; + } + else + { + iterate(); + } + + if ( Double.isNaN( xcNew ) ) + xcNew = xc; + + // Compute the functions and the required derivates at the new point + delpolyfuncdiff = 0; + polyfunc = 0; + polyfuncdiff = 0; + secdelpolyfuncdiff = 0; + + // precompute the powers + updatePowCache( xcNew ); + + computeFunctions( coeff ); + if ( iteration >= MAX_ITER ) + break; + } + while ( Math.abs( ( xcNew - xc ) ) > MIN_CHANGE ); + + // After the solution is found compute the y co-oordinate of the point + // on the curve + polyfunc = 0; + for (int j = degree; j >= 0; j--) + polyfunc += coeff[j] * Math.pow(xc, j); + + // Get the distance of (x1, y1) point from the curve and return the + // value + return distance( x, y, xc, polyfunc ); + } + + protected void updatePowCache( final double xc ) + { + for ( int j = degree; j >= -3; j-- ) + if ( j >= 0 ) + powCache[ j + 3 ] = pow( xc, j ); + else + powCache[ j + 3 ] = Math.pow( xc, j ); + } + + protected void computeFunctions( final double[] coeff ) + { + for ( int j = degree; j >= 0; j-- ) + { + double c = coeff[ j ]; + polyfunc += c * powCache[ j + 3 ]; + + c *= j; + polyfuncdiff += c * powCache[ j + 2 ]; + + c *= ( j - 1 ); + delpolyfuncdiff += c * powCache[ j + 1 ]; + + c *= ( j - 2 ); + secdelpolyfuncdiff += c * powCache[ j ]; + } + } + + protected void iterate() + { + this.xcNew = iterate( xc, dmin, dMinDiff, dminsecdiff ); + + } + + public double iterate( final double oldpoint, final double function, final double functionderiv, final double functionsecderiv ) + { + + return oldpoint - (function / functionderiv) * (1 + damp * 0.5 * function * functionsecderiv / (functionderiv * functionderiv) ); + } + + public static double distance( final double minX, final double minY, final double maxX, final double maxY ) + { + double tmp; + double distance = 0; + + tmp = maxX - minX; + distance += tmp*tmp; + + tmp = maxY - minY; + distance += tmp*tmp; + return Math.sqrt( distance ); + } + + public static double pow( final double a, final int b ) + { + if ( b == 0 ) + return 1; + else if ( b == 1 ) + return a; + else + { + double result = a; + + for ( int i = 1; i < b; i++ ) + result *= a; + + return result; + } + } +} \ No newline at end of file diff --git a/src/main/java/ransacPoly/Polynomial.java b/src/main/java/ransacPoly/Polynomial.java new file mode 100644 index 0000000..f3ae49f --- /dev/null +++ b/src/main/java/ransacPoly/Polynomial.java @@ -0,0 +1,42 @@ +package ransacPoly; + + +import mpicbg.models.Point; +import ransac.PointFunctionMatch.Function; + +/** + * @author Varun Kapoor and Stephan Preibisch + */ +public interface Polynomial < F extends Polynomial< F, P >, P extends Point > extends Function< F, P > +{ + /** + * @return The degree of the polynomial + */ + public int degree(); + + /** + * @param j - the j'th coefficient of the polynomial ( c_0 + c_1*x + c_2*x*x + ... + c_N*x^N) + * @return + */ + public double getCoefficient( final int j ); + + /** + * @param x + * @return - the corresponding y value + */ + public double predict( final double x ); + + /** + * @param x + * @return - the corresponding y derivative value + */ + public double predictFirstderivative( final double x ); + + /** + * @param x + * @return - the corresponding y Second derivative value + */ + public double predictSecondderivative( final double x ); + +} + diff --git a/src/main/java/ransacPoly/QuadraticFunction.java b/src/main/java/ransacPoly/QuadraticFunction.java new file mode 100644 index 0000000..cd24f2d --- /dev/null +++ b/src/main/java/ransacPoly/QuadraticFunction.java @@ -0,0 +1,255 @@ +package ransacPoly; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Random; + +import mpicbg.models.IllDefinedDataPointsException; +import mpicbg.models.NoninvertibleModelException; +import mpicbg.models.NotEnoughDataPointsException; +import mpicbg.models.Point; +import ransac.PointFunctionMatch.PointFunctionMatch; + +/** + * @author Stephan Preibisch and Varun Kapoor + */ +public class QuadraticFunction extends AbstractFunction2D< QuadraticFunction > implements Polynomial< QuadraticFunction, Point > +{ + private static final long serialVersionUID = 5289346951323596267L; + + // For initial guesses for Newton Raphson + final Random rndx = new Random( 43583458 ); + + final int minNumPoints = 3; + double a, b, c; // a*x*x + b*x + c + + public QuadraticFunction() { this( 0, 0, 0 ); } + public QuadraticFunction( final double a, final double b, final double c ) + { + this.a = a; + this.b = b; + this.c = c; + } + + public double getA(){ return a; } + public double getB(){ return b; } + public double getC(){ return c; } + + @Override + public int degree() { return 2; } + + @Override + public double getCoefficient( final int j ) + { + if ( j == 0 ) + return c; + else if ( j == 1 ) + return b; + else if ( j == 2 ) + return a; + else + return 0; + } + + @Override + public double predict( final double x ) { return a*x*x + b*x + c; } + + @Override + public double predictFirstderivative( final double x ) { return 2 * a * x + b; } + + @Override + public double predictSecondderivative( final double x ) { return 2 * a; } + + @Override + public int getMinNumPoints() { return minNumPoints; } + + @Override + public void fitFunction( final Collection< Point > points ) throws NotEnoughDataPointsException, IllDefinedDataPointsException + { + final int numPoints = points.size(); + + if ( numPoints < minNumPoints ) + throw new NotEnoughDataPointsException( "Not enough points, at least " + minNumPoints + " are necessary and available are: " + numPoints ); + + // compute matrices + final double[] delta = new double[ 9 ]; + final double[] tetha = new double[ 3 ]; + + for ( final Point p : points ) + { + final double x = p.getW()[ 0 ]; + final double y = p.getW()[ 1 ]; + + final double xx = x*x; + final double xxx = xx*x; + + delta[ 0 ] += xx * xx; + delta[ 1 ] += xxx; + delta[ 2 ] += xx; + + delta[ 3 ] += xxx; + delta[ 4 ] += xx; + delta[ 5 ] += x; + + delta[ 6 ] += xx; + delta[ 7 ] += x; + delta[ 8 ] += 1; + + tetha[ 0 ] += xx * y; + tetha[ 1 ] += x * y; + tetha[ 2 ] += y; + } + + // invert matrix + try + { + MatrixFunctions.invert3x3( delta ); + } + catch ( final NoninvertibleModelException e ) + { + this.a = this.b = this.c = 0; + throw new IllDefinedDataPointsException( "Cannot not invert Delta-Matrix, failed to fit function" ); + } + + this.a = delta[ 0 ] * tetha[ 0 ] + delta[ 1 ] * tetha[ 1 ] + delta[ 2 ] * tetha[ 2 ]; + this.b = delta[ 3 ] * tetha[ 0 ] + delta[ 4 ] * tetha[ 1 ] + delta[ 5 ] * tetha[ 2 ]; + this.c = delta[ 6 ] * tetha[ 0 ] + delta[ 7 ] * tetha[ 1 ] + delta[ 8 ] * tetha[ 2 ]; + } + + @Override + public double distanceTo( final Point point ) + { + final double x1 = point.getW()[0]; + final double y1 = point.getW()[1]; + + // return new NewtonRaphson( rndx, 2 ).run( x1, y1, new double[]{ c, b, a } ); + + final double a3 = 2 * this.a * this.a ; + final double a2 = 3 * this.b * this.a / a3 ; + final double a1 = (2 * this.c * this.a - 2 * this.a * y1 + 1 + this.b * this.b ) / a3; + final double a0 = (this.c * this.b - y1 * this.b - x1) / a3 ; + + final double p = (3 * a1 - a2 * a2) / 3; + final double q = (-9 * a1 * a2 + 27 * a0 + 2 * a2 * a2 * a2) / 27 ; + + final double tmp1 = Math.sqrt( -p / 3 ); + final double tmp2 = (q * q / 4 + p * p * p / 27); + + final double xc1, xc2, xc3; + + + if ( tmp2 > 0 ) + { + final double aBar = Math.cbrt(-q/2 + Math.sqrt( q * q / 4 + p * p * p / 27)); + final double bBar = Math.cbrt(-q/2 - Math.sqrt( q * q / 4 + p * p * p / 27)); + + xc1 = xc2 = xc3 = aBar + bBar - a2 / 3; + } + else if ( tmp2 == 0 ) + { + if ( q > 0 ) + { + xc1 = -2 * tmp1; + xc2 = tmp1; + xc3 = xc2; + } + else if ( q < 0 ) + { + xc1 = 2 * tmp1; + xc2 = -tmp1; + xc3 = xc2; + } + else + { + xc1 = 0; + xc2 = 0; + xc3 = 0; + } + } + else + { + final double phi; + + if ( q >= 0 ) + phi = Math.acos(-Math.sqrt( q * q * 0.25 / (-p * p * p / 27))); + else + phi = Math.acos(Math.sqrt( q * q * 0.25 / (-p * p * p / 27))); + + xc1 = 2 * tmp1 * Math.cos(phi / 3) - a2 / 3; + xc2 = 2 * tmp1 * Math.cos((phi + 2 * Math.PI) / 3) - a2 / 3; + xc3 = 2 * tmp1 * Math.cos((phi + 4 * Math.PI) / 3) - a2 / 3; + } + + final double returndistA = NewtonRaphson.distance( x1, y1, xc1, c + b*xc1 + a*xc1*xc1 ); + final double returndistB = NewtonRaphson.distance( x1, y1, xc2, c + b*xc2 + a*xc2*xc2 ); + final double returndistC = NewtonRaphson.distance( x1, y1, xc3, c + b*xc3 + a*xc3*xc3 ); + + return Math.min( returndistA, Math.min( returndistB, returndistC ) ); + + } + + @Override + public void set( final QuadraticFunction m ) + { + this.a = m.getA(); + this.b = m.getB(); + this.c = m.getC(); + this.setCost( m.getCost() ); + } + + @Override + public QuadraticFunction copy() + { + final QuadraticFunction c = new QuadraticFunction(); + + c.a = getA(); + c.b = getB(); + c.c = getC(); + c.setCost( getCost() ); + + return c; + } + + @Override + public String toString() { return "f(x)=" + getA() + "*x*x + " + getB() + "*x + " + getC(); } + + @SuppressWarnings("deprecation") + public static void main( String[] args ) throws NotEnoughDataPointsException, IllDefinedDataPointsException + { + final ArrayList points = new ArrayList(); + + points.add(new Point(new double[] { 1f, -3.95132f })); + points.add(new Point(new double[] { 2f, 6.51205f })); + points.add(new Point(new double[] { 3f, 18.03612f })); + points.add(new Point(new double[] { 4f, 28.65245f })); + points.add(new Point(new double[] { 5f, 42.05581f })); + points.add(new Point(new double[] { 6f, 54.01327f })); + points.add(new Point(new double[] { 7f, 64.58747f })); + points.add(new Point(new double[] { 8f, 76.48754f })); + points.add(new Point(new double[] { 9f, 89.00033f })); + + final ArrayList candidates = new ArrayList(); + final ArrayList inliersPoly = new ArrayList(); + long startTime = System.nanoTime(); + for (final Point p : points) + candidates.add(new PointFunctionMatch(p)); + + final int degree = 2; + // Using the polynomial model to do the fitting + final QuadraticFunction regression = new QuadraticFunction(); + + regression.ransac( candidates, inliersPoly, 100, 0.1, 0.5 ); + + System.out.println("inliers: " + inliersPoly.size()); + for ( final PointFunctionMatch p : inliersPoly ) + System.out.println( regression.distanceTo( p.getP1() ) ); + regression.fit(inliersPoly); + System.out.println(" y = " ); + for (int i = degree; i >= 0; --i) + System.out.println(regression.getCoefficient(i) + "*" + "x" + "^" + i ); + long totalTime = (System.nanoTime()- startTime)/1000; + System.out.println("Time: " + totalTime); + + } +} diff --git a/src/main/java/ransacPoly/RansacFunction.java b/src/main/java/ransacPoly/RansacFunction.java new file mode 100644 index 0000000..cd7d072 --- /dev/null +++ b/src/main/java/ransacPoly/RansacFunction.java @@ -0,0 +1,87 @@ +package ransacPoly; + +import java.util.ArrayList; + + +import ransac.PointFunctionMatch.PointFunctionMatch; + +public class RansacFunction { + + + public final HigherOrderPolynomialFunction function; + + public final MixedPolynomialFunction > mixedfunction; + + public final LinearFunction backup; + + public final ArrayList inliers; + + public final ArrayList candidates; + + /** + * + * A ransac function output containing a quadratic function + * @param function + * @param linearfunction + * @param inliers + */ + public RansacFunction(final HigherOrderPolynomialFunction function, ArrayList inliers, ArrayList candidates ) { + + + this.function = function; + + this.backup = null; + + this.mixedfunction = null; + + this.inliers = inliers; + + this.candidates = candidates; + + } + + /** + * + * A ransac function output containing a linear function + * @param function + * @param linearfunction + * @param inliers + */ + public RansacFunction(final LinearFunction backup, ArrayList inliers, ArrayList candidates ) { + + + this.function = null; + + this.backup = backup; + + this.mixedfunction = null; + + this.inliers = inliers; + + this.candidates = candidates; + + } + /** + * + * A ransac function output containing a mix between a linear and a quadratic function + * @param mixedfunction + * @param inliers + * @param candidates + */ + + public RansacFunction(final MixedPolynomialFunction > mixedfunction, ArrayList inliers, ArrayList candidates ) { + + + this.function = null; + + this.backup = null; + + this.mixedfunction = mixedfunction; + + this.inliers = inliers; + + this.candidates = candidates; + + } + +} diff --git a/src/main/java/ransacPoly/RegressionFunction.java b/src/main/java/ransacPoly/RegressionFunction.java new file mode 100644 index 0000000..3f92ced --- /dev/null +++ b/src/main/java/ransacPoly/RegressionFunction.java @@ -0,0 +1,217 @@ +package ransacPoly; + +import java.util.ArrayList; + +import ransac.PointFunctionMatch.PointFunctionMatch; +import varun_algorithm_ransac_Ransac.Circle; + +public class RegressionFunction { + + + + public final Threepointfit regression; + + public final Circle ellipse; + + public final HigherOrderPolynomialFunction higherorder; + + public final LinearFunction back; + + public final MixedPolynomialFunction> mixedfunction; + + public final ArrayList Curvaturepoints; + + public final ArrayList inliers; + + public final ArrayList candidates; + + public final double[] SinglePoint; + + /** + * + * Constructor choice for regression + * + * @param regression + * @param Curvaturepoints + * @param perimeter + */ + public RegressionFunction(final Threepointfit regression, final ArrayList Curvaturepoints) { + + this.regression = regression; + + this.higherorder = null; + + this.back = null; + + this.mixedfunction = null; + + this.Curvaturepoints = Curvaturepoints; + + this.inliers = null; + + this.candidates = null; + + this.ellipse = null; + + this.SinglePoint = null; + + } + + public RegressionFunction(final Circle ellipse, final double[] SinglePoint) { + + this.regression = null; + + this.higherorder = null; + + this.back = null; + + this.mixedfunction = null; + + this.Curvaturepoints = null; + + this.inliers = null; + + this.candidates = null; + + this.ellipse = ellipse; + + this.SinglePoint = SinglePoint; + + } + + + public RegressionFunction(final ArrayList Curvaturepoints) { + + this.regression = null; + + this.higherorder = null; + + this.back = null; + + this.mixedfunction = null; + + this.Curvaturepoints = Curvaturepoints; + + this.inliers = null; + + this.candidates = null; + + this.ellipse = null; + + this.SinglePoint = null; + + } + + public RegressionFunction(final Circle ellipse, final ArrayList Curvaturepoints) { + + this.regression = null; + + this.higherorder = null; + + this.back = null; + + this.mixedfunction = null; + + this.Curvaturepoints = Curvaturepoints; + + this.inliers = null; + + this.candidates = null; + + this.ellipse = ellipse; + + this.SinglePoint = null; + + } + + /** + * + * Constructor choice for Ransac + * + * @param higherorder + * @param Curvaturepoints + * @param perimeter + */ + public RegressionFunction(final HigherOrderPolynomialFunction higherorder, + final ArrayList Curvaturepoints, final ArrayList inliers, + final ArrayList candidates) { + + this.regression = null; + + this.back = null; + + this.higherorder = higherorder; + + this.mixedfunction = null; + + this.Curvaturepoints = Curvaturepoints; + + this.inliers = inliers; + + this.candidates = candidates; + + this.ellipse = null; + + this.SinglePoint = null; + + } + + /** + * + * Constructor choice for Ransac + * + * @param higherorder + * @param Curvaturepoints + * @param perimeter + */ + public RegressionFunction(final LinearFunction back, final ArrayList Curvaturepoints, + final ArrayList inliers, final ArrayList candidates) { + + this.regression = null; + + this.back = back; + + this.higherorder = null; + + this.mixedfunction = null; + + this.Curvaturepoints = Curvaturepoints; + + this.inliers = inliers; + + this.candidates = candidates; + + this.ellipse = null; + + this.SinglePoint = null; + + } + + public RegressionFunction( + final MixedPolynomialFunction> mixedfunction, + final ArrayList Curvaturepoints, final ArrayList inliers, + final ArrayList candidates) { + + this.regression = null; + + this.mixedfunction = mixedfunction; + + this.higherorder = null; + + this.back = null; + + this.Curvaturepoints = Curvaturepoints; + + this.inliers = inliers; + + this.candidates = candidates; + + this.ellipse = null; + + this.SinglePoint = null; + + } + + + +} diff --git a/src/main/java/ransacPoly/Sort.java b/src/main/java/ransacPoly/Sort.java new file mode 100644 index 0000000..1a468ff --- /dev/null +++ b/src/main/java/ransacPoly/Sort.java @@ -0,0 +1,32 @@ +package ransacPoly; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +import net.imglib2.util.Pair; +import ransac.PointFunctionMatch.PointFunctionMatch; + +public class Sort { + + + + public static void sort(final Pair> segment) { + Collections.sort(segment.getB(), new Comparator() { + + @Override + public int compare(final PointFunctionMatch o1, final PointFunctionMatch o2) { + final double t1 = o1.getP1().getL()[0]; + final double t2 = o2.getP1().getL()[0]; + + if (t1 < t2) + return -1; + else if (t1 == t2) + return 0; + else + return 1; + } + }); + } + +} diff --git a/src/main/java/ransacPoly/Threepointfit.java b/src/main/java/ransacPoly/Threepointfit.java new file mode 100644 index 0000000..4bc80eb --- /dev/null +++ b/src/main/java/ransacPoly/Threepointfit.java @@ -0,0 +1,103 @@ +package ransacPoly; + +import Jama.Matrix; +import Jama.QRDecomposition; + +public class Threepointfit { + + + private final int degree; + private final int Npoints; + private final Matrix Coefficients; + private double SSE; + private double SST; + + public Threepointfit(double[] x, double[] y, int degree){ + + this.degree = degree; + Npoints = x.length; + + // Vandermonde matrix + double[][] vandermonde = new double[Npoints][degree+1]; + for (int i = 0; i < Npoints; i++) { + for (int j = 0; j <= degree; j++) { + vandermonde[i][j] = Math.pow(x[i], j); + } + } + Matrix X = new Matrix(vandermonde); + + // create matrix from vector + Matrix Y = new Matrix(y, Npoints); + + // find least squares solution + QRDecomposition qr = new QRDecomposition(X); + Coefficients = qr.solve(Y); + + + // mean of y[] values + double sum = 0.0; + for (int i = 0; i < Npoints; i++) + sum += y[i]; + double mean = sum / Npoints; + + + // total variation to be accounted for + for (int i = 0; i < Npoints; i++) { + double dev = y[i] - mean; + SST += dev*dev; + } + + // variation not accounted for + Matrix residuals = X.times(Coefficients).minus(Y); + SSE = residuals.norm2() * residuals.norm2(); + + } + public double GetCoefficients(int j) { + return Coefficients.get(j, 0); + } + + public int degree() { + return degree; + } + + public double R2() { + return 1.0 - SSE/SST; + } + + // Horner's method to get y values correspoing to x + public double predict(double x) { + // horner's method + double y = 0.0; + for (int j = degree; j >= 0; j--) + y = GetCoefficients(j) + (x * y); + return y; + } + + // Horner's method to get y values correspoing to x + public double predictderivative(double x) { + // horner's method for derivative of a function + double y = 0; + for (int j = 0; j <= degree; j++) { + if(j >= 1) + y += j * GetCoefficients(j) * Math.pow(x, j - 1); + } + + return y; + } + + // Horner's method to get y values correspoing to x + public double predictsecderivative(double x) { + // horner's method for derivative of a function + double y = 0.0; + for (int j = 0; j <= degree; j++) { + if(j >= 2) + y += j * ( j - 1) * GetCoefficients(j) * Math.pow(x, j - 2); + } + return y; + } + + + + + +} diff --git a/src/main/java/regionRemoval/RemoveTiny.java b/src/main/java/regionRemoval/RemoveTiny.java new file mode 100644 index 0000000..f9ceb8e --- /dev/null +++ b/src/main/java/regionRemoval/RemoveTiny.java @@ -0,0 +1,71 @@ +package regionRemoval; + +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.neighborhood.HyperSphereShape; +import net.imglib2.algorithm.neighborhood.Neighborhood; +import net.imglib2.algorithm.neighborhood.RectangleShape; +import net.imglib2.algorithm.region.hypersphere.HyperSphere; +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.real.FloatType; +import net.imglib2.util.Intervals; +import net.imglib2.view.Views; + +public class RemoveTiny { + + + public static RandomAccessibleInterval removeNbyNregion(RandomAccessibleInterval current, int N) { + + + RandomAccessibleInterval cleancurrent = new ArrayImgFactory().create(current, new BitType()); + RandomAccess ranac = cleancurrent.randomAccess(); + Interval interval = Intervals.expand( current, -N ); + + // create a view on the source with this interval + current = Views.interval( current, interval ); + + final Cursor< BitType > center = Views.iterable( current ).cursor(); + + final HyperSphereShape shape = new HyperSphereShape( N ); + + int span = 2 * N + 1; + int maxblackpixel = span * span / 2; + + + for ( final Neighborhood< BitType > localNeighborhood : shape.neighborhoods( current ) ) { + + int blackcount = 0; + + final BitType centerValue = center.next(); + + ranac.setPosition(center); + for(final BitType value : localNeighborhood) + { + + if (centerValue.compareTo(value) > 0) + blackcount++; + + } + + if (blackcount >= maxblackpixel) + ranac.get().setZero(); + else + ranac.get().set(centerValue); + + + } + + + + + + return cleancurrent; + + } + +} diff --git a/src/main/java/removeSpotGUI/CovistoRemovePanel.java b/src/main/java/removeSpotGUI/CovistoRemovePanel.java new file mode 100644 index 0000000..aa16658 --- /dev/null +++ b/src/main/java/removeSpotGUI/CovistoRemovePanel.java @@ -0,0 +1,50 @@ +package removeSpotGUI; + +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.TextField; + +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoRemovePanel { + + + + + public static JPanel RemovePanel = new JPanel(); + public static Label removeText = new Label("Distance Threshold = ", Label.CENTER); + + public static TextField inputFieldSpot; + public static double distthreshold = 10; + + public static JPanel RemoveselectPanel() { + + layoutManager.Setlayout.LayoutSetter(RemovePanel); + + inputFieldSpot = new TextField(5); + inputFieldSpot.setText(Double.toString(distthreshold)); + + + Border removeborder = new CompoundBorder(new TitledBorder("Spurios Spot removal"), new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + RemovePanel.add(removeText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + RemovePanel.add(inputFieldSpot, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + + RemovePanel.setBorder(removeborder); + + return RemovePanel; + + } + + + +} diff --git a/src/main/java/saveTracks/CovistoSavePanel.java b/src/main/java/saveTracks/CovistoSavePanel.java new file mode 100644 index 0000000..7ba8899 --- /dev/null +++ b/src/main/java/saveTracks/CovistoSavePanel.java @@ -0,0 +1,67 @@ +package saveTracks; + +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoSavePanel { + + public static JPanel SavePanel = new JPanel(); + + public static JButton Savebutton = new JButton("Save Track"); + public static JButton SaveAllbutton = new JButton("Save All Tracks"); + + public static JLabel inputLabel = new JLabel("Filename:"); + public static TextField inputField; + public static final JButton ChooseDirectory = new JButton("Choose Directory to save results in"); + public static TextField inputtrackField; + + public static JLabel inputtrackLabel = new JLabel("Enter trackID to save"); + + public static JPanel SavePanel() { + inputtrackField = new TextField(5); + inputField = new TextField(5); + layoutManager.Setlayout.LayoutSetter(SavePanel); + Border origborder = new CompoundBorder(new TitledBorder("Enter filename for results files"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + + + SavePanel.add(inputLabel, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SavePanel.add(inputField, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SavePanel.add(inputtrackLabel, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SavePanel.add(inputtrackField, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SavePanel.add(ChooseDirectory, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.NORTH, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + SavePanel.add(Savebutton, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, GridBagConstraints.NORTH, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SavePanel.add(SaveAllbutton, new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.NORTH, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SavePanel.setBorder(origborder); + + + + + return SavePanel; + + } + +} diff --git a/src/main/java/scrollbar/Utility.java b/src/main/java/scrollbar/Utility.java new file mode 100644 index 0000000..6c52301 --- /dev/null +++ b/src/main/java/scrollbar/Utility.java @@ -0,0 +1,95 @@ +package scrollbar; + +import javax.swing.JProgressBar; + +public class Utility { + + + + public static float computeValueFromScrollbarPosition(final int scrollbarPosition, final float min, + final float max, final int scrollbarSize) { + return min + (scrollbarPosition / (float) scrollbarSize) * (max - min); + } + + public static float computeIntValueFromScrollbarPosition(final int scrollbarPosition, final float min, + final float max, final int scrollbarSize) { + return min + (scrollbarPosition / (max)) * (max - min); + } + + public static int computeScrollbarPositionFromValue(final float sigma, final float min, final float max, + final int scrollbarSize) { + return Math.round(((sigma - min) / (max - min)) * scrollbarSize); + } + + public static int computeIntScrollbarPositionFromValue(final float thirdDimensionslider, final float min, + final float max, final int scrollbarSize) { + return Math.round(((thirdDimensionslider - min) / (max - min)) * max); + } + + public static float computeSigma2(final float sigma1, final int sensitivity) { + final float k = (float) computeK(sensitivity); + final float[] sigma = computeSigma(k, sigma1); + + return sigma[1]; + } + public static double computeK( final float stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); } + public static double computeK( final int stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); } + public static float computeKWeight( final float k ) { return 1.0f / (k - 1.0f); } + public static float[] computeSigma( final float k, final float initialSigma ) + { + final float[] sigma = new float[ 2 ]; + + sigma[ 0 ] = initialSigma; + sigma[ 1 ] = sigma[ 0 ] * k; + + return sigma; + } + + public static void SetProgressBar(JProgressBar jpb, double percent) { + + jpb.setValue((int) Math.round(percent)); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString("Finding MT ends"); + + } + + + public static void SetProgressBar(JProgressBar jpb, double percent, String message) { + + jpb.setValue((int) Math.round(percent)); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString(message); + + } + + public static void SetProgressBar(JProgressBar jpb) { + jpb.setValue(0); + jpb.setIndeterminate(true); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString("Pre-processing Image"); + + } + + public static void SetProgressBarTime(JProgressBar jpb, double percent, int framenumber, int thirdDimsize) { + + jpb.setValue((int) percent); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString("Time point = " + framenumber + "/" + thirdDimsize); + + } + + public static void SetProgressBarTime(JProgressBar jpb, double percent, int framenumber, int thirdDimsize, + String message) { + + jpb.setValue((int) percent); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString(message + "= " + framenumber + "/" + thirdDimsize); + + } + +} diff --git a/src/main/java/snakeGUI/CovistoSnakePanel.java b/src/main/java/snakeGUI/CovistoSnakePanel.java new file mode 100644 index 0000000..da569ff --- /dev/null +++ b/src/main/java/snakeGUI/CovistoSnakePanel.java @@ -0,0 +1,100 @@ +package snakeGUI; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.Label; +import java.awt.TextField; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoSnakePanel { + + public static JPanel SnakePanel = new JPanel(); + + public static Label Snakelabel, gradientlabel, distlabel; + public static TextField Snakeiter, gradientthresh, maxdist; + + public static int snakeiterations = 200; + public static int displaysnake = snakeiterations / 2; + public static int Gradthresh = 1; + public static int DistMax = 100; + public static double Displacement_min = 0.5; + public static double Displacement_max = 5.0; + public static double Threshold_dist_positive = 10; + public static double Threshold_dist_negative = 10; + public static double Inv_alpha_min = 0.2; + public static double Inv_alpha_max = 10.0; + public static double Mul_factor = 0.99; + // maximum displacement + public static double force = 10; + // regulari1ation factors, min and max + public static double reg = 5; + public static double regmin, regmax; + public static final JButton Singlesnake = new JButton("Apply snakes to CurrentView"); + public static JButton AllSnake = new JButton("Snake in 3D/4D"); + + public static final Checkbox advanced = new Checkbox("Display advanced Snake parameters"); + + + + public static JPanel SnakePanel(int ndims) { + + regmin = reg / 2.0; + regmax = reg; + + + layoutManager.Setlayout.LayoutSetter(SnakePanel); + Border snakeborder = new CompoundBorder(new TitledBorder("Active Contour refinement"), + new EmptyBorder(layoutManager.Setlayout.c.insets)); + Snakelabel = new Label("Enter number of max snake iterations"); + gradientlabel = new Label("Enter gradient threshold"); + distlabel = new Label("Enter max distance to search for edges"); + + Snakeiter = new TextField(1); + gradientthresh = new TextField(1); + maxdist = new TextField(1); + + Snakeiter.setText(Integer.toString(snakeiterations)); + gradientthresh.setText(Integer.toString(Gradthresh)); + maxdist.setText(Integer.toString(DistMax)); + SnakePanel.add(Snakelabel, new GridBagConstraints(0, 0, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + SnakePanel.add(Snakeiter, new GridBagConstraints(0, 1, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SnakePanel.add(gradientlabel, new GridBagConstraints(0, 2, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + SnakePanel.add(gradientthresh, new GridBagConstraints(0, 3, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SnakePanel.add(distlabel, new GridBagConstraints(3, 0, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + SnakePanel.add(maxdist, new GridBagConstraints(3, 1, 3, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SnakePanel.add(Singlesnake, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + if (ndims > 2) + SnakePanel.add(AllSnake, new GridBagConstraints(5, 2, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + + SnakePanel.add(advanced, new GridBagConstraints(5, 3, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + SnakePanel.setBorder(snakeborder); + + + return SnakePanel; + + } + + +} diff --git a/src/main/java/strategies/Abstract3x3NeighbourhoodThinning.java b/src/main/java/strategies/Abstract3x3NeighbourhoodThinning.java new file mode 100644 index 0000000..035864c --- /dev/null +++ b/src/main/java/strategies/Abstract3x3NeighbourhoodThinning.java @@ -0,0 +1,167 @@ +package strategies; + +/* + * ------------------------------------------------------------------------ + * + * Copyright (C) 2003 - 2013 + * University of Konstanz, Germany and + * KNIME GmbH, Konstanz, Germany + * Website: http://www.knime.org; Email: contact@knime.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * --------------------------------------------------------------------- + * + * Created on 01.12.2013 by Andreas + */ + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.type.logic.BitType; + +/** + * This abstract class represents an rough framework for a Thinning algorithm operating on a 3x3 neighbourhood by providing + * some often needed functions. + * + * @author Andreas Burger, University of Konstanz + */ +public abstract class Abstract3x3NeighbourhoodThinning implements ThinningStrategy { + + /** + * Boolean value of the foreground. + */ + protected boolean m_foreground; + + /** + * Boolean value of the background. + */ + protected boolean m_background; + + /** + * Create a new abstract thinning strategy. The passed boolean will represent the foreground-value of the image. + * + * @param foreground Value determining the boolean value of foreground pixels. + */ + protected Abstract3x3NeighbourhoodThinning(final boolean foreground) + { + m_foreground = foreground; + m_background = !foreground; + } + /** + * {@inheritDoc} + */ + @Override + public boolean removePixel(final long[] position, final RandomAccessible access) { + // TODO Auto-generated method stub + return false; + } + + /** + * Returns all booleans in a 3x3 neighbourhood of the pixel the RandomAccess points to. + * These booleans are stored in an Array in the following order:
      + * + * 8 1 2
      + * 7 0 3
      + * 6 5 4
      + * + * @param access A RandomAccess pointing to a pixel of the image + * @return A boolean Array holding the values of the neighbourhood in clockwise order. + */ + protected boolean[] getNeighbourhood(final RandomAccess access) { + boolean[] vals = new boolean[9]; + + vals[0] = access.get().get(); + + access.move(-1, 1); + vals[1] = access.get().get(); + + access.move(1, 0); + vals[2] = access.get().get(); + + access.move(1, 1); + vals[3] = access.get().get(); + + access.move(1, 1); + vals[4] = access.get().get(); + + access.move(-1, 0); + vals[5] = access.get().get(); + + access.move(-1, 0); + vals[6] = access.get().get(); + + access.move(-1, 1); + vals[7] = access.get().get(); + + access.move(-1, 1); + vals[8] = access.get().get(); + + return vals; + + } + + /** + * Returns the amount of switches from foreground to background occurring in the circle around vals[1] + * @param vals Boolean Array holding the neighbourhood. + * @return Amount of true-false switches in the neighbourhood. + */ + protected int findPatternSwitches(final boolean[] vals) { + int res = 0; + for (int i = 1; i < vals.length - 1; ++i) { + if (vals[i] == m_foreground && vals[i + 1] == m_background) { + ++res; + } + if (vals[vals.length - 1] == m_foreground && vals[1] == m_background) { + ++res; + } + } + return res; + } + + @Override + public void afterCycle() { + // Intentionally left blank. + } + + + @Override + public int getIterationsPerCycle() { + return 1; + } + +} diff --git a/src/main/java/strategies/GuoHallAlgorithm.java b/src/main/java/strategies/GuoHallAlgorithm.java new file mode 100644 index 0000000..89ce44e --- /dev/null +++ b/src/main/java/strategies/GuoHallAlgorithm.java @@ -0,0 +1,177 @@ +package strategies; + +/* + * ------------------------------------------------------------------------ + * + * Copyright (C) 2003 - 2013 + * University of Konstanz, Germany and + * KNIME GmbH, Konstanz, Germany + * Website: http://www.knime.org; Email: contact@knime.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * --------------------------------------------------------------------- + * + * Created on 01.12.2013 by Andreas + */ + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.type.logic.BitType; + +/** + * Represents the thinning algorithm proposed by Z. Guo and R. W. Hall. + * @author Andreas Burger, University of Konstanz + */ +public class GuoHallAlgorithm extends Abstract3x3NeighbourhoodThinning { + + private int iteration = 0; + + /** + * Create a new Guo-Hall thinning strategy. The passed boolean will represent the foreground-value of the image. + * + * @param foreground Value determining the boolean value of foreground pixels. + */ + public GuoHallAlgorithm(final boolean foreground) + { + super(foreground); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean removePixel(final long[] position, final RandomAccessible accessible) { + + // Setup + RandomAccess access = accessible.randomAccess(); + access.setPosition(position); + + boolean[] vals = getNeighbourhood(access); + + // First we need to count the amount of connected neighbours in the vicinity. + int simpleConnectedNeighbours = 0; + if (vals[1] == m_background && (vals[2] == m_foreground || vals[3] == m_foreground)) { + ++simpleConnectedNeighbours; + } + if (vals[3] == m_background && (vals[4] == m_foreground || vals[5] == m_foreground)) { + ++simpleConnectedNeighbours; + } + if (vals[5] == m_background && (vals[6] == m_foreground || vals[7] == m_foreground)) { + ++simpleConnectedNeighbours; + } + if (vals[7] == m_background && (vals[8] == m_foreground || vals[1] == m_foreground)) { + ++simpleConnectedNeighbours; + } + // First condition: Exactly one simple connected neighbour. + if (simpleConnectedNeighbours != 1) { + return false; + } + + // Check for foreground pixels in each sector. + int sectorsOne = countSectors(vals, -1); + int sectorsTwo = countSectors(vals, 1); + int minSectors = Math.min(sectorsOne, sectorsTwo); + if (!((2 <= minSectors) && (minSectors <= 3))) { + return false; + } + + boolean oddEvenCheck; + + // Depending on the step in the cycle, we need to perform different calculations. + if (iteration % 2 == 1) { + oddEvenCheck = + (vals[1] == m_foreground || vals[2] == m_foreground || vals[4] == m_background) && vals[3] == m_foreground; + } else { + oddEvenCheck = + (vals[5] == m_foreground || vals[6] == m_foreground || vals[8] == m_background) && vals[7] == m_foreground; + } + if (oddEvenCheck) { + return false; + } + return true; + } + + // Count the foreground pixels in each of the four sectors. Depending on the offset, the sector borders are + // rotated by 45 degrees. + private int countSectors(final boolean[] vals, final int offset) { + int res = 0; + for (int i = 1; i < vals.length - 1; i = i + 2) { + if (i + offset == 0) { + if (vals[1] == m_foreground || vals[8] == m_foreground) { + ++res; + } + } else if (vals[i] == m_foreground || vals[i + offset] == m_foreground) { + ++res; + + } + } + return res; + } + + + + /** + * {@inheritDoc} + */ + @Override + public void afterCycle() { + ++iteration; + } + + @Override + public int getIterationsPerCycle() + { + return 2; + + } + + /** + * {@inheritDoc} + */ + @Override + public ThinningStrategy copy() { + return new GuoHallAlgorithm(m_foreground); + } + +} + + + + + diff --git a/src/main/java/strategies/HilditchAlgorithm.java b/src/main/java/strategies/HilditchAlgorithm.java new file mode 100644 index 0000000..5e284d8 --- /dev/null +++ b/src/main/java/strategies/HilditchAlgorithm.java @@ -0,0 +1,135 @@ +package strategies; + +/* + * ------------------------------------------------------------------------ + * + * Copyright (C) 2003 - 2013 + * University of Konstanz, Germany and + * KNIME GmbH, Konstanz, Germany + * Website: http://www.knime.org; Email: contact@knime.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * --------------------------------------------------------------------- + * + * Created on 01.12.2013 by Andreas + */ + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.type.logic.BitType; + +/** + * An implementation of the Algorithm proposed by C. J. Hilditch. + * + * @author Andreas Burger, University of Konstanz + */ +public class HilditchAlgorithm extends Abstract3x3NeighbourhoodThinning { + + /** + * Create a new hilditch strategy. The passed boolean will represent the foreground-value of the image. + * + * @param foreground Value determining the boolean value of foreground pixels. + */ + public HilditchAlgorithm(final boolean foreground) + { + super(foreground); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean removePixel(final long[] position, final RandomAccessible accessible) { + RandomAccess access = accessible.randomAccess(); + access.setPosition(position); + + boolean[] vals = getNeighbourhood(access); + + // First condition is to ensure there are at least 2 and at most 6 neighbouring foreground pixels. + int numForeground = 0; + for (int i = 1; i < vals.length; ++i) { + if (vals[i] == m_foreground) { + ++numForeground; + } + } + + if (!(2 <= numForeground && numForeground <= 6)) { + return false; + } + + // Second condition checks for transitions between foreground and background. Exactly 1 such transition + // is required. + int numPatterns = findPatternSwitches(vals); + if (!(numPatterns == 1)) { + return false; + } + + // The third and fourth conditions require neighbourhoods of adjacent pixels. + + // Access has to be reset to current image-position before moving it, since + // the getNeighbourhood() method moves it to the top-left of the initial pixel. + access.setPosition(position); + access.move(-1, 1); + int p2Patterns = findPatternSwitches((getNeighbourhood(access))); + if (!( (vals[1] == m_background || vals[3] == m_background || vals[7] == m_background) || p2Patterns != 1)) { + return false; + } + + access.setPosition(position); + access.move(1, 0); + int p4Patterns = findPatternSwitches((getNeighbourhood(access))); + + if (!((vals[1] == m_background || vals[3] == m_background || vals[5] == m_background) || p4Patterns != 1)) { + return false; + } + + // If all conditions are met, we can safely remove the pixel. + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public ThinningStrategy copy() { + return new HilditchAlgorithm(m_foreground); + } + + +} \ No newline at end of file diff --git a/src/main/java/strategies/MorphologicalThinning.java b/src/main/java/strategies/MorphologicalThinning.java new file mode 100644 index 0000000..94d16bf --- /dev/null +++ b/src/main/java/strategies/MorphologicalThinning.java @@ -0,0 +1,197 @@ +package strategies; +/* + * ------------------------------------------------------------------------ + * + * Copyright (C) 2003 - 2013 + * University of Konstanz, Germany and + * KNIME GmbH, Konstanz, Germany + * Website: http://www.knime.org; Email: contact@knime.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * --------------------------------------------------------------------- + * + * Created on 01.12.2013 by Andreas + */ + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.type.logic.BitType; + +/** + * A Class implementing a standard morphological thinning. + * @author Andreas Burger, University of Konstanz + */ +public class MorphologicalThinning extends Abstract3x3NeighbourhoodThinning { + + private int iteration = 0; + + /** + * Create a new morphological thinning strategy. The passed boolean will represent the foreground-value of the image. + * + * @param foreground Value determining the boolean value of foreground pixels. + */ + public MorphologicalThinning(final boolean foreground) + { + super(foreground); + } + + @Override + public boolean removePixel(final long[] position, final RandomAccessible accessible) { + + // Setup + RandomAccess access = accessible.randomAccess(); + access.setPosition(position); + + boolean[] vals = getNeighbourhood(access); + + // Depending on the current step of the cycle, we rotate the two Filters by 0, 90, 180 or 270 Degrees. + if(iteration % 4 == 0) { + return top(vals); + } + + if(iteration % 4 == 1) { + return right(vals); + } + + if(iteration % 4 == 2) { + return bottom(vals); + } + + if(iteration % 4 == 3) { + return left(vals); + } + + return false; + + } + + /* + * This method applies the filters without rotating. The actual filters are given by: + * + * 0 0 0 0 0 + * 1 1 1 0 + * 1 1 1 1 + * + * (Zero stands for background, 1 stands for foreground, no value represents a wildcard.) + * Since the ThinningOp only checks pixels which are in the foreground, the center pixel is alway 1. + */ + private boolean top(final boolean[] vals) { + if (vals[1] == m_background && vals[2] == m_background && vals[8] == m_background && vals[4] == m_foreground + && vals[5] == m_foreground && vals[6] == m_foreground) { + return true; + } + if (vals[1] == m_background && vals[2] == m_background && vals[3] == m_background && vals[5] == m_foreground + && vals[7] == m_foreground) { + return true; + } + + return false; + } + + // Rotated by 90 degrees RIGHT + private boolean right(final boolean[] vals) { + if (vals[2] == m_background && vals[3] == m_background && vals[4] == m_background && vals[6] == m_foreground + && vals[7] == m_foreground && vals[8] == m_foreground) { + return true; + } + if (vals[3] == m_background && vals[4] == m_background && vals[5] == m_background && vals[1] == m_foreground + && vals[7] == m_foreground) { + return true; + } + + return false; + } + + // Rotated by 180 degrees + private boolean bottom(final boolean[] vals) { + if (vals[4] == m_background && vals[5] == m_background && vals[6] == m_background && vals[1] == m_foreground + && vals[2] == m_foreground && vals[8] == m_foreground) { + return true; + } + if (vals[5] == m_background && vals[6] == m_background && vals[7] == m_background && vals[1] == m_foreground + && vals[3] == m_foreground) { + return true; + } + + return false; + } + + // Rotated by 270 degrees RIGHT or 90 degrees LEFT. + private boolean left(final boolean[] vals) { + if (vals[8] == m_background && vals[7] == m_background && vals[6] == m_background && vals[2] == m_foreground + && vals[3] == m_foreground && vals[4] == m_foreground) { + return true; + } + if (vals[7] == m_background && vals[8] == m_background && vals[1] == m_background && vals[5] == m_foreground + && vals[3] == m_foreground) { + return true; + } + + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public void afterCycle() { + // Keep track of current step in the cycle. + ++iteration; + } + + + /** + * {@inheritDoc} + */ + @Override + public int getIterationsPerCycle() { + // To ensure correct order of filter applications and correct termination, we need at least 4 iterations per cycle. + // This guarantees that each filter is checked before terminating. + return 4; + } + + /** + * {@inheritDoc} + */ + @Override + public ThinningStrategy copy() { + return new MorphologicalThinning(m_foreground); + } + +} \ No newline at end of file diff --git a/src/main/java/strategies/ThinningStrategy.java b/src/main/java/strategies/ThinningStrategy.java new file mode 100644 index 0000000..0f1711f --- /dev/null +++ b/src/main/java/strategies/ThinningStrategy.java @@ -0,0 +1,42 @@ +package strategies; + +import net.imglib2.RandomAccessible; +import net.imglib2.type.logic.BitType; + +/** + * An interface for a simple thinning strategy employed by the thinningOp. + * @author Andreas Burger, University of Konstanz + */ +public interface ThinningStrategy { + + /** + * This method should determine whether to keep a foreground pixel or not. + * + * @param position Long Array containing the current position in the image. + * @param access The image to thin. + * @return True if pixel can be switched to background, false otherwise. + */ + public boolean removePixel(final long[] position, final RandomAccessible accessible); + + /** + * Returns the minimum number of iterations necessary for the algorithm to run. This delays termination of + * the thinning algorithm until the end of the current cycle. If, for example, no changes occur during the second + * iteration of a 4-iteration-cycle, iterations 3 and 4 still take place. + * + * @return The number of iterations per cycle. + */ + public int getIterationsPerCycle(); + + /** + * Called by the ThinningOp after each cycle, and thus exactly getIterationsPerCycle()-times per iteration. Used for + * performing different calculations in each step of the cycle. + */ + public void afterCycle(); + + /** + * Returns a seperate copy of this strategy. + * @return A new instance of this strategy with the same values. + */ + public ThinningStrategy copy(); + +} \ No newline at end of file diff --git a/src/main/java/strategies/ThinningStrategyFactory.java b/src/main/java/strategies/ThinningStrategyFactory.java new file mode 100644 index 0000000..60324d2 --- /dev/null +++ b/src/main/java/strategies/ThinningStrategyFactory.java @@ -0,0 +1,125 @@ +package strategies; +/* + * ------------------------------------------------------------------------ + * + * Copyright (C) 2003 - 2013 + * University of Konstanz, Germany and + * KNIME GmbH, Konstanz, Germany + * Website: http://www.knime.org; Email: contact@knime.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * --------------------------------------------------------------------- + * + * Created on 01.12.2013 by Andreas + */ + +import java.util.LinkedList; +import java.util.List; + +/** + * + * @author Andreas + */ +public class ThinningStrategyFactory { + + private boolean m_foreground; + + public ThinningStrategyFactory(final boolean foreground) + { + m_foreground = foreground; + } + + public static enum Strategy { + + MORPHOLOGICAL, HILDITCH, ZHANGSUEN, GUOHALL; + + public static List getNames() { + Strategy[] algorithms = values(); + List names = new LinkedList(); + + for (int i = 0; i < algorithms.length; ++i) { + names.add(algorithms[i].toString()); + } + return names; + + } + + @Override + public String toString() { + switch (this) { + case MORPHOLOGICAL: + return "Morphological Thinning"; + case HILDITCH: + return "Hilditch Algorithm"; + case ZHANGSUEN: + return "Zhang-Suen Algorithm"; + case GUOHALL: + return "Guo-Hall Algorithm"; + default: + throw new IllegalArgumentException(); + } + } + }; + + public ThinningStrategy getStrategy(final Strategy strategy) { + switch (strategy) { + case MORPHOLOGICAL: + return new MorphologicalThinning(m_foreground); + case HILDITCH: + return new HilditchAlgorithm(m_foreground); + case ZHANGSUEN: + return new ZhangSuenAlgorithm(m_foreground); + case GUOHALL: + return new GuoHallAlgorithm(m_foreground); + default: + return new MorphologicalThinning(m_foreground); + } + + } + + public ThinningStrategy getStrategy(final String strategy) { + for(Strategy s: Strategy.values()) + { + if (s.toString().equals(strategy)) { + return getStrategy(s); + } + } + return new MorphologicalThinning(m_foreground); + } +} \ No newline at end of file diff --git a/src/main/java/strategies/ZhangSuenAlgorithm.java b/src/main/java/strategies/ZhangSuenAlgorithm.java new file mode 100644 index 0000000..28cf1b0 --- /dev/null +++ b/src/main/java/strategies/ZhangSuenAlgorithm.java @@ -0,0 +1,155 @@ +package strategies; + +/* + * ------------------------------------------------------------------------ + * + * Copyright (C) 2003 - 2013 + * University of Konstanz, Germany and + * KNIME GmbH, Konstanz, Germany + * Website: http://www.knime.org; Email: contact@knime.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License, Version 3, as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * + * Additional permission under GNU GPL version 3 section 7: + * + * KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. + * Hence, KNIME and ECLIPSE are both independent programs and are not + * derived from each other. Should, however, the interpretation of the + * GNU GPL Version 3 ("License") under any applicable laws result in + * KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants + * you the additional permission to use and propagate KNIME together with + * ECLIPSE with only the license terms in place for ECLIPSE applying to + * ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the + * license terms of ECLIPSE themselves allow for the respective use and + * propagation of ECLIPSE together with KNIME. + * + * Additional permission relating to nodes for KNIME that extend the Node + * Extension (and in particular that are based on subclasses of NodeModel, + * NodeDialog, and NodeView) and that only interoperate with KNIME through + * standard APIs ("Nodes"): + * Nodes are deemed to be separate and independent programs and to not be + * covered works. Notwithstanding anything to the contrary in the + * License, the License does not apply to Nodes, you are not required to + * license Nodes under the License, and you are granted a license to + * prepare and propagate Nodes, in each case even if such Nodes are + * propagated with or for interoperation with KNIME. The owner of a Node + * may freely choose the license terms applicable to such Node, including + * when such Node is propagated with or for interoperation with KNIME. + * --------------------------------------------------------------------- + * + * Created on 01.12.2013 by Andreas + */ + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.type.logic.BitType; + +/** + * Implementation of the thinning algorithm proposed by T. Y. Zhang and C. Y. Suen. + * @author Andreas Burger, University of Konstanz + */ +public class ZhangSuenAlgorithm extends Abstract3x3NeighbourhoodThinning { + + private int iteration = 0; + + /** + * Create a new Zhang-Suen thinning strategy. The passed boolean will represent the foreground-value of the image. + * + * @param foreground Value determining the boolean value of foreground pixels. + */ + public ZhangSuenAlgorithm(final boolean foreground) + { + super(foreground); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean removePixel(final long[] position, final RandomAccessible accessible) { + + // Setup + RandomAccess access = accessible.randomAccess(); + access.setPosition(position); + + boolean[] vals = getNeighbourhood(access); + + // First two conditions are similar to Hilditch-Thinning. + int numForeground = 0; + + for (int i = 1; i < vals.length; ++i) { + if (vals[i] == m_foreground) { + ++numForeground; + } + } + + if (!(2 <= numForeground && numForeground <= 6)) { + return false; + } + int numPatternSwitches = findPatternSwitches(vals); + if (!(numPatternSwitches == 1)) { + return false; + } + + // Currently, this thinning algorithm runs as 1-iteration-per-cycle, since the order of operations is not important. + if ((iteration % 2) != 1) { + return evenIteration(vals); + } else { + return oddIteration(vals); + } + + } + + // Check for background pixels in the vicinity. + private boolean evenIteration(final boolean[] vals) { + if (!(vals[1] == m_background || vals[3] == m_background || vals[5] == m_background)) { + return false; + } + + if (!(vals[3] == m_background || vals[5] == m_background || vals[7] == m_background)) { + return false; + } + + return true; + } + + // Variation of the checks in an even iteration. + private boolean oddIteration(final boolean[] vals) { + if (!(vals[1] == m_background || vals[3] == m_background || vals[7] == m_background)) { + return false; + } + + if (!(vals[1] == m_background || vals[5] == m_background || vals[7] == m_background)) { + return false; + } + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public void afterCycle() { + ++iteration; + } + + /** + * {@inheritDoc} + */ + @Override + public ThinningStrategy copy() { + return new ZhangSuenAlgorithm(m_foreground); + } + + +} diff --git a/src/main/java/thinning/ThinningOp.java b/src/main/java/thinning/ThinningOp.java new file mode 100644 index 0000000..ec9e5dc --- /dev/null +++ b/src/main/java/thinning/ThinningOp.java @@ -0,0 +1,222 @@ +package thinning; + +/* +* ------------------------------------------------------------------------ +* +* Copyright (C) 2003 - 2013 +* University of Konstanz, Germany and +* KNIME GmbH, Konstanz, Germany +* Website: http://www.knime.org; Email: contact@knime.org +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License, Version 3, as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, but +* WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, see . +* +* Additional permission under GNU GPL version 3 section 7: +* +* KNIME interoperates with ECLIPSE solely via ECLIPSE's plug-in APIs. +* Hence, KNIME and ECLIPSE are both independent programs and are not +* derived from each other. Should, however, the interpretation of the +* GNU GPL Version 3 ("License") under any applicable laws result in +* KNIME and ECLIPSE being a combined program, KNIME GMBH herewith grants +* you the additional permission to use and propagate KNIME together with +* ECLIPSE with only the license terms in place for ECLIPSE applying to +* ECLIPSE and the GNU GPL Version 3 applying for KNIME, provided the +* license terms of ECLIPSE themselves allow for the respective use and +* propagation of ECLIPSE together with KNIME. +* +* Additional permission relating to nodes for KNIME that extend the Node +* Extension (and in particular that are based on subclasses of NodeModel, +* NodeDialog, and NodeView) and that only interoperate with KNIME through +* standard APIs ("Nodes"): +* Nodes are deemed to be separate and independent programs and to not be +* covered works. Notwithstanding anything to the contrary in the +* License, the License does not apply to Nodes, you are not required to +* license Nodes under the License, and you are granted a license to +* prepare and propagate Nodes, in each case even if such Nodes are +* propagated with or for interoperation with KNIME. The owner of a Node +* may freely choose the license terms applicable to such Node, including +* when such Node is propagated with or for interoperation with KNIME. +* --------------------------------------------------------------------- +* +* Created on 07.11.2013 by Daniel +*/ + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.ops.operation.UnaryOperation; +import net.imglib2.type.logic.BitType; +import net.imglib2.view.Views; +import strategies.ThinningStrategy; + + +/** + * Thinning Operation + * + * @author Andreas Burger, University of Konstanz + */ +public class ThinningOp implements UnaryOperation, RandomAccessibleInterval> { + + private boolean m_foreground = true; + + private boolean m_background = false; + + private ThinningStrategy m_strategy; + + private ImgFactory m_factory; + + /** + * Instantiate a new ThinningOp using the given strategy and considering the given boolean value as foreground. + * + * @param strategy thinning strategy to use + * @param foreground Boolean value of foreground pixels. + * @param factory for temporary result image + */ + public ThinningOp(final ThinningStrategy strategy, final boolean foreground, final ImgFactory factory) { + m_strategy = strategy; + m_foreground = foreground; + m_background = !foreground; + m_factory = factory; + } + + /** + * {@inheritDoc} + */ + @Override + public RandomAccessibleInterval compute(final RandomAccessibleInterval input, + final RandomAccessibleInterval output) { + + // Create a new image as a buffer to store the thinning image in each iteration. + // This image and output are swapped each iteration since we need to work on the image + // without changing it. + final Img buffer = m_factory.create(input, new BitType()); + + final IterableInterval it1 = Views.iterable(buffer); + final IterableInterval it2 = Views.iterable(output); + + // Extend the buffer in order to be able to iterate care-free later. + final RandomAccessible ra1 = Views.extendBorder(buffer); + final RandomAccessible ra2 = Views.extendBorder(output); + RandomAccessible currRa = Views.extendBorder(input); // Used only in first iteration. + + // Create cursors. + final Cursor firstCursor = it1.localizingCursor(); + Cursor currentCursor = Views.iterable(input).localizingCursor(); + final Cursor secondCursor = it2.localizingCursor(); + + // Create pointers to the current and next cursor and set them to Buffer and output respectively. + Cursor nextCursor; + nextCursor = secondCursor; + + // The main loop. + boolean changes = true; + int i = 0; + // Until no more changes, do: + while (changes) { + changes = false; + // This For-Loop makes sure, that iterations only end on full cycles (as defined by the strategies). + for (int j = 0; j < m_strategy.getIterationsPerCycle(); ++j) { + // For each pixel in the image. + while (currentCursor.hasNext()) { + // Move both cursors + currentCursor.fwd(); + nextCursor.fwd(); + // Get the position of the current cursor. + long[] coordinates = new long[currentCursor.numDimensions()]; + currentCursor.localize(coordinates); + + // Copy the value of the image currently operated upon. + boolean curr = currentCursor.get().get(); + nextCursor.get().set(curr); + + // Only foreground pixels may be thinned + if (curr == m_foreground) { + + // Ask the strategy whether to flip the foreground pixel or not. + boolean flip = m_strategy.removePixel(coordinates, currRa); + + // If yes - change and keep track of the change. + if (flip) { + nextCursor.get().set(m_background); + changes = true; + } + } + } + // One step of the cycle is finished, notify the strategy. + m_strategy.afterCycle(); + + // Reset the cursors to the beginning and assign pointers for the next iteration. + currentCursor.reset(); + nextCursor.reset(); + + // Keep track of the most recent image. Needed for output. + if (currRa == ra2) { + currRa = ra1; + currentCursor = firstCursor; + nextCursor = secondCursor; + } else { + currRa = ra2; + currentCursor = secondCursor; + nextCursor = firstCursor; + } + + // Keep track of iterations. + ++i; + } + } + + // Depending on the iteration count, the final image is either in ra1 or ra2. Copy it to output. + if (i % 2 == 0) { + //Ra1 points to img1, ra2 points to output. + copy(buffer, output); + + } + + return output; + } + + /** + * {@inheritDoc} + */ + @Override + public UnaryOperation, RandomAccessibleInterval> copy() { + return new ThinningOp(m_strategy.copy(), m_foreground, m_factory); + } + + private void copy(final RandomAccessibleInterval source, final RandomAccessibleInterval target) { + IterableInterval targetIt = Views.iterable(target); + IterableInterval sourceIt = Views.iterable(source); + + if (sourceIt.iterationOrder().equals(targetIt.iterationOrder())) { + Cursor targetCursor = targetIt.cursor(); + Cursor sourceCursor = sourceIt.cursor(); + while (sourceCursor.hasNext()) { + targetCursor.fwd(); + sourceCursor.fwd(); + targetCursor.get().set(sourceCursor.get().get()); + } + } else { // Fallback to random access + RandomAccess targetRA = target.randomAccess(); + Cursor sourceCursor = sourceIt.localizingCursor(); + while (sourceCursor.hasNext()) { + sourceCursor.fwd(); + targetRA.setPosition(sourceCursor); + targetRA.get().set(sourceCursor.get().get()); + } + } + } + +} diff --git a/src/main/java/timeGUI/CovistoTimeselectPanel.java b/src/main/java/timeGUI/CovistoTimeselectPanel.java new file mode 100644 index 0000000..339ac0b --- /dev/null +++ b/src/main/java/timeGUI/CovistoTimeselectPanel.java @@ -0,0 +1,79 @@ +package timeGUI; + +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoTimeselectPanel { + + public static final int scrollbarSize = 1000; + + + public static JPanel Timeselect = new JPanel(); + public static Label timeText = new Label("Current T = " + 1, Label.CENTER); + + public static int fourthDimensionslider = 1; + public static int fourthDimensionsliderInit = 1; + public static int fourthDimensionSize; + public static final String timestring = "Current T"; + public static JScrollBar timeslider = new JScrollBar(Scrollbar.HORIZONTAL, fourthDimensionsliderInit, 10, 0, + scrollbarSize + 10); + public static int fourthDimension; + public static TextField inputFieldT; + + public static void setTime(final int value) { + + fourthDimensionslider = value; + fourthDimensionsliderInit = value; + fourthDimension = value; + } + public int getTimeMax() { + + return fourthDimensionSize; + } + public static JPanel TimeselectPanel(int ndims) { + + layoutManager.Setlayout.LayoutSetter(Timeselect); + setTime(fourthDimension); + + + inputFieldT = new TextField(); + inputFieldT = new TextField(5); + inputFieldT.setText(Integer.toString(fourthDimension)); + + + // Put time slider + Border timeborder = new CompoundBorder(new TitledBorder("Select time"), new EmptyBorder(layoutManager.Setlayout.c.insets)); + + if (ndims < 4) { + + timeslider.setEnabled(false); + inputFieldT.setEnabled(false); + } + Timeselect.add(timeText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Timeselect.add(timeslider, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Timeselect.add(inputFieldT, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Timeselect.setBorder(timeborder); + + + + + return Timeselect; + } + + +} diff --git a/src/main/java/trackMateBridge/Bridging.java b/src/main/java/trackMateBridge/Bridging.java new file mode 100644 index 0000000..4f12650 --- /dev/null +++ b/src/main/java/trackMateBridge/Bridging.java @@ -0,0 +1,13 @@ +package trackMateBridge; + + +public class Bridging { + + + public void CreatBridge() { + + + + } + +} diff --git a/src/main/java/userTESTING/BorisData.java b/src/main/java/userTESTING/BorisData.java new file mode 100644 index 0000000..5c122bd --- /dev/null +++ b/src/main/java/userTESTING/BorisData.java @@ -0,0 +1,42 @@ +package userTESTING; + +import distanceTransform.CreateBinary; +import distanceTransform.CreateDistanceTransform; +import distanceTransform.CreateWatershed; +import ij.IJ; +import ij.ImageJ; +import ij.ImagePlus; +import ij.io.Opener; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import preProcessing.FlatFieldOnly; +import preProcessing.Kernels; + +public class BorisData { + + + public static void main(String[] args) { + + + new ImageJ(); + + + ImagePlus imp = new Opener().openImage("/Users/aimachine/Documents/Boris_Data/wt-PHGFP_scutellum_18-19hAPF/TESTgfp-pre.tif"); + RandomAccessibleInterval inputimage = ImageJFunctions.convertFloat(imp); + new Normalize(); + FloatType minval = new FloatType(0); + FloatType maxval = new FloatType(1); + + + IJ.runMacroFile("/Users/aimachine/Documents/Boris_Data/wt-PHGFP_scutellum_18-19hAPF/Testwatershed.ijm"); + } + + + +} diff --git a/src/main/java/utility/ActionListenablePanel.java b/src/main/java/utility/ActionListenablePanel.java new file mode 100644 index 0000000..4d84d2e --- /dev/null +++ b/src/main/java/utility/ActionListenablePanel.java @@ -0,0 +1,45 @@ +package utility; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.ArrayList; +import java.util.Collection; + +import javax.swing.JPanel; + +public class ActionListenablePanel extends JPanel { + + private static final long serialVersionUID = -1732282609704990375L; + + protected ArrayList actionListeners = new ArrayList<>(); + + /** + * Adds an {@link ActionListener} to this panel. These listeners will be notified when + * a button is pushed or when the feature to color is changed. + */ + public void addActionListener(ActionListener listener) { + actionListeners.add(listener); + } + + /** + * Removes an ActionListener from this panel. + * @return true if the listener was in the ActionListener collection of this instance. + */ + public boolean removeActionListener(ActionListener listener) { + return actionListeners.remove(listener); + } + + public Collection getActionListeners() { + return actionListeners; + } + + + /** + * Forwards the given {@link ActionEvent} to all the {@link ActionListener} of this panel. + */ + protected void fireAction(ActionEvent e) { + for (ActionListener l : actionListeners) + l.actionPerformed(e); + } + +} diff --git a/src/main/java/utility/AnalyzeTTrack.java b/src/main/java/utility/AnalyzeTTrack.java new file mode 100644 index 0000000..b0433d6 --- /dev/null +++ b/src/main/java/utility/AnalyzeTTrack.java @@ -0,0 +1,84 @@ +package utility; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import linkers.TrackModel; +import linkers.TrackModel3D; + + +public class AnalyzeTTrack { + + + public static HashMap> get3DTracks(SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > Tgraph){ + + TrackModel3D model = new TrackModel3D(Tgraph); + HashMap> fourDmap = new HashMap>(); + + for (final Integer id : model.trackIDs(true)) { + + model.setName(id, "Track" + id); + + final HashSet Snakeset = model.trackThreeDRoiobjects(id); + ArrayList list = new ArrayList(); + + Comparator FourthDimcomparison = new Comparator() { + + @Override + public int compare(final ThreeDRoiobject A, final ThreeDRoiobject B) { + + return A.fourthDimension - B.fourthDimension; + + } + + }; + + Iterator Snakeiter = Snakeset.iterator(); + + while (Snakeiter.hasNext()) { + + ThreeDRoiobject currentsnake = Snakeiter.next(); + + list.add(currentsnake); + + } + Collections.sort(list, FourthDimcomparison); + + fourDmap.put(id, list); + + + } + + return fourDmap; + + } + + public static int getMaxID(SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > Tgraph) { + + int maxid = Integer.MIN_VALUE; + + TrackModel3D model = new TrackModel3D(Tgraph); + for (final Integer id : model.trackIDs(true)) { + + if (id > maxid) + maxid = id; + + } + + + + + return maxid; + + } + + + +} diff --git a/src/main/java/utility/AnalzeZTrack.java b/src/main/java/utility/AnalzeZTrack.java new file mode 100644 index 0000000..4031cd0 --- /dev/null +++ b/src/main/java/utility/AnalzeZTrack.java @@ -0,0 +1,82 @@ +package utility; + + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import linkers.TrackModel; + +public class AnalzeZTrack { + + + public static HashMap> get3Dobjects(SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > Zgraph){ + + TrackModel model = new TrackModel(Zgraph); + HashMap> threeDmap = new HashMap>(); + + for (final Integer id : model.trackIDs(true)) { + + model.setName(id, "Track" + id); + + final HashSet Snakeset = model.trackPreRoiobjects(id); + ArrayList list = new ArrayList(); + + Comparator ThirdDimcomparison = new Comparator() { + + @Override + public int compare(final PreRoiobject A, final PreRoiobject B) { + + return A.thirdDimension - B.thirdDimension; + + } + + }; + + Iterator Snakeiter = Snakeset.iterator(); + + while (Snakeiter.hasNext()) { + + PreRoiobject currentsnake = Snakeiter.next(); + + list.add(currentsnake); + + } + Collections.sort(list, ThirdDimcomparison); + + threeDmap.put(id, list); + + } + + + return threeDmap; + + } + + public static int getMaxID(SimpleWeightedGraph< PreRoiobject, DefaultWeightedEdge > Zgraph) { + + int maxid = Integer.MIN_VALUE; + + TrackModel model = new TrackModel(Zgraph); + for (final Integer id : model.trackIDs(true)) { + + if (id > maxid) + maxid = id; + + } + + + + + return maxid; + + } + + +} diff --git a/src/main/java/utility/ConfigurationPanel.java b/src/main/java/utility/ConfigurationPanel.java new file mode 100644 index 0000000..3d49ffc --- /dev/null +++ b/src/main/java/utility/ConfigurationPanel.java @@ -0,0 +1,42 @@ +package utility; +import java.awt.event.ActionEvent; +import java.util.Map; + + +/** + * The mother class for all the configuration panels that can configure a + * {@link fiji.plugin.trackmate.detection.SpotDetectorFactory}, a + * {@link fiji.plugin.trackmate.tracking.SpotTracker}, ... + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> 2011 - 2012 + * + */ +public abstract class ConfigurationPanel extends ActionListenablePanel +{ + + private static final long serialVersionUID = 1L; + + /** + * This event is fired when a preview button is fired within this + * configuration panel. It is the responsibility of concrete implementation + * to do whatever they want with it. + */ + public final ActionEvent PREVIEW_BUTTON_PUSHED = new ActionEvent( this, 0, "PreviewButtonPushed" ); + + /** + * Echo the parameters of the given settings on this panel. + */ + public abstract void setSettings( final Map< String, Object > settings ); + + /** + * @return a new settings map object with its values set by this panel. + */ + public abstract Map< String, Object > getSettings(); + + /** + * Executes any task related to cleaning the possible previews generated by + * this panel prior to moving to another GUI panel. + */ + public abstract void clean(); + +} diff --git a/src/main/java/utility/Convolvers.java b/src/main/java/utility/Convolvers.java new file mode 100644 index 0000000..07d50e4 --- /dev/null +++ b/src/main/java/utility/Convolvers.java @@ -0,0 +1,1147 @@ +package utility; + +public class Convolvers { + + + public static double[][] movingSum(double[][] paramArrayOfDouble, int paramInt) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble[0].length; + double[][] arrayOfDouble = new double[i + paramInt - 1][j + paramInt - 1]; + int m; + for (int k = 0; k < j; k++) + { + arrayOfDouble[0][k] = paramArrayOfDouble[0][k]; + for (m = 1; m < paramInt; m++) { + arrayOfDouble[0][k] += paramArrayOfDouble[m][k]; + } + for (m = 1; m < paramInt; m++) { + arrayOfDouble[m][k] = (arrayOfDouble[(m - 1)][k] - paramArrayOfDouble[(paramInt - m)][k] + paramArrayOfDouble[m][k]); + } + for (m = paramInt; m < i; m++) { + arrayOfDouble[m][k] = (arrayOfDouble[(m - 1)][k] - paramArrayOfDouble[(m - paramInt)][k] + paramArrayOfDouble[m][k]); + } + for (m = i; m < i + paramInt; m++) { + arrayOfDouble[m][k] = (arrayOfDouble[(m - 1)][k] - paramArrayOfDouble[(m - paramInt)][k] + paramArrayOfDouble[(2 * i - m - 2)][k]); + } + } + for (int k = 0; k < i + paramInt - 1; k++) + { + arrayOfDouble[k][0] = arrayOfDouble[k][0]; + for (m = 1; m < paramInt; m++) { + arrayOfDouble[k][0] += arrayOfDouble[k][m]; + } + for (m = 1; m < paramInt; m++) { + arrayOfDouble[k][m] = (arrayOfDouble[k][(m - 1)] - arrayOfDouble[k][(paramInt - m)] + arrayOfDouble[k][m]); + } + for (m = paramInt; m < j; m++) { + arrayOfDouble[k][m] = (arrayOfDouble[k][(m - 1)] - arrayOfDouble[k][(m - paramInt)] + arrayOfDouble[k][m]); + } + for (m = j; m < j + paramInt - m; m++) { + arrayOfDouble[k][m] = (arrayOfDouble[k][(m - 1)] - arrayOfDouble[k][(m - paramInt)] + arrayOfDouble[k][(2 * j - m - 2)]); + } + } + return arrayOfDouble; + } + + public static double[][] movingSumX(double[][] paramArrayOfDouble, int paramInt) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble[0].length; + double[][] arrayOfDouble = new double[i + paramInt - 1][j + paramInt - 1]; + for (int k = 0; k < j; k++) + { + arrayOfDouble[0][k] = paramArrayOfDouble[0][k]; + for (int m = 1; m < paramInt; m++) { + arrayOfDouble[0][k] += paramArrayOfDouble[m][k]; + } + for (int m = 1; m < paramInt; m++) { + arrayOfDouble[m][k] = (arrayOfDouble[(m - 1)][k] - paramArrayOfDouble[(paramInt - m)][k] + paramArrayOfDouble[m][k]); + } + for (int m = paramInt; m < i; m++) { + arrayOfDouble[m][k] = (arrayOfDouble[(m - 1)][k] - paramArrayOfDouble[(m - paramInt)][k] + paramArrayOfDouble[m][k]); + } + for (int m = i; m < i + paramInt; m++) { + arrayOfDouble[m][k] = (arrayOfDouble[(m - 1)][k] - paramArrayOfDouble[(m - paramInt)][k] + paramArrayOfDouble[(2 * i - m - 2)][k]); + } + } + return arrayOfDouble; + } + + public static double[][] movingSumY(double[][] paramArrayOfDouble, int paramInt) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble[0].length; + double[][] arrayOfDouble = new double[i + paramInt - 1][j + paramInt - 1]; + for (int k = 0; k < i + paramInt - 1; k++) + { + arrayOfDouble[k][0] = arrayOfDouble[k][0]; + for (int m = 1; m < paramInt; m++) { + arrayOfDouble[k][0] += arrayOfDouble[k][m]; + } + for (int m = 1; m < paramInt; m++) { + arrayOfDouble[k][m] = (arrayOfDouble[k][(m - 1)] - arrayOfDouble[k][(paramInt - m)] + arrayOfDouble[k][m]); + } + for (int m = paramInt; m < j; m++) { + arrayOfDouble[k][m] = (arrayOfDouble[k][(m - 1)] - arrayOfDouble[k][(m - paramInt)] + arrayOfDouble[k][m]); + } + for (int m = j; m < j + paramInt - m; m++) { + arrayOfDouble[k][m] = (arrayOfDouble[k][(m - 1)] - arrayOfDouble[k][(m - paramInt)] + arrayOfDouble[k][(2 * j - m - 2)]); + } + } + return arrayOfDouble; + } + + public static double[][] convolveEvenX(double[][] paramArrayOfDouble, double[] paramArrayOfDouble1) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble[0].length; + int k = paramArrayOfDouble1.length; + int m = k - 1; + int n = 2 * i - 2; + double[][] arrayOfDouble = new double[i][j]; + int i1 = 0; + for (int i2 = 0; i2 < j; i2++) + { + int i4; + for (int i3 = 0; i3 < m; i3++) + { + arrayOfDouble[i3][i2] = (paramArrayOfDouble1[0] * paramArrayOfDouble[i3][i2]); + for (i4 = 1; i4 <= i3; i4++) { + arrayOfDouble[i3][i2] += paramArrayOfDouble1[i4] * (paramArrayOfDouble[(i3 - i4)][i2] + paramArrayOfDouble[(i3 + i4)][i2]); + } + for (i4 = i3 + 1; i4 < k; i4++) { + arrayOfDouble[i3][i2] += paramArrayOfDouble1[i4] * (paramArrayOfDouble[(i4 - i3)][i2] + paramArrayOfDouble[(i3 + i4)][i2]); + } + } + for (int i3 = m; i3 <= i - k; i3++) + { + arrayOfDouble[i3][i2] = (paramArrayOfDouble1[0] * paramArrayOfDouble[i3][i2]); + for (i4 = 1; i4 < k; i4++) { + arrayOfDouble[i3][i2] += paramArrayOfDouble1[i4] * (paramArrayOfDouble[(i3 - i4)][i2] + paramArrayOfDouble[(i3 + i4)][i2]); + } + } + for (int i3 = i - m; i3 < i; i3++) + { + arrayOfDouble[i3][i2] = (paramArrayOfDouble1[0] * paramArrayOfDouble[i3][i2]); + for (i4 = 1; i4 < i - i3; i4++) { + arrayOfDouble[i3][i2] += paramArrayOfDouble1[i4] * (paramArrayOfDouble[(i3 - i4)][i2] + paramArrayOfDouble[(i3 + i4)][i2]); + } + for (i4 = i - i3; i4 < k; i4++) { + arrayOfDouble[i3][i2] += paramArrayOfDouble1[i4] * (paramArrayOfDouble[(n - i4 - i3)][i2] + paramArrayOfDouble[(i3 - i4)][i2]); + } + } + } + return arrayOfDouble; + } + + public static double[] convolveEvenX(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int paramInt1, int paramInt2) + { + int i = paramArrayOfDouble2.length; + int j = i - 1; + int k = 2 * paramInt1 - 2; + double[] arrayOfDouble = new double[paramInt1 * paramInt2]; + int m = 0; + for (int n = 0; n < paramInt2; n++) + { + int i2; + for (int i1 = 0; i1 < j; i1++) + { + arrayOfDouble[m] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[m]); + for (i2 = 1; i2 <= i1; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m - i2)] + paramArrayOfDouble1[(m + i2)]); + } + for (i2 = i1 + 1; i2 < i; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(i2 - i1 + n * paramInt1)] + paramArrayOfDouble1[(m + i2)]); + } + m++; + } + for (int i1 = j; i1 <= paramInt1 - i; i1++) + { + arrayOfDouble[m] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[m]); + for (i2 = 1; i2 < i; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m - i2)] + paramArrayOfDouble1[(m + i2)]); + } + m++; + } + for (int i1 = paramInt1 - j; i1 < paramInt1; i1++) + { + arrayOfDouble[m] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[m]); + for (i2 = 1; i2 < paramInt1 - i1; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m - i2)] + paramArrayOfDouble1[(m + i2)]); + } + for (i2 = paramInt1 - i1; i2 < i; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(k - i2 - i1 + n * paramInt1)] + paramArrayOfDouble1[(m - i2)]); + } + m++; + } + } + return arrayOfDouble; + } + + public static float[] convolveEvenX(float[] paramArrayOfFloat1, float[] paramArrayOfFloat2, int paramInt1, int paramInt2) + { + int i = paramArrayOfFloat2.length; + int j = i - 1; + int k = 2 * paramInt1 - 2; + float[] arrayOfFloat = new float[paramInt1 * paramInt2]; + int m = 0; + for (int n = 0; n < paramInt2; n++) + { + int i2; + for (int i1 = 0; i1 < j; i1++) + { + arrayOfFloat[m] = (paramArrayOfFloat2[0] * paramArrayOfFloat1[m]); + for (i2 = 1; i2 <= i1; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m - i2)] + paramArrayOfFloat1[(m + i2)]); + } + for (i2 = i1 + 1; i2 < i; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(i2 - i1 + n * paramInt1)] + paramArrayOfFloat1[(m + i2)]); + } + m++; + } + for (int i1 = j; i1 <= paramInt1 - i; i1++) + { + arrayOfFloat[m] = (paramArrayOfFloat2[0] * paramArrayOfFloat1[m]); + for (i2 = 1; i2 < i; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m - i2)] + paramArrayOfFloat1[(m + i2)]); + } + m++; + } + for (int i1 = paramInt1 - j; i1 < paramInt1; i1++) + { + arrayOfFloat[m] = (paramArrayOfFloat2[0] * paramArrayOfFloat1[m]); + for (i2 = 1; i2 < paramInt1 - i1; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m - i2)] + paramArrayOfFloat1[(m + i2)]); + } + for (i2 = paramInt1 - i1; i2 < i; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(k - i2 - i1 + n * paramInt1)] + paramArrayOfFloat1[(m - i2)]); + } + m++; + } + } + return arrayOfFloat; + } + + public static double[][] convolveEvenY(double[][] paramArrayOfDouble, double[] paramArrayOfDouble1) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble[0].length; + int k = paramArrayOfDouble1.length; + int m = k - 1; + int n = 2 * j - 2; + double[][] arrayOfDouble = new double[i][j]; + for (int i1 = 0; i1 < i; i1++) + { + int i3; + for (int i2 = 0; i2 < m; i2++) + { + arrayOfDouble[i1][i2] = (paramArrayOfDouble1[0] * paramArrayOfDouble[i1][i2]); + for (i3 = 1; i3 <= i2; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 - i3)] + paramArrayOfDouble[i1][(i2 + i3)]); + } + for (i3 = i2 + 1; i3 < k; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i3 - i2)] + paramArrayOfDouble[i1][(i2 + i3)]); + } + } + for (int i2 = m; i2 <= j - k; i2++) + { + arrayOfDouble[i1][i2] = (paramArrayOfDouble1[0] * paramArrayOfDouble[i1][i2]); + for (i3 = 1; i3 < k; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 - i3)] + paramArrayOfDouble[i1][(i2 + i3)]); + } + } + for (int i2 = j - m; i2 < j; i2++) + { + arrayOfDouble[i1][i2] = (paramArrayOfDouble1[0] * paramArrayOfDouble[i1][i2]); + for (i3 = 1; i3 < j - i2; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 - i3)] + paramArrayOfDouble[i1][(i2 + i3)]); + } + for (i3 = j - i2; i3 < k; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(n - i3 - i2)] + paramArrayOfDouble[i1][(i2 - i3)]); + } + } + } + return arrayOfDouble; + } + + public static double[] convolveEvenY(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int paramInt1, int paramInt2) + { + int i = paramArrayOfDouble2.length; + int j = i - 1; + int k = 2 * paramInt2 - 2; + double[] arrayOfDouble = new double[paramInt1 * paramInt2]; + for (int i1 = 0; i1 < paramInt1; i1++) + { + int m; + int i3; + int n; + for (int i2 = 0; i2 < j; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfDouble[m] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[m]); + for (i3 = 1; i3 <= i2; i3++) + { + n = i3 * paramInt1; + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m - n)] + paramArrayOfDouble1[(m + n)]); + } + for (i3 = i2 + 1; i3 < i; i3++) { + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[((i3 - i2) * paramInt1 + i1)] + paramArrayOfDouble1[(m + i3 * paramInt1)]); + } + } + for (int i2 = j; i2 <= paramInt2 - i; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfDouble[m] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[m]); + for (i3 = 1; i3 < i; i3++) + { + n = i3 * paramInt1; + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m - n)] + paramArrayOfDouble1[(m + n)]); + } + } + for (int i2 = paramInt2 - j; i2 < paramInt2; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfDouble[m] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[m]); + for (i3 = 1; i3 < paramInt2 - i2; i3++) + { + n = i3 * paramInt1; + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m - n)] + paramArrayOfDouble1[(m + n)]); + } + for (i3 = paramInt2 - i2; i3 < i; i3++) { + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[((k - i3 - i2) * paramInt1 + i1)] + paramArrayOfDouble1[(m - i3 * paramInt1)]); + } + } + } + return arrayOfDouble; + } + + public static float[] convolveEvenY(float[] paramArrayOfFloat1, float[] paramArrayOfFloat2, int paramInt1, int paramInt2) + { + int i = paramArrayOfFloat2.length; + int j = i - 1; + int k = 2 * paramInt2 - 2; + float[] arrayOfFloat = new float[paramInt1 * paramInt2]; + for (int i1 = 0; i1 < paramInt1; i1++) + { + int m; + int i3; + int n; + for (int i2 = 0; i2 < j; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfFloat[m] = (paramArrayOfFloat2[0] * paramArrayOfFloat1[m]); + for (i3 = 1; i3 <= i2; i3++) + { + n = i3 * paramInt1; + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m - n)] + paramArrayOfFloat1[(m + n)]); + } + for (i3 = i2 + 1; i3 < i; i3++) { + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[((i3 - i2) * paramInt1 + i1)] + paramArrayOfFloat1[(m + i3 * paramInt1)]); + } + } + for (int i2 = j; i2 <= paramInt2 - i; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfFloat[m] = (paramArrayOfFloat2[0] * paramArrayOfFloat1[m]); + for (i3 = 1; i3 < i; i3++) + { + n = i3 * paramInt1; + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m - n)] + paramArrayOfFloat1[(m + n)]); + } + } + for (int i2 = paramInt2 - j; i2 < paramInt2; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfFloat[m] = (paramArrayOfFloat2[0] * paramArrayOfFloat1[m]); + for (i3 = 1; i3 < paramInt2 - i2; i3++) + { + n = i3 * paramInt1; + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m - n)] + paramArrayOfFloat1[(m + n)]); + } + for (i3 = paramInt2 - i2; i3 < i; i3++) { + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[((k - i3 - i2) * paramInt1 + i1)] + paramArrayOfFloat1[(m - i3 * paramInt1)]); + } + } + } + return arrayOfFloat; + } + + public static double[][] convolveOddX(double[][] paramArrayOfDouble, double[] paramArrayOfDouble1) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble[0].length; + int k = paramArrayOfDouble1.length; + int m = k - 1; + int n = 2 * i - 2; + double[][] arrayOfDouble = new double[i][j]; + for (int i1 = 0; i1 < j; i1++) + { + int i3; + for (int i2 = 0; i2 < m; i2++) + { + arrayOfDouble[i2][i1] = 0.0D; + for (i3 = 1; i3 <= i2; i3++) { + arrayOfDouble[i2][i1] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[(i2 + i3)][i1] - paramArrayOfDouble[(i2 - i3)][i1]); + } + for (i3 = i2 + 1; i3 < k; i3++) { + arrayOfDouble[i2][i1] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[(i2 + i3)][i1] - paramArrayOfDouble[(i3 - i2)][i1]); + } + } + for (int i2 = m; i2 <= i - k; i2++) + { + arrayOfDouble[i2][i1] = 0.0D; + for (i3 = 1; i3 < k; i3++) { + arrayOfDouble[i2][i1] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[(i2 + i3)][i1] - paramArrayOfDouble[(i2 - i3)][i1]); + } + } + for (int i2 = i - m; i2 < i; i2++) + { + arrayOfDouble[i2][i1] = 0.0D; + for (i3 = 1; i3 < i - i2; i3++) { + arrayOfDouble[i2][i1] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[(i2 + i3)][i1] - paramArrayOfDouble[(i2 - i3)][i1]); + } + for (i3 = i - i2; i3 < k; i3++) { + arrayOfDouble[i2][i1] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[(n - i3 - i2)][i1] - paramArrayOfDouble[(i2 - i3)][i1]); + } + } + } + return arrayOfDouble; + } + + public static double[] convolveOddX(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int paramInt1, int paramInt2) + { + int i = paramArrayOfDouble2.length; + int j = i - 1; + int k = 2 * paramInt1 - 2; + double[] arrayOfDouble = new double[paramInt1 * paramInt2]; + int m = 0; + for (int n = 0; n < paramInt2; n++) + { + int i2; + for (int i1 = 0; i1 < j; i1++) + { + arrayOfDouble[m] = 0.0D; + for (i2 = 1; i2 <= i1; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m + i2)] - paramArrayOfDouble1[(m - i2)]); + } + for (i2 = i1 + 1; i2 < i; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m + i2)] - paramArrayOfDouble1[(i2 - i1 + n * paramInt1)]); + } + m++; + } + for (int i1 = j; i1 <= paramInt1 - i; i1++) + { + arrayOfDouble[m] = 0.0D; + for (i2 = 1; i2 < i; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m + i2)] - paramArrayOfDouble1[(m - i2)]); + } + m++; + } + for (int i1 = paramInt1 - j; i1 < paramInt1; i1++) + { + arrayOfDouble[m] = 0.0D; + for (i2 = 1; i2 < paramInt1 - i1; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(m + i2)] - paramArrayOfDouble1[(m - i2)]); + } + for (i2 = paramInt1 - i1; i2 < i; i2++) { + arrayOfDouble[m] += paramArrayOfDouble2[i2] * (paramArrayOfDouble1[(k - i2 - i1 + n * paramInt1)] - paramArrayOfDouble1[(m - i2)]); + } + m++; + } + } + return arrayOfDouble; + } + + public static float[] convolveOddX(float[] paramArrayOfFloat1, float[] paramArrayOfFloat2, int paramInt1, int paramInt2) + { + int i = paramArrayOfFloat2.length; + int j = i - 1; + int k = 2 * paramInt1 - 2; + float[] arrayOfFloat = new float[paramInt1 * paramInt2]; + int m = 0; + for (int n = 0; n < paramInt2; n++) + { + int i2; + for (int i1 = 0; i1 < j; i1++) + { + arrayOfFloat[m] = 0.0F; + for (i2 = 1; i2 <= i1; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m + i2)] - paramArrayOfFloat1[(m - i2)]); + } + for (i2 = i1 + 1; i2 < i; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m + i2)] - paramArrayOfFloat1[(i2 - i1 + n * paramInt1)]); + } + m++; + } + for (int i1 = j; i1 <= paramInt1 - i; i1++) + { + arrayOfFloat[m] = 0.0F; + for (i2 = 1; i2 < i; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m + i2)] - paramArrayOfFloat1[(m - i2)]); + } + m++; + } + for (int i1 = paramInt1 - j; i1 < paramInt1; i1++) + { + arrayOfFloat[m] = 0.0F; + for (i2 = 1; i2 < paramInt1 - i1; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(m + i2)] - paramArrayOfFloat1[(m - i2)]); + } + for (i2 = paramInt1 - i1; i2 < i; i2++) { + arrayOfFloat[m] += paramArrayOfFloat2[i2] * (paramArrayOfFloat1[(k - i2 - i1 + n * paramInt1)] - paramArrayOfFloat1[(m - i2)]); + } + m++; + } + } + return arrayOfFloat; + } + + public static double[][] convolveOddY(double[][] paramArrayOfDouble, double[] paramArrayOfDouble1) + { + int i = paramArrayOfDouble.length; + int j = paramArrayOfDouble.length; + int k = paramArrayOfDouble1.length; + int m = k - 1; + int n = 2 * j - 2; + double[][] arrayOfDouble = new double[i][j]; + for (int i1 = 0; i1 < i; i1++) + { + int i3; + for (int i2 = 0; i2 < m; i2++) + { + arrayOfDouble[i1][i2] = 0.0D; + for (i3 = 1; i3 <= i2; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 + i3)] - paramArrayOfDouble[i1][(i2 - i3)]); + } + for (i3 = i2 + 1; i3 < k; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 + i3)] - paramArrayOfDouble[i1][(i3 - i2)]); + } + } + for (int i2 = m; i2 <= j - k; i2++) + { + arrayOfDouble[i1][i2] = 0.0D; + for (i3 = 1; i3 < k; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 + i3)] - paramArrayOfDouble[i1][(i2 - i3)]); + } + } + for (int i2 = j - m; i2 < j; i2++) + { + arrayOfDouble[i1][i2] = 0.0D; + for (i3 = 1; i3 < j - i2; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(i2 + i3)] - paramArrayOfDouble[i1][(i2 - i3)]); + } + for (i3 = j - i2; i3 < k; i3++) { + arrayOfDouble[i1][i2] += paramArrayOfDouble1[i3] * (paramArrayOfDouble[i1][(n - i3 - i2)] - paramArrayOfDouble[i1][(i2 - i3)]); + } + } + } + return arrayOfDouble; + } + + public static double[] convolveOddY(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int paramInt1, int paramInt2) + { + int i = paramArrayOfDouble2.length; + int j = i - 1; + int k = 2 * paramInt2 - 2; + double[] arrayOfDouble = new double[paramInt1 * paramInt2]; + for (int i1 = 0; i1 < paramInt1; i1++) + { + int m; + int i3; + int n; + for (int i2 = 0; i2 < j; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfDouble[m] = 0.0D; + for (i3 = 1; i3 <= i2; i3++) + { + n = i3 * paramInt1; + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m + n)] - paramArrayOfDouble1[(m - n)]); + } + for (i3 = i2 + 1; i3 < i; i3++) { + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m + i3 * paramInt1)] - paramArrayOfDouble1[((i3 - i2) * paramInt1 + i1)]); + } + } + for (int i2 = j; i2 <= paramInt2 - i; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfDouble[m] = 0.0D; + for (i3 = 1; i3 < i; i3++) + { + n = i3 * paramInt1; + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m + n)] - paramArrayOfDouble1[(m - n)]); + } + } + for (int i2 = paramInt2 - j; i2 < paramInt2; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfDouble[m] = 0.0D; + for (i3 = 1; i3 < paramInt2 - i2; i3++) + { + n = i3 * paramInt1; + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[(m + n)] - paramArrayOfDouble1[(m - n)]); + } + for (i3 = paramInt2 - i2; i3 < i; i3++) { + arrayOfDouble[m] += paramArrayOfDouble2[i3] * (paramArrayOfDouble1[((k - i3 - i2) * paramInt1 + i1)] - paramArrayOfDouble1[(m - i3 * paramInt1)]); + } + } + } + return arrayOfDouble; + } + + public static float[] convolveOddY(float[] paramArrayOfFloat1, float[] paramArrayOfFloat2, int paramInt1, int paramInt2) + { + int i = paramArrayOfFloat2.length; + int j = i - 1; + int k = 2 * paramInt2 - 2; + float[] arrayOfFloat = new float[paramInt1 * paramInt2]; + for (int i1 = 0; i1 < paramInt1; i1++) + { + int m; + int i3; + int n; + for (int i2 = 0; i2 < j; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfFloat[m] = 0.0F; + for (i3 = 1; i3 <= i2; i3++) + { + n = i3 * paramInt1; + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m + n)] - paramArrayOfFloat1[(m - n)]); + } + for (i3 = i2 + 1; i3 < i; i3++) { + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m + i3 * paramInt1)] - paramArrayOfFloat1[((i3 - i2) * paramInt1 + i1)]); + } + } + for (int i2 = j; i2 <= paramInt2 - i; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfFloat[m] = 0.0F; + for (i3 = 1; i3 < i; i3++) + { + n = i3 * paramInt1; + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m + n)] - paramArrayOfFloat1[(m - n)]); + } + } + for (int i2 = paramInt2 - j; i2 < paramInt2; i2++) + { + m = i1 + i2 * paramInt1; + arrayOfFloat[m] = 0.0F; + for (i3 = 1; i3 < paramInt2 - i2; i3++) + { + n = i3 * paramInt1; + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[(m + n)] - paramArrayOfFloat1[(m - n)]); + } + for (i3 = paramInt2 - i2; i3 < i; i3++) { + arrayOfFloat[m] += paramArrayOfFloat2[i3] * (paramArrayOfFloat1[((k - i3 - i2) * paramInt1 + i1)] - paramArrayOfFloat1[(m - i3 * paramInt1)]); + } + } + } + return arrayOfFloat; + } + + public static double[] convolveEvenX(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int[] paramArrayOfInt, int paramInt1, int paramInt2, int paramInt3, int paramInt4) + { + int i = paramArrayOfInt[0]; + int j = paramArrayOfInt[1]; + int k = i * j; + int m = paramArrayOfDouble2.length; + int n = m - 1; + int i1 = 2 * i - 2; + double[] arrayOfDouble = new double[(paramInt2 - paramInt1 + 1) * (paramInt4 - paramInt3 + 1)]; + int i3 = 0; + int i4; + int i5; + int i2; + int i6; + if ((paramInt1 < n) && (paramInt2 > i - m)) { + for (i4 = paramInt3; i4 <= paramInt4; i4++) + { + for (i5 = paramInt1; i5 < n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 <= i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + for (i6 = i5 + 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i6 - i5 + i4 * i)] + paramArrayOfDouble1[(i2 + i6)]); + } + i3++; + } + for (i5 = n; i5 < paramInt2 - n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + i3++; + } + for (i5 = paramInt2 - n; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 < i - i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + for (i6 = i - i5; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i1 - i6 - i5 + i4 * i)] + paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + } + } else if ((paramInt1 < n) && (paramInt2 <= i - m)) { + for (i4 = paramInt3; i4 <= paramInt4; i4++) + { + for (i5 = paramInt1; i5 < n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 <= i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + for (i6 = i5 + 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i6 - i5 + i4 * i)] + paramArrayOfDouble1[(i2 + i6)]); + } + i3++; + } + for (i5 = n; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + i3++; + } + } + } else if ((paramInt1 >= n) && (paramInt2 > i - m)) { + for (i4 = paramInt3; i4 <= paramInt4; i4++) + { + for (i5 = paramInt1; i5 < paramInt2 - n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + i3++; + } + for (i5 = paramInt2 - n; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 < i - i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + for (i6 = i - i5; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i1 - i6 - i5 + i4 * i)] + paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + } + } else { + for (i4 = paramInt3; i4 <= paramInt4; i4++) { + for (i5 = paramInt1; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 - i6)] + paramArrayOfDouble1[(i2 + i6)]); + } + i3++; + } + } + } + return arrayOfDouble; + } + + public static double[] convolveOddX(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int[] paramArrayOfInt, int paramInt1, int paramInt2, int paramInt3, int paramInt4) + { + int i = paramArrayOfInt[0]; + int j = paramArrayOfInt[1]; + int k = i * j; + int m = paramArrayOfDouble2.length; + int n = m - 1; + int i1 = 2 * i - 2; + double[] arrayOfDouble = new double[(paramInt2 - paramInt1 + 1) * (paramInt4 - paramInt3 + 1)]; + int i3 = 0; + int i4; + int i5; + int i2; + int i6; + if ((paramInt1 < n) && (paramInt2 > i - m)) { + for (i4 = paramInt3; i4 <= paramInt4; i4++) + { + for (i5 = paramInt1; i5 < n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 <= i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + for (i6 = i5 + 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i6 - i5 + i4 * i)]); + } + i3++; + } + for (i5 = n; i5 < paramInt2 - n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + for (i5 = paramInt2 - n; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 < i - i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + for (i6 = i - i5; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i1 - i6 - i5 + i4 * i)] - paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + } + } else if ((paramInt1 < n) && (paramInt2 <= i - m)) { + for (i4 = paramInt3; i4 <= paramInt4; i4++) + { + for (i5 = paramInt1; i5 < n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 <= i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + for (i6 = i5 + 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i6 - i5 + i4 * i)]); + } + i3++; + } + for (i5 = n; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + } + } else if ((paramInt1 >= n) && (paramInt2 > i - m)) { + for (i4 = paramInt3; i4 <= paramInt4; i4++) + { + for (i5 = paramInt1; i5 < paramInt2 - n; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + for (i5 = paramInt2 - n; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 < i - i5; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + for (i6 = i - i5; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i1 - i6 - i5 + i4 * i)] - paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + } + } else { + for (i4 = paramInt3; i4 <= paramInt4; i4++) { + for (i5 = paramInt1; i5 <= paramInt2; i5++) + { + i2 = i5 + i4 * i; + arrayOfDouble[i3] = 0.0D; + for (i6 = 1; i6 < m; i6++) { + arrayOfDouble[i3] += paramArrayOfDouble2[i6] * (paramArrayOfDouble1[(i2 + i6)] - paramArrayOfDouble1[(i2 - i6)]); + } + i3++; + } + } + } + return arrayOfDouble; + } + + public static double[] convolveEvenY(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int[] paramArrayOfInt, int paramInt1, int paramInt2) + { + int i = paramArrayOfInt[0]; + int j = paramArrayOfInt[1]; + int k = i * j; + int m = paramArrayOfDouble2.length; + int n = m - 1; + int i1 = 2 * j - 2; + double[] arrayOfDouble = new double[i * (paramInt2 - paramInt1 + 1)]; + int i4 = 0; + int i5; + int i6; + int i2; + int i7; + int i3; + if ((paramInt1 < n) && (paramInt2 > j - m)) { + for (i5 = 0; i5 < i; i5++) + { + for (i6 = paramInt1; i6 < n; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 <= i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + for (i7 = i6 + 1; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[((i7 - i6) * i + i5)] + paramArrayOfDouble1[(i2 + i7 * i)]); + } + } + for (i6 = n; i6 < paramInt2 - n; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + } + for (i6 = paramInt2 - n; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 < j - i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + for (i7 = j - i6; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[((i1 - i7 - i6) * i + i5)] + paramArrayOfDouble1[(i2 - i7 * i)]); + } + } + } + } else if ((paramInt1 < n) && (paramInt2 <= j - m)) { + for (i5 = 0; i5 < i; i5++) + { + for (i6 = paramInt1; i6 < n; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 <= i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + for (i7 = i6 + 1; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[((i7 - i6) * i + i5)] + paramArrayOfDouble1[(i2 + i7 * i)]); + } + } + for (i6 = n; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + } + } + } else if ((paramInt1 >= n) && (paramInt2 > j - m)) { + for (i5 = 0; i5 < i; i5++) + { + for (i6 = paramInt1; i6 < j - n; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + } + for (i6 = j - n; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 < j - i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + for (i7 = j - i6; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[((i1 - i7 - i6) * i + i5)] + paramArrayOfDouble1[(i2 - i7 * i)]); + } + } + } + } else { + for (i5 = 0; i5 < i; i5++) { + for (i6 = paramInt1; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i2 - paramInt1 * i; + arrayOfDouble[i4] = (paramArrayOfDouble2[0] * paramArrayOfDouble1[i2]); + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 - i3)] + paramArrayOfDouble1[(i2 + i3)]); + } + } + } + } + return arrayOfDouble; + } + + public static double[] convolveOddY(double[] paramArrayOfDouble1, double[] paramArrayOfDouble2, int[] paramArrayOfInt, int paramInt1, int paramInt2) + { + int i = paramArrayOfInt[0]; + int j = paramArrayOfInt[1]; + int k = i * j; + int m = paramArrayOfDouble2.length; + int n = m - 1; + int i1 = 2 * j - 2; + double[] arrayOfDouble = new double[i * (paramInt2 - paramInt1 + 1)]; + int i4 = 0; + int i5; + int i6; + int i2; + int i7; + int i3; + if ((paramInt1 < n) && (paramInt2 > j - m)) { + for (i5 = 0; i5 < i; i5++) + { + for (i6 = paramInt1; i6 < n; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 <= i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + for (i7 = i6 + 1; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i7 * i)] - paramArrayOfDouble1[((i7 - i6) * i + i5)]); + } + } + for (i6 = n; i6 <= j - m; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + } + for (i6 = j - n; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 < j - i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + for (i7 = j - i6; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[((i1 - i7 - i6) * i + i5)] - paramArrayOfDouble1[(i2 - i7 * i)]); + } + } + } + } else if ((paramInt1 < n) && (paramInt2 <= j - m)) { + for (i5 = 0; i5 < i; i5++) + { + for (i6 = paramInt1; i6 < n; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 <= i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + for (i7 = i6 + 1; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i7 * i)] - paramArrayOfDouble1[((i7 - i6) * i + i5)]); + } + } + for (i6 = n; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + } + } + } else if ((paramInt1 >= n) && (paramInt2 > j - m)) { + for (i5 = 0; i5 < i; i5++) + { + for (i6 = paramInt1; i6 <= j - m; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + } + for (i6 = j - n; i6 < paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 < j - i6; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + for (i7 = j - i6; i7 < m; i7++) { + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[((i1 - i7 - i6) * i + i5)] - paramArrayOfDouble1[(i2 - i7 * i)]); + } + } + } + } else { + for (i5 = 0; i5 < i; i5++) { + for (i6 = paramInt1; i6 <= paramInt2; i6++) + { + i2 = i5 + i6 * i; + i4 = i5 + (i6 - paramInt1) * i; + arrayOfDouble[i4] = 0.0D; + for (i7 = 1; i7 < m; i7++) + { + i3 = i7 * i; + arrayOfDouble[i4] += paramArrayOfDouble2[i7] * (paramArrayOfDouble1[(i2 + i3)] - paramArrayOfDouble1[(i2 - i3)]); + } + } + } + } + return arrayOfDouble; + } + + +} diff --git a/src/main/java/utility/CovistoModule.java b/src/main/java/utility/CovistoModule.java new file mode 100644 index 0000000..9b54f0b --- /dev/null +++ b/src/main/java/utility/CovistoModule.java @@ -0,0 +1,79 @@ +package utility; + +import javax.swing.ImageIcon; + +import org.scijava.plugin.SciJavaPlugin; + +/** + * Interface for TrackMate modules. + *

      + * Modules are the way users can extend TrackMate. We rely on SciJava automatic + * plugin discovery to facilitate extension. To have a module discovered + * annotate its class with for instance + * + *

      + * @Plugin( type = SpotAnalyzerFactory.class, priority = 1d, visible = false )
      + * 
      + * + * This will have a + * {@link fiji.plugin.trackmate.features.spot.SpotAnalyzerFactory} module + * registered in TrackMate. It will be given a priority of 1 (0 is the default), + * which means that it will be run after the analyzers of a lower + * priority. This is used when some modules depends on the results generated by + * other modules. The visible tag determines whether the module is + * visible or not in the GUI. + *

      + * Currently there are 7 types of modules: + *

        + *
      • {@link fiji.plugin.trackmate.detection.SpotDetectorFactory}: generates + * detectors for the detection step of TrackMate. + *
      • {@link fiji.plugin.trackmate.features.spot.SpotAnalyzerFactory}: + * generates analyzers that grant spots with scalar numerical features. + *
      • {@link fiji.plugin.trackmate.visualization.ViewFactory}: generates views + * that can display the detection and tracking results. + *
      • {@link fiji.plugin.trackmate.tracking.SpotTrackerFactory}: generates + * particle linkers for the tracking step of TrackMate. + *
      • {@link fiji.plugin.trackmate.features.track.TrackAnalyzer}: compute + * scalar numerical features for tracks. + *
      • {@link fiji.plugin.trackmate.features.edges.EdgeAnalyzer}: compute scalar + * numerical features for edges (individual links between spots). + *
      • {@link fiji.plugin.trackmate.action.TrackMateActionFactory}: generates + * actions that provide general use actions for TrackMate from the GUI. + *
      + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> 2014 + * + */ +public interface CovistoModule extends SciJavaPlugin +{ + + /** + * Returns a html string containing a descriptive information about this + * module. + * + * @return a html string. + */ + public String getInfoText(); + + /** + * Returns the icon for this action. Can be null. + * + * @return the icon. Returns null to be safely ignored. + */ + public ImageIcon getIcon(); + + /** + * Returns a unique identifier of this module. + * + * @return the action key, as a string. + */ + public String getKey(); + + /** + * Returns the human-compliant name of this module. + * + * @return the name, as a String. + */ + public String getName(); + +} diff --git a/src/main/java/utility/CovistoSlicer.java b/src/main/java/utility/CovistoSlicer.java new file mode 100644 index 0000000..f198e8e --- /dev/null +++ b/src/main/java/utility/CovistoSlicer.java @@ -0,0 +1,288 @@ +package utility; + +import java.awt.Rectangle; +import java.util.ArrayList; + +import ij.gui.Roi; +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.stats.Normalize; +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.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; +import interactivePreprocessing.InteractiveMethods; + +public class CovistoSlicer { + + public static RandomAccessibleInterval getCurrentViewLarge(RandomAccessibleInterval originalimg, int thirdDimension) { + + + + final FloatType type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1), originalimg.dimension(2) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + + return totalimg; + + } + /** + * Generic, type-agnostic method to create an identical copy of an Img + * + * @param currentPreprocessedimg2 + * - the Img to copy + * @return - the copy of the Img + */ + public static Img PREcopytoByteImage(final RandomAccessibleInterval input) { + // create a new Image with the same properties + // note that the input provides the size for the new image as it + // implements + // the Interval interface + RandomAccessibleInterval inputcopy = copyImage(input); + Normalize.normalize(Views.iterable(inputcopy), new FloatType(0), new FloatType(255)); + final UnsignedByteType type = new UnsignedByteType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputcopy, type); + final Img output = factory.create(inputcopy, type); + // create a cursor for both images + RandomAccess ranac = inputcopy.randomAccess(); + Cursor cursorOutput = output.cursor(); + + // iterate over the input + while (cursorOutput.hasNext()) { + // move both cursors forward by one pixel + cursorOutput.fwd(); + + ranac.setPosition(cursorOutput); + + // set the value of this pixel of the output image to the same as + // the input, + // every Type supports T.set( T type ) + cursorOutput.get().set((int) Math.round(ranac.get().get())); + } + + // return the copy + return output; + } + public static Img copyImage(final RandomAccessibleInterval input) { + // create a new Image with the same dimensions but the other imgFactory + // note that the input provides the size for the new image by + // implementing the Interval interface + Img output = new ArrayImgFactory().create(input, Views.iterable(input).firstElement()); + + // create a cursor that automatically localizes itself on every move + Cursor cursorInput = Views.iterable(input).localizingCursor(); + RandomAccess randomAccess = output.randomAccess(); + + // iterate over the input cursor + while (cursorInput.hasNext()) { + // move input cursor forward + cursorInput.fwd(); + + // set the output cursor to the position of the input cursor + randomAccess.setPosition(cursorInput); + + // set the value of this pixel of the output image, every Type + // supports T.set( T type ) + randomAccess.get().set(cursorInput.get()); + } + + // return the copy + return output; + } + public static < T extends RealType< T > & NativeType< T >> RandomAccessibleInterval getCurrentView(RandomAccessibleInterval originalimg, int thirdDimension, int thirdDimensionSize, int fourthDimension, int fourthDimensionSize) { + + final T type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + if (thirdDimensionSize == 0) { + + totalimg = originalimg; + } + + if (thirdDimensionSize > 0 && fourthDimensionSize == 0) { + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + } + + if (fourthDimensionSize > 0) { + + RandomAccessibleInterval pretotalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + totalimg = Views.hyperSlice(pretotalimg, 2, fourthDimension - 1); + } + + return totalimg; + + } + + public static RandomAccessibleInterval getCurrentViewBit(RandomAccessibleInterval originalimg, int thirdDimension, int thirdDimensionSize, int fourthDimension, int fourthDimensionSize) { + + final BitType type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + if (thirdDimensionSize == 0) { + + totalimg = originalimg; + } + + if (thirdDimensionSize > 0 && fourthDimensionSize == 0) { + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + } + + if (fourthDimensionSize > 0) { + + RandomAccessibleInterval pretotalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + totalimg = Views.hyperSlice(pretotalimg, 2, fourthDimension - 1); + } + return totalimg; + + } + + public static RandomAccessibleInterval getCurrentViewInt(RandomAccessibleInterval originalimg, int thirdDimension, int thirdDimensionSize, int fourthDimension, int fourthDimensionSize) { + + final IntType type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + if (thirdDimensionSize == 0) { + + totalimg = originalimg; + } + + if (thirdDimensionSize > 0 && fourthDimensionSize == 0) { + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + } + + if (fourthDimensionSize > 0) { + + RandomAccessibleInterval pretotalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + totalimg = Views.hyperSlice(pretotalimg, 2, fourthDimension - 1); + } + + return totalimg; + + } + + + public static RandomAccessibleInterval getCurrentViewBitRectangle(RandomAccessibleInterval originalimg, int thirdDimension, int thirdDimensionSize, int fourthDimension, int fourthDimensionSize, Rectangle rect) { + + final BitType type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + long maxY = (long)rect.getMaxY(); + long maxX = (long)rect.getMaxY(); + long minY = (long)rect.getMinY(); + long minX = (long)rect.getMinX(); + + if (thirdDimensionSize == 0) { + + totalimg = originalimg; + } + + if (thirdDimensionSize > 0 && fourthDimensionSize == 0) { + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + } + + if (fourthDimensionSize > 0) { + + RandomAccessibleInterval pretotalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + totalimg = Views.hyperSlice(pretotalimg, 2, fourthDimension - 1); + } + + RandomAccessibleInterval< BitType > view = + Views.interval( totalimg, new long[] { minX, minY }, new long[]{ maxX, maxY } ); + + + return view; + + } + + public static RandomAccessibleInterval getCurrentViewIntRectangle(RandomAccessibleInterval originalimg, int thirdDimension, int thirdDimensionSize, int fourthDimension, int fourthDimensionSize, Rectangle rect) { + + final IntType type = originalimg.randomAccess().get().createVariable(); + long[] dim = { originalimg.dimension(0), originalimg.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + long maxY = (long)rect.getMaxY(); + long maxX = (long)rect.getMaxY(); + long minY = (long)rect.getMinY(); + long minX = (long)rect.getMinX(); + + if (thirdDimensionSize == 0) { + + totalimg = originalimg; + } + + if (thirdDimensionSize > 0 && fourthDimensionSize == 0) { + + totalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + } + + if (fourthDimensionSize > 0) { + + RandomAccessibleInterval pretotalimg = Views.hyperSlice(originalimg, 2, thirdDimension - 1); + + totalimg = Views.hyperSlice(pretotalimg, 2, fourthDimension - 1); + } + + RandomAccessibleInterval< IntType > view = + Views.interval( totalimg, new long[] { minX, minY }, new long[]{ maxX, maxY } ); + + + return view; + + } + + + + + + + + public static float computeValueFromScrollbarPosition(final int scrollbarPosition, final float min, final float max, + final int scrollbarSize) { + return min + (scrollbarPosition / (float) scrollbarSize) * (max - min); + } + + public static int computeScrollbarPositionFromValue(final float sigma, final float min, final float max, + final int scrollbarSize) { + return Math.round(((sigma - min) / (max - min)) * scrollbarSize); + } + + + + +} diff --git a/src/main/java/utility/CovistoUtils.java b/src/main/java/utility/CovistoUtils.java new file mode 100644 index 0000000..a69f26c --- /dev/null +++ b/src/main/java/utility/CovistoUtils.java @@ -0,0 +1,651 @@ +package utility; + + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +import ij.ImagePlus; +import linkers.Dimension; +import net.imagej.ImgPlus; +import net.imagej.ImgPlusMetadata; +import net.imagej.axis.Axes; +import net.imagej.axis.AxisType; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.img.ImagePlusAdapter; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.util.Util; + +/** + * List of static utilities for {@link fiji.plugin.trackmate.TrackMate}. + */ +public class CovistoUtils +{ + + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat( "EEE, d MMM yyyy HH:mm:ss" ); + + /* + * STATIC METHODS + */ + + /** + * Return a new map sorted by its values. Adapted from + * http://stackoverflow.com + * /questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java + */ + public static < K, V extends Comparable< ? super V > > Map< K, V > sortByValue( final Map< K, V > map, final Comparator< V > comparator ) + { + final List< Map.Entry< K, V > > list = new LinkedList< >( map.entrySet() ); + Collections.sort( list, new Comparator< Map.Entry< K, V > >() + { + @Override + public int compare( final Map.Entry< K, V > o1, final Map.Entry< K, V > o2 ) + { + return comparator.compare( o1.getValue(), o2.getValue() ); + } + } ); + + final LinkedHashMap< K, V > result = new LinkedHashMap< >(); + for ( final Map.Entry< K, V > entry : list ) + { + result.put( entry.getKey(), entry.getValue() ); + } + return result; + } + + /** + * Generate a string representation of a map, typically a settings map. + */ + public static final String echoMap( final Map< String, Object > map, final int indent ) + { + // Build string + final StringBuilder builder = new StringBuilder(); + for ( final String key : map.keySet() ) + { + for ( int i = 0; i < indent; i++ ) + { + builder.append( " " ); + } + builder.append( "- " ); + builder.append( key.toLowerCase().replace( "_", " " ) ); + builder.append( ": " ); + final Object obj = map.get( key ); + if ( obj instanceof Map ) + { + builder.append( '\n' ); + @SuppressWarnings( "unchecked" ) + final Map< String, Object > submap = ( Map< String, Object > ) obj; + builder.append( echoMap( submap, indent + 2 ) ); + } + else + { + builder.append( obj.toString() ); + builder.append( '\n' ); + } + } + return builder.toString(); + } + + /** + * Wraps an IJ {@link ImagePlus} in an imglib2 {@link ImgPlus}, without + * parameterized types. The only way I have found to beat javac constraints + * on bounded multiple wildcard. + */ + @SuppressWarnings( "rawtypes" ) + public static final ImgPlus rawWraps( final ImagePlus imp ) + { + final ImgPlus< DoubleType > img = ImagePlusAdapter.wrapImgPlus( imp ); + final ImgPlus raw = img; + return raw; + } + + /** + * Check that the given map has all some keys. Two String collection allows + * specifying that some keys are mandatory, other are optional. + * + * @param map + * the map to inspect. + * @param mandatoryKeys + * the collection of keys that are expected to be in the map. Can + * be null. + * @param optionalKeys + * the collection of keys that can be - or not - in the map. Can + * be null. + * @param errorHolder + * will be appended with an error message. + * @return if all mandatory keys are found in the map, and possibly some + * optional ones, but no others. + */ + public static final < T > boolean checkMapKeys( final Map< T, ? > map, Collection< T > mandatoryKeys, Collection< T > optionalKeys, final StringBuilder errorHolder ) + { + if ( null == optionalKeys ) + { + optionalKeys = new ArrayList< >(); + } + if ( null == mandatoryKeys ) + { + mandatoryKeys = new ArrayList< >(); + } + boolean ok = true; + final Set< T > keySet = map.keySet(); + for ( final T key : keySet ) + { + if ( !( mandatoryKeys.contains( key ) || optionalKeys.contains( key ) ) ) + { + ok = false; + errorHolder.append( "Map contains unexpected key: " + key + ".\n" ); + } + } + + for ( final T key : mandatoryKeys ) + { + if ( !keySet.contains( key ) ) + { + ok = false; + errorHolder.append( "Mandatory key " + key + " was not found in the map.\n" ); + } + } + return ok; + + } + + /** + * Check the presence and the validity of a key in a map, and test it is of + * the desired class. + * + * @param map + * the map to inspect. + * @param key + * the key to find. + * @param expectedClass + * the expected class of the target value . + * @param errorHolder + * will be appended with an error message. + * @return true if the key is found in the map, and map a value of the + * desired class. + */ + public static final boolean checkParameter( final Map< String, Object > map, final String key, final Class< ? > expectedClass, final StringBuilder errorHolder ) + { + final Object obj = map.get( key ); + if ( null == obj ) + { + errorHolder.append( "Parameter " + key + " could not be found in settings map, or is null.\n" ); + return false; + } + if ( !expectedClass.isInstance( obj ) ) + { + errorHolder.append( "Value for parameter " + key + " is not of the right class. Expected " + expectedClass.getName() + ", got " + obj.getClass().getName() + ".\n" ); + return false; + } + return true; + } + + /** + * Returns the mapping in a map that is targeted by a list of keys, in the + * order given in the list. + */ + public static final < J, K > List< K > getArrayFromMaping( final List< J > keys, final Map< J, K > mapping ) + { + final List< K > names = new ArrayList< >( keys.size() ); + for ( int i = 0; i < keys.size(); i++ ) + { + names.add( mapping.get( keys.get( i ) ) ); + } + return names; + } + + /** + * Translate each ThreeDRoiobject of the given collection by the amount specified in + * argument. The distances are all understood in physical units. + *

      + * This is meant to deal with a cropped image. The translation will bring + * the ThreeDRoiobject coordinates back to the top-left corner of the un-cropped image + * reference. + */ + public static void translateThreeDRoiobjects( final Collection< ThreeDRoiobject > ThreeDRoiobjects, final double dx, final double dy, final double dz ) + { + final double[] dval = new double[] { dx, dy, dz }; + final String[] features = new String[] { ThreeDRoiobject.XPOSITION, ThreeDRoiobject.YPOSITION, ThreeDRoiobject.ZPOSITION }; + Double val; + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + for ( int i = 0; i < features.length; i++ ) + { + val = ThreeDRoiobject.getFeature( features[ i ] ); + if ( null != val ) + { + ThreeDRoiobject.putFeature( features[ i ], val + dval[ i ] ); + } + } + } + } + + /* + * ImgPlus & calibration & axes + */ + + /** + * Returns the index of the target axis in the given metadata. Return -1 if + * the axis was not found. + */ + private static final int findAxisIndex( final ImgPlusMetadata img, final AxisType axis ) + { + return img.dimensionIndex( axis ); + } + + public static final int findXAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.X ); + } + + public static final int findYAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.Y ); + } + + public static final int findZAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.Z ); + } + + public static final int findTAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.TIME ); + } + + public static final int findCAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.CHANNEL ); + } + + /** + * Return the xyz calibration stored in an {@link ImgPlusMetadata} in a + * 3-elements double array. Calibration is ordered as X, Y, Z. If one axis + * is not found, then the calibration for this axis takes the value of 1. + */ + public static final double[] getSpatialCalibration( final ImgPlusMetadata img ) + { + final double[] calibration = Util.getArrayFromValue( 1d, 3 ); + + for ( int d = 0; d < img.numDimensions(); d++ ) + { + if ( img.axis( d ).type() == Axes.X ) + { + calibration[ 0 ] = img.averageScale( d ); + } + else if ( img.axis( d ).type() == Axes.Y ) + { + calibration[ 1 ] = img.averageScale( d ); + } + else if ( img.axis( d ).type() == Axes.Z ) + { + calibration[ 2 ] = img.averageScale( d ); + } + } + return calibration; + } + + public static double[] getSpatialCalibration( final ImagePlus imp ) + { + final double[] calibration = Util.getArrayFromValue( 1d, 3 ); + calibration[ 0 ] = imp.getCalibration().pixelWidth; + calibration[ 1 ] = imp.getCalibration().pixelHeight; + if ( imp.getNSlices() > 1 ) + { + calibration[ 2 ] = imp.getCalibration().pixelDepth; + } + return calibration; + } + + /** + * Returns an estimate of the pth percentile of the values in + * the values array. Taken from commons-math. + */ + public static final double getPercentile( final double[] values, final double p ) + { + + final int size = values.length; + if ( ( p > 1 ) || ( p <= 0 ) ) { throw new IllegalArgumentException( "invalid quantile value: " + p ); } + // always return single value for n = 1 + if ( size == 0 ) { return Double.NaN; } + if ( size == 1 ) { return values[ 0 ]; } + final double n = size; + final double pos = p * ( n + 1 ); + final double fpos = Math.floor( pos ); + final int intPos = ( int ) fpos; + final double dif = pos - fpos; + final double[] sorted = new double[ size ]; + System.arraycopy( values, 0, sorted, 0, size ); + Arrays.sort( sorted ); + + if ( pos < 1 ) { return sorted[ 0 ]; } + if ( pos >= n ) { return sorted[ size - 1 ]; } + final double lower = sorted[ intPos - 1 ]; + final double upper = sorted[ intPos ]; + return lower + dif * ( upper - lower ); + } + + /** + * Returns [range, min, max] of the given double array. + * + * @return A double[] of length 3, where index 0 is the range, index 1 is + * the min, and index 2 is the max. + */ + private static final double[] getRange( final double[] data ) + { + double min = Double.POSITIVE_INFINITY; + double max = Double.NEGATIVE_INFINITY; + double value; + for ( int i = 0; i < data.length; i++ ) + { + value = data[ i ]; + if ( value < min ) + { + min = value; + } + if ( value > max ) + { + max = value; + } + } + return new double[] { ( max - min ), min, max }; + } + + /** + * Store the x, y, z coordinates of the specified ThreeDRoiobject in the first 3 + * elements of the specified double array. + */ + public static final void localize( final ThreeDRoiobject ThreeDRoiobject, final double[] coords ) + { + coords[ 0 ] = ThreeDRoiobject.getFeature( ThreeDRoiobject.XPOSITION ).doubleValue(); + coords[ 1 ] = ThreeDRoiobject.getFeature( ThreeDRoiobject.YPOSITION ).doubleValue(); + coords[ 2 ] = ThreeDRoiobject.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + } + + /** + * Return the optimal bin number for a histogram of the data given in array, + * using the Freedman and Diaconis rule (bin_space = 2*IQR/n^(1/3)). It is + * ensured that the bin number returned is not smaller and no bigger than + * the bounds given in argument. + */ + public static final int getNBins( final double[] values, final int minBinNumber, final int maxBinNumber ) + { + final int size = values.length; + final double q1 = getPercentile( values, 0.25 ); + final double q3 = getPercentile( values, 0.75 ); + final double iqr = q3 - q1; + final double binWidth = 2 * iqr * Math.pow( size, -0.33 ); + final double[] range = getRange( values ); + int nBin = ( int ) ( range[ 0 ] / binWidth + 1 ); + if ( nBin > maxBinNumber ) + { + nBin = maxBinNumber; + } + else if ( nBin < minBinNumber ) + { + nBin = minBinNumber; + } + return nBin; + } + + /** + * Return the optimal bin number for a histogram of the data given in array, + * using the Freedman and Diaconis rule (bin_space = 2*IQR/n^(1/3)). It is + * ensured that the bin number returned is not smaller than 8 and no bigger + * than 256. + */ + private static final int getNBins( final double[] values ) + { + return getNBins( values, 8, 256 ); + } + + /** + * Create a histogram from the data given. + */ + private static final int[] histogram( final double data[], final int nBins ) + { + final double[] range = getRange( data ); + final double binWidth = range[ 0 ] / nBins; + final int[] hist = new int[ nBins ]; + int index; + + if ( nBins > 0 ) + { + for ( int i = 0; i < data.length; i++ ) + { + index = Math.min( ( int ) Math.floor( ( data[ i ] - range[ 1 ] ) / binWidth ), nBins - 1 ); + hist[ index ]++; + } + } + return hist; + } + + /** + * Return a threshold for the given data, using an Otsu histogram + * thresholding method. + */ + public static final double otsuThreshold( final double[] data ) + { + return otsuThreshold( data, getNBins( data ) ); + } + + /** + * Return a threshold for the given data, using an Otsu histogram + * thresholding method with a given bin number. + */ + private static final double otsuThreshold( final double[] data, final int nBins ) + { + final int[] hist = histogram( data, nBins ); + final int thresholdIndex = otsuThresholdIndex( hist, data.length ); + final double[] range = getRange( data ); + final double binWidth = range[ 0 ] / nBins; + return range[ 1 ] + binWidth * thresholdIndex; + } + + /** + * Given a histogram array hist, built with an initial amount + * of nPoints data item, this method return the bin index that + * thresholds the histogram in 2 classes. The threshold is performed using + * the Otsu Threshold Method. + * + * @param hist + * the histogram array + * @param nPoints + * the number of data items this histogram was built on + * @return the bin index of the histogram that thresholds it + */ + private static final int otsuThresholdIndex( final int[] hist, final int nPoints ) + { + final int total = nPoints; + + double sum = 0; + for ( int t = 0; t < hist.length; t++ ) + { + sum += t * hist[ t ]; + } + + double sumB = 0; + int wB = 0; + int wF = 0; + + double varMax = 0; + int threshold = 0; + + for ( int t = 0; t < hist.length; t++ ) + { + wB += hist[ t ]; // Weight Background + if ( wB == 0 ) + { + continue; + } + + wF = total - wB; // Weight Foreground + if ( wF == 0 ) + { + break; + } + + sumB += ( t * hist[ t ] ); + + final double mB = sumB / wB; // Mean Background + final double mF = ( sum - sumB ) / wF; // Mean Foreground + + // Calculate Between Class Variance + final double varBetween = wB * wF * ( mB - mF ) * ( mB - mF ); + + // Check if new maximum found + if ( varBetween > varMax ) + { + varMax = varBetween; + threshold = t; + } + } + return threshold; + } + + /** + * Return a String unit for the given dimension. When suitable, the unit is + * taken from the settings field, which contains the spatial and time units. + * Otherwise, default units are used. + */ + public static final String getUnitsFor( final Dimension dimension, final String spaceUnits, final String timeUnits ) + { + String units = "no unit"; + switch ( dimension ) + { + case ANGLE: + units = "Radians"; + break; + case INTENSITY: + units = "Counts"; + break; + case INTENSITY_SQUARED: + units = "Counts^2"; + break; + case NONE: + units = ""; + break; + case POSITION: + case LENGTH: + units = spaceUnits; + break; + case QUALITY: + units = "Quality"; + break; + case TIME: + units = timeUnits; + break; + case VELOCITY: + units = spaceUnits + "/" + timeUnits; + break; + case RATE: + units = "/" + timeUnits; + break; + default: + break; + case STRING: + return null; + } + return units; + } + + public static final String getCurrentTimeString() + { + return DATE_FORMAT.format( new Date() ); + } + + /** + * Returns an interval object that in the specified {@link ImgPlus} slice + * in a single time frame. + *

      + * The specified {@link Settings} object is used to determine a crop-cube + * that will determine the X,Y,Z size of the interval. A single channel will + * be taken in the case of a multi-channel image. If the detector set in the + * settings object has a parameter for the target channel + * {@link fiji.plugin.trackmate.detection.DetectorKeys#KEY_TARGET_CHANNEL}, + * it will be used; otherwise the first channel will be taken. + *

      + * If the specified {@link ImgPlus} has a time axis, it will be dropped and + * the returned interval will have one dimension less. + * + * @param img + * the source image into which the interval is to be defined. + * @param settings + * the settings object that will determine the interval size. + * @return a new interval. + */ + public static final Interval getInterval( final ImgPlus< ? > img, final Settings settings ) + { + final long[] max = new long[ img.numDimensions() ]; + final long[] min = new long[ img.numDimensions() ]; + + // X, we must have it. + final int xindex = CovistoUtils.findXAxisIndex( img ); + min[ xindex ] = settings.xstart; + max[ xindex ] = settings.xend; + + // Y, we must have it. + final int yindex = CovistoUtils.findYAxisIndex( img ); + min[ yindex ] = settings.ystart; + max[ yindex ] = settings.yend; + + // Z, we MIGHT have it. + final int zindex = CovistoUtils.findZAxisIndex( img ); + if ( zindex >= 0 ) + { + min[ zindex ] = settings.zstart; + max[ zindex ] = settings.zend; + } + + + + // TIME, we might have it, but anyway we leave the start & end + // management to elsewhere. + final int tindex = CovistoUtils.findTAxisIndex( img ); + + /* + * We want to exclude time (if we have it) from out interval and source, + * so that we can provide the detector instance with a hyperslice that + * does NOT have time as a dimension. + */ + final long[] intervalMin; + final long[] intervalMax; + if ( tindex >= 0 ) + { + intervalMin = new long[ min.length - 1 ]; + intervalMax = new long[ min.length - 1 ]; + int nindex = -1; + for ( int d = 0; d < min.length; d++ ) + { + if ( d == tindex ) + { + continue; + } + nindex++; + intervalMin[ nindex ] = Math.max( 0l, min[ d ] ); + intervalMax[ nindex ] = Math.min( img.max( d ), max[ d ] ); + } + } + else + { + intervalMin = min; + intervalMax = max; + } + final FinalInterval interval = new FinalInterval( intervalMin, intervalMax ); + return interval; + } + + private CovistoUtils() + {} +} diff --git a/src/main/java/utility/CovsitoProgressBar.java b/src/main/java/utility/CovsitoProgressBar.java new file mode 100644 index 0000000..bea0456 --- /dev/null +++ b/src/main/java/utility/CovsitoProgressBar.java @@ -0,0 +1,31 @@ +package utility; + +import java.awt.Color; + +import javax.swing.JProgressBar; + +public class CovsitoProgressBar { + + + public static void CovistoSetProgressBar(JProgressBar jpb, double percent, String message) { + + jpb.setValue((int) Math.round(percent)); + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString(message); + + } + + public static void CovistoSetProgressBar(JProgressBar jpb, String message) { + + + + jpb.setOpaque(true); + jpb.setStringPainted(true); + jpb.setString(message); + + } + + + +} diff --git a/src/main/java/utility/DetectorKeys.java b/src/main/java/utility/DetectorKeys.java new file mode 100644 index 0000000..dd9cbf0 --- /dev/null +++ b/src/main/java/utility/DetectorKeys.java @@ -0,0 +1,111 @@ +package utility; +/** + * A class to store key names for parameters of the current {@link SpotDetector} + * s. + */ +public class DetectorKeys +{ + + /** + * The attribute name for the {@link SpotDetectorFactory} key when + * marshalling to or unmarhsalling from XML. + */ + public static final String XML_ATTRIBUTE_DETECTOR_NAME = "DETECTOR_NAME"; + + /** + * The key identifying the parameter setting the target channel for + * detection in a possible multi-channel image. Channels are here + * 1-numbered, meaning that "1" is the first available channel (and all + * images have at least this channel). Expected valkues are {@link Integer}s + * greater than 1. + *

      + * Currently used by: + *

        + *
      • {@link LogDetector} + *
      • {@link DogDetector} + *
      • {@link DownsampleLogDetector} + *
      + */ + public static final String KEY_TARGET_CHANNEL = "TARGET_CHANNEL"; + + /** A default value for the {@link #KEY_TARGET_CHANNEL} parameter. */ + public static final int DEFAULT_TARGET_CHANNEL = 1; + + /** + * The key identifying the parameter that sets the target radius for the + * detector. Expected values are {@link Double}s. + *

      + * Currently used by: + *

        + *
      • {@link LogDetector} + *
      • {@link DogDetector} + *
      • {@link DownsampleLogDetector} + *
      • {@link ManualDetectorFactory} + *
      + */ + public static final String KEY_RADIUS = "RADIUS"; + + /** A default value for the {@link #KEY_RADIUS} parameter. */ + public static final double DEFAULT_RADIUS = 5d; + + /** + * The key identifying the parameter that sets the threshold for the LoG + * detector. Spot found with a filtered value lowered than this threshold + * will not be retained. Expected values are {@link Double}s. + *

      + * Currently used by: + *

        + *
      • {@link LogDetector} + *
      • {@link DogDetector} + *
      • {@link DownsampleLogDetector} + *
      + */ + public static final String KEY_THRESHOLD = "THRESHOLD"; + + /** A default value for the {@link #KEY_THRESHOLD} parameter. */ + public static final double DEFAULT_THRESHOLD = 0d; + + /** + * The key identifying the parameter that sets the downsampling factor + * applied to the source image prior to segmentation. Expected values are + * {@link Integer}s greater than 1. + *

      + * Currently used by {@link DownsampleLogDetector} + */ + public static final String KEY_DOWNSAMPLE_FACTOR = "DOWNSAMPLE_FACTOR"; + + /** A default value for the {@link #KEY_DOWNSAMPLE_FACTOR} parameter. */ + public static final int DEFAULT_DOWNSAMPLE_FACTOR = 4; + + /** + * The key identifying the parameter setting whether we pre-filter the + * target image with a median filter or not. Expected values are + * {@link Boolean}s. + *

      + * Currently used by: + *

        + *
      • {@link LogDetector} + *
      • {@link DogDetector} + *
      + */ + public static final String KEY_DO_MEDIAN_FILTERING = "DO_MEDIAN_FILTERING"; + + /** A default value for the {@link #KEY_DO_MEDIAN_FILTERING} parameter. */ + public static final boolean DEFAULT_DO_MEDIAN_FILTERING = false; + + /** + * The key identifying the parameter setting whether we use sub-pixel + * localization for spot position. Accepted values are {@link Boolean}s. + *

      + * Currently used by: + *

        + *
      • {@link LogDetector} + *
      • {@link DogDetector} + *
      + */ + public static final String KEY_DO_SUBPIXEL_LOCALIZATION = "DO_SUBPIXEL_LOCALIZATION"; + + /** A default value for the {@link #KEY_DO_SUBPIXEL_LOCALIZATION} parameter. */ + public static final boolean DEFAULT_DO_SUBPIXEL_LOCALIZATION = true; + +} diff --git a/src/main/java/utility/ETrackScrollbarUtils.java b/src/main/java/utility/ETrackScrollbarUtils.java new file mode 100644 index 0000000..09d3796 --- /dev/null +++ b/src/main/java/utility/ETrackScrollbarUtils.java @@ -0,0 +1,45 @@ +package utility; + + +public class ETrackScrollbarUtils { + + + public static float computeValueFromScrollbarPosition(final int scrollbarPosition, final float min, + final float max, final int scrollbarSize) { + return min + (scrollbarPosition / (float) scrollbarSize) * (max - min); + } + + public static float computeIntValueFromScrollbarPosition(final int scrollbarPosition, final float min, + final float max, final int scrollbarSize) { + return min + (scrollbarPosition / (max)) * (max - min); + } + + public static int computeScrollbarPositionFromValue(final float sigma, final float min, final float max, + final int scrollbarSize) { + return Math.round(((sigma - min) / (max - min)) * scrollbarSize); + } + + public static int computeIntScrollbarPositionFromValue(final float thirdDimensionslider, final float min, + final float max, final int scrollbarSize) { + return Math.round(((thirdDimensionslider - min) / (max - min)) * max); + } + + public static float computeSigma2(final float sigma1, final int sensitivity) { + final float k = (float) computeK(sensitivity); + final float[] sigma = computeSigma(k, sigma1); + + return sigma[1]; + } + public static double computeK( final float stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); } + public static double computeK( final int stepsPerOctave ) { return Math.pow( 2f, 1f / stepsPerOctave ); } + public static float computeKWeight( final float k ) { return 1.0f / (k - 1.0f); } + public static float[] computeSigma( final float k, final float initialSigma ) + { + final float[] sigma = new float[ 2 ]; + + sigma[ 0 ] = initialSigma; + sigma[ 1 ] = sigma[ 0 ] * k; + + return sigma; + } +} \ No newline at end of file diff --git a/src/main/java/utility/FeatureAnalyzer3D.java b/src/main/java/utility/FeatureAnalyzer3D.java new file mode 100644 index 0000000..92c49f4 --- /dev/null +++ b/src/main/java/utility/FeatureAnalyzer3D.java @@ -0,0 +1,56 @@ +package utility; + + +import java.util.List; +import java.util.Map; + +import linkers.Dimension; + +public interface FeatureAnalyzer3D extends CovistoModule +{ + + /** + * Returns the list of features this analyzer can compute. + */ + public List getFeatures(); + + /** + * Returns the map of short names for any feature the analyzer + * can compute. + */ + public Map getFeatureShortNames(); + + /** + * Returns the map of names for any feature this analyzer can compute. + */ + public Map getFeatureNames(); + + /** + * Returns the map of feature dimension this analyzer can compute. + */ + public Map getFeatureDimensions(); + + /** + * Returns the map that states whether the key feature is a feature that + * returns integers. If true, then special treatment is applied + * when saving/loading, etc. for clarity and precision. + */ + public Map< String, Boolean > getIsIntFeature(); + + /** + * Returns whether all the features declared in this + * {@link FeatureAnalyzer} are manual features. + *

      + * Manual features are not calculated normally using an analyzer, nor + * cleared at each recalculation. Another classes are responsible to set + * their value. The concrete {@link FeatureAnalyzer} calculation method is + * not called by TrackMate when a change happens to the model. + * Therefore the calculation routine of the {@link FeatureAnalyzer} can be + * used to discard the manually stored value of these features. + * + * @return true if the features declared in this analyzer are + * manual feature. + */ + public boolean isManualFeature(); + +} diff --git a/src/main/java/utility/FeatureFilter.java b/src/main/java/utility/FeatureFilter.java new file mode 100644 index 0000000..23b25bb --- /dev/null +++ b/src/main/java/utility/FeatureFilter.java @@ -0,0 +1,41 @@ +package utility; + +/** + * A helper class to store a feature filter. It is just made of 3 public fields. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> Sep 23, 2010 + * + */ +public class FeatureFilter +{ + public final String feature; + + public final Double value; + + public final boolean isAbove; + + public FeatureFilter( final String feature, final Double value, final boolean isAbove ) + { + this.feature = feature; + this.value = value; + this.isAbove = isAbove; + } + + public FeatureFilter( final String feature, final double value, final boolean isAbove ) + { + this( feature, Double.valueOf( value ), isAbove ); + } + + @Override + public String toString() + { + String str = feature.toString(); + if ( isAbove ) + str += " > "; + else + str += " < "; + str += value.toString(); + return str; + } + +} diff --git a/src/main/java/utility/FinderUtils.java b/src/main/java/utility/FinderUtils.java new file mode 100644 index 0000000..57d6987 --- /dev/null +++ b/src/main/java/utility/FinderUtils.java @@ -0,0 +1,1583 @@ +package utility; + +import java.awt.Font; +import java.awt.Rectangle; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.poi.hssf.usermodel.HSSFCell; +import org.apache.poi.hssf.usermodel.HSSFCellStyle; +import org.apache.poi.hssf.usermodel.HSSFDataFormat; +import org.apache.poi.hssf.usermodel.HSSFFont; +import org.apache.poi.hssf.usermodel.HSSFRow; +import org.apache.poi.hssf.usermodel.HSSFSheet; +import org.apache.poi.hssf.usermodel.HSSFWorkbook; +import org.jgrapht.graph.DefaultWeightedEdge; + +import ij.ImagePlus; +import ij.gui.EllipseRoi; +import ij.gui.OvalRoi; +import ij.gui.Roi; +import ij.measure.ResultsTable; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.IterableInterval; +import net.imglib2.KDTree; +import net.imglib2.Point; +import net.imglib2.PointSampleList; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.algorithm.componenttree.mser.Mser; +import net.imglib2.algorithm.componenttree.mser.MserTree; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.algorithm.localextrema.RefinedPeak; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.RealSum; +import net.imglib2.util.Util; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; + +public class FinderUtils{ + + public static ArrayList getcurrentRois(ArrayList> peaks, double sigma, double sigma2) { + + ArrayList Allrois = new ArrayList(); + + for (final RefinedPeak peak : peaks) { + float x = (float) (peak.getFloatPosition(0)); + float y = (float) (peak.getFloatPosition(1)); + + final OvalRoi or = new OvalRoi(Util.round(x - sigma), Util.round(y - sigma), Util.round(sigma + sigma2), + Util.round(sigma + sigma2)); + + Allrois.add(or); + + } + + return Allrois; + + } + + public static double getNumberofPixels(RandomAccessibleInterval source, Roi roi) { + + double NumberofPixels = 0; + + Cursor currentcursor = Views.iterable(source).localizingCursor(); + + final double[] position = new double[source.numDimensions()]; + + while (currentcursor.hasNext()) { + + currentcursor.fwd(); + + currentcursor.localize(position); + + int x = (int) position[0]; + int y = (int) position[1]; + + if (roi.contains(x, y)) { + + NumberofPixels++; + + } + + } + + return NumberofPixels; + + } + + + public static long[] GetMaxcorners(RandomAccessibleInterval inputimg, int label) { + + Cursor intCursor = Views.iterable(inputimg).localizingCursor(); + int n = inputimg.numDimensions(); + long[] maxVal = { inputimg.min(0), inputimg.min(1) }; + + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == label) { + + for (int d = 0; d < n; ++d) { + + final long p = intCursor.getLongPosition(d); + if (p > maxVal[d]) + maxVal[d] = p; + + } + + } + } + + return maxVal; + + } + + public static Roi CreateBigRoi (final Roi currentroi, final RandomAccessibleInterval currentimg, final double radius){ + + + + double width = currentroi.getFloatWidth(); + double height = currentroi.getFloatHeight(); + + double[] center = getCenter(currentroi, currentimg); + + + Roi Bigroi = new OvalRoi(Util.round(center[0] -(width + radius)/2), Util.round(center[1] - (height + radius)/2 ), Util.round(width + radius), + Util.round(height + radius)); + + if(radius == 0 ) + Bigroi = currentroi; + + + return Bigroi; + + + } + + public static double[] getCenter(Roi roi, final RandomAccessibleInterval source) { + + double Intensity = 0; + double[] center = new double[3]; + Cursor currentcursor = Views.iterable(source).localizingCursor(); + double SumX = 0; + double SumY = 0; + final double[] position = new double[source.numDimensions()]; + int count = 0; + while (currentcursor.hasNext()) { + + currentcursor.fwd(); + + currentcursor.localize(position); + + int x = (int) position[0]; + int y = (int) position[1]; + + if (roi.contains(x, y)) { + SumX += currentcursor.getDoublePosition(0) * currentcursor.get().getRealDouble(); + SumY += currentcursor.getDoublePosition(1) * currentcursor.get().getRealDouble(); + Intensity += currentcursor.get().getRealDouble(); + count++; + } + + } + center[0] = SumX / Intensity; + center[1] = SumY / Intensity; + + center[2] = 0; + + return center; + + } + public static long[] GetMincorners(RandomAccessibleInterval inputimg, int label) { + + Cursor intCursor = Views.iterable(inputimg).localizingCursor(); + int n = inputimg.numDimensions(); + long[] minVal = { inputimg.max(0), inputimg.max(1) }; + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == label) { + + for (int d = 0; d < n; ++d) { + + final long p = intCursor.getLongPosition(d); + if (p < minVal[d]) + minVal[d] = p; + } + + } + } + + return minVal; + + } + + public static double GetBoundingbox(RandomAccessibleInterval inputimg, int label) { + + Cursor intCursor = Views.iterable(inputimg).localizingCursor(); + int n = inputimg.numDimensions(); + long[] position = new long[n]; + long[] minVal = { inputimg.max(0), inputimg.max(1) }; + long[] maxVal = { inputimg.min(0), inputimg.min(1) }; + + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == label) { + + intCursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + + } + } + + double boxsize = Distance(minVal, maxVal); + + Pair boundingBox = new ValuePair(minVal, maxVal); + return boxsize; + } + + public static int GetMaxlabelsseeded(RandomAccessibleInterval intimg) { + + // To get maximum Labels on the image + Cursor intCursor = Views.iterable(intimg).cursor(); + int currentLabel = 1; + boolean anythingFound = true; + while (anythingFound) { + anythingFound = false; + intCursor.reset(); + while (intCursor.hasNext()) { + intCursor.fwd(); + int i = intCursor.get().get(); + if (i == currentLabel) { + + anythingFound = true; + + } + } + currentLabel++; + } + + return currentLabel; + + } + + public static NativeImgLabeling GetlabeledImage(RandomAccessibleInterval inputimg, + NativeImgLabeling seedLabeling) { + + int n = inputimg.numDimensions(); + long[] dimensions = new long[n]; + + for (int d = 0; d < n; ++d) + dimensions[d] = inputimg.dimension(d); + final NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + final Watershed watershed = new Watershed(); + + watershed.setSeeds(seedLabeling); + watershed.setIntensityImage(inputimg); + watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2)); + watershed.setOutputLabeling(outputLabeling); + watershed.process(); + DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory(); + LabelingROIStrategy> factory = deffactory + .createLabelingROIStrategy(watershed.getResult()); + outputLabeling.setLabelingCursorStrategy(factory); + + return outputLabeling; + + } + + public static Pair, FinalInterval> CurrentLabelImage(RandomAccessibleInterval img, EllipseRoi roi){ + + int n = img.numDimensions(); + long[] position = new long[n]; + long[] minVal = { img.max(0), img.max(1) }; + long[] maxVal = { img.min(0), img.min(1) }; + + Cursor localcursor = Views.iterable(img).localizingCursor(); + + while (localcursor.hasNext()) { + localcursor.fwd(); + int x = localcursor.getIntPosition(0); + int y = localcursor.getIntPosition(1); + if (roi.contains(x, y)){ + + localcursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + + } + } + FinalInterval interval = new FinalInterval(minVal, maxVal); + RandomAccessibleInterval currentimgsmall = Views.interval(img, interval); + + Pair, FinalInterval> pair = new ValuePair, FinalInterval>(currentimgsmall, interval); + + return pair; + } + + + + +/* +public static RandomAccessibleInterval CurrentLabeloffsetImage(ArrayList imgs, final EllipseRoi roi,int label) { + + RandomAccessibleInterval currentimg = imgs.get(label).Actualroi; + int n = currentimg.numDimensions(); + long[] position = new long[n]; + long[] minVal = { currentimg.max(0), currentimg.max(1) }; + long[] maxVal = { currentimg.min(0), currentimg.min(1) }; + + Cursor localcursor = Views.iterable(currentimg).localizingCursor(); + + while (localcursor.hasNext()) { + localcursor.fwd(); + int x = localcursor.getIntPosition(0); + int y = localcursor.getIntPosition(1); + if (roi.contains(x, y)){ + + localcursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + + } + } + + FinalInterval interval = new FinalInterval(minVal, maxVal) ; + RandomAccessibleInterval currentimgsmall = Views.offsetInterval(currentimg, interval); + return currentimgsmall; + + } +*/ + +public static FinalInterval CurrentroiInterval(RandomAccessibleInterval currentimg, final EllipseRoi roi){ + + int n = currentimg.numDimensions(); + long[] position = new long[n]; + long[] minVal = { currentimg.max(0), currentimg.max(1) }; + long[] maxVal = { currentimg.min(0), currentimg.min(1) }; + + Cursor localcursor = Views.iterable(currentimg).localizingCursor(); + + while (localcursor.hasNext()) { + localcursor.fwd(); + int x = localcursor.getIntPosition(0); + int y = localcursor.getIntPosition(1); + if (roi.contains(x, y)){ + + localcursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + + } + } + + FinalInterval interval = new FinalInterval(minVal, maxVal) ; + + return interval; + +} + +public static FinalInterval CurrentroiInterval(RandomAccessibleInterval currentimg, Roi roi) { + int n = currentimg.numDimensions(); + long[] position = new long[n]; + long[] minVal = { currentimg.max(0), currentimg.max(1) }; + long[] maxVal = { currentimg.min(0), currentimg.min(1) }; + + Cursor localcursor = Views.iterable(currentimg).localizingCursor(); + + while (localcursor.hasNext()) { + localcursor.fwd(); + int x = localcursor.getIntPosition(0); + int y = localcursor.getIntPosition(1); + if (roi.contains(x, y)){ + + localcursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + + } + } + + FinalInterval interval = new FinalInterval(minVal, maxVal) ; + + return interval; +} + public static Pair, FinalInterval> CurrentLabeloffsetImagepair(RandomAccessibleInterval Intimg, + RandomAccessibleInterval originalimg, int currentLabel) { + int n = originalimg.numDimensions(); + RandomAccess inputRA = originalimg.randomAccess(); + long[] position = new long[n]; + Cursor intCursor = Views.iterable(Intimg).cursor(); + final FloatType type = originalimg.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval outimg = factory.create(originalimg, type); + RandomAccess imageRA = outimg.randomAccess(); + long[] minVal = { originalimg.max(0), originalimg.max(1) }; + long[] maxVal = { originalimg.min(0), originalimg.min(1) }; + // Go through the whole image and add every pixel, that belongs to + // the currently processed label + + while (intCursor.hasNext()) { + intCursor.fwd(); + inputRA.setPosition(intCursor); + imageRA.setPosition(inputRA); + int i = intCursor.get().get(); + if (i == currentLabel) { + intCursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + imageRA.get().set(inputRA.get()); + + } + + } + FinalInterval intervalsmall = new FinalInterval(minVal, maxVal) ; + + RandomAccessibleInterval outimgsmall = Views.offsetInterval(outimg, intervalsmall); + + Pair, FinalInterval> pair = new ValuePair, FinalInterval>(outimgsmall, intervalsmall); + return pair; + + } + + + public static Pair, FinalInterval> CurrentLabelImagepair(RandomAccessibleInterval Intimg, + RandomAccessibleInterval originalimg, int currentLabel) { + int n = originalimg.numDimensions(); + RandomAccess inputRA = originalimg.randomAccess(); + long[] position = new long[n]; + Cursor intCursor = Views.iterable(Intimg).cursor(); + final FloatType type = originalimg.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval outimg = factory.create(originalimg, type); + RandomAccess imageRA = outimg.randomAccess(); + long[] minVal = { originalimg.max(0), originalimg.max(1) }; + long[] maxVal = { originalimg.min(0), originalimg.min(1) }; + // Go through the whole image and add every pixel, that belongs to + // the currently processed label + + while (intCursor.hasNext()) { + intCursor.fwd(); + inputRA.setPosition(intCursor); + imageRA.setPosition(inputRA); + int i = intCursor.get().get(); + if (i == currentLabel) { + intCursor.localize(position); + for (int d = 0; d < n; ++d) { + if (position[d] < minVal[d]) { + minVal[d] = position[d]; + } + if (position[d] > maxVal[d]) { + maxVal[d] = position[d]; + } + + } + imageRA.get().set(inputRA.get()); + + } + + } + FinalInterval intervalsmall = new FinalInterval(minVal, maxVal) ; + RandomAccessibleInterval outimgsmall = Views.interval(outimg, intervalsmall); + + Pair, FinalInterval> pair = new ValuePair, FinalInterval>(outimgsmall, intervalsmall); + return pair; + + } + + public static RandomAccessibleInterval CurrentLabelImage(RandomAccessibleInterval Intimg, + RandomAccessibleInterval originalimg, int currentLabel) { + int n = originalimg.numDimensions(); + RandomAccess inputRA = originalimg.randomAccess(); + long[] position = new long[n]; + Cursor intCursor = Views.iterable(Intimg).cursor(); + final FloatType type = originalimg.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval outimg = factory.create(originalimg, type); + RandomAccess imageRA = outimg.randomAccess(); + + // Go through the whole image and add every pixel, that belongs to + // the currently processed label + + while (intCursor.hasNext()) { + intCursor.fwd(); + inputRA.setPosition(intCursor); + imageRA.setPosition(inputRA); + int i = intCursor.get().get(); + if (i == currentLabel) { + intCursor.localize(position); + + imageRA.get().set(inputRA.get()); + + } + + } + + + return outimg; + + } + + public static RandomAccessibleInterval CurrentLabeloffsetImage(RandomAccessibleInterval Intimg, + RandomAccessibleInterval originalimg, int currentLabel) { + int n = originalimg.numDimensions(); + RandomAccess inputRA = originalimg.randomAccess(); + Cursor intCursor = Views.iterable(Intimg).cursor(); + final FloatType type = originalimg.randomAccess().get().createVariable(); + final ImgFactory factory = Util.getArrayOrCellImgFactory(originalimg, type); + RandomAccessibleInterval outimg = factory.create(originalimg, type); + RandomAccess imageRA = outimg.randomAccess(); + + // Go through the whole image and add every pixel, that belongs to + // the currently processed label + + while (intCursor.hasNext()) { + intCursor.fwd(); + inputRA.setPosition(intCursor); + imageRA.setPosition(inputRA); + int i = intCursor.get().get(); + if (i == currentLabel) { + + imageRA.get().set(inputRA.get()); + + } + + } + + + return outimg; + + } + + + public static double Distance(final long[] minCorner, final long[] maxCorner) { + + double distance = 0; + + for (int d = 0; d < minCorner.length; ++d) { + + distance += Math.pow((minCorner[d] - maxCorner[d]), 2); + + } + return Math.sqrt(distance); + } + + + + public static int CummulativeDistance (final double[] pointT, final double[] pointTp1, final double[] pointTp2, final double oldlength){ + + int grow = 0; + + + + if ((pointTp2[0] < pointTp1[0]) && (pointTp2[0] > pointT[0]) && (pointTp2[0] < pointTp1[0]) && (pointTp2[0] > pointT[0]) ) + grow = -1; + + + + + + return grow; + + + } + + public static float Distancesq(final double[] minCorner, final double[] maxCorner) { + + float distance = 0; + + for (int d = 0; d < minCorner.length; ++d) { + + distance += Math.pow((minCorner[d] - maxCorner[d]), 2); + + } + return distance; + } + + public static double Distance(final double[] minCorner, final double[] maxCorner) { + + double distance = 0; + + for (int d = 0; d < minCorner.length; ++d) { + + distance += Math.pow((minCorner[d] - maxCorner[d]), 2); + + } + return Math.sqrt(distance); + } + + public static double VelocityX(final double[] oldpoint, final double[] newpoint) { + + double Velocity = 0; + + int d = 0; + + Velocity = (-oldpoint[d] + newpoint[d]); + + + return Velocity; + } + + public static double VelocityY(final double[] oldpoint, final double[] newpoint) { + + double Velocity = 0; + + int d = oldpoint.length - 1; + + Velocity = (-oldpoint[d] + newpoint[d]); + + + return Velocity; + } + + public static < T extends RealType< T > & NativeType< T >> Pair, ArrayList> getKNearestRois(final RandomAccessibleInterval currentimg, + ArrayList Allrois, Roi kdtreeroi, int k) { + + ArrayList KnearestRoi = new ArrayList(); + ArrayList Knearestdist = new ArrayList(); + Roi Knear = null; + + ArrayList> distRoi = new ArrayList>(); + double[] kdcenter = getCenter(currentimg, kdtreeroi); + + + + for (int index = 0; index < Allrois.size(); ++index) { + + double[] roicenter = getCenter(currentimg, Allrois.get(index)); + + Pair distpair = new ValuePair(Distance(kdcenter, roicenter), Allrois.get(index)); + + distRoi.add(distpair); + + } + + Comparator> distcomparison = new Comparator>() { + + @Override + public int compare(final Pair A, final Pair B) { + + double diff= A.getA() - B.getA(); + + if ( diff < 0 ) + return -1; + else if ( diff > 0 ) + return 1; + else + return 0; + } + + }; + + Collections.sort(distRoi, distcomparison); + + for (int i = 0; i < k ; ++i){ + + KnearestRoi.add(distRoi.get(i).getB()); + Knearestdist.add(distRoi.get(i).getA()); + } + + + + return new ValuePair, ArrayList>(KnearestRoi, Knearestdist); + + } + + public static < T extends RealType< T > & NativeType< T >> ArrayList getKNearestRoisaboveCut(final RandomAccessibleInterval currentimg, + ArrayList Allrois, Roi kdtreeroi, double distthresh, int k) { + + ArrayList KnearestRoi = new ArrayList(); + ArrayList KnearestclearRoi = new ArrayList(); + ArrayList Knearestdist = new ArrayList(); + Roi Knear = null; + + ArrayList> distRoi = new ArrayList>(); + double[] kdcenter = getCenter(currentimg, kdtreeroi); + + + + for (int index = 0; index < Allrois.size(); ++index) { + + double[] roicenter = getCenter(currentimg, Allrois.get(index)); + + Pair distpair = new ValuePair(Distance(kdcenter, roicenter), Allrois.get(index)); + + distRoi.add(distpair); + + } + + Comparator> distcomparison = new Comparator>() { + + @Override + public int compare(final Pair A, final Pair B) { + + double diff= A.getA() - B.getA(); + + if ( diff < 0 ) + return -1; + else if ( diff > 0 ) + return 1; + else + return 0; + } + + }; + + Collections.sort(distRoi, distcomparison); + + for (int i = 0; i < k ; ++i){ + + KnearestRoi.add(distRoi.get(i).getB()); + Knearestdist.add(distRoi.get(i).getA()); + } + + for (int i = 0; i < Knearestdist.size(); ++i) { + + if(Knearestdist.get(i) > distthresh) + KnearestclearRoi.add(KnearestRoi.get(i)); + + } + + + + + + return KnearestclearRoi; + + } + + + public static < T extends RealType< T > & NativeType< T >> Pair mergeNearestRois(final RandomAccessibleInterval currentimg, ArrayList Allrois, double[] Clickedpoint, double distthresh) { + + + boolean merged = false; + double[] KDtreeroi = new double[Clickedpoint.length]; + + + final List targetCoords = new ArrayList(Allrois.size()); + final List> targetNodes = new ArrayList>(Allrois.size()); + for (int index = 0; index < Allrois.size(); ++index) { + + double[] r = Allrois.get(index); + + + targetCoords.add( new RealPoint(r[0], r[1] ) ); + + + targetNodes.add(new FlagNode(Allrois.get(index))); + + } + + + if (targetNodes.size() > 0 && targetCoords.size() > 0) { + + final KDTree> Tree = new KDTree>(targetNodes, targetCoords); + + final NNFlagsearchKDtree Search = new NNFlagsearchKDtree(Tree); + + + final double[] source = Clickedpoint; + final RealPoint sourceCoords = new RealPoint(source); + Search.search(sourceCoords); + + final FlagNode targetNode = Search.getSampler().get(); + + KDtreeroi = targetNode.getValue(); + } + + + + + if(KDtreeroi!=null) { + + double[] roicenter = KDtreeroi; + + double[] mergepoint = new double[roicenter.length]; + + + double distance = Distance(Clickedpoint, roicenter); + if (distance < distthresh) { + + + merged = true; + } + else + merged = false; + + mergepoint = roicenter; + + + return new ValuePair(mergepoint, merged); + } + + else return null; + + } + + + public static Roi getNearestRois(ArrayList Allrois, double[] Clickedpoint) { + + Roi KDtreeroi = null; + + final List targetCoords = new ArrayList(Allrois.size()); + final List> targetNodes = new ArrayList>(Allrois.size()); + for (int index = 0; index < Allrois.size(); ++index) { + + Roi r = Allrois.get(index); + Rectangle rect = r.getBounds(); + + targetCoords.add( new RealPoint(rect.x + rect.width/2.0, rect.y + rect.height/2.0 ) ); + + + targetNodes.add(new FlagNode(Allrois.get(index))); + + } + + if (targetNodes.size() > 0 && targetCoords.size() > 0) { + + final KDTree> Tree = new KDTree>(targetNodes, targetCoords); + + final NNFlagsearchKDtree Search = new NNFlagsearchKDtree(Tree); + + + final double[] source = Clickedpoint; + final RealPoint sourceCoords = new RealPoint(source); + Search.search(sourceCoords); + final FlagNode targetNode = Search.getSampler().get(); + + KDtreeroi = targetNode.getValue(); + + } + + return KDtreeroi; + } + + public static double getIntensity(RandomAccessibleInterval source, Roi roi) { + + double Intensity = 0; + + Cursor currentcursor = Views.iterable(source).localizingCursor(); + + final double[] position = new double[source.numDimensions()]; + + while (currentcursor.hasNext()) { + + currentcursor.fwd(); + + currentcursor.localize(position); + + int x = (int) position[0]; + int y = (int) position[1]; + + if (roi.contains(x, y)) { + + Intensity += currentcursor.get().getRealDouble(); + + } + + } + + return Intensity; + + } + + public static< T extends RealType< T > & NativeType< T >> double[] getCenter(RandomAccessibleInterval source, Roi roi) { + + double Intensity = 0; + double[] center = new double[source.numDimensions()]; + Cursor currentcursor = Views.iterable(source).localizingCursor(); + double SumX = 0; + double SumY = 0; + final double[] position = new double[source.numDimensions()]; + + while (currentcursor.hasNext()) { + + currentcursor.fwd(); + + currentcursor.localize(position); + + int x = (int) position[0]; + int y = (int) position[1]; + + if (roi.contains(x, y)) { + SumX += currentcursor.getDoublePosition(0) * currentcursor.get().getRealDouble(); + SumY += currentcursor.getDoublePosition(1) * currentcursor.get().getRealDouble(); + Intensity += currentcursor.get().getRealDouble(); + + } + + } + center[0] = SumX / Intensity; + center[1] = SumY / Intensity; + + + + return center; + + } + public static < T extends RealType< T > & NativeType< T >> ArrayList getcurrentRois(MserTree newtree) { + + ArrayList meanandcovchildlist = new ArrayList(); + ArrayList meanandcovlist = new ArrayList(); + ArrayList redmeanandcovlist = new ArrayList(); + final HashSet> rootset = newtree.roots(); + + + final Iterator> rootsetiterator = rootset.iterator(); + + + final ArrayList AllmeanCovar = new ArrayList(); + + while (rootsetiterator.hasNext()) { + + Mser rootmser = rootsetiterator.next(); + + if (rootmser.size() > 0) { + + final double[] meanandcov = { rootmser.mean()[0], rootmser.mean()[1], rootmser.cov()[0], + rootmser.cov()[1], rootmser.cov()[2] }; + meanandcovlist.add(meanandcov); + } + } + + // We do this so the ROI remains attached the the same label and is not changed if the program is run again + + final Iterator> treeiterator = newtree.iterator(); + + while (treeiterator.hasNext()) { + + Mser mser = treeiterator.next(); + //System.out.println(mser.getChildren().size()); + if (mser.getChildren().size() > 1) { + + for (int index = 0; index < mser.getChildren().size(); ++index) { + + final double[] meanandcovchild = { mser.getChildren().get(index).mean()[0], + mser.getChildren().get(index).mean()[1], mser.getChildren().get(index).cov()[0], + mser.getChildren().get(index).cov()[1], mser.getChildren().get(index).cov()[2] }; + + meanandcovchildlist.add(meanandcovchild); + AllmeanCovar.add(meanandcovchild); + + } + + } + + } + redmeanandcovlist = meanandcovlist; + + /* + * Remove parent, not always a good idea + * + for (int childindex = 0; childindex < meanandcovchildlist.size(); ++childindex) { + + final double[] meanchild = new double[] { meanandcovchildlist.get(childindex)[0], + meanandcovchildlist.get(childindex)[1] }; + + for (int index = 0; index < meanandcovlist.size(); ++index) { + + final double[] mean = new double[] { meanandcovlist.get(index)[0], meanandcovlist.get(index)[1] }; + final double[] covar = new double[] { meanandcovlist.get(index)[2], meanandcovlist.get(index)[3], + meanandcovlist.get(index)[4] }; + final EllipseRoi ellipse = createEllipse(mean, covar, 3); + + if (ellipse.contains((int) meanchild[0], (int) meanchild[1])) + redmeanandcovlist.remove(index); + + } + + } + */ + + for (int index = 0; index < redmeanandcovlist.size(); ++index) { + + final double[] meanandcov = new double[] { redmeanandcovlist.get(index)[0], redmeanandcovlist.get(index)[1], + redmeanandcovlist.get(index)[2], redmeanandcovlist.get(index)[3], redmeanandcovlist.get(index)[4] }; + AllmeanCovar.add(meanandcov); + + } + + ArrayList Allrois = new ArrayList(); + + + for (int index = 0; index < AllmeanCovar.size(); ++index) { + + final double[] mean = { AllmeanCovar.get(index)[0], AllmeanCovar.get(index)[1] }; + final double[] covar = { AllmeanCovar.get(index)[2], AllmeanCovar.get(index)[3], + AllmeanCovar.get(index)[4] }; + + Roi roi = createEllipse(mean, covar, 3); + + Allrois.add(roi); + + } + + return Allrois; + } + + + public static ArrayList getcurrentRoiswoChild(MserTree newtree) { + + final HashSet> rootset = newtree.roots(); + + ArrayList Allrois = new ArrayList(); + final Iterator> rootsetiterator = rootset.iterator(); + + ArrayList AllmeanCovar = new ArrayList(); + + while (rootsetiterator.hasNext()) { + + Mser rootmser = rootsetiterator.next(); + + if (rootmser.size() > 0) { + + final double[] meanandcov = { rootmser.mean()[0], rootmser.mean()[1], rootmser.cov()[0], + rootmser.cov()[1], rootmser.cov()[2] }; + AllmeanCovar.add(meanandcov); + + } + } + + // We do this so the ROI remains attached the the same label and is not + // changed if the program is run again + SortListbyproperty.sortpointList(AllmeanCovar); + for (int index = 0; index < AllmeanCovar.size(); ++index) { + + final double[] mean = { AllmeanCovar.get(index)[0], AllmeanCovar.get(index)[1] }; + final double[] covar = { AllmeanCovar.get(index)[2], AllmeanCovar.get(index)[3], + AllmeanCovar.get(index)[4] }; + + EllipseRoi roi = createEllipse(mean, covar, 3); + Allrois.add(roi); + + } + + return Allrois; + + } + + public static RandomAccessibleInterval getCurrentView(RandomAccessibleInterval originalimgA, + int thirdDimension) { + + final FloatType type = originalimgA.randomAccess().get().createVariable(); + long[] dim = { originalimgA.dimension(0), originalimgA.dimension(1) }; + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(originalimgA, type); + RandomAccessibleInterval totalimg = factory.create(dim, type); + + totalimg = Views.hyperSlice(originalimgA, 2, thirdDimension - 1); + + return totalimg; + + } + public static > double[] Transformback(double[] location, double[] size, double[] min, + double[] max) { + + int n = location.length; + + double[] delta = new double[n]; + + final double[] realpos = new double[n]; + + for (int d = 0; d < n; ++d){ + + delta[d] = (max[d] - min[d]) / size[d]; + + realpos[d] = (location[d] - min[d]) / delta[d]; + } + return realpos; + + } + /** + * Extract the current 2d region of interest from the souce image + * + * @param CurrentView + * - the CurrentView image, a {@link Image} which is a copy of + * the {@link ImagePlus} + * + * @return + */ + + public static RandomAccessibleInterval extractImage( + final RandomAccessibleInterval intervalView, FinalInterval interval) { +/* + final FloatType type = intervalView.randomAccess().get().createVariable(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(intervalView, type); + RandomAccessibleInterval totalimg = factory.create(intervalView, type); + + final RandomAccessibleInterval img = Views.interval(intervalView, interval); + double[] newmin = Transformback(new double[]{img.min(0), img.min(1)}, + new double[]{totalimg.dimension(0), totalimg.dimension(1)}, + new double[]{img.min(0), img.min(1)}, + new double[]{img.max(0), img.max(1)}); + + double[] newmax = Transformback(new double[]{img.max(0), img.max(1)}, + new double[]{totalimg.dimension(0), totalimg.dimension(1)}, + new double[]{totalimg.min(0), totalimg.min(1)}, + new double[]{totalimg.max(0), totalimg.max(1)}); + long[] newminlong = new long[]{Math.round(newmin[0]), Math.round(newmin[1])}; + long[] newmaxlong = new long[]{Math.round(newmax[0]), Math.round(newmax[1])}; + + RandomAccessibleInterval outimg = factory.create(new FinalInterval(newminlong, newmaxlong), type); + RandomAccess ranac = outimg.randomAccess(); + final Cursor cursor = Views.iterable(img).localizingCursor(); + + while(cursor.hasNext()){ + + cursor.fwd(); + + double[] newlocation = Transformback(new double[]{cursor.getDoublePosition(0), cursor.getDoublePosition(1)}, + new double[]{totalimg.dimension(0), totalimg.dimension(1)}, + new double[]{totalimg.min(0), totalimg.min(1)}, + new double[]{totalimg.max(0), totalimg.max(1)}); + long[] newlocationlong = new long[]{Math.round(newlocation[0]), Math.round(newlocation[1])}; + ranac.setPosition(newlocationlong); + ranac.get().set(cursor.get()); + + } + + + + //totalimg = Views.interval(Views.extendBorder(img), intervalView); +*/ + return intervalView; + } + + public static RandomAccessibleInterval oldextractImage( + final RandomAccessibleInterval intervalView, FinalInterval interval) { + + final FloatType type = intervalView.randomAccess().get().createVariable(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(intervalView, type); + RandomAccessibleInterval totalimg = factory.create(intervalView, type); + + final RandomAccessibleInterval img = Views.interval(intervalView, interval); + double[] newmin = Transformback(new double[]{img.min(0), img.min(1)}, + new double[]{totalimg.dimension(0), totalimg.dimension(1)}, + new double[]{img.min(0), img.min(1)}, + new double[]{img.max(0), img.max(1)}); + + double[] newmax = Transformback(new double[]{img.max(0), img.max(1)}, + new double[]{totalimg.dimension(0), totalimg.dimension(1)}, + new double[]{totalimg.min(0), totalimg.min(1)}, + new double[]{totalimg.max(0), totalimg.max(1)}); + long[] newminlong = new long[]{Math.round(newmin[0]), Math.round(newmin[1])}; + long[] newmaxlong = new long[]{Math.round(newmax[0]), Math.round(newmax[1])}; + + RandomAccessibleInterval outimg = factory.create(new FinalInterval(newminlong, newmaxlong), type); + RandomAccess ranac = outimg.randomAccess(); + final Cursor cursor = Views.iterable(img).localizingCursor(); + + while(cursor.hasNext()){ + + cursor.fwd(); + + double[] newlocation = Transformback(new double[]{cursor.getDoublePosition(0), cursor.getDoublePosition(1)}, + new double[]{totalimg.dimension(0), totalimg.dimension(1)}, + new double[]{totalimg.min(0), totalimg.min(1)}, + new double[]{totalimg.max(0), totalimg.max(1)}); + long[] newlocationlong = new long[]{Math.round(newlocation[0]), Math.round(newlocation[1])}; + ranac.setPosition(newlocationlong); + ranac.get().set(cursor.get()); + + } + + + + totalimg = Views.interval(Views.extendBorder(img), intervalView); + + return intervalView; + } + + /** + * Generic, type-agnostic method to create an identical copy of an Img + * + * @param currentPreprocessedimg2 + * - the Img to copy + * @return - the copy of the Img + */ + public static Img copytoByteImage(final RandomAccessibleInterval input) { + // create a new Image with the same properties + // note that the input provides the size for the new image as it + // implements + // the Interval interface + RandomAccessibleInterval inputcopy = copyImage(input); + Normalize.normalize(Views.iterable(inputcopy), new FloatType(0), new FloatType(255)); + final UnsignedByteType type = new UnsignedByteType(); + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputcopy, type); + final Img output = factory.create(inputcopy, type); + // create a cursor for both images + RandomAccess ranac = inputcopy.randomAccess(); + Cursor cursorOutput = output.cursor(); + + // iterate over the input + while (cursorOutput.hasNext()) { + // move both cursors forward by one pixel + cursorOutput.fwd(); + + ranac.setPosition(cursorOutput); + + // set the value of this pixel of the output image to the same as + // the input, + // every Type supports T.set( T type ) + cursorOutput.get().set((int) Math.round(ranac.get().get())); + } + + // return the copy + return output; + } + + public static ArrayList getRoiMean(MserTree newtree) { + + final HashSet> rootset = newtree.roots(); + + final Iterator> rootsetiterator = rootset.iterator(); + + ArrayList AllmeanCovar = new ArrayList(); + + while (rootsetiterator.hasNext()) { + + Mser rootmser = rootsetiterator.next(); + + if (rootmser.size() > 0) { + + final double[] meanandcov = { rootmser.mean()[0], rootmser.mean()[1] }; + AllmeanCovar.add(meanandcov); + + } + } + + // We do this so the ROI remains attached the the same label and is not + // changed if the program is run again + SortListbyproperty.sortpointList(AllmeanCovar); + + return AllmeanCovar; + + } + + public static Img copyImage(final RandomAccessibleInterval input) { + // create a new Image with the same dimensions but the other imgFactory + // note that the input provides the size for the new image by + // implementing the Interval interface + Img output = new ArrayImgFactory().create(input, Views.iterable(input).firstElement()); + + // create a cursor that automatically localizes itself on every move + Cursor cursorInput = Views.iterable(input).localizingCursor(); + RandomAccess randomAccess = output.randomAccess(); + + // iterate over the input cursor + while (cursorInput.hasNext()) { + // move input cursor forward + cursorInput.fwd(); + + // set the output cursor to the position of the input cursor + randomAccess.setPosition(cursorInput); + + // set the value of this pixel of the output image, every Type + // supports T.set( T type ) + randomAccess.get().set(cursorInput.get()); + } + + // return the copy + return output; + } + + public static Float AutomaticThresholding(RandomAccessibleInterval inputimg) { + + FloatType max = new FloatType(); + FloatType min = new FloatType(); + RandomAccessibleInterval inputimgcopy = copy(inputimg); + Float ThresholdNew, Thresholdupdate; + + max = computeMaxIntensity(inputimgcopy); + min = computeMinIntensity(inputimgcopy); + + ThresholdNew = (max.get() - min.get()) / 2; + + // Get the new threshold value after segmenting the inputimage with + // thresholdnew + Thresholdupdate = SegmentbyThresholding(Views.iterable(inputimgcopy), ThresholdNew); + + while (true) { + + ThresholdNew = SegmentbyThresholding(Views.iterable(inputimgcopy), Thresholdupdate); + + // Check if the new threshold value is close to the previous value + if (Math.abs(Thresholdupdate - ThresholdNew) < 1.0E-2) + break; + Thresholdupdate = ThresholdNew; + } + + return ThresholdNew; + + } + + public static Img copy(final RandomAccessibleInterval input) { + // create a new Image with the same dimensions but the other imgFactory + // note that the input provides the size for the new image by + // implementing the Interval interface + Img output = new ArrayImgFactory().create(input, Views.iterable(input).firstElement()); + + // create a cursor that automatically localizes itself on every move + Cursor cursorInput = Views.iterable(input).localizingCursor(); + RandomAccess randomAccess = output.randomAccess(); + + // iterate over the input cursor + while (cursorInput.hasNext()) { + // move input cursor forward + cursorInput.fwd(); + + // set the output cursor to the position of the input cursor + randomAccess.setPosition(cursorInput); + + // set the value of this pixel of the output image, every Type + // supports T.set( T type ) + randomAccess.get().set(cursorInput.get()); + } + + // return the copy + return output; + } + + public static FloatType computeMaxIntensity(final RandomAccessibleInterval inputimg) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType max = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(max) > 0) { + max.set(type); + + } + } + + return max; + } + + public static FloatType computeMinIntensity(final RandomAccessibleInterval inputimg) { + // create a cursor for the image (the order does not matter) + final Cursor cursor = Views.iterable(inputimg).cursor(); + + // initialize min and max with the first image value + FloatType type = cursor.next(); + FloatType min = type.copy(); + + // loop over the rest of the data and determine min and max value + while (cursor.hasNext()) { + // we need this type more than once + type = cursor.next(); + + if (type.compareTo(min) < 0) { + min.set(type); + + } + } + + return min; + } + + // Segment image by thresholding, used to determine automatic thresholding + // level + public static Float SegmentbyThresholding(IterableInterval inputimg, Float Threshold) { + + int n = inputimg.numDimensions(); + Float ThresholdNew; + PointSampleList listA = new PointSampleList(n); + PointSampleList listB = new PointSampleList(n); + Cursor cursor = inputimg.localizingCursor(); + while (cursor.hasNext()) { + cursor.fwd(); + + if (cursor.get().get() < Threshold) { + Point newpointA = new Point(n); + newpointA.setPosition(cursor); + listA.add(newpointA, cursor.get().copy()); + } else { + Point newpointB = new Point(n); + newpointB.setPosition(cursor); + listB.add(newpointB, cursor.get().copy()); + } + } + final RealSum realSumA = new RealSum(); + long countA = 0; + + for (final FloatType type : listA) { + realSumA.add(type.getRealDouble()); + ++countA; + } + + final double sumA = realSumA.getSum() / countA; + + final RealSum realSumB = new RealSum(); + long countB = 0; + + for (final FloatType type : listB) { + realSumB.add(type.getRealDouble()); + ++countB; + } + + final double sumB = realSumB.getSum() / countB; + + ThresholdNew = (float) (sumA + sumB) / 2; + + return ThresholdNew; + + } + + /** + * 2D correlated Gaussian + * + * @param mean + * (x,y) components of mean vector + * @param cov + * (xx, xy, yy) components of covariance matrix + * @return ImageJ roi + */ + public static EllipseRoi createEllipse(final double[] mean, final double[] cov, final double nsigmas) { + + final double a = cov[0]; + final double b = cov[1]; + final double c = cov[2]; + final double d = Math.sqrt(a * a + 4 * b * b - 2 * a * c + c * c); + final double scale1 = Math.sqrt(0.5 * (a + c + d)) * nsigmas; + final double scale2 = Math.sqrt(0.5 * (a + c - d)) * nsigmas; + final double theta = 0.5 * Math.atan2((2 * b), (a - c)); + final double x = mean[0]; + final double y = mean[1]; + final double dx = scale1 * Math.cos(theta); + final double dy = scale1 * Math.sin(theta); + final EllipseRoi ellipse = new EllipseRoi(x - dx, y - dy, x + dx, y + dy, scale2 / scale1); + return ellipse; + } + + + public void saveResultsToExcel(String xlFile, ResultsTable rt) { + + FileOutputStream xlOut = null; + try { + + xlOut = new FileOutputStream(xlFile); + } catch (FileNotFoundException ex) { + + } + + HSSFWorkbook xlBook = new HSSFWorkbook(); + HSSFSheet xlSheet = xlBook.createSheet("Results Object Tracker"); + + HSSFRow r = null; + HSSFCell c = null; + HSSFCellStyle cs = xlBook.createCellStyle(); + HSSFCellStyle cb = xlBook.createCellStyle(); + HSSFFont f = xlBook.createFont(); + HSSFFont fb = xlBook.createFont(); + HSSFDataFormat df = xlBook.createDataFormat(); + f.setFontHeightInPoints((short) 12); + fb.setFontHeightInPoints((short) 12); + fb.setBoldweight((short) Font.BOLD); + cs.setFont(f); + cb.setFont(fb); + cs.setDataFormat(df.getFormat("#,##0.000")); + cb.setDataFormat(HSSFDataFormat.getBuiltinFormat("text")); + cb.setFont(fb); + + int numRows = rt.size(); + String[] colHeaders = rt.getHeadings(); + int rownum = 0; + + // Create a Header + r = xlSheet.createRow(rownum); + + for (int cellnum = 0; cellnum < colHeaders.length; cellnum++) { + + c = r.createCell((short) cellnum); + c.setCellStyle(cb); + c.setCellValue(colHeaders[cellnum]); + + } + rownum++; + + for (int row = 0; row < numRows; row++) { + + r = xlSheet.createRow(rownum + row); + int numCols = rt.getLastColumn() + 1; + + for (int cellnum = 0; cellnum < numCols; cellnum++) { + + c = r.createCell((short) cellnum); + + c.setCellValue(rt.getValueAsDouble(cellnum, row)); + + } + + } + + try { + xlBook.write(xlOut); + xlOut.close(); + } catch (IOException ex) { + + + } + + } + + +} diff --git a/src/main/java/utility/IlastikConnectedComponents.java b/src/main/java/utility/IlastikConnectedComponents.java new file mode 100644 index 0000000..bd0daf2 --- /dev/null +++ b/src/main/java/utility/IlastikConnectedComponents.java @@ -0,0 +1,263 @@ +package utility; + +import java.util.Iterator; + +import ij.ImageJ; + +import net.imglib2.Cursor; +import net.imglib2.KDTree; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealPoint; +import net.imglib2.RealPointSampleList; +import net.imglib2.algorithm.labeling.AllConnectedComponents; +import net.imglib2.algorithm.labeling.Watershed; +import net.imglib2.algorithm.stats.Normalize; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.labeling.DefaultROIStrategyFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingROIStrategy; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.neighborsearch.NearestNeighborSearchOnKDTree; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.type.NativeType; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +public class IlastikConnectedComponents { + + static RandomAccessibleInterval connectedcomponentImage; + static RandomAccessibleInterval bitimg; + static RandomAccessibleInterval distimg; + + public static > RandomAccessibleInterval GetLabelledImage( + RandomAccessibleInterval source) { + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new FloatType()); + distimg = factory.create(source, new FloatType()); + DistanceTransformImage(source, distimg); + // Prepare seed image + NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + oldseedLabeling = PrepareSeedImage(source); + + NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + + outputLabeling = GetlabeledImage(source, oldseedLabeling); + connectedcomponentImage = oldseedLabeling.getStorageImg(); + return connectedcomponentImage; + } + + public static NativeImgLabeling GetlabeledImage( + RandomAccessibleInterval inputimg, NativeImgLabeling seedLabeling) { + + int n = inputimg.numDimensions(); + long[] dimensions = new long[n]; + + for (int d = 0; d < n; ++d) + dimensions[d] = inputimg.dimension(d); + final NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + final Watershed watershed = new Watershed(); + + watershed.setSeeds(seedLabeling); + watershed.setIntensityImage(inputimg); + watershed.setStructuringElement(AllConnectedComponents.getStructuringElement(2)); + watershed.setOutputLabeling(outputLabeling); + watershed.process(); + DefaultROIStrategyFactory deffactory = new DefaultROIStrategyFactory(); + LabelingROIStrategy> factory = deffactory + .createLabelingROIStrategy(watershed.getResult()); + outputLabeling.setLabelingCursorStrategy(factory); + + return outputLabeling; + + } + + public static > void Dodistance(RandomAccessibleInterval inputimg, + RandomAccessibleInterval bitimg, RandomAccessibleInterval outimg) { + int n = inputimg.numDimensions(); + + // make an empty list + final RealPointSampleList list = new RealPointSampleList(n); + + // cursor on the binary image + final Cursor cursor = Views.iterable(bitimg).localizingCursor(); + // for every pixel that is 1, make a new RealPoint at that location + while (cursor.hasNext()) + if (cursor.next().getInteger() == 1) + list.add(new RealPoint(cursor), cursor.get()); + + // build the KD-Tree from the list of points that == 1 + final KDTree tree = new KDTree(list); + + // Instantiate a nearest neighbor search on the tree (does not modifiy + // the tree, just uses it) + final NearestNeighborSearchOnKDTree search = new NearestNeighborSearchOnKDTree(tree); + + // randomaccess on the output + final RandomAccess ranac = outimg.randomAccess(); + + // reset cursor for the input (or make a new one) + cursor.reset(); + + // for every pixel of the binary image + while (cursor.hasNext()) { + cursor.fwd(); + + // set the randomaccess to the same location + ranac.setPosition(cursor); + + // if value == 0, look for the nearest 1-valued pixel + if (cursor.get().getInteger() == 0) { + // search the nearest 1 to the location of the cursor (the + // current 0) + search.search(cursor); + + // get the distance (the previous call could return that, this + // for generality that it is two calls) + + ranac.get().setReal(search.getDistance()); + + } else { + // if value == 1, no need to search + ranac.get().setZero(); + } + } + + } + + /*** + * + * Do the distance transform of the input image using the bit image provided. + * + * @param inputimg + * The pre-processed input image as RandomAccessibleInterval + * @param outimg + * The distance transormed image having the same dimensions as the + * input image. + * @param invtype + * Straight: The intensity value is set to the distance, gives white + * on black background. Inverse: The intensity is set to the negative + * of the distance, gives black on white background. + */ + + public static > void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval outimg) { + Dodistance(inputimg, bitimg, outimg); + + } + + public static > void DistanceTransformImage(RandomAccessibleInterval inputimg, + RandomAccessibleInterval bitimg, RandomAccessibleInterval outimg) { + + Dodistance(inputimg, bitimg, outimg); + } + + public static void ProcessSlice(final RandomAccessibleInterval source, + final RandomAccessibleInterval bitimg, final RandomAccessibleInterval intimg) { + final ImgFactory factory = Util.getArrayOrCellImgFactory(source, new FloatType()); + distimg = factory.create(source, new FloatType()); + DistanceTransformImage(source, bitimg, distimg); + // Prepare seed image + NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + oldseedLabeling = PrepareSeedImage(source, bitimg); + + NativeImgLabeling outputLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(source, new IntType())); + + outputLabeling = GetlabeledImage(distimg, oldseedLabeling); + connectedcomponentImage = outputLabeling.getStorageImg(); + + final Cursor cursor = Views.iterable(intimg).localizingCursor(); + final RandomAccess ranac = connectedcomponentImage.randomAccess(); + + while (cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + cursor.get().set(ranac.get()); + + } + + } + + public static > NativeImgLabeling PrepareSeedImage( + RandomAccessibleInterval inputimg) { + + // New Labeling type + final ImgLabeling seedLabeling = new ImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // Old Labeling type + final NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // The label generator for both new and old type + final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0); + + // Getting unique labelled image (old version) + AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, bitimg, labelGenerator, + AllConnectedComponents.getStructuringElement(inputimg.numDimensions())); + return oldseedLabeling; + } + + public static > NativeImgLabeling PrepareSeedImage( + RandomAccessibleInterval inputimg, RandomAccessibleInterval bitimg) { + + // New Labeling type + final ImgLabeling seedLabeling = new ImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // Old Labeling type + final NativeImgLabeling oldseedLabeling = new NativeImgLabeling( + new ArrayImgFactory().create(inputimg, new IntType())); + + // The label generator for both new and old type + final Iterator labelGenerator = AllConnectedComponents.getIntegerNames(0); + + // Getting unique labelled image (old version) + AllConnectedComponents.labelAllConnectedComponents(oldseedLabeling, bitimg, labelGenerator, + AllConnectedComponents.getStructuringElement(inputimg.numDimensions())); + return oldseedLabeling; + } + + public static RandomAccessibleInterval CreateBinaryImage(RandomAccessibleInterval inputimage, + final FloatType threshold) { + + final ImgFactory factory = net.imglib2.util.Util.getArrayOrCellImgFactory(inputimage, new BitType()); + RandomAccessibleInterval binaryimage = factory.create(inputimage, new BitType()); + + Cursor cursor = Views.iterable(inputimage).localizingCursor(); + RandomAccess ranac = binaryimage.randomAccess(); + + while (cursor.hasNext()) { + + cursor.fwd(); + + ranac.setPosition(cursor); + + if (cursor.get().compareTo(threshold) >= 1) + ranac.get().setOne(); + else + ranac.get().setZero(); + + } + + return binaryimage; + } + + + +} diff --git a/src/main/java/utility/Interpolator.java b/src/main/java/utility/Interpolator.java new file mode 100644 index 0000000..d0e39d5 --- /dev/null +++ b/src/main/java/utility/Interpolator.java @@ -0,0 +1,274 @@ +package utility; + +public class Interpolator +{ + private double[] input; + private double[] c; + private int nx; + private int ny; + private String mode; + private double a; + private double c0; + + public Interpolator(double[] paramArrayOfDouble, int paramInt1, int paramInt2, String paramString) + { + input = paramArrayOfDouble; + nx = paramInt1; + ny = paramInt2; + mode = paramString; + if (paramString.equals("linear")) + { + c = paramArrayOfDouble; + } + else if (paramString.equals("quadratic")) + { + c0 = 8.0D; + a = (-3.0D + 2.0D * Math.sqrt(2.0D)); + computeCoefficients(); + } + else if (paramString.equals("cubic")) + { + c0 = 6.0D; + a = (-2.0D + Math.sqrt(3.0D)); + computeCoefficients(); + } + } + + public double getValue(double paramDouble1, double paramDouble2) + { + int i = (int)paramDouble1; + int j = (int)paramDouble2; + int m, i1; + if (mode.equals("linear")) + { + double d1 = paramDouble1 - i; + double d2 = paramDouble2 - j; + if (paramDouble1 < 0.0D) + { + d1 = -d1; + m = mirror(i - 1, nx); + } + else + { + m = mirror(i + 1, nx); + } + if (paramDouble2 < 0.0D) + { + d2 = -d2; + i1 = mirror(j - 1, ny); + } + else + { + i1 = mirror(j + 1, ny); + } + int k = mirror(i, nx); + int n = mirror(j, ny); + return (1.0D - d2) * (d1 * input[(m + n * nx)] + (1.0D - d1) * input[(k + n * nx)]) + d2 * (d1 * input[(m + i1 * nx)] + (1.0D - d1) * input[(k + i1 * nx)]); + } + double d1 = paramDouble1 - i; + double d2 = paramDouble2 - j; + if (paramDouble1 < 0.0D) + { + i -= 1; + d1 = 1.0D + d1; + } + if (paramDouble2 < 0.0D) + { + j -= 1; + d2 = 1.0D + d2; + } + double[] arrayOfDouble1; + double[] arrayOfDouble2; + if (mode.equals("quadratic")) + { + arrayOfDouble1 = getQuadraticSpline(d1); + arrayOfDouble2 = getQuadraticSpline(d2); + } + else + { + arrayOfDouble1 = getCubicSpline(d1); + arrayOfDouble2 = getCubicSpline(d2); + } + int k = i - 1; + m = i; + int i2 = i + 1; + int i3 = i + 2; + k = mirror(k, nx); + m = mirror(m, nx); + i2 = mirror(i2, nx); + i3 = mirror(i3, nx); + int n = j - 1; + i1 = j; + int i4 = j + 1; + int i5 = j + 2; + n = mirror(n, ny); + i1 = mirror(i1, ny); + i4 = mirror(i4, ny); + i5 = mirror(i5, ny); + n *= nx; + i1 *= nx; + i4 *= nx; + i5 *= nx; + double d3 = arrayOfDouble1[0] * (arrayOfDouble2[0] * c[(k + n)] + arrayOfDouble2[1] * c[(k + i1)] + arrayOfDouble2[2] * c[(k + i4)] + arrayOfDouble2[3] * c[(k + i5)]) + arrayOfDouble1[1] * (arrayOfDouble2[0] * c[(m + n)] + arrayOfDouble2[1] * c[(m + i1)] + arrayOfDouble2[2] * c[(m + i4)] + arrayOfDouble2[3] * c[(m + i5)]) + arrayOfDouble1[2] * (arrayOfDouble2[0] * c[(i2 + n)] + arrayOfDouble2[1] * c[(i2 + i1)] + arrayOfDouble2[2] * c[(i2 + i4)] + arrayOfDouble2[3] * c[(i2 + i5)]) + arrayOfDouble1[3] * (arrayOfDouble2[0] * c[(i3 + n)] + arrayOfDouble2[1] * c[(i3 + i1)] + arrayOfDouble2[2] * c[(i3 + i4)] + arrayOfDouble2[3] * c[(i3 + i5)]); + return d3; + } + + private int mirror(int paramInt1, int paramInt2) + { + if ((paramInt1 >= 0) && (paramInt1 < paramInt2)) { + return paramInt1; + } + if (paramInt1 < 0) { + return -paramInt1; + } + return 2 * paramInt2 - 2 - paramInt1; + } + + private void computeCoefficients() + { + double[] arrayOfDouble = getCausalInitHorizontal(input, a); + int k; + for (int j = 0; j < ny; j++) { + int i; + for (k = 1; k < nx; k++) + { + i = k + j * nx; + arrayOfDouble[i] = (input[i] + a * arrayOfDouble[(i - 1)]); + } + } + c = getAntiCausalInitHorizontal(arrayOfDouble, a); + for (int j = 0; j < ny; j++) { + int i; + for (k = nx - 2; k >= 0; k--) + { + i = k + j * nx; + c[i] = (a * (c[(i + 1)] - arrayOfDouble[i])); + } + } + arrayOfDouble = getCausalInitVertical(c, a); + for (int j = 0; j < nx; j++) { + int i; + for (k = 1; k < ny; k++) + { + i = j + k * nx; + arrayOfDouble[i] = (c[i] + a * arrayOfDouble[(i - nx)]); + } + } + c = getAntiCausalInitVertical(arrayOfDouble, a); + for (int j = 0; j < nx; j++) { + int i; + for (k = ny - 2; k >= 0; k--) + { + i = j + k * nx; + c[i] = (a * (c[(i + nx)] - arrayOfDouble[i])); + } + } + double d = c0 * c0; + for (int i = 0; i < ny * ny; i++) { + c[i] = (d * c[i]); + } + } + + private static double[] getQuadraticSpline(double paramDouble) + { + if ((paramDouble < 0.0D) || (paramDouble > 1.0D)) { + throw new ArrayStoreException("Argument t for quadratic B-spline outside of expected range [0, 1]: " + paramDouble); + } + double[] arrayOfDouble = new double[4]; + if (paramDouble <= 0.5D) + { + arrayOfDouble[0] = ((paramDouble - 0.5D) * (paramDouble - 0.5D) / 2.0D); + arrayOfDouble[1] = (0.75D - paramDouble * paramDouble); + arrayOfDouble[2] = (1.0D - arrayOfDouble[1] - arrayOfDouble[0]); + arrayOfDouble[3] = 0.0D; + } + else + { + arrayOfDouble[0] = 0.0D; + arrayOfDouble[1] = ((paramDouble - 1.5D) * (paramDouble - 1.5D) / 2.0D); + arrayOfDouble[3] = ((paramDouble - 0.5D) * (paramDouble - 0.5D) / 2.0D); + arrayOfDouble[2] = (1.0D - arrayOfDouble[3] - arrayOfDouble[1]); + } + return arrayOfDouble; + } + + private static double[] getCubicSpline(double paramDouble) + { + if ((paramDouble < 0.0D) || (paramDouble > 1.0D)) { + throw new ArrayStoreException("Argument t for cubic B-spline outside of expected range [0, 1]: " + paramDouble); + } + double[] arrayOfDouble = new double[4]; + double d1 = 1.0D - paramDouble; + double d2 = paramDouble * paramDouble; + arrayOfDouble[0] = (d1 * d1 * d1 / 6.0D); + arrayOfDouble[1] = (0.6666666666666666D + 0.5D * d2 * (paramDouble - 2.0D)); + arrayOfDouble[3] = (d2 * paramDouble / 6.0D); + arrayOfDouble[2] = (1.0D - arrayOfDouble[3] - arrayOfDouble[1] - arrayOfDouble[0]); + return arrayOfDouble; + } + + private double[] getAntiCausalInitVertical(double[] paramArrayOfDouble, double paramDouble) + { + double[] arrayOfDouble = new double[nx * ny]; + int i = (ny - 1) * nx; + double d = paramDouble * paramDouble - 1.0D; + for (int j = 0; j < nx; j++) { + arrayOfDouble[(j + i)] = (paramDouble * (paramArrayOfDouble[(j + i)] + paramDouble * paramArrayOfDouble[(j + i - nx)]) / d); + } + return arrayOfDouble; + } + + private double[] getAntiCausalInitHorizontal(double[] paramArrayOfDouble, double paramDouble) + { + double[] arrayOfDouble = new double[nx * ny]; + double d = paramDouble * paramDouble - 1.0D; + for (int i = 0; i < ny; i++) { + arrayOfDouble[(nx - 1 + i * nx)] = (paramDouble * (paramArrayOfDouble[(nx - 1 + i * nx)] + paramDouble * paramArrayOfDouble[(nx - 2 + i * nx)]) / d); + } + return arrayOfDouble; + } + + private double[] getCausalInitVertical(double[] paramArrayOfDouble, double paramDouble) + { + double[] arrayOfDouble = new double[nx * ny]; + for (int i = 0; i < nx; i++) + { + double d1 = Math.pow(paramDouble, ny - 1); + double d2 = paramArrayOfDouble[i] + paramArrayOfDouble[(i + (ny - 1) * nx)] * d1; + double d3 = d1 * d1; + d1 = d3 / paramDouble; + double d4 = paramDouble; + for (int j = 1; j < ny - 1; j++) + { + d2 += paramArrayOfDouble[(i + j * nx)] * (d4 + d1); + d4 *= paramDouble; + d1 /= paramDouble; + } + d2 /= (1.0D - d3); + arrayOfDouble[i] = d2; + } + return arrayOfDouble; + } + + private double[] getCausalInitHorizontal(double[] paramArrayOfDouble, double paramDouble) + { + double[] arrayOfDouble = new double[nx * ny]; + for (int i = 0; i < ny; i++) + { + double d1 = Math.pow(paramDouble, nx - 1); + double d2 = paramArrayOfDouble[(i * nx)] + paramArrayOfDouble[(nx - 1 + i * nx)] * d1; + double d3 = d1 * d1; + d1 = d3 / paramDouble; + double d4 = paramDouble; + for (int j = 1; j < nx - 1; j++) + { + d2 += paramArrayOfDouble[(j + i * nx)] * (d4 + d1); + d4 *= paramDouble; + d1 /= paramDouble; + } + d2 /= (1.0D - d3); + arrayOfDouble[(i * nx)] = d2; + } + return arrayOfDouble; + } +} diff --git a/src/main/java/utility/LabelRansac.java b/src/main/java/utility/LabelRansac.java deleted file mode 100644 index 423ed0f..0000000 --- a/src/main/java/utility/LabelRansac.java +++ /dev/null @@ -1,176 +0,0 @@ -package utility; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - -import javax.swing.JProgressBar; - -import ellipsoidDetector.Distance; -import ellipsoidDetector.Intersectionobject; -import ellipsoidDetector.Tangentobject; -import ij.gui.EllipseRoi; -import ij.gui.Line; -import ij.gui.OvalRoi; -import ij.gui.Roi; -import net.imglib2.RandomAccessibleInterval; -import net.imglib2.RealLocalizable; - -import net.imglib2.img.display.imagej.ImageJFunctions; -import net.imglib2.type.logic.BitType; -import net.imglib2.type.numeric.integer.IntType; -import net.imglib2.type.numeric.real.FloatType; -import net.imglib2.util.Pair; -import net.imglib2.util.ValuePair; -import pluginTools.InteractiveSimpleEllipseFit; -import varun_algorithm_ransac_Ransac.*; - -public class LabelRansac implements Runnable { - - final InteractiveSimpleEllipseFit parent; - final RandomAccessibleInterval ActualRoiimg; - List> truths; - final int t; - final int z; - final ArrayList resultroi; - final ArrayList resultovalroi; - final ArrayList resultlineroi; - final ArrayList AllPointsofIntersect; - final ArrayList Allintersection; - - final ArrayList> fitmapspecial; - final boolean supermode; - final int percent; - final JProgressBar jpb; - - public LabelRansac(final InteractiveSimpleEllipseFit parent, final RandomAccessibleInterval ActualRoiimg, - List> truths, final int t, final int z, ArrayList resultroi, - ArrayList resultovalroi, ArrayList resultlineroi, - final ArrayList AllPointsofIntersect, final ArrayList Allintersection,final ArrayList> fitmapspecial, final boolean supermode) { - - this.parent = parent; - this.ActualRoiimg = ActualRoiimg; - this.truths = truths; - this.t = t; - this.z = z; - this.resultroi = resultroi; - this.resultovalroi = resultovalroi; - this.resultlineroi = resultlineroi; - this.Allintersection = Allintersection; - this.AllPointsofIntersect = AllPointsofIntersect; - this.fitmapspecial = fitmapspecial; - this.jpb = null; - this.supermode = supermode; - this.percent = 0; - } - - public LabelRansac(final InteractiveSimpleEllipseFit parent, final RandomAccessibleInterval source, - List> truths, final int t, final int z, ArrayList resultroi, - ArrayList resultovalroi, ArrayList resultlineroi, - final ArrayList AllPointsofIntersect, final ArrayList Allintersection, - final ArrayList> fitmapspecial, final JProgressBar jpb, final int percent, final boolean supermode) { - - this.parent = parent; - this.ActualRoiimg = source; - this.truths = truths; - this.t = t; - this.z = z; - this.resultroi = resultroi; - this.resultovalroi = resultovalroi; - this.resultlineroi = resultlineroi; - this.Allintersection = Allintersection; - this.AllPointsofIntersect = AllPointsofIntersect; - this.fitmapspecial = fitmapspecial; - this.jpb = jpb; - this.supermode = supermode; - this.percent = percent; - } - - - - @Override - public void run() { - - System.out.println("Running..."); - if(!parent.automode && !parent.supermode) { - if (parent.fourthDimensionSize != 0) - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.Accountedframes.entrySet().size()), - "Fitting ellipses and computing angles T = " + t + "/" + parent.fourthDimensionSize + " Z = " + z - + "/" + parent.thirdDimensionSize); - else - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.AccountedZ.entrySet().size()), - "Fitting ellipses and computing angles T/Z = " + z + "/" + parent.thirdDimensionSize); - } - else { - - utility.ProgressBar.SetProgressBar(jpb, 100 * percent / (parent.thirdDimensionSize), - "Fitting ellipses and computing angles T/Z = " + z + "/" + parent.thirdDimensionSize); - } - - - truths = ConnectedComponentCoordinates.GetCoordinates(ActualRoiimg , new FloatType(0)); - - - if(parent.fourthDimensionSize > 1) - parent.timeslider.setValue(utility.Slicer.computeScrollbarPositionFromValue(parent.fourthDimension, parent.fourthDimensionsliderInit, parent.fourthDimensionSize, parent.scrollbarSize)); - parent.zslider.setValue(utility.Slicer.computeScrollbarPositionFromValue(parent.thirdDimension, parent.thirdDimensionsliderInit, parent.thirdDimensionSize, parent.scrollbarSize)); - final int ndims = ActualRoiimg.numDimensions(); - final NumericalSolvers numsol = new BisectorEllipsoid(); - // Using the ellipse model to do the fitting - ArrayList>>> Reducedsamples = RansacEllipsoid.Allsamples( - truths, parent.outsideCutoff, parent.insideCutoff, parent.minpercent, parent.minperimeter, parent.maxperimeter, numsol, parent.maxtry, ndims); - String uniqueID = Integer.toString(z) + Integer.toString(t); - if (Reducedsamples != null) { - SortSegments.Sort(Reducedsamples); - for (int i = 0; i < Reducedsamples.size() - 1; ++i) { - - double[] center = Reducedsamples.get(i).getA().getCenter(); - - double[] centernext = Reducedsamples.get(i + 1).getA().getCenter(); - - double dist = Distance.DistanceSq(center, centernext); - - if (dist < parent.minSeperation * parent.minSeperation) - Reducedsamples.remove(Reducedsamples.get(i)); - - } - - for (int i = 0; i < Reducedsamples.size(); ++i) { - - EllipseRoi ellipse = DisplayasROI.create2DEllipse(Reducedsamples.get(i).getA().getCenter(), - new double[] { Reducedsamples.get(i).getA().getCovariance()[0][0], - Reducedsamples.get(i).getA().getCovariance()[0][1], - Reducedsamples.get(i).getA().getCovariance()[1][1] }); - - resultroi.add(ellipse); - - System.out.println("Center :" + Reducedsamples.get(i).getA().getCenter()[0] + " " - + Reducedsamples.get(i).getA().getCenter()[1] + " " + " Radius " - + Reducedsamples.get(i).getA().getRadii()[0] + " " + Reducedsamples.get(i).getA().getRadii()[1] - + "time " + " " + t + " " + "Z" + " " + z); - - } - - - parent.superReducedSamples.addAll(Reducedsamples); - - - - if (parent.automode || parent.supermode && !parent.redoing) { - - Roiobject currentobject = new Roiobject(resultroi,resultovalroi,resultlineroi, z, t, true); - parent.ZTRois.put(uniqueID, currentobject); - - DisplayAuto.Display(parent); - } - - - - } else - return; - } - -} diff --git a/src/main/java/utility/Logger.java b/src/main/java/utility/Logger.java new file mode 100644 index 0000000..8c882a8 --- /dev/null +++ b/src/main/java/utility/Logger.java @@ -0,0 +1,342 @@ +package utility; + +import java.awt.Color; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; + +import ij.IJ; + +/** + * This class is used to log messages occurring during TrackMate execution. + */ +public abstract class Logger extends PrintWriter +{ + + public Logger() + { + // Call super with a dummy writer + super( new Writer() + { + @Override + public void write( final char[] cbuf, final int off, final int len ) throws IOException + {} + + @Override + public void flush() throws IOException + {} + + @Override + public void close() throws IOException + {} + } ); + // Replace by a useful writer + this.out = new Writer() + { + @Override + public void close() throws IOException + {} + + @Override + public void flush() throws IOException + {} + + @Override + public void write( final char[] cbuf, final int off, final int len ) throws IOException + { + String str = ""; + for ( int i = off; i < len; i++ ) + str += cbuf[ i ]; + log( str ); + } + }; + } + + public static final Color NORMAL_COLOR = Color.BLACK; + + public static final Color ERROR_COLOR = new Color( 0.8f, 0, 0 ); + + public static final Color GREEN_COLOR = new Color( 0, 0.6f, 0 ); + + public static final Color BLUE_COLOR = new Color( 0, 0, 0.7f ); + + /** + * Append the message to the logger, with the specified color. + * + * @param message + * the message to append. + * @param color + * the color to use. + */ + public abstract void log( String message, Color color ); + + /** + * Send the message to the error channel of this logger. + * + * @param message + * the message to send. + */ + public abstract void error( String message ); + + /** + * Append the message to the logger with default black color. + * + * @param message + * the message to append. + */ + public void log( final String message ) + { + log( message, NORMAL_COLOR ); + } + + /** + * Set the progress value of the process logged by this logger. Values + * should be between 0 and 1, 1 meaning the process if finished. + * + * @param val + * the progress value (double from 0 to 1). + */ + public abstract void setProgress( double val ); + + /** + * Set the status to be displayed by this logger. + * + * @param status + * the status to display. + */ + public abstract void setStatus( String status ); + + /** + * This logger discard any message. + */ + public static final Logger VOID_LOGGER = new Logger() + { + + @Override + public void setStatus( final String status ) + {} + + @Override + public void setProgress( final double val ) + {} + + @Override + public void log( final String message, final Color color ) + {} + + @Override + public void error( final String message ) + {} + }; + + /** + * This {@link Logger} simply outputs to the standard output and standard + * error. The {@link #setProgress(double)} method is ignored, the + * {@link #setStatus(String)} is sent to the console. + */ + public static Logger DEFAULT_LOGGER = new Logger() + { + + @Override + public void log( final String message, final Color color ) + { + System.out.print( message ); + } + + @Override + public void error( final String message ) + { + System.err.print( message ); + } + + @Override + public void setProgress( final double val ) + {} + + @Override + public void setStatus( final String status ) + { + System.out.println( status ); + } + }; + + /** + * This {@link Logger} outputs to the ImageJ log window, and to the ImageJ + * toolbar to report progress. Colors are ignored. + */ + public static Logger IJ_LOGGER = new Logger() + { + + @Override + public void log( final String message, final Color color ) + { + IJ.log( message ); + } + + @Override + public void error( final String message ) + { + IJ.log( message ); + } + + @Override + public void setProgress( final double val ) + { + IJ.showProgress( val ); + } + + @Override + public void setStatus( final String status ) + { + IJ.showStatus( status ); + } + }; + + /** + * This {@link Logger} outputs everything to the ImageJ toolbar. This is not + * optimal for long messages. Colors are ignored. + */ + public static Logger IJTOOLBAR_LOGGER = new Logger() + { + @Override + public void log( final String message, final Color color ) + { + IJ.showStatus( message ); + } + + @Override + public void error( final String message ) + { + IJ.showStatus( message ); + } + + @Override + public void setProgress( final double val ) + { + IJ.showProgress( val ); + } + + @Override + public void setStatus( final String status ) + { + IJ.showStatus( status ); + } + }; + + /** + * This {@link Logger} outputs to a StringBuilder given at construction. + * Report progress and colors are ignored. + */ + public static class StringBuilderLogger extends Logger + { + + private final StringBuilder sb; + + public StringBuilderLogger( final StringBuilder sb ) + { + this.sb = sb; + } + + public StringBuilderLogger() + { + this( new StringBuilder() ); + } + + /* + * METHODS + */ + + @Override + public void log( final String message, final Color color ) + { + sb.append( message ); + } + + @Override + public void error( final String message ) + { + sb.append( message ); + } + + @Override + public void setProgress( final double val ) + {} + + @Override + public void setStatus( final String status ) + { + sb.append( status ); + } + + @Override + public String toString() + { + return sb.toString(); + } + } + + /** + * A logger that wraps a master logger and uses it to echo any received + * message. This class is used to report progress of a sub-process. If it is + * sent to a subprocess, the master logger can show a progress in a range + * and from a starting point that can be specified. + * + * @author Jean-Yves Tinevez - 2014 + * + */ + public static class SlaveLogger extends Logger + { + + private final Logger master; + + private final double progressStart; + + private final double progressRange; + + /** + * Create a new slave logger that will report progress to its master in + * the following way: If a sub-process reports a progress of + * val, then the master logger will receive the progress + * value progressStart + progressRange * val. + * + * @param master + * the master {@link Logger}. + * @param progressStart + * the progress to start with. + * @param progressRange + * the progress range to report. + */ + public SlaveLogger( final Logger master, final double progressStart, final double progressRange ) + { + this.master = master; + this.progressStart = progressStart; + this.progressRange = progressRange; + } + + @Override + public void log( final String message, final Color color ) + { + master.log( message, color ); + } + + @Override + public void error( final String message ) + { + master.error( message ); + } + + @Override + public void setProgress( final double val ) + { + master.setProgress( progressStart + progressRange * val ); + } + + @Override + public void setStatus( final String status ) + { + master.setStatus( status ); + } + + } + +} diff --git a/src/main/java/utility/ModelChangeEvent.java b/src/main/java/utility/ModelChangeEvent.java new file mode 100644 index 0000000..70eec43 --- /dev/null +++ b/src/main/java/utility/ModelChangeEvent.java @@ -0,0 +1,245 @@ +package utility; + +import java.util.Collection; +import java.util.EventObject; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.graph.DefaultWeightedEdge; + + +public class ModelChangeEvent extends EventObject { + + private static final long serialVersionUID = -1L; + /** Indicate that a ThreeDRoiobject was added to the model. */ + public static final int FLAG_ThreeDRoiobject_ADDED = 0; + /** Indicate that a ThreeDRoiobject was removed from the model. */ + public static final int FLAG_ThreeDRoiobject_REMOVED = 1; + /** + * Indicate a modification of the features of a ThreeDRoiobject. It may have changed of + * position and feature, but not of frame. + */ + public static final int FLAG_ThreeDRoiobject_MODIFIED = 2; + /** + * Indicate that a ThreeDRoiobject has changed of frame, and possible of position, + * features, etc.. . + */ + public static final int FLAG_ThreeDRoiobject_FRAME_CHANGED = 3; + /** Indicate that an edge was added to the model. */ + public static final int FLAG_EDGE_ADDED = 4; + /** Indicate that an edge was removed from the model. */ + public static final int FLAG_EDGE_REMOVED = 5; + /** + * Indicate that an edge has been modified. Edge modifications occur when + * the target or source ThreeDRoiobjects are modified, or when the weight of the edge + * has been modified. + */ + public static final int FLAG_EDGE_MODIFIED = 6; + + public static final Map flagsToString = new HashMap<>(7); + static { + flagsToString.put(FLAG_ThreeDRoiobject_ADDED, "ThreeDRoiobject added"); + flagsToString.put(FLAG_ThreeDRoiobject_FRAME_CHANGED, "ThreeDRoiobject frame changed"); + flagsToString.put(FLAG_ThreeDRoiobject_MODIFIED, "ThreeDRoiobject modified"); + flagsToString.put(FLAG_ThreeDRoiobject_REMOVED, "ThreeDRoiobject removed"); + flagsToString.put(FLAG_EDGE_ADDED, "Edge added"); + flagsToString.put(FLAG_EDGE_MODIFIED, "Edge modified"); + flagsToString.put(FLAG_EDGE_REMOVED, "Edge removed"); + } + + + /** + * Event type indicating that the ThreeDRoiobjects of the model were computed, and + * are now accessible through {@link Model#getThreeDRoiobjects()}. + */ + public static final int ThreeDRoiobjectS_COMPUTED = 4; + /** + * Event type indicating that the ThreeDRoiobjects of the model were filtered. + */ + public static final int ThreeDRoiobjectS_FILTERED = 5; + /** + * Event type indicating that the tracks of the model were computed. + */ + public static final int TRACKS_COMPUTED = 6; + /** + * Event type indicating that the tracks of the model had their + * visibility changed. + */ + public static final int TRACKS_VISIBILITY_CHANGED = 7; + + /** + * Event type indicating that model was modified, by adding, removing or + * changing the feature of some ThreeDRoiobjects, and/or adding or removing edges in + * the tracks. Content of the modification can be accessed by + * {@link #getThreeDRoiobjects()}, {@link #getThreeDRoiobjectFlag(ThreeDRoiobject)}, + * {@link #getFromFrame(ThreeDRoiobject)} and {@link #getToFrame(ThreeDRoiobject)}, and for the + * tracks: {@link #getEdges()} and {@link #getEdgeFlag(DefaultWeightedEdge)} + * . + */ + public static final int MODEL_MODIFIED = 8; + + /** ThreeDRoiobjects affected by this event. */ + private final HashSet ThreeDRoiobjects = new HashSet<>(); + /** Edges affected by this event. */ + private final HashSet edges = new HashSet<>(); + /** For ThreeDRoiobjects removed or moved: frame from which they were removed or moved. */ + private final HashMap fromFrame = new HashMap<>(); + /** For ThreeDRoiobjects removed or added: frame to which they were added or moved. */ + private final HashMap toFrame = new HashMap<>(); + /** Modification flag for ThreeDRoiobjects affected by this event. */ + private final HashMap ThreeDRoiobjectFlags = new HashMap<>(); + /** Modification flag for edges affected by this event. */ + private final HashMap edgeFlags = new HashMap<>(); + /** The event type for this instance. */ + private final int eventID; + private Set trackUpdated; + + /** + * Create a new event, reflecting a change in a {@link Model}. + * + * @param source + * the object source of this event. + * @param eventID + * the evend ID to use for this event. + */ + public ModelChangeEvent(final Object source, final int eventID) { + super(source); + this.eventID = eventID; + } + + public int getEventID() { + return this.eventID; + } + + public boolean addAllThreeDRoiobjects(final Collection lThreeDRoiobjects) { + return this.ThreeDRoiobjects.addAll(lThreeDRoiobjects); + } + + public boolean addThreeDRoiobject(final ThreeDRoiobject ThreeDRoiobject) { + return this.ThreeDRoiobjects.add(ThreeDRoiobject); + } + + public boolean addAllEdges(final Collection lEdges) { + return this.edges.addAll(lEdges); + } + public boolean addEdge(final DefaultWeightedEdge edge) { + return edges.add(edge); + } + + public Integer putEdgeFlag(final DefaultWeightedEdge edge, final Integer flag) { + return edgeFlags.put(edge, flag); + } + + public Integer putThreeDRoiobjectFlag(final ThreeDRoiobject ThreeDRoiobject, final Integer flag) { + return ThreeDRoiobjectFlags.put(ThreeDRoiobject, flag); + } + + public Integer putFromFrame(final ThreeDRoiobject ThreeDRoiobject, final Integer lFromFrame) { + return this.fromFrame.put(ThreeDRoiobject, lFromFrame); + } + + public Integer putToFrame(final ThreeDRoiobject ThreeDRoiobject, final Integer lToFrame) { + return this.toFrame.put(ThreeDRoiobject, lToFrame); + } + + /** + * @return the set of ThreeDRoiobject that are affected by this event. Is empty + * if no ThreeDRoiobject is affected by this event. + */ + public Set getThreeDRoiobjects() { + return ThreeDRoiobjects; + } + + /** + * @return the set of edges that are affected by this event. Is empty + * if no edge is affected by this event. + */ + public Set getEdges() { + return edges; + } + + /** + * Returns the modification flag for the given ThreeDRoiobject affected by this event. + * + * @param ThreeDRoiobject + * the ThreeDRoiobject to query. + * @return the modification flag. + * @see #FLAG_ThreeDRoiobject_ADDED + * @see #FLAG_ThreeDRoiobject_MODIFIED + * @see #FLAG_ThreeDRoiobject_REMOVED + */ + public Integer getThreeDRoiobjectFlag(final ThreeDRoiobject ThreeDRoiobject) { + return ThreeDRoiobjectFlags.get(ThreeDRoiobject); + } + + /** + * Returns the modification flag for the given edge affected by this event. + * + * @param edge + * the edge to query. + * @return the modification flag. + * @see #FLAG_EDGE_ADDED + * @see #FLAG_EDGE_REMOVED + */ + public Integer getEdgeFlag(final DefaultWeightedEdge edge) { + return edgeFlags.get(edge); + } + + public Integer getToFrame(final ThreeDRoiobject ThreeDRoiobject) { + return toFrame.get(ThreeDRoiobject); + } + + public Integer getFromFrame(final ThreeDRoiobject ThreeDRoiobject) { + return fromFrame.get(ThreeDRoiobject); + } + + public void setSource(final Object source) { + this.source = source; + } + + @Override + public String toString() { + final StringBuilder str = new StringBuilder("[ModelChangeEvent]:\n"); + str.append(" - source: "+source.getClass() + "_" + source.hashCode()+"\n"); + str.append(" - event type: "); + switch (eventID) { + case ThreeDRoiobjectS_COMPUTED: + str.append("ThreeDRoiobjects computed\n"); + break; + case ThreeDRoiobjectS_FILTERED: + str.append("ThreeDRoiobjects filtered\n"); + break; + case TRACKS_COMPUTED: + str.append("Tracks computed\n"); + break; + case TRACKS_VISIBILITY_CHANGED: + str.append("Track visibility changed\n"); + break; + case MODEL_MODIFIED: + str.append("Model modified, with:\n"); + str.append("\t- ThreeDRoiobjects modified: "+ (ThreeDRoiobjects != null ? ThreeDRoiobjects.size() : 0) +"\n"); + for (final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects) { + str.append("\t\t" + ThreeDRoiobject + ": " + flagsToString.get(ThreeDRoiobjectFlags.get(ThreeDRoiobject)) + "\n"); + } + str.append("\t- edges modified: "+ (edges != null ? edges.size() : 0) +"\n"); + for (final DefaultWeightedEdge edge : edges) { + str.append("\t\t" + edge + ": " + flagsToString.get(edgeFlags.get(edge)) + "\n"); + } + str.append("\t- tracks to update: " + trackUpdated + "\n"); + } + return str.toString(); + } + + public void setTracksUpdated(final Set tracksToUpdate) { + this.trackUpdated = tracksToUpdate; + } + + /** + * @return the IDs of track that were modified or created by this event. + */ + public Set getTrackUpdated() { + return trackUpdated; + } +} diff --git a/src/main/java/utility/ModelChangeListener.java b/src/main/java/utility/ModelChangeListener.java new file mode 100644 index 0000000..087ad66 --- /dev/null +++ b/src/main/java/utility/ModelChangeListener.java @@ -0,0 +1,23 @@ + +package utility; +import java.util.EventListener; + + + +/** + * An interface for listeners that will be notified when a {@link Model} + * is been changed. + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> May 30, 2011 + * + */ +public interface ModelChangeListener extends EventListener { + + /** + * This notification is fired when a {@link Model} has been changed. + * + * @param event + * the {@link ModelChangeEvent}. + */ + public void modelChanged(final ModelChangeEvent event); + +} diff --git a/src/main/java/utility/NNFlagsearchKDtree.java b/src/main/java/utility/NNFlagsearchKDtree.java new file mode 100644 index 0000000..7684e5d --- /dev/null +++ b/src/main/java/utility/NNFlagsearchKDtree.java @@ -0,0 +1,90 @@ +package utility; + + +import net.imglib2.KDTree; +import net.imglib2.KDTreeNode; +import net.imglib2.RealLocalizable; +import net.imglib2.Sampler; +import net.imglib2.neighborsearch.NearestNeighborSearch; + +public class NNFlagsearchKDtree implements NearestNeighborSearch< FlagNode > { + +protected KDTree< FlagNode > tree; + + protected final int n; + protected final double[] pos; + + protected KDTreeNode< FlagNode > bestPoint; + protected double bestSquDistance; + + public NNFlagsearchKDtree( KDTree< FlagNode > tree ) { + n = tree.numDimensions(); + pos = new double[ n ]; + this.tree = tree; + } + + @Override + public int numDimensions() { + return n; + } + + @Override + public void search( RealLocalizable p ) { + p.localize( pos ); + bestSquDistance = Double.MAX_VALUE; + searchNode( tree.getRoot() ); + } + + protected void searchNode( KDTreeNode< FlagNode > current ) { + // consider the current node + final double distance = current.squDistanceTo( pos ); + boolean visited = current.get().isVisited(); + if ( distance < bestSquDistance && !visited ) { + bestSquDistance = distance; + bestPoint = current; + } + + final double axisDiff = pos[ current.getSplitDimension() ] - current.getSplitCoordinate(); + final double axisSquDistance = axisDiff * axisDiff; + final boolean leftIsNearBranch = axisDiff < 0; + + // search the near branch + final KDTreeNode< FlagNode > nearChild = leftIsNearBranch ? current.left : current.right; + final KDTreeNode< FlagNode > awayChild = leftIsNearBranch ? current.right : current.left; + if ( nearChild != null ) + searchNode( nearChild ); + + // search the away branch - maybe + if ( ( axisSquDistance <= bestSquDistance ) && ( awayChild != null ) ) + searchNode( awayChild ); + } + + @Override + public Sampler> getSampler() { + return bestPoint; + } + + @Override + public RealLocalizable getPosition() { + return bestPoint; + } + + @Override + public double getSquareDistance() { + return bestSquDistance; + } + + @Override + public double getDistance() { + return Math.sqrt( bestSquDistance ); + } + + @Override + public NNFlagsearchKDtree copy() { + final NNFlagsearchKDtree copy = new NNFlagsearchKDtree( tree ); + System.arraycopy( pos, 0, copy.pos, 0, pos.length ); + copy.bestPoint = bestPoint; + copy.bestSquDistance = bestSquDistance; + return copy; + } +} diff --git a/src/main/java/utility/NormalIntersection.java b/src/main/java/utility/NormalIntersection.java deleted file mode 100644 index 72087ca..0000000 --- a/src/main/java/utility/NormalIntersection.java +++ /dev/null @@ -1,157 +0,0 @@ -package utility; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.Map; - -import ellipsoidDetector.Intersectionobject; -import ellipsoidDetector.Tangentobject; -import ij.gui.EllipseRoi; -import ij.gui.Line; -import ij.gui.OvalRoi; -import net.imglib2.RealLocalizable; -import net.imglib2.type.logic.BitType; -import net.imglib2.util.Pair; -import net.imglib2.util.ValuePair; -import pluginTools.InteractiveSimpleEllipseFit; -import varun_algorithm_ransac_Ransac.Angleobject; -import varun_algorithm_ransac_Ransac.Ellipsoid; -import varun_algorithm_ransac_Ransac.Intersections; -import varun_algorithm_ransac_Ransac.Tangent2D; - -public class NormalIntersection { - - - - final InteractiveSimpleEllipseFit parent; - - - public NormalIntersection(final InteractiveSimpleEllipseFit parent) { - - this.parent = parent; - - } - - // Normal - public void Getsuperintersection(ArrayList resultroi, ArrayList resultovalroi, - ArrayList resultlineroi, final ArrayList AllPointsofIntersect, - final ArrayList Allintersection, int t, int z) { - - String uniqueID = Integer.toString(z) + Integer.toString(t); - - final ArrayList> fitmapspecial = new ArrayList>(); - - for (int index = 0; index < parent.superReducedSamples.size(); ++index) { - - for (int indexx = 0; indexx < parent.superReducedSamples.size(); ++indexx) { - - if (index != indexx) { - - fitmapspecial.add(new ValuePair(parent.superReducedSamples.get(index).getA(), - parent.superReducedSamples.get(indexx).getA())); - - } - - } - - } - - final ArrayList> fitmapspecialred = new ArrayList>(); - fitmapspecialred.addAll(fitmapspecial); - // Remove duplicate pairs - for (int i = 0; i < fitmapspecialred.size(); ++i) { - - Pair ellipsepairA = fitmapspecialred.get(i); - - for (int j = 0; j < fitmapspecialred.size(); ++j) { - - if (i != j) { - Pair ellipsepairB = fitmapspecialred.get(j); - - if (ellipsepairA.getA().hashCode() == (ellipsepairB.getB().hashCode()) - && ellipsepairA.getB().hashCode() == (ellipsepairB.getA().hashCode())) { - fitmapspecialred.remove(ellipsepairB); - break; - } - - } - - } - - } - - for (int i = 0; i < fitmapspecialred.size(); ++i) { - - Pair ellipsepair = fitmapspecialred.get(i); - - ArrayList pos = Intersections.PointsofIntersection(ellipsepair); - - // Reject wrong points that are not candidate points - - - - Tangentobject PointsIntersect = new Tangentobject(pos, ellipsepair, t, z); - - for (int j = 0; j < pos.size(); ++j) { - - OvalRoi intersectionsRoi = new OvalRoi(pos.get(j)[0] - parent.radiusdetection, - pos.get(j)[1] - parent.radiusdetection, 2 * parent.radiusdetection, 2 * parent.radiusdetection); - intersectionsRoi.setStrokeColor(parent.colorDet); - resultovalroi.add(intersectionsRoi); - - double[] lineparamA = Tangent2D.GetTangent(ellipsepair.getA(), pos.get(j)); - - double[] lineparamB = Tangent2D.GetTangent(ellipsepair.getB(), pos.get(j)); - - Angleobject angleobject = Tangent2D.GetTriAngle(lineparamA, lineparamB, pos.get(j), ellipsepair); - resultlineroi.add(angleobject.lineA); - resultlineroi.add(angleobject.lineB); - - Intersectionobject currentintersection = new Intersectionobject(pos.get(j), angleobject.angle, - ellipsepair, resultlineroi, t, z); - - Allintersection.add(currentintersection); - - - } - - AllPointsofIntersect.add(PointsIntersect); - - } - - parent.ALLIntersections.put(uniqueID, Allintersection); - - // Add new result rois to ZTRois - for (Map.Entry entry : parent.ZTRois.entrySet()) { - - Roiobject currentobject = entry.getValue(); - - if (currentobject.fourthDimension == t && currentobject.thirdDimension == z) { - - currentobject.resultroi = resultroi; - currentobject.resultovalroi = resultovalroi; - currentobject.resultlineroi = resultlineroi; - - } - - } - - } - - - - public double Sqdistance(double[] sourceLocation, double[] targetLocation) { - - double distance = 0; - - for (int d = 0; d < sourceLocation.length; ++d) { - - distance += (sourceLocation[d] - targetLocation[d]) * (sourceLocation[d] - targetLocation[d]); - } - - return distance; - - } - - -} diff --git a/src/main/java/utility/PreRoiobject.java b/src/main/java/utility/PreRoiobject.java new file mode 100644 index 0000000..104e2fa --- /dev/null +++ b/src/main/java/utility/PreRoiobject.java @@ -0,0 +1,333 @@ +package utility; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import ij.gui.Roi; +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealLocalizable; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; +import utility.PreRoiobject; + +public class PreRoiobject extends AbstractEuclideanSpace implements RealLocalizable, Comparable { + + /* + * FIELDS + */ + + public static AtomicInteger IDcounter = new AtomicInteger( -1 ); + + /** Store the individual features, and their values. */ + private final ConcurrentHashMap< String, Double > features = new ConcurrentHashMap< String, Double >(); + + /** A user-supplied name for this spot. */ + private String name; + + /** This spot ID. */ + private final int ID; + + public Roi rois; + public double[] geometriccenter; + public double area; + public double totalintensity; + public double averageintensity; + public int thirdDimension; + public int fourthDimension; + + + public PreRoiobject(final Roi rois, final double[] geometriccenter, final double area, final double totalintensity, final double averageintensity, final int thirdDimension, final int fourthDimension) { + super(3); + this.rois = rois; + this.geometriccenter = geometriccenter; + this.area = area; + this.totalintensity = totalintensity; + this.averageintensity = averageintensity; + this.thirdDimension = thirdDimension; + this.fourthDimension = fourthDimension; + this.ID = IDcounter.incrementAndGet(); + putFeature( Time,Double.valueOf( fourthDimension ) ); + putFeature( Z,Double.valueOf( thirdDimension ) ); + putFeature( XPOSITION, Double.valueOf( geometriccenter[0] ) ); + putFeature( YPOSITION, Double.valueOf( geometriccenter[1] ) ); + + putFeature( ZPOSITION, Double.valueOf( geometriccenter[2] ) ); + } + /* + * STATIC KEYS + */ + + + + + + /** The name of the blob X position feature. */ + public static final String XPOSITION = "XPOSITION"; + + /** The name of the blob Y position feature. */ + public static final String YPOSITION = "YPOSITION"; + + /** The name of the blob Y position feature. */ + public static final String ZPOSITION = "ZPOSITION"; + + + /** The label of the blob position feature. */ + public static final String LABEL = "LABEL"; + + /** The name of the frame feature. */ + public static final String Time = "Time"; + + /** The name of the Z feature. */ + public static final String Z = "Z"; + + public final Double getFeature( final String feature ) + { + return features.get( feature ); + } + + /** + * Stores the specified feature value for this spot. + * + * @param feature + * the name of the feature to store, as a {@link String}. + * @param value + * the value to store, as a {@link Double}. Using + * null will have unpredicted outcomes. + */ + public final void putFeature( final String feature, final Double value ) + { + features.put( feature, value ); + } + public static < T extends RealType< T > & NativeType< T >> Pair getIntensity(Roi roi, RandomAccessibleInterval source) { + + double Intensity = 0; + int NumberofPixels = 0; + Cursor currentcursor = Views.iterable(source).localizingCursor(); + + final double[] position = new double[source.numDimensions()]; + + while (currentcursor.hasNext()) { + + currentcursor.fwd(); + + currentcursor.localize(position); + + int x = (int) position[0]; + int y = (int) position[1]; + + if (roi.contains(x, y)) { + + Intensity += currentcursor.get().getRealDouble(); + + NumberofPixels++; + } + + } + + + + return new ValuePair(Intensity, NumberofPixels); + + } + + /** + * Returns the Intnesity weighted squared distance between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the Intensity weighted distance to the current blob to target + * blob specified. + */ + + public double IntensityDistanceTo(PreRoiobject target) { + // Returns squared distance between the source Blob and the target Blob. + + + + double IntensityDistance = Math.pow((totalintensity - target.totalintensity), 2); + + return IntensityDistance; + } + /** + * Returns the Noramlized cost function based on ratio of pixels between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the ratio of pixels of the current blob to target blob specified. + */ + + public double numberofPixelsRatioTo(PreRoiobject target) { + // Returns squared distance between the source Blob and the target Blob. + + final int sourcePixels = (int) area; + final int targetPixels = (int) target.area; + + + if (targetPixels > 0){ + double ratio = sourcePixels/ targetPixels; + + double sigma = 10; + double cost = 0; + double coeff = 1 - Math.exp(-1/(4 * sigma)); + double a = -4*Math.log(coeff); + + + if (ratio > 0 && ratio <= 0.5) + cost = Math.exp(-a * ratio *ratio); + if (ratio > 0.5 && ratio <= 1.5) + cost = 1 - Math.exp(- (ratio - 1) *(ratio - 1)/ sigma); + if (ratio > 1.5 && ratio <= 2) + cost = Math.exp(-a * (ratio - 2)* (ratio - 2)); + else + cost = 1; + + + + return cost; + } + + else + return 0; + } + + /** + * Returns the Normalized combo cost function based on ratio of pixels b/w blobs and the Normalized square distances between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the Normalized distance to the current blob to target blob specified. + */ + + public double NormalizedPixelratioandDistanceTo(PreRoiobject target, final double alpha, final double beta) { + // Returns squared distance between the source Blob and the target Blob. + + final double[] sourceLocation = geometriccenter; + final double[] targetLocation = target.geometriccenter; + + double distance = 0; + + for (int d = 0; d < sourceLocation.length; ++d) { + + distance += (sourceLocation[d] - targetLocation[d]) * (sourceLocation[d] - targetLocation[d]); + } + + double cost = distance / (1 + distance); + + final int sourcePixels = (int) area; + final int targetPixels = (int) target.area; + + if (targetPixels > 0){ + double ratio = sourcePixels/ targetPixels; + + double sigma = 10; + double ratiocost = 0; + double coeff = 1 - Math.exp(-(0.2 * 0.2)/(sigma)); + double a = -(1.0/(0.8 * 0.8))*Math.log(coeff); + + + if (ratio > 0 && ratio <= 0.5) + ratiocost = Math.exp(-a * ratio * ratio); + if (ratio > 0.5 && ratio <= 1.5) + ratiocost = 1 - Math.exp(- (ratio - 1) * (ratio - 1)/ sigma); + if (ratio > 1.5 && ratio <= 2) + ratiocost = Math.exp(-a * (ratio - 2) * (ratio - 2)); + else + ratiocost = 1; + + double combinedcost = (alpha * cost + beta * ratiocost) / (alpha + beta); + + + return combinedcost; + } + + else + + return 0; + } + /** + * Returns the difference between the location of two blobs, this operation + * returns ( A.diffTo(B) = - B.diffTo(A)) + * + * @param target + * the Blob to compare to. + * @param int + * n n = 0 for X- coordinate, n = 1 for Y- coordinate + * @return the difference in co-ordinate specified. + */ + public double diffTo(final PreRoiobject target, int n) { + + final double thisBloblocation = geometriccenter[n]; + final double targetBloblocation = target.geometriccenter[n]; + return thisBloblocation - targetBloblocation; + } + /** + * Returns the squared distance between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the distance to the current blob to target blob specified. + */ + + public double squareDistanceTo(PreRoiobject target) { + // Returns squared distance between the source Blob and the target Blob. + + final double[] sourceLocation = geometriccenter; + final double[] targetLocation = target.geometriccenter; + + double distance = 0; + + for (int d = 0; d < sourceLocation.length; ++d) { + + distance += (sourceLocation[d] - targetLocation[d]) * (sourceLocation[d] - targetLocation[d]); + } + + return distance; + } + + @Override + public int compareTo(PreRoiobject o) { + + return hashCode() - o.hashCode(); + } + + @Override + public double getDoublePosition(int d) { + + return (float) getDoublePosition(d); + } + + @Override + public float getFloatPosition(int d) { + + return (float) getDoublePosition(d); + } + + @Override + public void localize(float[] position) { + int n = position.length; + for (int d = 0; d < n; ++d) + position[d] = getFloatPosition(d); + + } + + @Override + public void localize(double[] position) { + int n = position.length; + for (int d = 0; d < n; ++d) + position[d] = getFloatPosition(d); + + } + + +} diff --git a/src/main/java/utility/Settings.java b/src/main/java/utility/Settings.java new file mode 100644 index 0000000..f2e99df --- /dev/null +++ b/src/main/java/utility/Settings.java @@ -0,0 +1,763 @@ +package utility; + +import java.awt.Polygon; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import analyzers.EdgeAnalyzer; +import analyzers.TrackAnalyzer; +import ij.ImagePlus; +import ij.gui.Roi; +import ij.io.FileInfo; + +/** + * This class is used to store user settings for the {@link TrackMate} + * trackmate. It is simply made of public fields + */ +public class Settings +{ + + /** + * The ImagePlus to operate on. Will also be used by some + * {@link fiji.plugin.trackmate.visualization.TrackMateModelView} as a GUI + * target. + */ + public ImagePlus imp; + + /** + * The polygon of interest. This will be used to crop the image and to + * discard found ThreeDRoiobjects out of the polygon. If null, the whole + * image is considered. + */ + public Polygon polygon; + + /** + * The region of interest (ROI). This will be used to crop the image and to + * discard found ThreeDRoiobjects outside the ROI. If null, the whole + * image is considered. + */ + public Roi roi; + + // Crop cube + /** + * The time-frame index, 0-based, of the first time-point to process. + */ + public int tstart; + + /** + * The time-frame index, 0-based, of the last time-point to process. + */ + public int tend; + + /** + * The lowest pixel X position, 0-based, of the volume to process. + */ + public int xstart; + + /** + * The highest pixel X position, 0-based, of the volume to process. + */ + public int xend; + + /** + * The lowest pixel Y position, 0-based, of the volume to process. + */ + public int ystart; + + /** + * The lowest pixel Y position, 0-based, of the volume to process. + */ + public int yend; + + /** + * The lowest pixel Z position, 0-based, of the volume to process. + */ + public int zstart; + + /** + * The lowest pixel Z position, 0-based, of the volume to process. + */ + public int zend; + + /** Target channel for detection, 1-based. */ + // public int detectionChannel = 1; + // Image info + public double dt = 1; + + public double dx = 1; + + public double dy = 1; + + public double dz = 1; + + public int width; + + public int height; + + public int nslices; + + public int nframes; + + public String imageFolder = ""; + + public String imageFileName = ""; + + /** + * The name of the detector factory to use. It will be used to generate + * {@link fiji.plugin.trackmate.detection.ThreeDRoiobjectDetector} for each target + * frame. + */ + public ThreeDRoiobjectDetectorFactory< ? > detectorFactory; + + /** The the tracker to use. */ + public ThreeDRoiobjectTrackerFactory trackerFactory; + + /** + * Settings map for {@link fiji.plugin.trackmate.detection.ThreeDRoiobjectDetector}. + * + * @see fiji.plugin.trackmate.detection.DetectorKeys + */ + public Map< String, Object > detectorSettings = new HashMap< >(); + + /** + * Settings map for {@link fiji.plugin.trackmate.tracking.ThreeDRoiobjectTracker}. + * + * @see fiji.plugin.trackmate.tracking.TrackerKeys + */ + public Map< String, Object > trackerSettings = new HashMap< >(); + + // Filters + + /** + * The feature filter list. + */ + protected List< FeatureFilter > ThreeDRoiobjectFilters = new ArrayList< >(); + + /** + * The initial quality filter value that is used to clip ThreeDRoiobjects of low + * quality from ThreeDRoiobjects. + */ + public Double initialThreeDRoiobjectFilterValue = Double.valueOf( 0 ); + + /** The track filter list that is used to prune track and ThreeDRoiobjects. */ + protected List< FeatureFilter > trackFilters = new ArrayList< >(); + + protected String errorMessage; + + // ThreeDRoiobject features + + /** + * The {@link ThreeDRoiobjectAnalyzerFactory}s that will be used to compute ThreeDRoiobject + * features. They are ordered in a {@link List} in case some analyzers + * requires the results of another analyzer to proceed. + */ + protected List< ThreeDRoiobjectAnalyzerFactory< ? > > ThreeDRoiobjectAnalyzerFactories = new ArrayList< >(); + + // Edge features + + /** + * The {@link EdgeAnalyzer}s that will be used to compute edge features. + * They are ordered in a {@link List} in case some analyzers requires the + * results of another analyzer to proceed. + */ + protected List< EdgeAnalyzer > edgeAnalyzers = new ArrayList< >(); + + // Track features + + /** + * The {@link TrackAnalyzer}s that will be used to compute track features. + * They are ordered in a {@link List} in case some analyzers requires the + * results of another analyzer to proceed. + */ + protected List< TrackAnalyzer > trackAnalyzers = new ArrayList< >(); + + /* + * METHODS + */ + + public void setFrom( final ImagePlus imp ) + { + // Source image + this.imp = imp; + + if ( null == imp ) { return; // we leave field default values + } + + // File info + final FileInfo fileInfo = imp.getOriginalFileInfo(); + if ( null != fileInfo ) + { + this.imageFileName = fileInfo.fileName; + this.imageFolder = fileInfo.directory; + } + else + { + this.imageFileName = imp.getShortTitle(); + this.imageFolder = ""; + + } + // Image size + this.width = imp.getWidth(); + this.height = imp.getHeight(); + this.nslices = imp.getNSlices(); + this.nframes = imp.getNFrames(); + this.dx = ( float ) imp.getCalibration().pixelWidth; + this.dy = ( float ) imp.getCalibration().pixelHeight; + this.dz = ( float ) imp.getCalibration().pixelDepth; + this.dt = ( float ) imp.getCalibration().frameInterval; + + if ( dt == 0 ) + { + dt = 1; + } + + // Crop cube + this.zstart = 0; + this.zend = imp.getNSlices() - 1; + this.tstart = 0; + this.tend = imp.getNFrames() - 1; + this.roi = imp.getRoi(); + if ( roi == null ) + { + this.xstart = 0; + this.xend = width - 1; + this.ystart = 0; + this.yend = height - 1; + this.polygon = null; + } + else + { + final Rectangle boundingRect = roi.getBounds(); + this.xstart = boundingRect.x; + this.xend = boundingRect.width + boundingRect.x; + this.ystart = boundingRect.y; + this.yend = boundingRect.height + boundingRect.y; + this.polygon = roi.getPolygon(); + + } + // The rest is left to the user + } + + /* + * METHODS + */ + + /** + * Returns a string description of the target image. + * + * @return a string representation of the target image. + */ + public String toStringImageInfo() + { + final StringBuilder str = new StringBuilder(); + + str.append( "Image data:\n" ); + if ( null == imp ) + { + str.append( "Source image not set.\n" ); + } + else + { + str.append( "For the image named: " + imp.getTitle() + ".\n" ); + } + if ( imageFileName == null || imageFileName == "" ) + { + str.append( "Not matching any file.\n" ); + } + else + { + str.append( "Matching file " + imageFileName + " " ); + if ( imageFolder == null || imageFolder == "" ) + { + str.append( "in current folder.\n" ); + } + else + { + str.append( "in folder: " + imageFolder + "\n" ); + } + } + + str.append( "Geometry:\n" ); + str.append( String.format( " X = %4d - %4d, dx = %g\n", xstart, xend, dx ) ); + str.append( String.format( " Y = %4d - %4d, dy = %g\n", ystart, yend, dy ) ); + str.append( String.format( " Z = %4d - %4d, dz = %g\n", zstart, zend, dz ) ); + str.append( String.format( " T = %4d - %4d, dt = %g\n", tstart, tend, dt ) ); + + return str.toString(); + } + + public String toStringFeatureAnalyzersInfo() + { + final StringBuilder str = new StringBuilder(); + + if ( ThreeDRoiobjectAnalyzerFactories.isEmpty() ) + { + str.append( "No ThreeDRoiobject feature analyzers.\n" ); + } + else + { + str.append( "ThreeDRoiobject feature analyzers:\n" ); + prettyPrintFeatureAnalyzer( ThreeDRoiobjectAnalyzerFactories, str ); + } + + if ( edgeAnalyzers.isEmpty() ) + { + str.append( "No edge feature analyzers.\n" ); + } + else + { + str.append( "Edge feature analyzers:\n" ); + prettyPrintFeatureAnalyzer( edgeAnalyzers, str ); + } + + if ( trackAnalyzers.isEmpty() ) + { + str.append( "No track feature analyzers.\n" ); + } + else + { + str.append( "Track feature analyzers:\n" ); + prettyPrintFeatureAnalyzer( trackAnalyzers, str ); + } + + return str.toString(); + } + + @Override + public String toString() + { + final StringBuilder str = new StringBuilder(); + + str.append( toStringImageInfo() ); + + str.append( '\n' ); + str.append( "ThreeDRoiobject detection:\n" ); + if ( null == detectorFactory ) + { + str.append( "No detector factory set.\n" ); + } + else + { + str.append( "Detector: " + detectorFactory.toString() + ".\n" ); + if ( null == detectorSettings ) + { + str.append( "No detector settings found.\n" ); + } + else + { + str.append( "Detector settings:\n" ); + str.append( detectorSettings ); + str.append( '\n' ); + } + } + + str.append( '\n' ); + str.append( toStringFeatureAnalyzersInfo() ); + + str.append( '\n' ); + str.append( "Initial ThreeDRoiobject filter:\n" ); + if ( null == initialThreeDRoiobjectFilterValue ) + { + str.append( "No initial quality filter.\n" ); + } + else + { + str.append( "Initial quality filter value: " + initialThreeDRoiobjectFilterValue + ".\n" ); + } + + str.append( '\n' ); + str.append( "ThreeDRoiobject feature filters:\n" ); + if ( ThreeDRoiobjectFilters == null || ThreeDRoiobjectFilters.size() == 0 ) + { + str.append( "No ThreeDRoiobject feature filters.\n" ); + } + else + { + str.append( "Set with " + ThreeDRoiobjectFilters.size() + " ThreeDRoiobject feature filters:\n" ); + for ( final FeatureFilter featureFilter : ThreeDRoiobjectFilters ) + { + str.append( " - " + featureFilter + "\n" ); + } + } + + str.append( '\n' ); + str.append( "Particle linking:\n" ); + if ( null == trackerFactory ) + { + str.append( "No ThreeDRoiobject tracker set.\n" ); + } + else + { + str.append( "Tracker: " + trackerFactory.toString() + ".\n" ); + if ( null == trackerSettings ) + { + str.append( "No tracker settings found.\n" ); + } + else + { + str.append( "Tracker settings:\n" ); + str.append( trackerSettings ); + str.append( '\n' ); + } + } + + str.append( '\n' ); + str.append( "Track feature filters:\n" ); + if ( trackFilters == null || trackFilters.size() == 0 ) + { + str.append( "No track feature filters.\n" ); + } + else + { + str.append( "Set with " + trackFilters.size() + " track feature filters:\n" ); + for ( final FeatureFilter featureFilter : trackFilters ) + { + str.append( " - " + featureFilter + "\n" ); + } + } + + return str.toString(); + } + + public boolean checkValidity() + { + if ( null == imp ) + { + errorMessage = "The source image is null.\n"; + return false; + } + if ( null == detectorFactory ) + { + errorMessage = "The detector factory is null.\n"; + return false; + } + if ( null == detectorSettings ) + { + errorMessage = "The detector settings is null.\n"; + return false; + } + if ( null == initialThreeDRoiobjectFilterValue ) + { + errorMessage = "Initial ThreeDRoiobject quality threshold is not set.\n"; + return false; + } + if ( null == trackerFactory ) + { + errorMessage = "The tracker factory is null.\n"; + return false; + } + if ( !trackerFactory.checkSettingsValidity( trackerSettings ) ) + { + errorMessage = "The tracker has invalid input:\n" + trackerFactory.getErrorMessage(); + return false; + } + return true; + } + + public String getErrorMessage() + { + return errorMessage; + } + + /* + * ThreeDRoiobject FEATURES + */ + + /** + * Remove any {@link ThreeDRoiobjectAnalyzerFactory} to this object. + */ + public void clearThreeDRoiobjectAnalyzerFactories() + { + ThreeDRoiobjectAnalyzerFactories.clear(); + } + + /** + * Returns a copy of the list of {@link ThreeDRoiobjectAnalyzerFactory}s configured in + * this settings object. They are returned in an ordered list, to enforce + * processing order in case some analyzers requires the results of another + * analyzers to proceed. + * + * @return the list of {@link ThreeDRoiobjectAnalyzerFactory}s. + */ + public List< ThreeDRoiobjectAnalyzerFactory< ? > > getThreeDRoiobjectAnalyzerFactories() + { + return new ArrayList< >( ThreeDRoiobjectAnalyzerFactories ); + } + + /** + * Adds a {@link ThreeDRoiobjectAnalyzerFactory} to the {@link List} of ThreeDRoiobject analyzers + * configured. + * + * @param ThreeDRoiobjectAnalyzer + * the {@link fiji.plugin.trackmate.features.ThreeDRoiobject.ThreeDRoiobjectAnalyzer} + * to add, at the end of the list. + */ + public void addThreeDRoiobjectAnalyzerFactory( final ThreeDRoiobjectAnalyzerFactory< ? > ThreeDRoiobjectAnalyzer ) + { + ThreeDRoiobjectAnalyzerFactories.add( ThreeDRoiobjectAnalyzer ); + } + + /** + * Adds a {@link ThreeDRoiobjectAnalyzerFactory} to the {@link List} of ThreeDRoiobject analyzers + * configured, at the specified index. + * + * @param index + * index at which the analyzer is to be inserted. + * @param ThreeDRoiobjectAnalyzer + * the {@link fiji.plugin.trackmate.features.ThreeDRoiobject.ThreeDRoiobjectAnalyzer} + * to add, at the specified index in the list. + */ + public void addThreeDRoiobjectAnalyzerFactory( final int index, final ThreeDRoiobjectAnalyzerFactory< ? > ThreeDRoiobjectAnalyzer ) + { + ThreeDRoiobjectAnalyzerFactories.add( index, ThreeDRoiobjectAnalyzer ); + } + + /** + * Removes the specified {@link ThreeDRoiobjectAnalyzerFactory} from the analyzers + * configured. + * + * @param ThreeDRoiobjectAnalyzer + * the {@link ThreeDRoiobjectAnalyzerFactory} to remove. + * @return true if the specified {@link ThreeDRoiobjectAnalyzerFactory} was in the list + * and was removed. + */ + public boolean removeThreeDRoiobjectAnalyzerFactory( final ThreeDRoiobjectAnalyzerFactory< ? > ThreeDRoiobjectAnalyzer ) + { + return ThreeDRoiobjectAnalyzerFactories.remove( ThreeDRoiobjectAnalyzer ); + } + + /* + * EDGE FEATURE ANALYZERS + */ + + /** + * Remove any {@link EdgeAnalyzer} to this object. + */ + public void clearEdgeAnalyzers() + { + edgeAnalyzers.clear(); + } + + /** + * Returns a copy of the list of {@link EdgeAnalyzer}s configured in this + * settings object. They are returned in an ordered list, to enforce + * processing order in case some analyzers requires the results of another + * analyzers to proceed. + * + * @return the list of {@link EdgeAnalyzer}s. + */ + public List< EdgeAnalyzer > getEdgeAnalyzers() + { + return new ArrayList< >( edgeAnalyzers ); + } + + /** + * Adds a {@link EdgeAnalyzer} to the {@link List} of edge analyzers + * configured. + * + * @param edgeAnalyzer + * the {@link EdgeAnalyzer} to add, at the end of the list. + */ + public void addEdgeAnalyzer( final EdgeAnalyzer edgeAnalyzer ) + { + edgeAnalyzers.add( edgeAnalyzer ); + } + + /** + * Adds a {@link EdgeAnalyzer} to the {@link List} of edge analyzers + * configured, at the specified index. + * + * @param index + * index at which the analyzer is to be inserted. + * + * @param edgeAnalyzer + * the {@link EdgeAnalyzer} to add, at the specified index in the + * list. + */ + public void addEdgeAnalyzer( final int index, final EdgeAnalyzer edgeAnalyzer ) + { + edgeAnalyzers.add( index, edgeAnalyzer ); + } + + /** + * Removes the specified {@link EdgeAnalyzer} from the analyzers configured. + * + * @param edgeAnalyzer + * the {@link EdgeAnalyzer} to remove. + * @return true if the specified {@link EdgeAnalyzer} was in the list and + * was removed. + */ + public boolean removeEdgeAnalyzer( final EdgeAnalyzer edgeAnalyzer ) + { + return edgeAnalyzers.remove( edgeAnalyzer ); + } + + /* + * TRACK FEATURE ANALYZERS + */ + + /** + * Remove any {@link TrackAnalyzer} to this object. + */ + public void clearTrackAnalyzers() + { + trackAnalyzers.clear(); + } + + /** + * Returns a copy of the list of {@link TrackAnalyzer}s configured in this + * settings object. They are returned in an ordered list, to enforce + * processing order in case some analyzers requires the results of another + * analyzers to proceed. + * + * @return the list of {@link TrackAnalyzer}s. + */ + public List< TrackAnalyzer > getTrackAnalyzers() + { + return new ArrayList< >( trackAnalyzers ); + } + + /** + * Adds a {@link TrackAnalyzer} to the {@link List} of track analyzers + * configured. + * + * @param trackAnalyzer + * the {@link TrackAnalyzer} to add, at the end of the list. + */ + public void addTrackAnalyzer( final TrackAnalyzer trackAnalyzer ) + { + trackAnalyzers.add( trackAnalyzer ); + } + + /** + * Adds a {@link TrackAnalyzer} to the {@link List} of track analyzers + * configured, at the specified index. + * + * @param index + * index at which the analyzer is to be inserted. + * + * @param trackAnalyzer + * the {@link TrackAnalyzer} to add, at the specified index in + * the list. + */ + public void addTrackAnalyzer( final int index, final TrackAnalyzer trackAnalyzer ) + { + trackAnalyzers.add( index, trackAnalyzer ); + } + + /** + * Removes the specified {@link TrackAnalyzer} from the analyzers + * configured. + * + * @param trackAnalyzer + * the {@link TrackAnalyzer} to remove. + * @return true if the specified {@link TrackAnalyzer} was in the list and + * was removed. + */ + public boolean removeTrackAnalyzer( final TrackAnalyzer trackAnalyzer ) + { + return trackAnalyzers.remove( trackAnalyzer ); + } + + /* + * FEATURE FILTERS + */ + + /** + * Add a filter to the list of ThreeDRoiobject filters. + * + * @param filter + * the filter to add. + */ + public void addThreeDRoiobjectFilter( final FeatureFilter filter ) + { + ThreeDRoiobjectFilters.add( filter ); + } + + public void removeThreeDRoiobjectFilter( final FeatureFilter filter ) + { + ThreeDRoiobjectFilters.remove( filter ); + } + + /** Remove all ThreeDRoiobject filters stored in this model. */ + public void clearThreeDRoiobjectFilters() + { + ThreeDRoiobjectFilters.clear(); + } + + public List< FeatureFilter > getThreeDRoiobjectFilters() + { + return ThreeDRoiobjectFilters; + } + + public void setThreeDRoiobjectFilters( final List< FeatureFilter > ThreeDRoiobjectFilters ) + { + this.ThreeDRoiobjectFilters = ThreeDRoiobjectFilters; + } + + /** + * Add a filter to the list of track filters. + * + * @param filter + * the filter to add. + */ + public void addTrackFilter( final FeatureFilter filter ) + { + trackFilters.add( filter ); + } + + public void removeTrackFilter( final FeatureFilter filter ) + { + trackFilters.remove( filter ); + } + + /** Remove all track filters stored in this model. */ + public void clearTrackFilters() + { + trackFilters.clear(); + } + + public List< FeatureFilter > getTrackFilters() + { + return trackFilters; + } + + public void setTrackFilters( final List< FeatureFilter > trackFilters ) + { + this.trackFilters = trackFilters; + } + + /* + * PRIVATE METHODS + */ + + private final void prettyPrintFeatureAnalyzer( final List< ? extends FeatureAnalyzer3D > analyzers, final StringBuilder str ) + { + for ( final FeatureAnalyzer3D analyzer : analyzers ) + { + str.append( " - " + analyzer.getName() + " provides: " ); + for ( final String feature : analyzer.getFeatures() ) + { + str.append( analyzer.getFeatureShortNames().get( feature ) + ", " ); + } + str.deleteCharAt( str.length() - 1 ); + str.deleteCharAt( str.length() - 1 ); + // be precise + if ( str.charAt( str.length() - 1 ) != '.' ) + { + str.append( '.' ); + } + // manual? + if ( analyzer.isManualFeature() ) + { + str.deleteCharAt( str.length() - 1 ); + str.append( "; is manual." ); + } + str.append( '\n' ); + } + } + +} diff --git a/src/main/java/utility/SortCoordinates.java b/src/main/java/utility/SortCoordinates.java new file mode 100644 index 0000000..99729b6 --- /dev/null +++ b/src/main/java/utility/SortCoordinates.java @@ -0,0 +1,35 @@ +package utility; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; + +public class SortCoordinates { + + + public static void sortByXY(ArrayList inputlist) { + + + Collections.sort(inputlist, new Comparator () { + + @Override + public int compare(double[] o1, double[] o2) { + int n = o1.length; + int i = 0; + while ( i < n ) + { + if ( o1[i] != o2[i] ) { return ( int ) Math.signum( o1[i] - o2[i] ); } + i++; + + } + return o1.hashCode() - o2.hashCode(); + + } + }); + + + + } + + +} diff --git a/src/main/java/utility/SortListbyproperty.java b/src/main/java/utility/SortListbyproperty.java new file mode 100644 index 0000000..683ea57 --- /dev/null +++ b/src/main/java/utility/SortListbyproperty.java @@ -0,0 +1,105 @@ +package utility; + +import java.util.ArrayList; +import java.util.Iterator; + + +public class SortListbyproperty { + + + + public static void sortpointList(ArrayList pointlist) { + if (pointlist.size() <= 1) + return; + + else { + + // the first element belonging to the right list childB + final int splitIndex = (int) pointlist.size() / 2; + + Iterator iterator = pointlist.iterator(); + + final ArrayList childA = new ArrayList((int) pointlist.size() / 2); + + final ArrayList childB = new ArrayList((int) (pointlist.size() / 2 + pointlist.size() % 2)); + + int index = 0; + + while (iterator.hasNext()) { + iterator.next(); + + if (index < splitIndex) + childA.add(pointlist.get(index)); + + else + + childB.add(pointlist.get(index)); + + index++; + + } + + sortpointList(childA); + + sortpointList(childB); + + mergepointListValue(pointlist, childA, childB); + + + + + + } + + } + + /// ***** Returns a sorted list *********//// + public static void mergepointListValue(ArrayList sortedlist, ArrayList listA, ArrayList listB) { + + int i = 0, j = 0, k = 0; + + while (i < listA.size() && j < listB.size()) { + + // Decide the property by which the list has to be sorted by + if (listA.get(i)[0] < (listB.get(j)[0])) { + + sortedlist.set(k, listA.get(i)); + + ++i; + ++k; + } + + else { + + sortedlist.set(k, listB.get(j)); + + ++j; + ++k; + + } + + } + + while (i < listA.size()) { + sortedlist.set(k, listA.get(i)); + ++i; + ++k; + + } + + while (j < listB.size()) { + sortedlist.set(k, listB.get(j)); + ++j; + ++k; + + } + + } + + + + + + + +} diff --git a/src/main/java/utility/SuperIntersection.java b/src/main/java/utility/SuperIntersection.java deleted file mode 100644 index 67f1704..0000000 --- a/src/main/java/utility/SuperIntersection.java +++ /dev/null @@ -1,162 +0,0 @@ -package utility; - -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import ellipsoidDetector.Intersectionobject; -import ellipsoidDetector.Tangentobject; -import ij.gui.EllipseRoi; -import ij.gui.Line; -import ij.gui.OvalRoi; -import net.imglib2.Point; -import net.imglib2.RealLocalizable; -import net.imglib2.type.logic.BitType; -import net.imglib2.util.Pair; -import net.imglib2.util.ValuePair; -import pluginTools.InteractiveSimpleEllipseFit; -import varun_algorithm_ransac_Ransac.Angleobject; -import varun_algorithm_ransac_Ransac.Ellipsoid; -import varun_algorithm_ransac_Ransac.Intersections; -import varun_algorithm_ransac_Ransac.Tangent2D; - -public class SuperIntersection { - - final InteractiveSimpleEllipseFit parent; - - - public SuperIntersection(final InteractiveSimpleEllipseFit parent) { - - this.parent = parent; - - } - - // Supermode - public void Getsuperintersection(ArrayList resultroi, ArrayList resultovalroi, - ArrayList resultlineroi, final ArrayList AllPointsofIntersect, - final ArrayList Allintersection, int t, int z) { - - String uniqueID = Integer.toString(z) + Integer.toString(t); - - final ArrayList> fitmapspecial = new ArrayList>(); - - for (int index = 0; index < parent.superReducedSamples.size(); ++index) { - - for (int indexx = 0; indexx < parent.superReducedSamples.size(); ++indexx) { - - if (index != indexx) { - - fitmapspecial.add(new ValuePair(parent.superReducedSamples.get(index).getA(), - parent.superReducedSamples.get(indexx).getA())); - - } - - } - - } - - final ArrayList> fitmapspecialred = new ArrayList>(); - fitmapspecialred.addAll(fitmapspecial); - // Remove duplicate pairs - for (int i = 0; i < fitmapspecialred.size(); ++i) { - - Pair ellipsepairA = fitmapspecialred.get(i); - - for (int j = 0; j < fitmapspecialred.size(); ++j) { - - if (i != j) { - Pair ellipsepairB = fitmapspecialred.get(j); - - if (ellipsepairA.getA().hashCode() == (ellipsepairB.getB().hashCode()) - && ellipsepairA.getB().hashCode() == (ellipsepairB.getA().hashCode())) { - fitmapspecialred.remove(ellipsepairB); - break; - } - - } - - } - - } - - for (int i = 0; i < fitmapspecialred.size(); ++i) { - - Pair ellipsepair = fitmapspecialred.get(i); - - ArrayList pos = Intersections.PointsofIntersection(ellipsepair); - - // Reject wrong points that are not candidate points - - - - Tangentobject PointsIntersect = new Tangentobject(pos, ellipsepair, t, z); - - for (int j = 0; j < pos.size(); ++j) { - - OvalRoi intersectionsRoi = new OvalRoi(pos.get(j)[0] - parent.radiusdetection, - pos.get(j)[1] - parent.radiusdetection, 2 * parent.radiusdetection, 2 * parent.radiusdetection); - intersectionsRoi.setStrokeColor(parent.colorDet); - resultovalroi.add(intersectionsRoi); - - double[] lineparamA = Tangent2D.GetTangent(ellipsepair.getA(), pos.get(j)); - - double[] lineparamB = Tangent2D.GetTangent(ellipsepair.getB(), pos.get(j)); - - Angleobject angleobject = Tangent2D.GetTriAngle(lineparamA, lineparamB, pos.get(j), ellipsepair); - resultlineroi.add(angleobject.lineA); - resultlineroi.add(angleobject.lineB); - - Intersectionobject currentintersection = new Intersectionobject(pos.get(j), angleobject.angle, - ellipsepair, resultlineroi, t, z); - - Allintersection.add(currentintersection); - - - } - - AllPointsofIntersect.add(PointsIntersect); - - } - - parent.ALLIntersections.put(uniqueID, Allintersection); - - // Add new result rois to ZTRois - for (Map.Entry entry : parent.ZTRois.entrySet()) { - - Roiobject currentobject = entry.getValue(); - - if (currentobject.fourthDimension == t && currentobject.thirdDimension == z) { - - currentobject.resultroi = resultroi; - currentobject.resultovalroi = resultovalroi; - currentobject.resultlineroi = resultlineroi; - - } - - } - - Roiobject currentobject = new Roiobject(resultroi, resultovalroi, resultlineroi, z, t, true); - parent.ZTRois.put(uniqueID, currentobject); - - - DisplayAuto.Display(parent); - - } - - - public double Sqdistance(double[] sourceLocation, double[] targetLocation) { - - double distance = 0; - - for (int d = 0; d < sourceLocation.length; ++d) { - - distance += (sourceLocation[d] - targetLocation[d]) * (sourceLocation[d] - targetLocation[d]); - } - - return distance; - - } - -} diff --git a/src/main/java/utility/ThreeDRoiobject.java b/src/main/java/utility/ThreeDRoiobject.java new file mode 100644 index 0000000..fd3c8a8 --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobject.java @@ -0,0 +1,444 @@ +package utility; + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +import ij.gui.Roi; +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.RealLocalizable; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; + +public class ThreeDRoiobject extends AbstractEuclideanSpace implements RealLocalizable, Comparable { + + + + /* + * FIELDS + */ + + public static AtomicInteger IDcounter = new AtomicInteger( -1 ); + + /** Store the individual features, and their values. */ + private final ConcurrentHashMap< String, Double > features = new ConcurrentHashMap< String, Double >(); + + /** A user-supplied name for this spot. */ + private String name; + + /** This spot ID. */ + private final int ID; + + public ArrayList roiobject; + public double[] geometriccenter; + public double volume; + public double totalintensity; + public double averageintensity; + public int fourthDimension; + + public ThreeDRoiobject(final ArrayList roiobject, final double[] geometriccenter, final double volume, final double totalintensity, final double averageintensity, final int fourthDimension) { + super(3); + this.roiobject = roiobject; + this.geometriccenter = geometriccenter; + this.volume = volume; + this.totalintensity = totalintensity; + this.averageintensity = averageintensity; + this.fourthDimension = fourthDimension; + this.ID = IDcounter.incrementAndGet(); + putFeature( Time,Double.valueOf( fourthDimension ) ); + putFeature( XPOSITION, Double.valueOf( geometriccenter[0] ) ); + putFeature( YPOSITION, Double.valueOf( geometriccenter[1] ) ); + putFeature( ZPOSITION, Double.valueOf( geometriccenter[2] ) ); + putFeature( Size, Double.valueOf( volume ) ); + + this.name = "ID" + ID; + } + + + /* + * STATIC KEYS + */ + /** + * Set the name of this Spot. + * + * @param name + * the name to use. + */ + public void setName( final String name ) + { + this.name = name; + } + + public int ID() + { + return ID; + } + + @Override + public String toString() + { + String str; + if ( null == name || name.equals( "" ) ) + str = "ID" + ID; + else + str = name; + return str; + } + + /** The name of the blob X position feature. */ + public static final String XPOSITION = "XPOSITION"; + + /** The name of the blob Y position feature. */ + public static final String YPOSITION = "YPOSITION"; + + /** The name of the blob Y position feature. */ + public static final String ZPOSITION = "ZPOSITION"; + /** The name of the blob Y position feature. */ + public static final String Size = "Size"; + + /** The label of the blob position feature. */ + public static final String LABEL = "LABEL"; + + /** The name of the frame feature. */ + public static final String Time = "Time"; + + + + /** The position features. */ + public final static String[] POSITION_FEATURES = new String[] { XPOSITION, YPOSITION, ZPOSITION }; + + /** The name of the Z feature. */ + public static final String Z = "Z"; + + public final Double getFeature( final String feature ) + { + return features.get( feature ); + } + /** + * The 7 privileged spot features that must be set by a spot detector: + * {@link #QUALITY}, {@link #POSITION_X}, {@link #POSITION_Y}, + * {@link #POSITION_Z}, {@link #POSITION_Z}, {@link #RADIUS}, {@link #FRAME} + * . + */ + public final static Collection< String > FEATURES = new ArrayList< >( 4 ); + + /** The 7 privileged spot feature names. */ + public final static Map< String, String > FEATURE_NAMES = new HashMap< >( 4 ); + + /** The 7 privileged spot feature short names. */ + public final static Map< String, String > FEATURE_SHORT_NAMES = new HashMap< >( 4 ); + + /** The 7 privileged spot feature dimensions. */ + public final static Map< String, linkers.Dimension > FEATURE_DIMENSIONS = new HashMap< >( 4 ); + + /** The 7 privileged spot feature isInt flags. */ + public final static Map< String, Boolean > IS_INT = new HashMap< >( 4 ); + + static + { + FEATURES.add( XPOSITION ); + FEATURES.add( YPOSITION ); + FEATURES.add( ZPOSITION ); + FEATURES.add( Size ); + FEATURES.add( Time ); + + FEATURE_NAMES.put( XPOSITION, "X" ); + FEATURE_NAMES.put( YPOSITION, "Y" ); + FEATURE_NAMES.put( ZPOSITION, "Z" ); + FEATURE_NAMES.put( Size, "S" ); + FEATURE_NAMES.put( Time, "T" ); + + + FEATURE_SHORT_NAMES.put( XPOSITION, "X" ); + FEATURE_SHORT_NAMES.put( YPOSITION, "Y" ); + FEATURE_SHORT_NAMES.put( ZPOSITION, "Z" ); + FEATURE_SHORT_NAMES.put( Size, "S" ); + FEATURE_SHORT_NAMES.put( Time, "T" ); + + + FEATURE_DIMENSIONS.put( XPOSITION, linkers.Dimension.POSITION ); + FEATURE_DIMENSIONS.put( YPOSITION, linkers.Dimension.POSITION ); + FEATURE_DIMENSIONS.put( ZPOSITION, linkers.Dimension.POSITION ); + FEATURE_DIMENSIONS.put( Size, linkers.Dimension.LENGTH ); + FEATURE_DIMENSIONS.put( Time, linkers.Dimension.TIME ); + + + IS_INT.put( XPOSITION, Boolean.FALSE ); + IS_INT.put( YPOSITION, Boolean.FALSE ); + IS_INT.put( ZPOSITION, Boolean.FALSE ); + IS_INT.put( Size, Boolean.FALSE ); + IS_INT.put( Time, Boolean.FALSE ); + + } + + /** + * Blank constructor meant to be used when loading a spot collection from a + * file. Will mess with the {@link #IDcounter} field, so this + * constructor should not be used for normal spot creation. + * + * @param ID + * the spot ID to set + */ + public ThreeDRoiobject( final int ID ) + { + super( 3 ); + this.ID = ID; + synchronized ( IDcounter ) + { + if ( IDcounter.get() < ID ) + { + IDcounter.set( ID ); + } + } + } + + + /** + * Stores the specified feature value for this spot. + * + * @param feature + * the name of the feature to store, as a {@link String}. + * @param value + * the value to store, as a {@link Double}. Using + * null will have unpredicted outcomes. + */ + public final void putFeature( final String feature, final Double value ) + { + features.put( feature, value ); + } + + public static double[] getCentroid3D(ArrayList roiobject) { + + int ndims = roiobject.get(0).geometriccenter.length; + double[] centroid = new double[ndims]; + + for(PreRoiobject current: roiobject) { + + + for (int i= 0; i < ndims; ++i) { + centroid[i]+=current.geometriccenter[i] * current.area; + centroid[i]/=current.area; + } + } + + return centroid; + + } + + public static Pair getIntensity3D(ArrayList roiobject){ + + double Intensity = 0; + int NumberofPixels = 0; + + for(PreRoiobject current: roiobject) { + + Intensity += current.totalintensity; + NumberofPixels += current.area; + + + } + + return new ValuePair(Intensity, NumberofPixels); + + } + + /** + * Returns the Intnesity weighted squared distance between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the Intensity weighted distance to the current blob to target + * blob specified. + */ + + public double IntensityDistanceTo(ThreeDRoiobject target) { + // Returns squared distance between the source Blob and the target Blob. + + + + double IntensityDistance = 1 - Math.pow((totalintensity / target.totalintensity), 2); + + return IntensityDistance; + } + + /** + * Returns the Noramlized cost function based on ratio of pixels between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the ratio of pixels of the current blob to target blob specified. + */ + + public double numberofPixelsRatioTo(ThreeDRoiobject target) { + // Returns squared distance between the source Blob and the target Blob. + + final int sourcePixels = (int) volume; + final int targetPixels = (int) target.volume; + + + if (targetPixels > 0){ + double ratio = sourcePixels/ targetPixels; + + double sigma = 10; + double cost = 0; + double coeff = 1 - Math.exp(-1/(4 * sigma)); + double a = -4*Math.log(coeff); + + + if (ratio > 0 && ratio <= 0.5) + cost = Math.exp(-a * ratio *ratio); + if (ratio > 0.5 && ratio <= 1.5) + cost = 1 - Math.exp(- (ratio - 1) *(ratio - 1)/ sigma); + if (ratio > 1.5 && ratio <= 2) + cost = Math.exp(-a * (ratio - 2)* (ratio - 2)); + else + cost = 1; + + + + return cost; + } + + else + return 0; + } + + /** + * Returns the Normalized combo cost function based on ratio of pixels b/w blobs and the Normalized square distances between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the Normalized distance to the current blob to target blob specified. + */ + + public double NormalizedPixelratioandDistanceTo(ThreeDRoiobject target, final double alpha, final double beta) { + // Returns squared distance between the source Blob and the target Blob. + + final double[] sourceLocation = geometriccenter; + final double[] targetLocation = target.geometriccenter; + + double distance = 0; + + for (int d = 0; d < sourceLocation.length; ++d) { + + distance += (sourceLocation[d] - targetLocation[d]) * (sourceLocation[d] - targetLocation[d]); + } + + double cost = distance / (1 + distance); + + final int sourcePixels = (int) volume; + final int targetPixels = (int) target.volume; + + if (targetPixels > 0){ + double ratio = sourcePixels/ targetPixels; + + double sigma = 10; + double ratiocost = 0; + double coeff = 1 - Math.exp(-(0.2 * 0.2)/(sigma)); + double a = -(1.0/(0.8 * 0.8))*Math.log(coeff); + + + if (ratio > 0 && ratio <= 0.5) + ratiocost = Math.exp(-a * ratio * ratio); + if (ratio > 0.5 && ratio <= 1.5) + ratiocost = 1 - Math.exp(- (ratio - 1) * (ratio - 1)/ sigma); + if (ratio > 1.5 && ratio <= 2) + ratiocost = Math.exp(-a * (ratio - 2) * (ratio - 2)); + else + ratiocost = 1; + + double combinedcost = (alpha * cost + beta * ratiocost) / (alpha + beta); + + + return combinedcost; + } + + else + + return 0; + } + /** + * Returns the difference between the location of two blobs, this operation + * returns ( A.diffTo(B) = - B.diffTo(A)) + * + * @param target + * the Blob to compare to. + * @param int + * n n = 0 for X- coordinate, n = 1 for Y- coordinate + * @return the difference in co-ordinate specified. + */ + public double diffTo(final ThreeDRoiobject target, int n) { + + final double thisBloblocation = geometriccenter[n]; + final double targetBloblocation = target.geometriccenter[n]; + return thisBloblocation - targetBloblocation; + } + /** + * Returns the squared distance between two blobs. + * + * @param target + * the Blob to compare to. + * + * @return the distance to the current blob to target blob specified. + */ + + public double squareDistanceTo(ThreeDRoiobject target) { + // Returns squared distance between the source Blob and the target Blob. + + final double[] sourceLocation = geometriccenter; + final double[] targetLocation = target.geometriccenter; + + double distance = 0; + + for (int d = 0; d < sourceLocation.length; ++d) { + + distance += (sourceLocation[d] - targetLocation[d]) * (sourceLocation[d] - targetLocation[d]); + } + + return distance; + } + + @Override + public int compareTo(ThreeDRoiobject o) { + + return hashCode() - o.hashCode(); + } + + @Override + public double getDoublePosition(int d) { + + return (float) getDoublePosition(d); + } + + @Override + public float getFloatPosition(int d) { + + return (float) getDoublePosition(d); + } + + @Override + public void localize(float[] position) { + int n = position.length; + for (int d = 0; d < n; ++d) + position[d] = getFloatPosition(d); + + } + + @Override + public void localize(double[] position) { + int n = position.length; + for (int d = 0; d < n; ++d) + position[d] = getFloatPosition(d); + + } + + + +} diff --git a/src/main/java/utility/ThreeDRoiobjectAnalyzer.java b/src/main/java/utility/ThreeDRoiobjectAnalyzer.java new file mode 100644 index 0000000..10ff4a0 --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectAnalyzer.java @@ -0,0 +1,15 @@ +package utility; +import net.imglib2.algorithm.Algorithm; +import net.imglib2.algorithm.Benchmark; + +/** + * Interface for a class that can compute feature on a collection of spots. + *

      + * The spot collection to operate on is given at construction by the + * {@link SpotAnalyzerFactory} that instantiated and configured this instance. + * Calling the {@link #process()} method result in updating the feature map of + * each spot directly, calling + * {@link fiji.plugin.trackmate.Spot#putFeature(String, Double)}. + */ +public interface ThreeDRoiobjectAnalyzer< T > extends Algorithm, Benchmark +{} diff --git a/src/main/java/utility/ThreeDRoiobjectAnalyzerFactory.java b/src/main/java/utility/ThreeDRoiobjectAnalyzerFactory.java new file mode 100644 index 0000000..242c9c6 --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectAnalyzerFactory.java @@ -0,0 +1,44 @@ +package utility; + + +import linkers.Model3D; +import net.imagej.ImgPlus; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; + +/** + * Interface for factories that can generate a {@link SpotAnalyzer} configured + * to operate on a specific frame of a model. + *

      + * Concrete implementation should declare what features they can compute + * numerically, and make this info available in the + * {@link fiji.plugin.trackmate.providers.SpotAnalyzerProvider} that returns + * them. + *

      + * Feature key names are for historical reason all capitalized in an enum + * manner. For instance: POSITION_X, MAX_INTENSITY, etc... They must be suitable + * to be used as a attribute key in an xml file. + * + * @author Jean-Yves Tinevez - 2012 + */ +public interface ThreeDRoiobjectAnalyzerFactory< T extends RealType< T > & NativeType< T > > extends FeatureAnalyzer3D +{ + + /** + * Returns a configured {@link SpotAnalyzer} ready to operate on the given + * frame (0-based) and given channel (0-based). The target frame image and + * the target spots are retrieved from the {@link Model} thanks to the given + * frame and channel index. + * + * @param model + * the {@link Model} to take the spots from. + * @param img + * the 5D (X, Y, Z, C, T) source image. + * @param frame + * the target frame to operate on. + * @param channel + * the target channel to operate on. + */ + public ThreeDRoiobjectAnalyzer< T > getAnalyzer( final Model3D model, ImgPlus< T > img, int frame, int channel ); + +} diff --git a/src/main/java/utility/ThreeDRoiobjectCollection.java b/src/main/java/utility/ThreeDRoiobjectCollection.java new file mode 100644 index 0000000..ac5bbaa --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectCollection.java @@ -0,0 +1,1208 @@ +package utility; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import net.imglib2.algorithm.MultiThreaded; + +/** + * A utility class that wrap the {@link java.util.SortedMap} we use to store the + * ThreeDRoiobjects contained in each Time with a few utility methods. + *

      + * Internally we rely on ConcurrentSkipListMap to allow concurrent access + * without clashes. + *

      + * This class is {@link MultiThreaded}. There are a few processes that can + * benefit from multithreaded computation ({@link #filter(Collection)}, + * {@link #filter(FeatureFilter)} + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> - Feb 2011 - + * 2013 + * + */ +public class ThreeDRoiobjectCollection implements MultiThreaded +{ + + public static final Double ZERO = Double.valueOf( 0d ); + + public static final Double ONE = Double.valueOf( 1d ); + + public static final String VISIBLITY = "VISIBILITY"; + + /** + * Time units for filtering and cropping operation timeouts. Filtering + * should not take more than 1 minute. + */ + private static final TimeUnit TIME_OUT_UNITS = TimeUnit.MINUTES; + + /** + * Time for filtering and cropping operation timeouts. Filtering should not + * take more than 1 minute. + */ + private static final long TIME_OUT_DELAY = 1; + + /** The Time by Time list of ThreeDRoiobject this object wrap. */ + private ConcurrentSkipListMap< Integer, Set< ThreeDRoiobject > > content = new ConcurrentSkipListMap< >(); + + private int numThreads; + + /* + * CONSTRUCTORS + */ + + /** + * Construct a new empty ThreeDRoiobject collection. + */ + public ThreeDRoiobjectCollection() + { + setNumThreads(); + } + + /* + * METHODS + */ + + /** + * Retrieves and returns the {@link ThreeDRoiobject} object in this collection with the + * specified ID. Returns null if the ThreeDRoiobject cannot be found. All + * ThreeDRoiobjects, visible or not, are searched for. + * + * @param ID + * the ID to look for. + * @return the ThreeDRoiobject with the specified ID or null if this ThreeDRoiobject + * does not exist or does not belong to this collection. + */ + public ThreeDRoiobject search( final int ID ) + { + ThreeDRoiobject ThreeDRoiobject = null; + for ( final ThreeDRoiobject s : iterable( false ) ) + { + if ( s.ID() == ID ) + { + ThreeDRoiobject = s; + break; + } + } + return ThreeDRoiobject; + } + + @Override + public String toString() + { + String str = super.toString(); + str += ": contains " + getNThreeDRoiobjects( false ) + " ThreeDRoiobjects total in " + keySet().size() + " different Times, over which " + getNThreeDRoiobjects( true ) + " are visible:\n"; + for ( final int key : content.keySet() ) + { + str += "\tTime " + key + ": " + getNThreeDRoiobjects( key, false ) + " ThreeDRoiobjects total, " + getNThreeDRoiobjects( key, true ) + " visible.\n"; + } + return str; + } + + /** + * Adds the given ThreeDRoiobject to this collection, at the specified Time, and mark + * it as visible. + *

      + * If the Time does not exist yet in the collection, it is created and + * added. Upon adding, the added ThreeDRoiobject has its feature {@link ThreeDRoiobject#Time} + * updated with the passed Time value. + * + * @param ThreeDRoiobject + * the ThreeDRoiobject to add. + * @param Time + * the Time to add it to. + */ + public void add( final ThreeDRoiobject ThreeDRoiobject, final Integer Time ) + { + Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + if ( null == ThreeDRoiobjects ) + { + ThreeDRoiobjects = new HashSet< >(); + content.put( Time, ThreeDRoiobjects ); + } + ThreeDRoiobjects.add( ThreeDRoiobject ); + ThreeDRoiobject.putFeature( ThreeDRoiobject.Time, Double.valueOf( Time ) ); + ThreeDRoiobject.putFeature( VISIBLITY, ONE ); + } + + /** + * Removes the given ThreeDRoiobject from this collection, at the specified Time. + *

      + * If the ThreeDRoiobject Time collection does not exist yet, nothing is done and + * false is returned. If the ThreeDRoiobject cannot be found in the Time + * content, nothing is done and false is returned. + * + * @param ThreeDRoiobject + * the ThreeDRoiobject to remove. + * @param Time + * the Time to remove it from. + * @return true if the ThreeDRoiobject was succesfully removed. + */ + public boolean remove( final ThreeDRoiobject ThreeDRoiobject, final Integer Time ) + { + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + if ( null == ThreeDRoiobjects ) { return false; } + return ThreeDRoiobjects.remove( ThreeDRoiobject ); + } + + /** + * Marks all the content of this collection as visible or invisible. + * + * @param visible + * if true, all ThreeDRoiobjects will be marked as visible. + */ + public void setVisible( final boolean visible ) + { + final Double val = visible ? ONE : ZERO; + final Collection< Integer > Times = content.keySet(); + + final ExecutorService executors = Executors.newFixedThreadPool( numThreads ); + for ( final Integer Time : Times ) + { + + final Runnable command = new Runnable() + { + @Override + public void run() + { + + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + ThreeDRoiobject.putFeature( VISIBLITY, val ); + } + + } + }; + executors.execute( command ); + } + + executors.shutdown(); + try + { + final boolean ok = executors.awaitTermination( TIME_OUT_DELAY, TIME_OUT_UNITS ); + if ( !ok ) + { + System.err.println( "[ThreeDRoiobjectCollection.setVisible()] Timeout of " + TIME_OUT_DELAY + " " + TIME_OUT_UNITS + " reached." ); + } + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + } + + /** + * Filters out the content of this collection using the specified + * {@link FeatureFilter}. ThreeDRoiobjects that are filtered out are marked as + * invisible, and visible otherwise. + * + * @param featurefilter + * the filter to use. + */ + public final void filter( final FeatureFilter featurefilter ) + { + + final Collection< Integer > Times = content.keySet(); + final ExecutorService executors = Executors.newFixedThreadPool( numThreads ); + + for ( final Integer Time : Times ) + { + + final Runnable command = new Runnable() + { + @Override + public void run() + { + + Double val, tval; + + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + tval = featurefilter.value; + + if ( featurefilter.isAbove ) + { + + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + val = ThreeDRoiobject.getFeature( featurefilter.feature ); + if ( val.compareTo( tval ) < 0 ) + { + ThreeDRoiobject.putFeature( VISIBLITY, ZERO ); + } + else + { + ThreeDRoiobject.putFeature( VISIBLITY, ONE ); + } + } + + } + else + { + + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + val = ThreeDRoiobject.getFeature( featurefilter.feature ); + if ( val.compareTo( tval ) > 0 ) + { + ThreeDRoiobject.putFeature( VISIBLITY, ZERO ); + } + else + { + ThreeDRoiobject.putFeature( VISIBLITY, ONE ); + } + } + } + } + }; + executors.execute( command ); + } + + executors.shutdown(); + try + { + final boolean ok = executors.awaitTermination( TIME_OUT_DELAY, TIME_OUT_UNITS ); + if ( !ok ) + { + System.err.println( "[ThreeDRoiobjectCollection.filter()] Timeout of " + TIME_OUT_DELAY + " " + TIME_OUT_UNITS + " reached while filtering." ); + } + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + } + + /** + * Filters out the content of this collection using the specified + * {@link FeatureFilter} collection. ThreeDRoiobjects that are filtered out are marked + * as invisible, and visible otherwise. To be marked as visible, a ThreeDRoiobject must + * pass all of the specified filters (AND chaining). + * + * @param filters + * the filter collection to use. + */ + public final void filter( final Collection< FeatureFilter > filters ) + { + + final Collection< Integer > Times = content.keySet(); + final ExecutorService executors = Executors.newFixedThreadPool( numThreads ); + + for ( final Integer Time : Times ) + { + final Runnable command = new Runnable() + { + @Override + public void run() + { + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + + Double val, tval; + boolean isAbove, shouldNotBeVisible; + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + + shouldNotBeVisible = false; + for ( final FeatureFilter featureFilter : filters ) + { + + val = ThreeDRoiobject.getFeature( featureFilter.feature ); + tval = featureFilter.value; + isAbove = featureFilter.isAbove; + + if ( isAbove && val.compareTo( tval ) < 0 || !isAbove && val.compareTo( tval ) > 0 ) + { + shouldNotBeVisible = true; + break; + } + } // loop over filters + + if ( shouldNotBeVisible ) + { + ThreeDRoiobject.putFeature( VISIBLITY, ZERO ); + } + else + { + ThreeDRoiobject.putFeature( VISIBLITY, ONE ); + } + } // loop over ThreeDRoiobjects + + } + + }; + executors.execute( command ); + } + + executors.shutdown(); + try + { + final boolean ok = executors.awaitTermination( TIME_OUT_DELAY, TIME_OUT_UNITS ); + if ( !ok ) + { + System.err.println( "[ThreeDRoiobjectCollection.filter()] Timeout of " + TIME_OUT_DELAY + " " + TIME_OUT_UNITS + " reached while filtering." ); + } + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + } + + /** + * Returns the closest {@link ThreeDRoiobject} to the given location (encoded as a + * ThreeDRoiobject), contained in the Time Time. If the Time has no + * ThreeDRoiobject, return null. + * + * @param location + * the location to search for. + * @param Time + * the Time to inspect. + * @param visibleThreeDRoiobjectsOnly + * if true, will only search though visible ThreeDRoiobjects. If false, will + * search through all ThreeDRoiobjects. + * @return the closest ThreeDRoiobject to the specified location, member of this + * collection. + */ + public final ThreeDRoiobject getClosestThreeDRoiobject( final ThreeDRoiobject location, final int Time, final boolean visibleThreeDRoiobjectsOnly ) + { + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + if ( null == ThreeDRoiobjects ) + return null; + double d2; + double minDist = Double.POSITIVE_INFINITY; + ThreeDRoiobject target = null; + for ( final ThreeDRoiobject s : ThreeDRoiobjects ) + { + + if ( visibleThreeDRoiobjectsOnly && ( s.getFeature( VISIBLITY ).compareTo( ZERO ) <= 0 ) ) + { + continue; + } + + d2 = s.squareDistanceTo( location ); + if ( d2 < minDist ) + { + minDist = d2; + target = s; + } + + } + return target; + } + + /** + * Returns the {@link ThreeDRoiobject} at the given location (encoded as a ThreeDRoiobject), + * contained in the Time Time. A ThreeDRoiobject is returned only + * if there exists a ThreeDRoiobject such that the given location is within the ThreeDRoiobject + * radius. Otherwise null is returned. + * + * @param location + * the location to search for. + * @param Time + * the Time to inspect. + * @param visibleThreeDRoiobjectsOnly + * if true, will only search though visible ThreeDRoiobjects. If false, will + * search through all ThreeDRoiobjects. + * @return the closest ThreeDRoiobject such that the specified location is within its + * radius, member of this collection, or null is such a + * ThreeDRoiobjects cannot be found. + */ + public final ThreeDRoiobject getThreeDRoiobjectAt( final ThreeDRoiobject location, final int Time, final boolean visibleThreeDRoiobjectsOnly ) + { + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + if ( null == ThreeDRoiobjects || ThreeDRoiobjects.isEmpty() ) { return null; } + + final TreeMap< Double, ThreeDRoiobject > distanceToThreeDRoiobject = new TreeMap< >(); + double d2; + for ( final ThreeDRoiobject s : ThreeDRoiobjects ) + { + if ( visibleThreeDRoiobjectsOnly && ( s.getFeature( VISIBLITY ).compareTo( ZERO ) <= 0 ) ) + continue; + + d2 = s.squareDistanceTo( location ); + if ( d2 < s.getFeature( ThreeDRoiobject.Size ) * s.getFeature( ThreeDRoiobject.Size ) ) + distanceToThreeDRoiobject.put( d2, s ); + } + if ( distanceToThreeDRoiobject.isEmpty() ) + return null; + + return distanceToThreeDRoiobject.firstEntry().getValue(); + } + + /** + * Returns the n closest {@link ThreeDRoiobject} to the given location + * (encoded as a ThreeDRoiobject), contained in the Time Time. If the + * number of ThreeDRoiobjects in the Time is exhausted, a shorter list is returned. + *

      + * The list is ordered by increasing distance to the given location. + * + * @param location + * the location to search for. + * @param Time + * the Time to inspect. + * @param n + * the number of ThreeDRoiobjects to search for. + * @param visibleThreeDRoiobjectsOnly + * if true, will only search though visible ThreeDRoiobjects. If false, will + * search through all ThreeDRoiobjects. + * @return a new list, with of at most n ThreeDRoiobjects, ordered by + * increasing distance from the specified location. + */ + public final List< ThreeDRoiobject > getNClosestThreeDRoiobjects( final ThreeDRoiobject location, final int Time, int n, final boolean visibleThreeDRoiobjectsOnly ) + { + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + final TreeMap< Double, ThreeDRoiobject > distanceToThreeDRoiobject = new TreeMap< >(); + + double d2; + for ( final ThreeDRoiobject s : ThreeDRoiobjects ) + { + + if ( visibleThreeDRoiobjectsOnly && ( s.getFeature( VISIBLITY ).compareTo( ZERO ) <= 0 ) ) + { + continue; + } + + d2 = s.squareDistanceTo( location ); + distanceToThreeDRoiobject.put( d2, s ); + } + + final List< ThreeDRoiobject > selectedThreeDRoiobjects = new ArrayList< >( n ); + final Iterator< Double > it = distanceToThreeDRoiobject.keySet().iterator(); + while ( n > 0 && it.hasNext() ) + { + selectedThreeDRoiobjects.add( distanceToThreeDRoiobject.get( it.next() ) ); + n--; + } + return selectedThreeDRoiobjects; + } + + /** + * Returns the total number of ThreeDRoiobjects in this collection, over all Times. + * + * @param visibleThreeDRoiobjectsOnly + * if true, will only count visible ThreeDRoiobjects. If false count all + * ThreeDRoiobjects. + * @return the total number of ThreeDRoiobjects in this collection. + */ + public final int getNThreeDRoiobjects( final boolean visibleThreeDRoiobjectsOnly ) + { + int nThreeDRoiobjects = 0; + if ( visibleThreeDRoiobjectsOnly ) + { + + final Iterator< ThreeDRoiobject > it = iterator( true ); + while ( it.hasNext() ) + { + it.next(); + nThreeDRoiobjects++; + } + + } + else + { + + for ( final Set< ThreeDRoiobject > ThreeDRoiobjects : content.values() ) + nThreeDRoiobjects += ThreeDRoiobjects.size(); + } + return nThreeDRoiobjects; + } + + /** + * Returns the number of ThreeDRoiobjects at the given Time. + * + * @param Time + * the Time. + * @param visibleThreeDRoiobjectsOnly + * if true, will only count visible ThreeDRoiobjects. If false count all + * ThreeDRoiobjects. + * @return the number of ThreeDRoiobjects at the given Time. + */ + public int getNThreeDRoiobjects( final int Time, final boolean visibleThreeDRoiobjectsOnly ) + { + if ( visibleThreeDRoiobjectsOnly ) + { + final Iterator< ThreeDRoiobject > it = iterator( Time, true ); + int nThreeDRoiobjects = 0; + while ( it.hasNext() ) + { + it.next(); + nThreeDRoiobjects++; + } + return nThreeDRoiobjects; + } + + final Set< ThreeDRoiobject > ThreeDRoiobjects = content.get( Time ); + if ( null == ThreeDRoiobjects ) + return 0; + + return ThreeDRoiobjects.size(); + } + + /* + * FEATURES + */ + + /** + * Builds and returns a new map of feature values for this ThreeDRoiobject collection. + * Each feature maps a double array, with 1 element per {@link ThreeDRoiobject}, all + * pooled together. + * + * @param features + * the features to collect + * @param visibleOnly + * if true, only the visible ThreeDRoiobject values will be + * collected. + * @return a new map instance. + */ + public Map< String, double[] > collectValues( final Collection< String > features, final boolean visibleOnly ) + { + final Map< String, double[] > featureValues = new ConcurrentHashMap< >( features.size() ); + final ExecutorService executors = Executors.newFixedThreadPool( numThreads ); + + for ( final String feature : features ) + { + final Runnable command = new Runnable() + { + @Override + public void run() + { + final double[] values = collectValues( feature, visibleOnly ); + featureValues.put( feature, values ); + } + + }; + executors.execute( command ); + } + + executors.shutdown(); + try + { + final boolean ok = executors.awaitTermination( TIME_OUT_DELAY, TIME_OUT_UNITS ); + if ( !ok ) + { + System.err.println( "[ThreeDRoiobjectCollection.collectValues()] Timeout of " + TIME_OUT_DELAY + " " + TIME_OUT_UNITS + " reached while filtering." ); + } + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + + return featureValues; + } + + /** + * Returns the feature values of this ThreeDRoiobject collection as a new double array. + *

      + * If some ThreeDRoiobjects do not have the interrogated feature set (stored value is + * null) or if the value is {@link Double#NaN}, they are + * skipped. The returned array might be therefore of smaller size than the + * number of ThreeDRoiobjects interrogated. + * + * @param feature + * the feature to collect. + * @param visibleOnly + * if true, only the visible ThreeDRoiobject values will be + * collected. + * @return a new double array. + */ + public final double[] collectValues( final String feature, final boolean visibleOnly ) + { + final double[] values = new double[ getNThreeDRoiobjects( visibleOnly ) ]; + int index = 0; + for ( final ThreeDRoiobject ThreeDRoiobject : iterable( visibleOnly ) ) + { + final Double feat = ThreeDRoiobject.getFeature( feature ); + if ( null == feat ) + { + continue; + } + final double val = feat.doubleValue(); + if ( Double.isNaN( val ) ) + { + continue; + } + values[ index ] = val; + index++; + } + return values; + } + + /* + * ITERABLE & co + */ + + /** + * Return an iterator that iterates over all the ThreeDRoiobjects contained in this + * collection. + * + * @param visibleThreeDRoiobjectsOnly + * if true, the returned iterator will only iterate through + * visible ThreeDRoiobjects. If false, it will iterate over all ThreeDRoiobjects. + * @return an iterator that iterates over this collection. + */ + public Iterator< ThreeDRoiobject > iterator( final boolean visibleThreeDRoiobjectsOnly ) + { + if ( visibleThreeDRoiobjectsOnly ) + return new VisibleThreeDRoiobjectsIterator(); + + return new AllThreeDRoiobjectsIterator(); + } + + /** + * Return an iterator that iterates over the ThreeDRoiobjects in the specified Time. + * + * @param visibleThreeDRoiobjectsOnly + * if true, the returned iterator will only iterate through + * visible ThreeDRoiobjects. If false, it will iterate over all ThreeDRoiobjects. + * @param Time + * the Time to iterate over. + * @return an iterator that iterates over the content of a Time of this + * collection. + */ + public Iterator< ThreeDRoiobject > iterator( final Integer Time, final boolean visibleThreeDRoiobjectsOnly ) + { + final Set< ThreeDRoiobject > TimeContent = content.get( Time ); + if ( null == TimeContent ) { return EMPTY_ITERATOR; } + if ( visibleThreeDRoiobjectsOnly ) + return new VisibleThreeDRoiobjectsTimeIterator( TimeContent ); + + return TimeContent.iterator(); + } + + /** + * A convenience methods that returns an {@link Iterable} wrapper for this + * collection as a whole. + * + * @param visibleThreeDRoiobjectsOnly + * if true, the iterable will contains only visible ThreeDRoiobjects. + * Otherwise, it will contain all the ThreeDRoiobjects. + * @return an iterable view of this ThreeDRoiobject collection. + */ + public Iterable< ThreeDRoiobject > iterable( final boolean visibleThreeDRoiobjectsOnly ) + { + return new WholeCollectionIterable( visibleThreeDRoiobjectsOnly ); + } + + /** + * A convenience methods that returns an {@link Iterable} wrapper for a + * specific Time of this ThreeDRoiobject collection. The iterable is backed-up by the + * actual collection content, so modifying it can have unexpected results. + * + * @param visibleThreeDRoiobjectsOnly + * if true, the iterable will contains only visible ThreeDRoiobjects of the + * specified Time. Otherwise, it will contain all the ThreeDRoiobjects of + * the specified Time. + * @param Time + * the Time of the content the returned iterable will wrap. + * @return an iterable view of the content of a single Time of this ThreeDRoiobject + * collection. + */ + public Iterable< ThreeDRoiobject > iterable( final int Time, final boolean visibleThreeDRoiobjectsOnly ) + { + if ( visibleThreeDRoiobjectsOnly ) + return new TimeVisibleIterable( Time ); + + return content.get( Time ); + } + + /* + * SORTEDMAP + */ + + /** + * Stores the specified ThreeDRoiobjects as the content of the specified Time. The + * added ThreeDRoiobjects are all marked as not visible. Their {@link ThreeDRoiobject#Time} is + * updated to be the specified Time. + * + * @param Time + * the Time to store these ThreeDRoiobjects at. The specified ThreeDRoiobjects replace + * the previous content of this Time, if any. + * @param ThreeDRoiobjects + * the ThreeDRoiobjects to store. + */ + public void put( final int Time, final Collection< ThreeDRoiobject > ThreeDRoiobjects ) + { + final Set< ThreeDRoiobject > value = new HashSet< >( ThreeDRoiobjects ); + for ( final ThreeDRoiobject ThreeDRoiobject : value ) + { + ThreeDRoiobject.putFeature( ThreeDRoiobject.Time, Double.valueOf( Time ) ); + ThreeDRoiobject.putFeature( VISIBLITY, ZERO ); + } + content.put( Time, value ); + } + + /** + * Returns the first (lowest) Time currently in this collection. + * + * @return the first (lowest) Time currently in this collection. + */ + public Integer firstKey() + { + if ( content.isEmpty() ) { return 0; } + return content.firstKey(); + } + + /** + * Returns the last (highest) Time currently in this collection. + * + * @return the last (highest) Time currently in this collection. + */ + public Integer lastKey() + { + if ( content.isEmpty() ) { return 0; } + return content.lastKey(); + } + + /** + * Returns a NavigableSet view of the Times contained in this collection. + * The set's iterator returns the keys in ascending order. The set is backed + * by the map, so changes to the map are reflected in the set, and + * vice-versa. The set supports element removal, which removes the + * corresponding mapping from the map, via the Iterator.remove, Set.remove, + * removeAll, retainAll, and clear operations. It does not support the add + * or addAll operations. + *

      + * The view's iterator is a "weakly consistent" iterator that will never + * throw ConcurrentModificationException, and guarantees to traverse + * elements as they existed upon construction of the iterator, and may (but + * is not guaranteed to) reflect any modifications subsequent to + * construction. + * + * @return a navigable set view of the Times in this collection. + */ + public NavigableSet< Integer > keySet() + { + return content.keySet(); + } + + /** + * Removes all the content from this collection. + */ + public void clear() + { + content.clear(); + } + + /* + * MULTITHREADING + */ + + @Override + public void setNumThreads() + { + this.numThreads = Runtime.getRuntime().availableProcessors(); + } + + @Override + public void setNumThreads( final int numThreads ) + { + this.numThreads = numThreads; + } + + @Override + public int getNumThreads() + { + return numThreads; + } + + /* + * PRIVATE CLASSES + */ + + private class AllThreeDRoiobjectsIterator implements Iterator< ThreeDRoiobject > + { + + private boolean hasNext = true; + + private final Iterator< Integer > TimeIterator; + + private Iterator< ThreeDRoiobject > contentIterator; + + private ThreeDRoiobject next = null; + + public AllThreeDRoiobjectsIterator() + { + this.TimeIterator = content.keySet().iterator(); + if ( !TimeIterator.hasNext() ) + { + hasNext = false; + return; + } + final Set< ThreeDRoiobject > currentTimeContent = content.get( TimeIterator.next() ); + contentIterator = currentTimeContent.iterator(); + iterate(); + } + + private void iterate() + { + while ( true ) + { + + // Is there still ThreeDRoiobjects in current content? + if ( !contentIterator.hasNext() ) + { + // No. Then move to next Time. + // Is there still Times to iterate over? + if ( !TimeIterator.hasNext() ) + { + // No. Then we are done + hasNext = false; + next = null; + return; + } + + contentIterator = content.get( TimeIterator.next() ).iterator(); + continue; + } + next = contentIterator.next(); + return; + } + } + + @Override + public boolean hasNext() + { + return hasNext; + } + + @Override + public ThreeDRoiobject next() + { + final ThreeDRoiobject toReturn = next; + iterate(); + return toReturn; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException( "Remove operation is not supported for ThreeDRoiobjectCollection iterators." ); + } + + } + + private class VisibleThreeDRoiobjectsIterator implements Iterator< ThreeDRoiobject > + { + + private boolean hasNext = true; + + private final Iterator< Integer > TimeIterator; + + private Iterator< ThreeDRoiobject > contentIterator; + + private ThreeDRoiobject next = null; + + private Set< ThreeDRoiobject > currentTimeContent; + + public VisibleThreeDRoiobjectsIterator() + { + this.TimeIterator = content.keySet().iterator(); + if ( !TimeIterator.hasNext() ) + { + hasNext = false; + return; + } + currentTimeContent = content.get( TimeIterator.next() ); + contentIterator = currentTimeContent.iterator(); + iterate(); + } + + private void iterate() + { + + while ( true ) + { + // Is there still ThreeDRoiobjects in current content? + if ( !contentIterator.hasNext() ) + { + // No. Then move to next Time. + // Is there still Times to iterate over? + if ( !TimeIterator.hasNext() ) + { + // No. Then we are done + hasNext = false; + next = null; + return; + } + + // Yes. Then start iterating over the next Time. + currentTimeContent = content.get( TimeIterator.next() ); + contentIterator = currentTimeContent.iterator(); + continue; + } + next = contentIterator.next(); + // Is it visible? + if ( next.getFeature( VISIBLITY ).compareTo( ZERO ) > 0 ) + { + // Yes! Be happy and return + return; + } + } + } + + @Override + public boolean hasNext() + { + return hasNext; + } + + @Override + public ThreeDRoiobject next() + { + final ThreeDRoiobject toReturn = next; + iterate(); + return toReturn; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException( "Remove operation is not supported for ThreeDRoiobjectCollection iterators." ); + } + + } + + private class VisibleThreeDRoiobjectsTimeIterator implements Iterator< ThreeDRoiobject > + { + + private boolean hasNext = true; + + private ThreeDRoiobject next = null; + + private final Iterator< ThreeDRoiobject > contentIterator; + + public VisibleThreeDRoiobjectsTimeIterator( final Set< ThreeDRoiobject > TimeContent ) + { + if ( null == TimeContent ) + { + this.contentIterator = EMPTY_ITERATOR; + } + else + { + this.contentIterator = TimeContent.iterator(); + } + iterate(); + } + + private void iterate() + { + while ( true ) + { + if ( !contentIterator.hasNext() ) + { + // No. Then we are done + hasNext = false; + next = null; + return; + } + next = contentIterator.next(); + // Is it visible? + if ( next.getFeature( VISIBLITY ).compareTo( ZERO ) > 0 ) + { + // Yes. Be happy, and return. + return; + } + } + } + + @Override + public boolean hasNext() + { + return hasNext; + } + + @Override + public ThreeDRoiobject next() + { + final ThreeDRoiobject toReturn = next; + iterate(); + return toReturn; + } + + @Override + public void remove() + { + throw new UnsupportedOperationException( "Remove operation is not supported for ThreeDRoiobjectCollection iterators." ); + } + + } + + /** + * Returns a new {@link ThreeDRoiobjectCollection}, made of only the ThreeDRoiobjects marked as + * visible. All the ThreeDRoiobjects will then be marked as not-visible. + * + * @return a new ThreeDRoiobject collection, made of only the ThreeDRoiobjects marked as visible. + */ + public ThreeDRoiobjectCollection crop() + { + final ThreeDRoiobjectCollection ns = new ThreeDRoiobjectCollection(); + ns.setNumThreads( numThreads ); + + final Collection< Integer > Times = content.keySet(); + final ExecutorService executors = Executors.newFixedThreadPool( numThreads ); + for ( final Integer Time : Times ) + { + + final Runnable command = new Runnable() + { + @Override + public void run() + { + final Set< ThreeDRoiobject > fc = content.get( Time ); + final Set< ThreeDRoiobject > nfc = new HashSet< >( getNThreeDRoiobjects( Time, true ) ); + + for ( final ThreeDRoiobject ThreeDRoiobject : fc ) + { + if ( ThreeDRoiobject.getFeature( VISIBLITY ).compareTo( ZERO ) > 0 ) + { + nfc.add( ThreeDRoiobject ); + ThreeDRoiobject.putFeature( VISIBLITY, ZERO ); + } + } + ns.content.put( Time, nfc ); + } + }; + executors.execute( command ); + } + + executors.shutdown(); + try + { + final boolean ok = executors.awaitTermination( TIME_OUT_DELAY, TIME_OUT_UNITS ); + if ( !ok ) + { + System.err.println( "[ThreeDRoiobjectCollection.crop()] Timeout of " + TIME_OUT_DELAY + " " + TIME_OUT_UNITS + " reached while cropping." ); + } + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + return ns; + } + + /** + * A convenience wrapper that implements {@link Iterable} for this ThreeDRoiobject + * collection. + */ + private final class WholeCollectionIterable implements Iterable< ThreeDRoiobject > + { + + private final boolean visibleThreeDRoiobjectsOnly; + + public WholeCollectionIterable( final boolean visibleThreeDRoiobjectsOnly ) + { + this.visibleThreeDRoiobjectsOnly = visibleThreeDRoiobjectsOnly; + } + + @Override + public Iterator< ThreeDRoiobject > iterator() + { + if ( visibleThreeDRoiobjectsOnly ) + return new VisibleThreeDRoiobjectsIterator(); + + return new AllThreeDRoiobjectsIterator(); + } + } + + /** + * A convenience wrapper that implements {@link Iterable} for this ThreeDRoiobject + * collection. + */ + private final class TimeVisibleIterable implements Iterable< ThreeDRoiobject > + { + + private final int Time; + + public TimeVisibleIterable( final int Time ) + { + this.Time = Time; + } + + @Override + public Iterator< ThreeDRoiobject > iterator() + { + return new VisibleThreeDRoiobjectsTimeIterator( content.get( Time ) ); + } + } + + private static final Iterator< ThreeDRoiobject > EMPTY_ITERATOR = new Iterator< ThreeDRoiobject >() + { + + @Override + public boolean hasNext() + { + return false; + } + + @Override + public ThreeDRoiobject next() + { + return null; + } + + @Override + public void remove() + {} + }; + + /* + * STATIC METHODS + */ + + /** + * Creates a new {@link ThreeDRoiobjectCollection} containing only the specified ThreeDRoiobjects. + * Their Time origin is retrieved from their {@link ThreeDRoiobject#Time} feature, so + * it must be set properly for all ThreeDRoiobjects. All the ThreeDRoiobjects of the new + * collection have the same visibility that the one they carry. + * + * @param ThreeDRoiobjects + * the ThreeDRoiobject collection to build from. + * @return a new {@link ThreeDRoiobjectCollection} instance. + */ + public static ThreeDRoiobjectCollection fromCollection( final Iterable< ThreeDRoiobject > ThreeDRoiobjects ) + { + final ThreeDRoiobjectCollection sc = new ThreeDRoiobjectCollection(); + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + final int Time = ThreeDRoiobject.getFeature( ThreeDRoiobject.Time ).intValue(); + Set< ThreeDRoiobject > fc = sc.content.get( Time ); + if ( null == fc ) + { + fc = new HashSet< >(); + sc.content.put( Time, fc ); + } + fc.add( ThreeDRoiobject ); + } + return sc; + } + + /** + * Creates a new {@link ThreeDRoiobjectCollection} from a copy of the specified map of + * sets. The ThreeDRoiobjects added this way are completely untouched. In particular, + * their {@link #VISIBLITY} feature is left untouched, which makes this + * method suitable to de-serialize a {@link ThreeDRoiobjectCollection}. + * + * @param source + * the map to buidl the ThreeDRoiobject collection from. + * @return a new ThreeDRoiobjectCollection. + */ + public static ThreeDRoiobjectCollection fromMap( final Map< Integer, Set< ThreeDRoiobject > > source ) + { + final ThreeDRoiobjectCollection sc = new ThreeDRoiobjectCollection(); + sc.content = new ConcurrentSkipListMap< >( source ); + return sc; + } +} diff --git a/src/main/java/utility/ThreeDRoiobjectDetector.java b/src/main/java/utility/ThreeDRoiobjectDetector.java new file mode 100644 index 0000000..ff0532d --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectDetector.java @@ -0,0 +1,24 @@ +package utility; + +import java.util.List; + +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.OutputAlgorithm; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; + +/** + * Interface for Spot detector classes, that are able to segment spots of a + * given estimated radius within a 2D or 3D image. + *

      + * Normally, concrete implementation are not expected to be multi-threaded. + * Indeed, the {@link fiji.plugin.trackmate.TrackMate} trackmate generates one + * instance of the concrete implementation per thread, to process multiple + * frames simultaneously. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> 2010 - 2012 + * + */ +public interface ThreeDRoiobjectDetector< T extends RealType< T > & NativeType< T >> extends OutputAlgorithm< List< ThreeDRoiobject >>, Benchmark +{} + diff --git a/src/main/java/utility/ThreeDRoiobjectDetectorFactory.java b/src/main/java/utility/ThreeDRoiobjectDetectorFactory.java new file mode 100644 index 0000000..49263f6 --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectDetectorFactory.java @@ -0,0 +1,127 @@ +package utility; + +import java.util.Map; + +import org.w3c.dom.Element; + +import linkers.Model3D; +import net.imagej.ImgPlus; +import net.imglib2.Interval; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; + + + +/** + * Mother interface for {@link SpotDetector} factories. + *

      + * These classes are able to configure a {@link SpotDetector} to operate on a + * target {@link ImgPlus}. It is also responsible for loading/saving the + * detector parameters from/to XML, and of generating a suitable configuration + * panel for GUI interaction. + */ +public interface ThreeDRoiobjectDetectorFactory< T extends RealType< T > & NativeType< T >> extends CovistoModule +{ + + /** + * Returns a new {@link SpotDetector} configured to operate on the given + * target frame. This factory must be first given the {@link ImgPlus} and + * the settings map, through the {@link #setTarget(ImgPlus, Map)} method. + * + * @param interval + * the interval that determines the region in the source image to + * operate on. This must not have a dimension for time + * (e.g. if the source image is 2D+T (3D), then the + * interval must be 2D; if the source image is 3D without time, + * then the interval must be 3D). + * @param frame + * the frame index in the source image to operate on + */ + public ThreeDRoiobjectDetector< T > getDetector( final Interval interval, int frame ); + + /** + * Configure this factory to operate on the given source image (possibly + * 5D), with the given settings map. + *

      + * Also checks the validity of the given settings map for this factory. If + * check fails, return false, an error message can be obtained + * through {@link #getErrorMessage()}. + * + * @param img + * the {@link ImgPlus} to operate on, possible 5D. + * @param settings + * the settings map, must be suitable for this detector factory. + * @return false is the given settings map is not suitable for + * this detector factory. + * @see SpotDetectorFactory#getErrorMessage() + */ + public boolean setTarget( final ImgPlus< T > img, final Map< String, Object > settings ); + + /** + * Returns a meaningful error message for the last action on this factory. + * + * @see #setTarget(ImgPlus, Map) + * @see #marshall(Map, Element) + * @see #unmarshall(Element, Map) + */ + public String getErrorMessage(); + + /** + * Marshalls a settings map to a JDom element, ready for saving to XML. The + * element is updated with new attributes. + *

      + * Only parameters specific to the specific detector factory are marshalled. + * The element also always receive an attribute named + * {@value DetectorKeys#XML_ATTRIBUTE_DETECTOR_NAME} that saves the target + * {@link SpotDetectorFactory} key. + * + * @return true if marshalling was successful. If not, check + * {@link #getErrorMessage()} + */ + public boolean marshall( final Map< String, Object > settings, final Element element ); + + /** + * Un-marshalls a JDom element to update a settings map. + * + * @param element + * the JDom element to read from. + * @param settings + * the map to update. Is cleared prior to updating, so that it + * contains only the parameters specific to the target detector + * factory. + * @return true if un-marshalling was successful. If not, check + * {@link #getErrorMessage()} + */ + public boolean unmarshall( final Element element, final Map< String, Object > settings ); + + /** + * Returns a new GUI panel able to configure the settings suitable for this + * specific detector factory. + * + * @param settings + * the current settings, used to get info to display on the GUI + * panel. + * @param model + * the current model, used to get info to display on the GUI + * panel. + */ + public ConfigurationPanel getDetectorConfigurationPanel( final Settings settings, final Model3D model ); + + /** + * Returns a new default settings map suitable for the target detector. + * Settings are instantiated with default values. + * + * @return a new map. + */ + public Map< String, Object > getDefaultSettings(); + + /** + * Check that the given settings map is suitable for target detector. + * + * @param settings + * the map to test. + * @return true if the settings map is valid. + */ + public boolean checkSettings( final Map< String, Object > settings ); + +} diff --git a/src/main/java/utility/ThreeDRoiobjectTracker.java b/src/main/java/utility/ThreeDRoiobjectTracker.java new file mode 100644 index 0000000..d2ba1b3 --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectTracker.java @@ -0,0 +1,44 @@ +package utility; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import net.imglib2.algorithm.MultiThreaded; +import net.imglib2.algorithm.OutputAlgorithm; + +/** + * This interface should be used when creating algorithms for linking objects + * across multiple frames in time-lapse images. + *

      + * A SpotTracker algorithm is simply expected to create a new + * {@link SimpleWeightedGraph} from the spot collection help in the + * {@link fiji.plugin.trackmate.Model} that is given to it. We use a simple + * weighted graph: + *

        + *
      • Though the weights themselves are not used for subsequent steps, it is + * suggested to use edge weight to report the cost of a link. + *
      • The graph is undirected, however, some link direction can be retrieved + * later on using the {@link Spot#FRAME} feature. The {@link SpotTracker} + * implementation does not have to deal with this; only undirected edges are + * created. + *
      • Several links between two spots are not permitted. + *
      • A link with the same spot for source and target is not allowed. + *
      • A link with the source spot and the target spot in the same frame is not + * allowed. This must be enforced by implementations. + *
      + *

      + * A {@link SpotTracker} implements {@link MultiThreaded}. If concrete + * implementations are not multithreaded, they can safely ignore the associated + * methods. + */ +public interface ThreeDRoiobjectTracker extends OutputAlgorithm< SimpleWeightedGraph< ThreeDRoiobject, DefaultWeightedEdge > >, MultiThreaded +{ + /** + * Sets the {@link Logger} instance that will receive messages from this + * {@link SpotTracker}. + * + * @param logger + * the logger to echo messages to. + */ + public void setLogger( final Logger logger ); +} diff --git a/src/main/java/utility/ThreeDRoiobjectTrackerFactory.java b/src/main/java/utility/ThreeDRoiobjectTrackerFactory.java new file mode 100644 index 0000000..4b94ace --- /dev/null +++ b/src/main/java/utility/ThreeDRoiobjectTrackerFactory.java @@ -0,0 +1,105 @@ +package utility; + +import java.util.Map; + +import org.w3c.dom.Element; + +import linkers.Model3D; + + + +public interface ThreeDRoiobjectTrackerFactory extends CovistoModule +{ + + /** + * Instantiates and returns a new {@link ThreeDRoiobjectTracker} configured to operate + * on the specified {@link ThreeDRoiobjectCollection}, using the specified settins map. + * + * @param ThreeDRoiobjects + * the {@link ThreeDRoiobjectCollection} containing the ThreeDRoiobjects to track. + * @param settings + * the settings map configuring the tracker. + * @return a new {@link ThreeDRoiobjectTracker} instance. + */ + public ThreeDRoiobjectTracker create( final ThreeDRoiobjectCollection ThreeDRoiobjects, final Map< String, Object > settings ); + + /** + * Returns a new GUI panel able to configure the settings suitable for the + * target tracker identified by the key parameter. + * + * @param model + * the model that will be modified by the target tracker. + * @return a new configuration panel. + */ + public ConfigurationPanel getTrackerConfigurationPanel( final Model3D model ); + + /** + * Marshalls a settings map to a JDom element, ready for saving to XML. The + * element is updated with new attributes. + *

      + * Only parameters specific to the concrete tracker factory are marshalled. + * The element also always receive an attribute named + * {@value TrackerKeys#XML_ATTRIBUTE_TRACKER_NAME} that saves the target + * {@link ThreeDRoiobjectTracker} key. + * + * @return true if marshalling was successful. If not, check + * {@link #getErrorMessage()} + */ + public boolean marshall( final Map< String, Object > settings, final Element element ); + + /** + * Un-marshall a JDom element to update a settings map, and sets the target + * tracker of this provider from the element. + *

      + * Concretely: the the specific settings map for the targeted tracker is + * updated from the element. + * + * @param element + * the JDom element to read from. + * @param settings + * the map to update. Is cleared prior to updating, so that it + * contains only the parameters specific to the target tracker. + * @return true if unmarshalling was successful. If not, check + * {@link #getErrorMessage()} + */ + public boolean unmarshall( final Element element, final Map< String, Object > settings ); + + /** + * A utility method that builds a string representation of a settings map + * owing to the currently selected tracker in this provider. + * + * @param sm + * the map to echo. + * @return a string representation of the map. + */ + public String toString( final Map< String, Object > sm ); + + /** + * Returns a new default settings map suitable for the tracker Settings are + * instantiated with default values. is returned. + * + * @return a settings map. + */ + public Map< String, Object > getDefaultSettings(); + + /** + * Checks the validity of the given settings map for the tracker. The + * validity check is strict: we check that all needed parameters are here + * and are of the right class, and that there is no extra unwanted + * parameters. + * + * @return true if the settings map can be used with the target factory. If + * not, check {@link #getErrorMessage()} + */ + public boolean checkSettingsValidity( final Map< String, Object > settings ); + + /** + * Returns a meaningful error message for the last action on this factory. + * + * @return an error message. + * @see #marshall(Map, Element) + * @see #unmarshall(Element, Map) + * @see #checkSettingsValidity(Map) + */ + public String getErrorMessage(); +} diff --git a/src/main/java/utility/ViewUtils.java b/src/main/java/utility/ViewUtils.java new file mode 100644 index 0000000..f5896ba --- /dev/null +++ b/src/main/java/utility/ViewUtils.java @@ -0,0 +1,737 @@ +package utility; + + +import static utility.DetectorKeys.KEY_TARGET_CHANNEL; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Date; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + + +import ij.ImagePlus; +import linkers.Dimension; +import linkers.Model3D; +import net.imagej.ImgPlus; +import net.imagej.ImgPlusMetadata; +import net.imagej.axis.Axes; +import net.imagej.axis.AxisType; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.ImagePlusAdapter; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + +/** + * A collection of static utilities related to {@link TrackMateModelView}s. + * + * @author Jean-Yves Tinevez - 2013 + * + */ +public class ViewUtils { + + private static final double TARGET_X_IMAGE_SIZE = 512; + private static final double TARGET_Z_IMAGE_SIZE = 128; + private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat( "EEE, d MMM yyyy HH:mm:ss" ); + /* + * STATIC METHODS + */ + + /** + * Return a new map sorted by its values. Adapted from + * http://stackoverflow.com + * /questions/109383/how-to-sort-a-mapkey-value-on-the-values-in-java + */ + public static < K, V extends Comparable< ? super V > > Map< K, V > sortByValue( final Map< K, V > map, final Comparator< V > comparator ) + { + final List< Map.Entry< K, V > > list = new LinkedList< >( map.entrySet() ); + Collections.sort( list, new Comparator< Map.Entry< K, V > >() + { + @Override + public int compare( final Map.Entry< K, V > o1, final Map.Entry< K, V > o2 ) + { + return comparator.compare( o1.getValue(), o2.getValue() ); + } + } ); + + final LinkedHashMap< K, V > result = new LinkedHashMap< >(); + for ( final Map.Entry< K, V > entry : list ) + { + result.put( entry.getKey(), entry.getValue() ); + } + return result; + } + + /** + * Generate a string representation of a map, typically a settings map. + */ + public static final String echoMap( final Map< String, Object > map, final int indent ) + { + // Build string + final StringBuilder builder = new StringBuilder(); + for ( final String key : map.keySet() ) + { + for ( int i = 0; i < indent; i++ ) + { + builder.append( " " ); + } + builder.append( "- " ); + builder.append( key.toLowerCase().replace( "_", " " ) ); + builder.append( ": " ); + final Object obj = map.get( key ); + if ( obj instanceof Map ) + { + builder.append( '\n' ); + @SuppressWarnings( "unchecked" ) + final Map< String, Object > submap = ( Map< String, Object > ) obj; + builder.append( echoMap( submap, indent + 2 ) ); + } + else + { + builder.append( obj.toString() ); + builder.append( '\n' ); + } + } + return builder.toString(); + } + + /** + * Wraps an IJ {@link ImagePlus} in an imglib2 {@link ImgPlus}, without + * parameterized types. The only way I have found to beat javac constraints + * on bounded multiple wildcard. + */ + @SuppressWarnings( "rawtypes" ) + public static final ImgPlus rawWraps( final ImagePlus imp ) + { + final ImgPlus< DoubleType > img = ImagePlusAdapter.wrapImgPlus( imp ); + final ImgPlus raw = img; + return raw; + } + + /** + * Check that the given map has all some keys. Two String collection allows + * specifying that some keys are mandatory, other are optional. + * + * @param map + * the map to inspect. + * @param mandatoryKeys + * the collection of keys that are expected to be in the map. Can + * be null. + * @param optionalKeys + * the collection of keys that can be - or not - in the map. Can + * be null. + * @param errorHolder + * will be appended with an error message. + * @return if all mandatory keys are found in the map, and possibly some + * optional ones, but no others. + */ + public static final < T > boolean checkMapKeys( final Map< T, ? > map, Collection< T > mandatoryKeys, Collection< T > optionalKeys, final StringBuilder errorHolder ) + { + if ( null == optionalKeys ) + { + optionalKeys = new ArrayList< >(); + } + if ( null == mandatoryKeys ) + { + mandatoryKeys = new ArrayList< >(); + } + boolean ok = true; + final Set< T > keySet = map.keySet(); + for ( final T key : keySet ) + { + if ( !( mandatoryKeys.contains( key ) || optionalKeys.contains( key ) ) ) + { + ok = false; + errorHolder.append( "Map contains unexpected key: " + key + ".\n" ); + } + } + + for ( final T key : mandatoryKeys ) + { + if ( !keySet.contains( key ) ) + { + ok = false; + errorHolder.append( "Mandatory key " + key + " was not found in the map.\n" ); + } + } + return ok; + + } + + /** + * Check the presence and the validity of a key in a map, and test it is of + * the desired class. + * + * @param map + * the map to inspect. + * @param key + * the key to find. + * @param expectedClass + * the expected class of the target value . + * @param errorHolder + * will be appended with an error message. + * @return true if the key is found in the map, and map a value of the + * desired class. + */ + public static final boolean checkParameter( final Map< String, Object > map, final String key, final Class< ? > expectedClass, final StringBuilder errorHolder ) + { + final Object obj = map.get( key ); + if ( null == obj ) + { + errorHolder.append( "Parameter " + key + " could not be found in settings map, or is null.\n" ); + return false; + } + if ( !expectedClass.isInstance( obj ) ) + { + errorHolder.append( "Value for parameter " + key + " is not of the right class. Expected " + expectedClass.getName() + ", got " + obj.getClass().getName() + ".\n" ); + return false; + } + return true; + } + + /** + * Returns the mapping in a map that is targeted by a list of keys, in the + * order given in the list. + */ + public static final < J, K > List< K > getArrayFromMaping( final List< J > keys, final Map< J, K > mapping ) + { + final List< K > names = new ArrayList< >( keys.size() ); + for ( int i = 0; i < keys.size(); i++ ) + { + names.add( mapping.get( keys.get( i ) ) ); + } + return names; + } + + /** + * Translate each ThreeDRoiobject of the given collection by the amount specified in + * argument. The distances are all understood in physical units. + *

      + * This is meant to deal with a cropped image. The translation will bring + * the ThreeDRoiobject coordinates back to the top-left corner of the un-cropped image + * reference. + */ + public static void translateThreeDRoiobjects( final Collection< ThreeDRoiobject > ThreeDRoiobjects, final double dx, final double dy, final double dz ) + { + final double[] dval = new double[] { dx, dy, dz }; + final String[] features = new String[] { ThreeDRoiobject.XPOSITION, ThreeDRoiobject.YPOSITION, ThreeDRoiobject.ZPOSITION }; + Double val; + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects ) + { + for ( int i = 0; i < features.length; i++ ) + { + val = ThreeDRoiobject.getFeature( features[ i ] ); + if ( null != val ) + { + ThreeDRoiobject.putFeature( features[ i ], val + dval[ i ] ); + } + } + } + } + + /* + * ImgPlus & calibration & axes + */ + + /** + * Returns the index of the target axis in the given metadata. Return -1 if + * the axis was not found. + */ + private static final int findAxisIndex( final ImgPlusMetadata img, final AxisType axis ) + { + return img.dimensionIndex( axis ); + } + + public static final int findXAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.X ); + } + + public static final int findYAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.Y ); + } + + public static final int findZAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.Z ); + } + + public static final int findTAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.TIME ); + } + + public static final int findCAxisIndex( final ImgPlusMetadata img ) + { + return findAxisIndex( img, Axes.CHANNEL ); + } + + /** + * Return the xyz calibration stored in an {@link ImgPlusMetadata} in a + * 3-elements double array. Calibration is ordered as X, Y, Z. If one axis + * is not found, then the calibration for this axis takes the value of 1. + */ + public static final double[] getSpatialCalibration( final ImgPlusMetadata img ) + { + final double[] calibration = Util.getArrayFromValue( 1d, 3 ); + + for ( int d = 0; d < img.numDimensions(); d++ ) + { + if ( img.axis( d ).type() == Axes.X ) + { + calibration[ 0 ] = img.averageScale( d ); + } + else if ( img.axis( d ).type() == Axes.Y ) + { + calibration[ 1 ] = img.averageScale( d ); + } + else if ( img.axis( d ).type() == Axes.Z ) + { + calibration[ 2 ] = img.averageScale( d ); + } + } + return calibration; + } + + public static double[] getSpatialCalibration( final ImagePlus imp ) + { + final double[] calibration = Util.getArrayFromValue( 1d, 3 ); + calibration[ 0 ] = imp.getCalibration().pixelWidth; + calibration[ 1 ] = imp.getCalibration().pixelHeight; + if ( imp.getNSlices() > 1 ) + { + calibration[ 2 ] = imp.getCalibration().pixelDepth; + } + return calibration; + } + + /** + * Returns an estimate of the pth percentile of the values in + * the values array. Taken from commons-math. + */ + public static final double getPercentile( final double[] values, final double p ) + { + + final int size = values.length; + if ( ( p > 1 ) || ( p <= 0 ) ) { throw new IllegalArgumentException( "invalid quantile value: " + p ); } + // always return single value for n = 1 + if ( size == 0 ) { return Double.NaN; } + if ( size == 1 ) { return values[ 0 ]; } + final double n = size; + final double pos = p * ( n + 1 ); + final double fpos = Math.floor( pos ); + final int intPos = ( int ) fpos; + final double dif = pos - fpos; + final double[] sorted = new double[ size ]; + System.arraycopy( values, 0, sorted, 0, size ); + Arrays.sort( sorted ); + + if ( pos < 1 ) { return sorted[ 0 ]; } + if ( pos >= n ) { return sorted[ size - 1 ]; } + final double lower = sorted[ intPos - 1 ]; + final double upper = sorted[ intPos ]; + return lower + dif * ( upper - lower ); + } + + /** + * Returns [range, min, max] of the given double array. + * + * @return A double[] of length 3, where index 0 is the range, index 1 is + * the min, and index 2 is the max. + */ + private static final double[] getRange( final double[] data ) + { + double min = Double.POSITIVE_INFINITY; + double max = Double.NEGATIVE_INFINITY; + double value; + for ( int i = 0; i < data.length; i++ ) + { + value = data[ i ]; + if ( value < min ) + { + min = value; + } + if ( value > max ) + { + max = value; + } + } + return new double[] { ( max - min ), min, max }; + } + + /** + * Store the x, y, z coordinates of the specified ThreeDRoiobject in the first 3 + * elements of the specified double array. + */ + public static final void localize( final ThreeDRoiobject ThreeDRoiobject, final double[] coords ) + { + coords[ 0 ] = ThreeDRoiobject.getFeature( ThreeDRoiobject.XPOSITION ).doubleValue(); + coords[ 1 ] = ThreeDRoiobject.getFeature( ThreeDRoiobject.YPOSITION ).doubleValue(); + coords[ 2 ] = ThreeDRoiobject.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + } + + /** + * Return the optimal bin number for a histogram of the data given in array, + * using the Freedman and Diaconis rule (bin_space = 2*IQR/n^(1/3)). It is + * ensured that the bin number returned is not smaller and no bigger than + * the bounds given in argument. + */ + public static final int getNBins( final double[] values, final int minBinNumber, final int maxBinNumber ) + { + final int size = values.length; + final double q1 = getPercentile( values, 0.25 ); + final double q3 = getPercentile( values, 0.75 ); + final double iqr = q3 - q1; + final double binWidth = 2 * iqr * Math.pow( size, -0.33 ); + final double[] range = getRange( values ); + int nBin = ( int ) ( range[ 0 ] / binWidth + 1 ); + if ( nBin > maxBinNumber ) + { + nBin = maxBinNumber; + } + else if ( nBin < minBinNumber ) + { + nBin = minBinNumber; + } + return nBin; + } + + /** + * Return the optimal bin number for a histogram of the data given in array, + * using the Freedman and Diaconis rule (bin_space = 2*IQR/n^(1/3)). It is + * ensured that the bin number returned is not smaller than 8 and no bigger + * than 256. + */ + private static final int getNBins( final double[] values ) + { + return getNBins( values, 8, 256 ); + } + + /** + * Create a histogram from the data given. + */ + private static final int[] histogram( final double data[], final int nBins ) + { + final double[] range = getRange( data ); + final double binWidth = range[ 0 ] / nBins; + final int[] hist = new int[ nBins ]; + int index; + + if ( nBins > 0 ) + { + for ( int i = 0; i < data.length; i++ ) + { + index = Math.min( ( int ) Math.floor( ( data[ i ] - range[ 1 ] ) / binWidth ), nBins - 1 ); + hist[ index ]++; + } + } + return hist; + } + + /** + * Return a threshold for the given data, using an Otsu histogram + * thresholding method. + */ + public static final double otsuThreshold( final double[] data ) + { + return otsuThreshold( data, getNBins( data ) ); + } + + /** + * Return a threshold for the given data, using an Otsu histogram + * thresholding method with a given bin number. + */ + private static final double otsuThreshold( final double[] data, final int nBins ) + { + final int[] hist = histogram( data, nBins ); + final int thresholdIndex = otsuThresholdIndex( hist, data.length ); + final double[] range = getRange( data ); + final double binWidth = range[ 0 ] / nBins; + return range[ 1 ] + binWidth * thresholdIndex; + } + + /** + * Given a histogram array hist, built with an initial amount + * of nPoints data item, this method return the bin index that + * thresholds the histogram in 2 classes. The threshold is performed using + * the Otsu Threshold Method. + * + * @param hist + * the histogram array + * @param nPoints + * the number of data items this histogram was built on + * @return the bin index of the histogram that thresholds it + */ + private static final int otsuThresholdIndex( final int[] hist, final int nPoints ) + { + final int total = nPoints; + + double sum = 0; + for ( int t = 0; t < hist.length; t++ ) + { + sum += t * hist[ t ]; + } + + double sumB = 0; + int wB = 0; + int wF = 0; + + double varMax = 0; + int threshold = 0; + + for ( int t = 0; t < hist.length; t++ ) + { + wB += hist[ t ]; // Weight Background + if ( wB == 0 ) + { + continue; + } + + wF = total - wB; // Weight Foreground + if ( wF == 0 ) + { + break; + } + + sumB += ( t * hist[ t ] ); + + final double mB = sumB / wB; // Mean Background + final double mF = ( sum - sumB ) / wF; // Mean Foreground + + // Calculate Between Class Variance + final double varBetween = wB * wF * ( mB - mF ) * ( mB - mF ); + + // Check if new maximum found + if ( varBetween > varMax ) + { + varMax = varBetween; + threshold = t; + } + } + return threshold; + } + + /** + * Return a String unit for the given dimension. When suitable, the unit is + * taken from the settings field, which contains the spatial and time units. + * Otherwise, default units are used. + */ + public static final String getUnitsFor( final Dimension dimension, final String spaceUnits, final String timeUnits ) + { + String units = "no unit"; + switch ( dimension ) + { + case ANGLE: + units = "Radians"; + break; + case INTENSITY: + units = "Counts"; + break; + case INTENSITY_SQUARED: + units = "Counts^2"; + break; + case NONE: + units = ""; + break; + case POSITION: + case LENGTH: + units = spaceUnits; + break; + case QUALITY: + units = "Quality"; + break; + case TIME: + units = timeUnits; + break; + case VELOCITY: + units = spaceUnits + "/" + timeUnits; + break; + case RATE: + units = "/" + timeUnits; + break; + default: + break; + case STRING: + return null; + } + return units; + } + + public static final String getCurrentTimeString() + { + return DATE_FORMAT.format( new Date() ); + } + + /** + * Returns an interval object that in the specified {@link ImgPlus} slice + * in a single time frame. + *

      + * The specified {@link Settings} object is used to determine a crop-cube + * that will determine the X,Y,Z size of the interval. A single channel will + * be taken in the case of a multi-channel image. If the detector set in the + * settings object has a parameter for the target channel + * {@link fiji.plugin.trackmate.detection.DetectorKeys#KEY_TARGET_CHANNEL}, + * it will be used; otherwise the first channel will be taken. + *

      + * If the specified {@link ImgPlus} has a time axis, it will be dropped and + * the returned interval will have one dimension less. + * + * @param img + * the source image into which the interval is to be defined. + * @param settings + * the settings object that will determine the interval size. + * @return a new interval. + */ + public static final Interval getInterval( final ImgPlus< ? > img, final Settings settings ) + { + final long[] max = new long[ img.numDimensions() ]; + final long[] min = new long[ img.numDimensions() ]; + + // X, we must have it. + final int xindex = findXAxisIndex( img ); + min[ xindex ] = settings.xstart; + max[ xindex ] = settings.xend; + + // Y, we must have it. + final int yindex = findYAxisIndex( img ); + min[ yindex ] = settings.ystart; + max[ yindex ] = settings.yend; + + // Z, we MIGHT have it. + final int zindex = findZAxisIndex( img ); + if ( zindex >= 0 ) + { + min[ zindex ] = settings.zstart; + max[ zindex ] = settings.zend; + } + + // CHANNEL, we might have it. + final int cindex = findCAxisIndex( img ); + if ( cindex >= 0 ) + { + Integer c = ( Integer ) settings.detectorSettings.get( KEY_TARGET_CHANNEL ); // 1-based. + if ( null == c ) + { + c = 1; + } + min[ cindex ] = c - 1; // 0-based. + max[ cindex ] = min[ cindex ]; + } + + // TIME, we might have it, but anyway we leave the start & end + // management to elsewhere. + final int tindex = findTAxisIndex( img ); + + /* + * We want to exclude time (if we have it) from out interval and source, + * so that we can provide the detector instance with a hyperslice that + * does NOT have time as a dimension. + */ + final long[] intervalMin; + final long[] intervalMax; + if ( tindex >= 0 ) + { + intervalMin = new long[ min.length - 1 ]; + intervalMax = new long[ min.length - 1 ]; + int nindex = -1; + for ( int d = 0; d < min.length; d++ ) + { + if ( d == tindex ) + { + continue; + } + nindex++; + intervalMin[ nindex ] = Math.max( 0l, min[ d ] ); + intervalMax[ nindex ] = Math.min( img.max( d ), max[ d ] ); + } + } + else + { + intervalMin = min; + intervalMax = max; + } + final FinalInterval interval = new FinalInterval( intervalMin, intervalMax ); + return interval; + } + private ViewUtils() {} + + public static final ImagePlus makeEmptyImagePlus(final int width, final int height, final int nslices, final int nframes, final double[] calibration) { + final RandomAccessible< UnsignedByteType > randomAccessible = Views.extendBorder( ArrayImgs.unsignedBytes( new long[] { 1, 1, 1, 1 } ) ); + final Interval interval = new FinalInterval( width, height, nslices, nframes ); + final RandomAccessibleInterval< UnsignedByteType > view = Views.interval( randomAccessible, interval ); + + final ImagePlus imp = ImageJFunctions.wrap(view, "blank"); + imp.getCalibration().pixelWidth = calibration[0]; + imp.getCalibration().pixelHeight = calibration[1]; + imp.getCalibration().pixelDepth = calibration[2]; + imp.setDimensions(1, nslices, nframes); + imp.setOpenAsHyperStack(true); + + return imp; + } + + public static final ImagePlus makeEmpytImagePlus(final Model3D model) { + + double maxX = 0; + double maxY = 0; + double maxZ = 0; + int nframes = 0; + + for (final ThreeDRoiobject ThreeDRoiobject : model.getThreeDRoiobjects().iterable(true)) { + final double r = ThreeDRoiobject.getFeature(ThreeDRoiobject.Size); + final double x = Math.ceil(r + ThreeDRoiobject.getFeature(ThreeDRoiobject.XPOSITION)); + final double y = Math.ceil(r + ThreeDRoiobject.getFeature(ThreeDRoiobject.YPOSITION)); + final double z = Math.ceil(ThreeDRoiobject.getFeature(ThreeDRoiobject.ZPOSITION)); + final int t = ThreeDRoiobject.getFeature(ThreeDRoiobject.Time).intValue(); + + if (x > maxX) { + maxX = x; + } + if (y > maxY) { + maxY = y; + } + if (z > maxZ) { + maxZ = z; + } + if (t > nframes) { + nframes = t; + } + } + + final double calX = maxX / TARGET_X_IMAGE_SIZE; + final double calY = maxY / TARGET_X_IMAGE_SIZE; + final double calxy = Math.max(calX, calY); + final double calZ = maxZ / TARGET_Z_IMAGE_SIZE; + + final int width = (int) Math.ceil(maxX / calxy); + final int height = (int) Math.ceil(maxY / calxy); + int nslices; + if (maxZ == 0) { + nslices = 1; + } else { + nslices = (int) Math.ceil(maxZ / calZ); + } + final double[] calibration = new double[] { calxy, calxy, calZ }; + + final ImagePlus imp = makeEmptyImagePlus(width, height, nslices, nframes + 1, calibration); + imp.getCalibration().setUnit(model.getSpaceUnits()); + imp.getCalibration().setTimeUnit(model.getTimeUnits()); + return imp; + } + +} diff --git a/src/main/java/utils/AddGaussian.java b/src/main/java/utils/AddGaussian.java new file mode 100644 index 0000000..99ad16d --- /dev/null +++ b/src/main/java/utils/AddGaussian.java @@ -0,0 +1,148 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package utils; + +import ij.IJ; +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +public class AddGaussian { + + + final public static & NativeType> void addGaussian( final RandomAccessibleInterval< FloatType > image, final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + final double[] two_sq_sigma = new double[ numDimensions ]; + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = 2 * getSuggestedKernelDiameter( sigma[ d ] ); + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ] / 2; + max[ d ] = min[ d ] + size[ d ] - 1; + two_sq_sigma[ d ] = sigma[ d ] * sigma[ d ]; + } + + final RandomAccessible< FloatType > infinite = Views.extendZero( image ); + final RandomAccessibleInterval< FloatType > interval = Views.interval( infinite, min, max ); + final IterableInterval< FloatType > iterable = Views.iterable( interval ); + final Cursor< FloatType > cursor = iterable.localizingCursor(); + + + + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = 1; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getDoublePosition( d ); + value *= Math.exp( -(x * x) / two_sq_sigma[ d ] ); + + + } + + + cursor.get().set( cursor.get().get() + (float)value ); + + + } + + + + + } + + + + + + + final public static void addGaussian( final IterableInterval< FloatType > image, final double Amplitude, + final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = getSuggestedKernelDiameter( sigma[ d ] ) * 2; + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ]/2; + max[ d ] = min[ d ] + size[ d ] - 1; + + } + + + final Cursor< FloatType > cursor = image.localizingCursor(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = Amplitude; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getIntPosition( d ); + value *= Math.exp( -(x * x) / (sigma[ d ] * sigma[ d ] ) ); + } + + + cursor.get().set( cursor.get().get() + (float)value ); + + + } + + } + + public static int getSuggestedKernelDiameter( final double sigma ) + { + int size = 0; + int cutoff = 5; // This number means cutoff is chosen to be cutoff times sigma. + if ( sigma > 0 ) + size = Math.max( cutoff, ( 2 * ( int ) ( cutoff * sigma + 0.5 ) + 1 ) ); + + return size; + } + + + + + +} diff --git a/src/main/java/utils/DistanceMetrics.java b/src/main/java/utils/DistanceMetrics.java new file mode 100644 index 0000000..0966fe0 --- /dev/null +++ b/src/main/java/utils/DistanceMetrics.java @@ -0,0 +1,19 @@ +package utils; + +public class DistanceMetrics { + + + public static double StraightLineDistance(final double[] cordone, final double[] cordtwo) { + + double distance = 0; + final double ndims = cordone.length; + + for (int d = 0; d < ndims; ++d) { + + distance += Math.pow((cordone[d] - cordtwo[d]), 2); + + } + return Math.sqrt(distance); + } + +} diff --git a/src/main/java/varun_algorithm/Algorithm.java b/src/main/java/varun_algorithm/Algorithm.java new file mode 100644 index 0000000..b2ecc52 --- /dev/null +++ b/src/main/java/varun_algorithm/Algorithm.java @@ -0,0 +1,50 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +/** + * TODO This interface should extend Runnable + * + * @author Stephan Preibisch + * @author Stephan Saalfeld + */ +public interface Algorithm +{ + public boolean checkInput(); + + public boolean process(); + + public String getErrorMessage(); +} diff --git a/src/main/java/varun_algorithm/Benchmark.java b/src/main/java/varun_algorithm/Benchmark.java new file mode 100644 index 0000000..542d57a --- /dev/null +++ b/src/main/java/varun_algorithm/Benchmark.java @@ -0,0 +1,45 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public interface Benchmark +{ + public long getProcessingTime(); +} diff --git a/src/main/java/varun_algorithm/BenchmarkAlgorithm.java b/src/main/java/varun_algorithm/BenchmarkAlgorithm.java new file mode 100644 index 0000000..da6850d --- /dev/null +++ b/src/main/java/varun_algorithm/BenchmarkAlgorithm.java @@ -0,0 +1,68 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +import net.imglib2.algorithm.Algorithm; +import net.imglib2.algorithm.Benchmark; + +/** + * This is a convenience implementation of an algorithm that implements + * {@link Benchmark} and {@link Algorithm} so that less code has to be + * re-implemented. + * + * IMPORTANT: It is not meant to be used for any other purpose than that, it + * should not be demanded by any other method or generic construct, use the + * interfaces instead. + * + * @author Stephan Preibisch + */ +public abstract class BenchmarkAlgorithm implements Benchmark, Algorithm +{ + protected long processingTime = -1; + + protected String errorMessage = ""; + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } +} diff --git a/src/main/java/varun_algorithm/MultiThreaded.java b/src/main/java/varun_algorithm/MultiThreaded.java new file mode 100644 index 0000000..c98c409 --- /dev/null +++ b/src/main/java/varun_algorithm/MultiThreaded.java @@ -0,0 +1,63 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public interface MultiThreaded +{ + /** + * Sets the number of threads to the amount of processors available + */ + public void setNumThreads(); + + /** + * Sets the number of threads + * + * @param numThreads + * - number of threads to use + */ + public void setNumThreads( final int numThreads ); + + /** + * The number of threads used by the algorithm + * + * @return - the number of threads + */ + public int getNumThreads(); +} diff --git a/src/main/java/varun_algorithm/MultiThreadedAlgorithm.java b/src/main/java/varun_algorithm/MultiThreadedAlgorithm.java new file mode 100644 index 0000000..302aa51 --- /dev/null +++ b/src/main/java/varun_algorithm/MultiThreadedAlgorithm.java @@ -0,0 +1,85 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +import net.imglib2.algorithm.Algorithm; +import net.imglib2.algorithm.MultiThreaded; + +/** + * This is a convenience implementation of an algorithm that implements + * {@link MultiThreaded} and {@link Algorithm} so that less code has to be + * re-implemented. + * + * IMPORTANT: It is not meant to be used for any other purpose than that, it + * should not be demanded by any other method or generic construct, use the + * interfaces instead. + * + * @author Stephan Preibisch + */ +public abstract class MultiThreadedAlgorithm implements MultiThreaded, Algorithm +{ + protected int numThreads; + + protected String errorMessage = ""; + + public MultiThreadedAlgorithm() + { + setNumThreads(); + } + + @Override + public void setNumThreads() + { + this.numThreads = Runtime.getRuntime().availableProcessors(); + } + + @Override + public void setNumThreads( final int numThreads ) + { + this.numThreads = numThreads; + } + + @Override + public int getNumThreads() + { + return numThreads; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } +} diff --git a/src/main/java/varun_algorithm/MultiThreadedBenchmarkAlgorithm.java b/src/main/java/varun_algorithm/MultiThreadedBenchmarkAlgorithm.java new file mode 100644 index 0000000..b3c45d0 --- /dev/null +++ b/src/main/java/varun_algorithm/MultiThreadedBenchmarkAlgorithm.java @@ -0,0 +1,67 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +import net.imglib2.algorithm.Algorithm; +import net.imglib2.algorithm.Benchmark; +import net.imglib2.algorithm.MultiThreaded; +import net.imglib2.algorithm.MultiThreadedAlgorithm; + +/** + * This is a convenience implementation of an algorithm that implements + * {@link MultiThreaded}, {@link Benchmark} and {@link Algorithm} so that less + * code has to be re-implemented. + * + * IMPORTANT: It is not meant to be used for any other purpose than that, it + * should not be demanded by any other method or generic construct, use the + * interfaces instead. + * + * @author Stephan Preibisch + */ +public abstract class MultiThreadedBenchmarkAlgorithm extends MultiThreadedAlgorithm implements Benchmark +{ + protected long processingTime = -1; + + public MultiThreadedBenchmarkAlgorithm() + { + super(); + } + + @Override + public long getProcessingTime() + { + return processingTime; + } +} diff --git a/src/main/java/varun_algorithm/OutputAlgorithm.java b/src/main/java/varun_algorithm/OutputAlgorithm.java new file mode 100644 index 0000000..88f0a7b --- /dev/null +++ b/src/main/java/varun_algorithm/OutputAlgorithm.java @@ -0,0 +1,49 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm; + +import net.imglib2.algorithm.Algorithm; + +/** + * TODO This interface should be empty and extend {@code Callable}, + * {@link Algorithm}. + * + * @author Stephan Preibisch + * @author Stephan Saalfeld + */ +public interface OutputAlgorithm< F > extends Algorithm +{ + public F getResult(); +} diff --git a/src/main/java/varun_algorithm_binary/Thresholder.java b/src/main/java/varun_algorithm_binary/Thresholder.java new file mode 100644 index 0000000..60b0157 --- /dev/null +++ b/src/main/java/varun_algorithm_binary/Thresholder.java @@ -0,0 +1,167 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_binary; + +import java.util.Vector; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.converter.Converter; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import varun_multithreading.Chunk; +import varun_multithreading.SimpleMultiThreading; + +/** + * Collection of static utilities meant to generate {@link BitType} images from + * {@link Comparable} images. + * + * @author Jean-Yves Tinevez + */ +public class Thresholder +{ + + /** + * Returns a new boolean {@link Img} generated by thresholding the values of + * the source image. + * + * @param source + * the image to threshold. + * @param threshold + * the threshold. + * @param above + * if {@code true}, the target value will be true for source + * values above the threshold, {@code false} otherwise. + * @param numThreads + * the number of threads to use for thresholding. + * @return a new {@link Img} of type {@link BitType} and of same dimension + * that the source image. + */ + public static final < T extends Type< T > & Comparable< T >> Img< BitType > threshold( final Img< T > source, final T threshold, final boolean above, final int numThreads ) + { + final ImgFactory< T > factory = source.factory(); + try + { + final ImgFactory< BitType > bitFactory = factory.imgFactory( new BitType() ); + final Img< BitType > target = bitFactory.create( source, new BitType() ); + + final Converter< T, BitType > converter; + if ( above ) + { + converter = new Converter< T, BitType >() + { + @Override + public void convert( final T input, final BitType output ) + { + output.set( input.compareTo( threshold ) > 0 ); + } + }; + } + else + { + converter = new Converter< T, BitType >() + { + @Override + public void convert( final T input, final BitType output ) + { + output.set( input.compareTo( threshold ) < 0 ); + } + }; + } + + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + if ( target.iterationOrder().equals( source.iterationOrder() ) ) + { + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Thresholder thread " + i ) + { + @Override + public void run() + { + final Cursor< BitType > cursorTarget = target.cursor(); + cursorTarget.jumpFwd( chunk.getStartPosition() ); + final Cursor< T > cursorSource = source.cursor(); + cursorSource.jumpFwd( chunk.getStartPosition() ); + for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) + { + cursorTarget.fwd(); + cursorSource.fwd(); + converter.convert( cursorSource.get(), cursorTarget.get() ); + } + } + }; + } + } + else + { + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Thresholder thread " + i ) + { + @Override + public void run() + { + final Cursor< BitType > cursorTarget = target.cursor(); + cursorTarget.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > ra = source.randomAccess( target ); + for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) + { + cursorTarget.fwd(); + ra.setPosition( cursorTarget ); + converter.convert( ra.get(), cursorTarget.get() ); + } + } + }; + } + } + + SimpleMultiThreading.startAndJoin( threads ); + return target; + } + catch ( final IncompatibleTypeException e ) + { + e.printStackTrace(); + return null; + } + } + +} diff --git a/src/main/java/varun_algorithm_componenttree/BuildComponentTree.java b/src/main/java/varun_algorithm_componenttree/BuildComponentTree.java new file mode 100644 index 0000000..6d80437 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/BuildComponentTree.java @@ -0,0 +1,488 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree; + +import java.util.ArrayDeque; +import java.util.Comparator; +import java.util.PriorityQueue; + +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import varun_algorithm_componenttree.pixellist.PixelListComponentTree; + +/** + * Build the component tree of an image. This is an implementation of the + * algorithm described by D. Nister and H. Stewenius in "Linear Time Maximally + * Stable Extremal Regions" (ECCV 2008). + *

      + * The input to the algorithm is a {@code RandomAccessibleInterval< T >}. + * Further, a {@code Comparator} and a {@link PartialComponent.Generator} to + * instantiate new components are required. Pixel locations are aggregated in + * {@link PartialComponent}s which are passed to a + * {@link PartialComponent.Handler} whenever a connected component for a + * specific threshold is completed. + *

      + *

      + * Building up a tree structure out of the completed components should happen in + * the {@link PartialComponent.Handler} implementation. See + * {@link PixelListComponentTree} for an example. + *

      + *

      + * TODO Add support for non-zero-min RandomAccessibleIntervals. + * (Currently, we assume that the input image is a zero-min interval.) + *

      + * + * @param + * value type of the input image. + * @param + * component type. + * + * @author Tobias Pietzsch + */ +public final class BuildComponentTree< T extends Type< T >, C extends PartialComponent< T, C > > +{ + /** + * Run the algorithm. Completed components are emitted to the + * {@link PartialComponent.Handler} which is responsible for building up the + * tree structure. An implementations of {@link PartialComponent.Handler} is + * provided for example by {@link PixelListComponentTree}. + * + * @param input + * input image. + * @param componentGenerator + * provides new {@link PartialComponent} instances. + * @param componentHandler + * receives completed {@link PartialComponent}s. + * @param comparator + * determines ordering of threshold values. + */ + public static < T extends Type< T >, C extends PartialComponent< T, C > > void buildComponentTree( + final RandomAccessibleInterval< T > input, + final PartialComponent.Generator< T, C > componentGenerator, + final PartialComponent.Handler< C > componentHandler, + final Comparator< T > comparator ) + { + new BuildComponentTree< T, C >( input, componentGenerator, componentHandler, comparator ); + } + + /** + * Run the algorithm. Completed components are emitted to the + * {@link PartialComponent.Handler} which is responsible for building up the + * tree structure. An implementations of {@link PartialComponent.Handler} is + * provided for example by {@link PixelListComponentTree}. + * + * @param input + * input image of a comparable value type. + * @param componentGenerator + * provides new {@link PartialComponent} instances. + * @param componentHandler + * receives completed {@link PartialComponent}s. + * @param darkToBright + * determines ordering of threshold values. If it is true, then + * thresholds are applied from low to high values. Note that the + * {@link PartialComponent.Generator#createMaxComponent()} needs + * to match this ordering. For example when IntType using + * darkToBright=false, then + * {@link PartialComponent.Generator#createMaxComponent()} should + * provide a Integer.MIN_VALUE valued component. + */ + public static < T extends Type< T > & Comparable< T >, C extends PartialComponent< T, C > > void buildComponentTree( + final RandomAccessibleInterval< T > input, + final PartialComponent.Generator< T, C > componentGenerator, + final PartialComponent.Handler< C > componentHandler, + final boolean darkToBright ) + { + new BuildComponentTree< T, C >( input, componentGenerator, componentHandler, darkToBright ? new DarkToBright< T >() : new BrightToDark< T >() ); + } + + /** + * Default comparator for {@link Comparable} pixel values for dark-to-bright + * pass. + */ + public static final class DarkToBright< T extends Comparable< T > > implements Comparator< T > + { + @Override + public int compare( final T o1, final T o2 ) + { + return o1.compareTo( o2 ); + } + } + + /** + * Default comparator for {@link Comparable} pixel values for bright-to-dark + * pass. + */ + public static final class BrightToDark< T extends Comparable< T > > implements Comparator< T > + { + @Override + public int compare( final T o1, final T o2 ) + { + return o2.compareTo( o1 ); + } + } + + /** + * Iterate pixel positions in 4-neighborhood. + */ + private static final class Neighborhood + { + /** + * index of the next neighbor to visit. 0 is pixel at x-1, 1 is pixel at + * x+1, 2 is pixel at y-1, 3 is pixel at y+1, and so on. + */ + private int n; + + /** + * number of neighbors, e.g., 4 for 2d images. + */ + private final int nBound; + + /** + * image dimensions. used to check out-of-bounds. + */ + final long[] dimensions; + + public Neighborhood( final long[] dim ) + { + n = 0; + nBound = dim.length * 2; + dimensions = dim; + } + + public int getNextNeighborIndex() + { + return n; + } + + public void setNextNeighborIndex( final int n ) + { + this.n = n; + } + + public void reset() + { + n = 0; + } + + public boolean hasNext() + { + return n < nBound; + } + + /** + * Set neighbor to the next (according to + * {@link BuildComponentTree.Neighborhood#n}) neighbor position of + * current. Assumes that prior to any call to next() neighbor was a the + * same position as current, i.e. neighbor position is only modified + * incrementally. + * + * @param current + * @param neighbor + * @return false if the neighbor position is out of bounds, true + * otherwise. + */ + public boolean next( final Localizable current, final Positionable neighbor, final Positionable neighbor2 ) + { + final int d = n / 2; + final boolean bck = ( n == 2 * d ); // n % 2 == 0 + ++n; + if ( bck ) + { + if ( d > 0 ) + { + neighbor.setPosition( current.getLongPosition( d - 1 ), d - 1 ); + neighbor2.setPosition( current.getLongPosition( d - 1 ), d - 1 ); + } + final long dpos = current.getLongPosition( d ) - 1; + neighbor.setPosition( dpos, d ); + neighbor2.setPosition( dpos, d ); + return dpos >= 0; + } + else + { + final long dpos = current.getLongPosition( d ) + 1; + neighbor.setPosition( dpos, d ); + neighbor2.setPosition( dpos, d ); + return dpos < dimensions[ d ]; + } + } + } + + /** + * A pixel position on the heap of boundary pixels to be processed next. The + * heap is sorted by pixel values. + */ + private final class BoundaryPixel extends Point implements Comparable< BoundaryPixel > + { + private final T value; + + // TODO: this should be some kind of iterator over the neighborhood + private int nextNeighborIndex; + + public BoundaryPixel( final Localizable position, final T value, final int nextNeighborIndex ) + { + super( position ); + this.nextNeighborIndex = nextNeighborIndex; + this.value = value.copy(); + } + + public int getNextNeighborIndex() + { + return nextNeighborIndex; + } + + public T get() + { + return value; + } + + @Override + public int compareTo( final BoundaryPixel o ) + { + return comparator.compare( value, o.value ); + } + } + + private final ArrayDeque< BoundaryPixel > reusableBoundaryPixels; + + private BoundaryPixel createBoundaryPixel( final Localizable position, final T value, final int nextNeighborIndex ) + { + if ( reusableBoundaryPixels.isEmpty() ) + return new BoundaryPixel( position, value, nextNeighborIndex ); + else + { + final BoundaryPixel p = reusableBoundaryPixels.pop(); + p.setPosition( position ); + p.value.set( value ); + p.nextNeighborIndex = nextNeighborIndex; + return p; + } + } + + private void freeBoundaryPixel( final BoundaryPixel p ) + { + reusableBoundaryPixels.push( p ); + } + + private final PartialComponent.Generator< T, C > componentGenerator; + + private final PartialComponent.Handler< C > componentOutput; + + private final Neighborhood neighborhood; + + private final RandomAccessible< BitType > visited; + + private final RandomAccess< BitType > visitedRandomAccess; + + private final PriorityQueue< BoundaryPixel > boundaryPixels; + + private final ArrayDeque< C > componentStack; + + private final Comparator< T > comparator; + + /** + * Set up data structures and run the algorithm. Completed components are + * emitted to the provided {@link PartialComponent.Handler}. + * + * @param input + * input image. + * @param componentGenerator + * provides new {@link PartialComponent} instances. + * @param componentOutput + * receives completed {@link PartialComponent}s. + * @param comparator + * determines ordering of threshold values. + */ + private BuildComponentTree( final RandomAccessibleInterval< T > input, final PartialComponent.Generator< T, C > componentGenerator, final PartialComponent.Handler< C > componentOutput, final Comparator< T > comparator ) + { + reusableBoundaryPixels = new ArrayDeque< BoundaryPixel >(); + this.componentGenerator = componentGenerator; + this.componentOutput = componentOutput; + + final long[] dimensions = new long[ input.numDimensions() ]; + input.dimensions( dimensions ); + + final ImgFactory< BitType > imgFactory = new ArrayImgFactory< BitType >(); + visited = imgFactory.create( dimensions, new BitType() ); + visitedRandomAccess = visited.randomAccess(); + + neighborhood = new Neighborhood( dimensions ); + + boundaryPixels = new PriorityQueue< BoundaryPixel >(); + + componentStack = new ArrayDeque< C >(); + componentStack.push( componentGenerator.createMaxComponent() ); + + this.comparator = comparator; + + run( input ); + } + + /** + * Main loop of the algorithm. This follows exactly along steps of the + * algorithm as described in the paper. + * + * @param input + * the input image. + */ + private void run( final RandomAccessibleInterval< T > input ) + { + final RandomAccess< T > current = input.randomAccess(); + final RandomAccess< T > neighbor = input.randomAccess(); + input.min( current ); + neighbor.setPosition( current ); + visitedRandomAccess.setPosition( current ); + final T currentLevel = current.get().createVariable(); + final T neighborLevel = current.get().createVariable(); + + // Note that step numbers in the comments below refer to steps in the + // Nister & Stewenius paper. + + // step 2 + visitedRandomAccess.get().set( true ); + currentLevel.set( current.get() ); + + // step 3 + componentStack.push( componentGenerator.createComponent( currentLevel ) ); + + // step 4 + while ( true ) + { + while ( neighborhood.hasNext() ) + { + if ( !neighborhood.next( current, neighbor, visitedRandomAccess ) ) + continue; + if ( !visitedRandomAccess.get().get() ) + { + // actually we could + // visit( neighbor ); + // here. + // however, because wasVisited() already set the + // visitedRandomAccess to the correct position, this is + // faster: + visitedRandomAccess.get().set( true ); + + neighborLevel.set( neighbor.get() ); + if ( comparator.compare( neighborLevel, currentLevel ) >= 0 ) + { + boundaryPixels.add( createBoundaryPixel( neighbor, neighborLevel, 0 ) ); + } + else + { + boundaryPixels.add( createBoundaryPixel( current, currentLevel, neighborhood.getNextNeighborIndex() ) ); + current.setPosition( neighbor ); + currentLevel.set( neighborLevel ); + + // go to 3, i.e.: + componentStack.push( componentGenerator.createComponent( currentLevel ) ); + neighborhood.reset(); + } + } + } + + // step 5 + final C component = componentStack.peek(); + component.addPosition( current ); + + // step 6 + if ( boundaryPixels.isEmpty() ) + { + processStack( currentLevel ); + return; + } + + final BoundaryPixel p = boundaryPixels.poll(); + if ( comparator.compare( p.get(), currentLevel ) != 0 ) + { + // step 7 + processStack( p.get() ); + } + current.setPosition( p ); + neighbor.setPosition( current ); + visitedRandomAccess.setPosition( current ); + currentLevel.set( p.get() ); + neighborhood.setNextNeighborIndex( p.getNextNeighborIndex() ); + freeBoundaryPixel( p ); + } + } + + /** + * This is called whenever the current value is raised. + * + * @param value + */ + private void processStack( final T value ) + { + while ( true ) + { + // process component on top of stack + final C component = componentStack.pop(); + componentOutput.emit( component ); + + // get level of second component on stack + final C secondComponent = componentStack.peek(); + try + { + final int c = comparator.compare( value, secondComponent.getValue() ); + if ( c < 0 ) + { + component.setValue( value ); + componentStack.push( component ); + } + else + { + secondComponent.merge( component ); + if ( c > 0 ) + continue; + } + return; + } + catch ( final NullPointerException e ) + { + componentStack.push( component ); + return; + } + } + } +} diff --git a/src/main/java/varun_algorithm_componenttree/Component.java b/src/main/java/varun_algorithm_componenttree/Component.java new file mode 100644 index 0000000..7220bb8 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/Component.java @@ -0,0 +1,82 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree; + +import java.util.List; + +import net.imglib2.Localizable; + + +/** + * Represents a connected component and a node in a {@link ComponentTree}. The + * child and parent nodes can be accessed by {@link #getChildren()} and + * {@link #getParent()}. The set of pixels can be accessed by iterating ( + * {@link #iterator()}) the component. The threshold value that created the + * component (extremal region) can be obtained by {@link #value()}. + * + * @author Florian Jug + * @author Tobias Pietzsch + */ +public interface Component< T, C extends Component< T, C > > extends Iterable< Localizable > +{ + /** + * Get the number of pixels in the connected component. + * + * @return number of pixels in the connected component. + */ + public long size(); + + /** + * Get the image threshold that created the connected component (extremal + * region). + * + * @return the image threshold that created the connected component. + */ + public T value(); + + /** + * Get the parent of this node in the {@link ComponentTree}. + * + * @return the parent of this node in the {@link ComponentTree}. + */ + public C getParent(); + + /** + * Get the children of this node in the {@link ComponentTree}. + * + * @return the children of this node in the {@link ComponentTree}. + */ + public List< C > getChildren(); +} diff --git a/src/main/java/varun_algorithm_componenttree/ComponentForest.java b/src/main/java/varun_algorithm_componenttree/ComponentForest.java new file mode 100644 index 0000000..f7b9489 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/ComponentForest.java @@ -0,0 +1,57 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree; + +import java.util.Set; + + +/** + * A component forest. + * + * @param + * component type + * + * @author Tobias Pietzsch + * @author Florian Jug + */ +public interface ComponentForest< C extends Component< ?, C > > +{ + /** + * Get the set of root nodes of this component forest. + * + * @return set of roots. + */ + public Set< C > roots(); +} diff --git a/src/main/java/varun_algorithm_componenttree/ComponentTree.java b/src/main/java/varun_algorithm_componenttree/ComponentTree.java new file mode 100644 index 0000000..32e5efd --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/ComponentTree.java @@ -0,0 +1,55 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree; + + +/** + * A component tree. + * + * @param + * component type + * + * @author Tobias Pietzsch + * @author Florian Jug + */ +public interface ComponentTree< C extends Component< ?, C > > extends ComponentForest< C > +{ + /** + * Get the root component. + * + * @return root component. + */ + public C root(); +} diff --git a/src/main/java/varun_algorithm_componenttree/PartialComponent.java b/src/main/java/varun_algorithm_componenttree/PartialComponent.java new file mode 100644 index 0000000..856eba9 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/PartialComponent.java @@ -0,0 +1,144 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree; + +import net.imglib2.Localizable; + + + +/** + * This interface is used by {@link BuildComponentTree} to build the component + * tree of an image. In the algorithm described by D. Nister and H. Stewenius in + * "Linear Time Maximally Stable Extremal Regions" (ECCV 2008) a stack of + * incomplete components is maintained while visiting the pixels of the input + * image. {@link PartialComponent} represents an element on the component stack, + * i.e., a connected component in the making. + * + * It provides methods to get/set the threshold value for the connected + * component, to add pixels to the component, and to merge it with another + * component. + * + * {@link BuildComponentTree} uses a {@link PartialComponent.Generator} to + * create new components and emits completed components to a + * {@link PartialComponent.Handler}. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public interface PartialComponent< T, C extends PartialComponent< T, C > > +{ + /** + * Create new components. + * + * @param + * value type of the input image. + * @param + * component type. + */ + public interface Generator< T, C extends PartialComponent< T, C > > + { + /** + * Create a new empty component with the given value (e.g., grey-level). + * + * @param value + * value of the component + * @return new component + */ + public C createComponent( final T value ); + + /** + * Create a component with a value (e.g., grey-level) greater than any + * occurring in the input for the {@link BuildComponentTree}. This is + * used as a terminator element on the component stack. + * + * @return new component + */ + public C createMaxComponent(); + } + + /** + * Handle completed components that are output by {@link BuildComponentTree} + * . + * + * @param + * component type. + */ + public interface Handler< C > + { + /** + * {@link BuildComponentTree} calls this for every completed component. + * NOTE THAT THE COMPONENT IS RE-USED BY {@link BuildComponentTree}! + * That is, after calling emit() new pixels may be added, etc. Do not + * store the component object but rather copy the relevant data! + * + * @param component + * a completed component + */ + public void emit( C component ); + } + + /** + * Set the threshold value (e.g., grey-level) for this component. + * + * @param value + * the threshold value + */ + public abstract void setValue( final T value ); + + /** + * Get the threshold value (e.g., grey-level) for this component. + * + * @return the threshold value + */ + public abstract T getValue(); + + /** + * Add a pixel to the set of pixels represented by this component. + * + * @param position + * a pixel position + */ + public abstract void addPosition( final Localizable position ); + + /** + * Merge other component (of the same concrete type) into this component. + * + * @param component + * the other component + */ + public abstract void merge( final C component ); +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/ComputeDelta.java b/src/main/java/varun_algorithm_componenttree/mser/ComputeDelta.java new file mode 100644 index 0000000..26aa47f --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/ComputeDelta.java @@ -0,0 +1,59 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + + +/** + * For a given threshold value compute the threshold value delta steps down the + * component tree. This might mean addition or subtraction, depending on whether + * it's a dark-to-bright or bright-to-dark pass. + * + * @see ComputeDeltaBrightToDark + * @see ComputeDeltaDarkToBright + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public interface ComputeDelta< T > +{ + /** + * Compute the threshold = (value - delta). + * + * @return (value - delta) + */ + public T valueMinusDelta( final T value ); +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaBrightToDark.java b/src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaBrightToDark.java new file mode 100644 index 0000000..da81dde --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaBrightToDark.java @@ -0,0 +1,66 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + + +import net.imglib2.type.numeric.NumericType; + +/** + * Default implementation of {@link ComputeDelta} for bright-to-dark pass for + * {@link NumericType}. For a given threshold value a compute the + * threshold value delta steps down the component tree: a+delta. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public final class ComputeDeltaBrightToDark< T extends NumericType< T > > implements ComputeDelta< T > +{ + private final T delta; + + ComputeDeltaBrightToDark( final T delta ) + { + this.delta = delta; + } + + @Override + public T valueMinusDelta( final T value ) + { + final T valueMinus = value.copy(); + valueMinus.add( delta ); + return valueMinus; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaDarkToBright.java b/src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaDarkToBright.java new file mode 100644 index 0000000..36a3b4d --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/ComputeDeltaDarkToBright.java @@ -0,0 +1,65 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + +import net.imglib2.type.numeric.NumericType; + +/** + * Default implementation of {@link ComputeDelta} for dark-to-bright pass for + * {@link NumericType}. For a given threshold value a compute the + * threshold value delta steps down the component tree: a-delta. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public final class ComputeDeltaDarkToBright< T extends NumericType< T > > implements ComputeDelta< T > +{ + private final T delta; + + ComputeDeltaDarkToBright( final T delta ) + { + this.delta = delta; + } + + @Override + public T valueMinusDelta( final T value ) + { + final T valueMinus = value.copy(); + valueMinus.sub( delta ); + return valueMinus; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/Mser.java b/src/main/java/varun_algorithm_componenttree/mser/Mser.java new file mode 100644 index 0000000..d46f7c7 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/Mser.java @@ -0,0 +1,196 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + +import java.util.ArrayList; +import java.util.Iterator; + +import net.imglib2.Localizable; + +import net.imglib2.type.Type; +import varun_algorithm_componenttree.Component; +import varun_algorithm_componenttree.pixellist.PixelList; + +/** + * A maximally stable extremal region (MSER) of the image thresholded at + * {@link #value()}. The set of pixels can be accessed by iterating ( + * {@link #iterator()}) the component. + * + * This is a node in a {@link MserTree}. The child and parent nodes can be + * accessed by {@link #getChildren()} and {@link #getParent()}. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public final class Mser< T extends Type< T > > implements Component< T, Mser< T > > +{ + /** + * child nodes in the {@link MserTree}. + */ + final ArrayList< Mser< T > > children; + + /** + * parent node in the {@link MserTree}. + */ + Mser< T > parent; + + /** + * Threshold value of the connected component. + */ + private final T value; + + /** + * Pixels in the component. + */ + private final PixelList pixelList; + + /** + * MSER score : |R_i - R_{i-\Delta}| / |R_i|. + */ + private final double score; + + /** + * Mean of the pixel positions in the region. + */ + private final double[] mean; + + /** + * Covariance of the pixel positions in the region. + */ + private final double[] cov; + + Mser( final MserEvaluationNode< T > node ) + { + children = new ArrayList< Mser< T > >(); + parent = null; + + value = node.value; + score = node.score; + pixelList = node.pixelList; + mean = node.mean; + cov = node.cov; + } + + /** + * Get the image threshold that created the extremal region. + * + * @return the image threshold that created the extremal region. + */ + @Override + public T value() + { + return value; + } + + /** + * Get the number of pixels in the extremal region. + * + * @return number of pixels in the extremal region. + */ + @Override + public long size() + { + return pixelList.size(); + } + + /** + * The MSER score is computed as |R_i - R_{i-\Delta}| / |R_i|. + * + * @return the MSER score. + */ + public double score() + { + return score; + } + + /** + * Mean of the pixel positions in the region. This is a position vector (x, + * y, z, ...) + * + * @return mean vector. + */ + public double[] mean() + { + return mean; + } + + /** + * Covariance of the pixel positions in the region. This is a vector of the + * independent elements of the covariance matrix (xx, xy, xz, ..., yy, yz, + * ..., zz, ...) + * + * @return vector of covariance elements. + */ + public double[] cov() + { + return cov; + } + + /** + * Get an iterator over the pixel locations ({@link Localizable}) in this + * connected component. + * + * @return iterator over locations. + */ + @Override + public Iterator< Localizable > iterator() + { + return pixelList.iterator(); + } + + /** + * Get the children of this node in the {@link MserTree}. + * + * @return the children of this node in the {@link MserTree}. + */ + @Override + public ArrayList< Mser< T > > getChildren() + { + return children; + } + + /** + * Get the parent of this node in the {@link MserTree}. + * + * @return the parent of this node in the {@link MserTree}. + */ + @Override + public Mser< T > getParent() + { + return parent; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/MserEvaluationNode.java b/src/main/java/varun_algorithm_componenttree/mser/MserEvaluationNode.java new file mode 100644 index 0000000..11ccc38 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/MserEvaluationNode.java @@ -0,0 +1,314 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + +import java.util.ArrayList; +import java.util.Comparator; + + +import net.imglib2.type.Type; +import varun_algorithm_componenttree.pixellist.PixelList; + +/** + * Store {@link PixelList}, mean, covariance, instability score, parent, and + * children of a extremal region. A tree of {@link MserEvaluationNode} is built + * from emitted {@link MserPartialComponent}. As soon as the parent of a node is + * available, it is checked whether the instability score is a local minimum. In + * this case, it is passed to + * {@link MserTree#foundNewMinimum(MserEvaluationNode)}, where a MSER is + * created. + * + * We construct the component tree for generic types, which means that we cannot + * raise the threshold values in steps of 1 (value type might be continuous or + * non-numeric). To create a tree whose every branch covers a continuous range + * of values, two nodes are created for every {@link MserPartialComponent}. + * These mark the range of values covered by that component. The first node is + * called the "direct" or "non-intermediate node" and is + * created, when the {@link MserPartialComponent} is emitted. It marks the lower + * bound (inclusive) of the value range. When the parent of the + * {@link MserPartialComponent} is emitted, the "intermediate node" is + * created which covers the same pixels as the direct node but marks the upper + * bound (exclusive) of the value range. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +final class MserEvaluationNode< T extends Type< T > > +{ + /** + * Threshold value of the connected component. + */ + final T value; + + /** + * Size (number of pixels) of the connected component. + */ + final long size; + + /** + * Pixels in the component. + */ + final PixelList pixelList; + + /** + * The child in the component tree from which we inherit the component size + * history. + */ + private final MserEvaluationNode< T > historyChild; + + /** + * Parent of this {@link MserEvaluationNode} in the component tree. + */ + private MserEvaluationNode< T > parent; + + /** + * MSER score : |R_i - R_{i-\Delta}| / |R_i|. + */ + double score; + + /** + * Whether the {@link #score} is valid. (Otherwise it has not or cannot be + * computed.) + */ + private final boolean isScoreValid; + + /** + * Number of dimensions in the image. + */ + final int n; + + /** + * Mean of pixel positions (x, y, z, ...). + */ + final double[] mean; + + /** + * Independent elements of the covariance of pixel positions (xx, xy, xz, + * ..., yy, yz, ..., zz, ...). + */ + final double[] cov; + + /** + * {@link Mser}s associated to this region or its children. To build up the + * MSER tree. + */ + final ArrayList< Mser< T > > mserThisOrChildren; + + MserEvaluationNode( final MserPartialComponent< T > component, final Comparator< T > comparator, final ComputeDelta< T > delta, final MserTree< T > tree ) + { + value = component.getValue().copy(); + pixelList = new PixelList( component.pixelList ); + size = pixelList.size(); + + final ArrayList< MserEvaluationNode< T > > children = new ArrayList< MserEvaluationNode< T > >(); + MserEvaluationNode< T > node = component.getEvaluationNode(); + long historySize = 0; + if ( node != null ) + { + historySize = node.size; + // create intermediate MserEvaluationNode between last emitted and this node. + node = new MserEvaluationNode< T >( node, value, comparator, delta ); + children.add( node ); + node.setParent( this ); + } + + MserEvaluationNode< T > historyWinner = node; + for ( final MserPartialComponent< T > c : component.children ) + { + // create intermediate MserEvaluationNode between child and this node. + node = new MserEvaluationNode< T >( c.getEvaluationNode(), value, comparator, delta ); + children.add( node ); + node.setParent( this ); + if ( c.size() > historySize ) + { + historyWinner = node; + historySize = c.size(); + } + } + + historyChild = historyWinner; + + n = component.n; + mean = new double[ n ]; + cov = new double[ ( n * (n+1) ) / 2 ]; + for ( int i = 0; i < n; ++i ) + mean[ i ] = component.sumPos[ i ] / size; + int k = 0; + for ( int i = 0; i < n; ++i ) + for ( int j = i; j < n; ++j ) + { + cov[ k ] = component.sumSquPos[ k ] / size - mean[ i ] * mean[ j ]; + ++k; + } + + component.setEvaluationNode( this ); + isScoreValid = computeMserScore( delta, comparator, false ); + if ( isScoreValid ) + for ( final MserEvaluationNode< T > a : children ) + a.evaluateLocalMinimum( tree, delta, comparator ); + + if ( children.size() == 1 ) + mserThisOrChildren = children.get( 0 ).mserThisOrChildren; + else + { + mserThisOrChildren = new ArrayList< Mser< T > >(); + for ( final MserEvaluationNode< T > a : children ) + mserThisOrChildren.addAll( a.mserThisOrChildren ); + } + } + + private MserEvaluationNode( final MserEvaluationNode< T > child, final T value, final Comparator< T > comparator, final ComputeDelta< T > delta ) + { + child.setParent( this ); + + historyChild = child; + size = child.size; + pixelList = child.pixelList; + this.value = value; + n = child.n; + mean = child.mean; + cov = child.cov; + + isScoreValid = computeMserScore( delta, comparator, true ); +// All our children are non-intermediate, and +// non-intermediate nodes are never minimal because their score is +// never smaller than that of the parent intermediate node. +// if ( isScoreValid ) +// child.evaluateLocalMinimum( minimaProcessor, delta ); + + mserThisOrChildren = child.mserThisOrChildren; + } + + private void setParent( final MserEvaluationNode< T > node ) + { + parent = node; + } + + /** + * Evaluate the mser score at this connected component. This may fail if the + * connected component tree is not built far enough down from the current + * node. The mser score is computed as |R_i - R_{i-\Delta}| / |R_i|, where + * R_i is this component and R_{i-delta} is the component delta steps down + * the component tree (threshold level is delta lower than this). + * + * @param delta + * @param isIntermediate + * whether this is an intermediate node. This influences the + * search for the R_{i-delta} in the following way. If a node + * with value equal to i-delta is found, then this is a + * non-intermediate node and there is an intermediate node with + * the same value below it. If isIntermediate is true R_{i-delta} + * is set to the intermediate node. (The other possibility is, + * that we find a node with value smaller than i-delta, i.e., + * there is no node with that exact value. In this case, + * isIntermediate has no influence.) + */ + private boolean computeMserScore( final ComputeDelta< T > delta, final Comparator< T > comparator, final boolean isIntermediate ) + { + // we are looking for a precursor node with value == (this.value - delta) + final T valueMinus = delta.valueMinusDelta( value ); + + // go back in history until we find a node with (value <= valueMinus) + MserEvaluationNode< T > node = historyChild; + while ( node != null && comparator.compare( node.value, valueMinus ) > 0 ) + node = node.historyChild; + if ( node == null ) + // we cannot compute the mser score because the history is too short. + return false; + if ( isIntermediate && comparator.compare( node.value, valueMinus ) == 0 && node.historyChild != null ) + node = node.historyChild; + score = ( size - node.size ) / ( ( double ) size ); + return true; + } + + /** + * Check whether the mser score is a local minimum at this connected + * component. This may fail if the mser score for this component, or the + * previous one in the branch are not available. (Note, that this is only + * called, when the mser score for the next component in the branch is + * available.) + */ + private void evaluateLocalMinimum( final MserTree< T > tree, final ComputeDelta< T > delta, final Comparator< T > comparator ) + { + if ( isScoreValid ) + { + MserEvaluationNode< T > below = historyChild; + while ( below.isScoreValid && below.size == size ) + below = below.historyChild; + if ( below.isScoreValid ) + { + below = below.historyChild; + if ( ( score <= below.score ) && ( score < parent.score ) ) + tree.foundNewMinimum( this ); + } + else + { + final T valueMinus = delta.valueMinusDelta( value ); + if ( comparator.compare( valueMinus, below.value ) > 0 ) + // we are just above the bottom of a branch and this components + // value is high enough above the bottom value to make its score=0. + // so let's pretend we found a minimum here... + tree.foundNewMinimum( this ); + } + } + } + + @Override + public String toString() + { + String s = "SimpleMserEvaluationNode"; + s += ", size = " + size; + s += ", history = ["; + MserEvaluationNode< T > node = historyChild; + boolean first = true; + while ( node != null ) + { + if ( first ) + first = false; + else + s += ", "; + s += "(" + node.value + "; " + node.size; + if ( node.isScoreValid ) + s += " s " + node.score + ")"; + else + s += " s --)"; + node = node.historyChild; + } + s += "]"; + return s; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/MserPartialComponent.java b/src/main/java/varun_algorithm_componenttree/mser/MserPartialComponent.java new file mode 100644 index 0000000..76e2262 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/MserPartialComponent.java @@ -0,0 +1,206 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + +import java.util.ArrayList; + +import net.imglib2.Localizable; + +import net.imglib2.type.Type; +import varun_algorithm_componenttree.PartialComponent; +import varun_algorithm_componenttree.pixellist.PixelList; + +/** + * Implementation of {@link PartialComponent} that stores a list of associated pixels + * in a {@link PixelList}, maintains running sums over pixel positions to + * compute mean and covariance. It keeps track of which components were merged + * into this since it was last emitted (this is used to establish region size + * history). + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +final class MserPartialComponent< T extends Type< T > > implements PartialComponent< T, MserPartialComponent< T > > +{ + /** + * Threshold value of the connected component. + */ + private final T value; + + /** + * Pixels in the component. + */ + final PixelList pixelList; + + /** + * number of dimensions in the image. + */ + final int n; + + /** + * sum of pixel positions (x, y, z, ...). + */ + final double[] sumPos; + + /** + * sum of independent elements of outer product of position (xx, xy, xz, ..., yy, yz, ..., zz, ...). + */ + final double[] sumSquPos; + + private final long[] tmp; + + /** + * A list of {@link MserPartialComponent} merged into this one since it + * was last emitted. (For building up MSER evaluation structure.) + */ + ArrayList< MserPartialComponent< T > > children; + + /** + * The {@link MserEvaluationNode} assigned to this + * {@link MserPartialComponent} when it was last emitted. (For building up + * MSER evaluation structure.) + */ + MserEvaluationNode< T > evaluationNode; + + /** + * Create new empty component. + * + * @param value + * (initial) threshold value {@see #getValue()}. + * @param generator + * the {@link MserPartialComponentGenerator#linkedList} is used to store + * the {@link #pixelList}. + */ + MserPartialComponent( final T value, final MserPartialComponentGenerator< T > generator ) + { + pixelList = new PixelList( generator.linkedList.randomAccess(), generator.dimensions ); + n = generator.dimensions.length; + sumPos = new double[ n ]; + sumSquPos = new double[ ( n * (n+1) ) / 2 ]; + this.value = value.copy(); + this.children = new ArrayList< MserPartialComponent< T > >(); + this.evaluationNode = null; + tmp = new long[ n ]; + } + + @Override + public void addPosition( final Localizable position ) + { + pixelList.addPosition( position ); + position.localize( tmp ); + int k = 0; + for ( int i = 0; i < n; ++i ) + { + sumPos[ i ] += tmp[ i ]; + for ( int j = i; j < n; ++j ) + sumSquPos[ k++ ] += tmp[ i ] * tmp[ j ]; + } + } + + @Override + public T getValue() + { + return value; + } + + @Override + public void setValue( final T value ) + { + this.value.set( value ); + } + + @Override + public void merge( final MserPartialComponent< T > component ) + { + pixelList.merge( component.pixelList ); + for ( int i = 0; i < sumPos.length; ++i ) + sumPos[ i ] += component.sumPos[ i ]; + for ( int i = 0; i < sumSquPos.length; ++i ) + sumSquPos[ i ] += component.sumSquPos[ i ]; + children.add( component ); + } + + @Override + public String toString() + { + String s = "{" + value.toString() + " : "; + boolean first = true; + for ( final Localizable l : pixelList ) + { + if ( first ) + { + first = false; + } + else + { + s += ", "; + } + s += l.toString(); + } + return s + "}"; + } + + /** + * Get the number of pixels in the component. + * + * @return number of pixels in the component. + */ + long size() + { + return pixelList.size(); + } + + /** + * Get the {@link MserEvaluationNode} assigned to this + * {@link MserPartialComponent} when it was last emitted. + * + * @return {@link MserEvaluationNode} last emitted from the component. + */ + MserEvaluationNode< T > getEvaluationNode() + { + return evaluationNode; + } + + /** + * Set the {@link MserEvaluationNode} created from this + * {@link MserPartialComponent} when it is emitted. + */ + void setEvaluationNode( final MserEvaluationNode< T > node ) + { + evaluationNode = node; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/MserPartialComponentGenerator.java b/src/main/java/varun_algorithm_componenttree/mser/MserPartialComponentGenerator.java new file mode 100644 index 0000000..df6f44a --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/MserPartialComponentGenerator.java @@ -0,0 +1,104 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + +import java.util.LinkedList; + +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.integer.LongType; +import varun_algorithm_componenttree.PartialComponent; +import varun_algorithm_componenttree.pixellist.PixelList; + +/** + * Implementation of {@link PartialComponent.Generator} for creating + * {@link MserPartialComponent} components. According to the dimensions of the + * input image it creates a {@link LongType} {@link Img} to store the + * {@link PixelList} used in the components. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +final class MserPartialComponentGenerator< T extends Type< T > > implements PartialComponent.Generator< T, MserPartialComponent< T > > +{ + final T maxValue; + + /** + * Dimensions of the {@link LinkedList} image. + */ + final long[] dimensions; + + /** + * Represents a singly-linked list of pixel locations {@see PixelList}. + */ + final Img< LongType > linkedList; + + /** + * According to the dimensions of the input image, create a {@link LongType} + * {@link Img} to store the {@link PixelList} used in the components + * generated {@link #createComponent(Type)}. + * + * @param maxValue + * a value (e.g., grey-level) greater than any occurring in the + * input image. + * @param input + * input image. + * @param imgFactory + * used to create PixelList image. + */ + public MserPartialComponentGenerator( final T maxValue, final RandomAccessibleInterval< T > input, final ImgFactory< LongType > imgFactory ) + { + this.maxValue = maxValue; + dimensions = new long[ input.numDimensions() ]; + input.dimensions( dimensions ); + linkedList = imgFactory.create( dimensions, new LongType() ); + } + + @Override + public MserPartialComponent< T > createComponent( final T value ) + { + return new MserPartialComponent< T >( value, this ); + } + + @Override + public MserPartialComponent< T > createMaxComponent() + { + return new MserPartialComponent< T >( maxValue, this ); + } +} diff --git a/src/main/java/varun_algorithm_componenttree/mser/MserTree.java b/src/main/java/varun_algorithm_componenttree/mser/MserTree.java new file mode 100644 index 0000000..d6da7b7 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/mser/MserTree.java @@ -0,0 +1,428 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.mser; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.util.Util; +import varun_algorithm_componenttree.BuildComponentTree; +import varun_algorithm_componenttree.ComponentForest; +import varun_algorithm_componenttree.PartialComponent; +import varun_algorithm_componenttree.pixellist.PixelList; +import varun_algorithm_componenttree.pixellist.PixelListComponent; + +//@formatter:off +/** + * MSER tree of an image stored as a tree of {@link PixelListComponent}s. This + * class is used both to represent and build the tree. For building the tree + * {@link PartialComponent.Handler} is implemented to gather + * {@link MserPartialComponent} emitted by {@link BuildComponentTree}. + * + *

      + * Maximally Stable Extremal Regions (MSER) are selected from the component tree + * as follows. For each component, an instability score is computed as + * + *

      + *
      + * \[\frac{|R_i - R_{i-\Delta}|}{|R_i|}\] + *
      + *

      + * Regions whose score is a local minimum are selected as MSER candidates. + *

      + * + *

      + * A candidate region is discarded if its size (number of pixels) is smaller + * than minSize or larger than maxSize. A candidate region is + * discarded if its instability score is greater than maxVar. + *

      + * + *

      + * A tree is build of the remaining candidates. Finally, candidates are pruned + * from the tree, if they are too similar to their parent: Let A, + * B be a region and its parent. Then A is discarded if + *

      + *
      + * \[\frac{|B - A|}{|B|} \leq minDiversity\] + *
      + *

      + * TODO Add support for non-zero-min RandomAccessibleIntervals. + * (Currently, we assume that the input image is a zero-min interval.) + *

      + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +//@formatter:on +public final class MserTree< T extends Type< T > > implements ComponentForest< Mser< T > >, Iterable< Mser< T > >, PartialComponent.Handler< MserPartialComponent< T > > +{ + /** + * Build a MSER tree from an input image. Calls + * {@link #buildMserTree(RandomAccessibleInterval, RealType, long, long, double, double, ImgFactory, boolean)} + * using an {@link ArrayImgFactory} or {@link CellImgFactory} depending on + * input image size. + * + * @param input + * the input image. + * @param delta + * delta for computing instability score. + * @param minSize + * minimum size (in pixels) of accepted MSER. + * @param maxSize + * maximum size (in pixels) of accepted MSER. + * @param maxVar + * maximum instability score of accepted MSER. + * @param minDiversity + * minimal diversity of adjacent accepted MSER. + * @param darkToBright + * whether to apply thresholds from dark to bright (true) or + * bright to dark (false) + * @return MSER tree of the image. + */ + public static < T extends RealType< T > > MserTree< T > buildMserTree( final RandomAccessibleInterval< T > input, final double delta, final long minSize, final long maxSize, final double maxVar, final double minDiversity, final boolean darkToBright ) + { + return buildMserTree( input, MserTree.getDeltaVariable( input, delta ), minSize, maxSize, maxVar, minDiversity, darkToBright ); + } + + /** + * Build a MSER tree from an input image. Calls + * {@link #buildMserTree(RandomAccessibleInterval, RealType, long, long, double, double, ImgFactory, boolean)} + * using an {@link ArrayImgFactory} or {@link CellImgFactory} depending on + * input image size. + * + * @param input + * the input image. + * @param delta + * delta for computing instability score. + * @param minSize + * minimum size (in pixels) of accepted MSER. + * @param maxSize + * maximum size (in pixels) of accepted MSER. + * @param maxVar + * maximum instability score of accepted MSER. + * @param minDiversity + * minimal diversity of adjacent accepted MSER. + * @param darkToBright + * whether to apply thresholds from dark to bright (true) or + * bright to dark (false) + * @return MSER tree of the image. + */ + public static < T extends RealType< T > > MserTree< T > buildMserTree( final RandomAccessibleInterval< T > input, final T delta, final long minSize, final long maxSize, final double maxVar, final double minDiversity, final boolean darkToBright ) + { + final ImgFactory< LongType > factory = Util.getArrayOrCellImgFactory( input, new LongType() ); + return buildMserTree( input, delta, minSize, maxSize, maxVar, minDiversity, factory, darkToBright ); + } + + /** + * Build a MSER tree from an input image. + * + * @param input + * the input image. + * @param delta + * delta for computing instability score. + * @param minSize + * minimum size (in pixels) of accepted MSER. + * @param maxSize + * maximum size (in pixels) of accepted MSER. + * @param maxVar + * maximum instability score of accepted MSER. + * @param minDiversity + * minimal diversity of adjacent accepted MSER. + * @param imgFactory + * used for creating the {@link PixelList} image + * @param darkToBright + * whether to apply thresholds from dark to bright (true) or + * bright to dark (false) + * @return MSER tree of the image. + * @see MserPartialComponentGenerator + */ + public static < T extends RealType< T > > MserTree< T > buildMserTree( final RandomAccessibleInterval< T > input, final T delta, final long minSize, final long maxSize, final double maxVar, final double minDiversity, final ImgFactory< LongType > imgFactory, final boolean darkToBright ) + { + final T max = delta.createVariable(); + max.setReal( darkToBright ? delta.getMaxValue() : delta.getMinValue() ); + final MserPartialComponentGenerator< T > generator = new MserPartialComponentGenerator< T >( max, input, imgFactory ); + final Comparator< T > comparator = darkToBright ? new BuildComponentTree.DarkToBright< T >() : new BuildComponentTree.BrightToDark< T >(); + final ComputeDelta< T > computeDelta = darkToBright ? new ComputeDeltaDarkToBright< T >( delta ) : new ComputeDeltaBrightToDark< T >( delta ); + final MserTree< T > tree = new MserTree< T >( comparator, computeDelta, minSize, maxSize, maxVar, minDiversity ); + BuildComponentTree.buildComponentTree( input, generator, tree, comparator ); + tree.pruneDuplicates(); + return tree; + } + + /** + * Build a MSER tree from an input image. Calls + * {@link #buildMserTree(RandomAccessibleInterval, ComputeDelta, long, long, double, double, ImgFactory, Type, Comparator)} + * using an {@link ArrayImgFactory} or {@link CellImgFactory} depending on + * input image size. + * + * @param input + * the input image. + * @param computeDelta + * to compute (value - delta). + * @param minSize + * minimum size (in pixels) of accepted MSER. + * @param maxSize + * maximum size (in pixels) of accepted MSER. + * @param maxVar + * maximum instability score of accepted MSER. + * @param minDiversity + * minimal diversity of adjacent accepted MSER. + * @param maxValue + * a value (e.g., grey-level) greater than any occurring in the + * input image. + * @param comparator + * determines ordering of threshold values. + * @return MSER tree of the image. + */ + public static < T extends Type< T > > MserTree< T > buildMserTree( final RandomAccessibleInterval< T > input, final ComputeDelta< T > computeDelta, final long minSize, final long maxSize, final double maxVar, final double minDiversity, final T maxValue, final Comparator< T > comparator ) + { + final ImgFactory< LongType > factory = Util.getArrayOrCellImgFactory( input, new LongType() ); + return buildMserTree( input, computeDelta, minSize, maxSize, maxVar, minDiversity, factory, maxValue, comparator ); + } + + /** + * Build a MSER tree from an input image. + * + * @param input + * the input image. + * @param computeDelta + * to compute (value - delta). + * @param minSize + * minimum size (in pixels) of accepted MSER. + * @param maxSize + * maximum size (in pixels) of accepted MSER. + * @param maxVar + * maximum instability score of accepted MSER. + * @param minDiversity + * minimal diversity of adjacent accepted MSER. + * @param imgFactory + * used for creating the {@link PixelList} image + * @param maxValue + * a value (e.g., grey-level) greater than any occurring in the + * input image. + * @param comparator + * determines ordering of threshold values. + * @return MSER tree of the image. + * @see MserPartialComponentGenerator + */ + public static < T extends Type< T > > MserTree< T > buildMserTree( final RandomAccessibleInterval< T > input, final ComputeDelta< T > computeDelta, final long minSize, final long maxSize, final double maxVar, final double minDiversity, final ImgFactory< LongType > imgFactory, final T maxValue, final Comparator< T > comparator ) + { + final MserPartialComponentGenerator< T > generator = new MserPartialComponentGenerator< T >( maxValue, input, imgFactory ); + final MserTree< T > tree = new MserTree< T >( comparator, computeDelta, minSize, maxSize, maxVar, minDiversity ); + BuildComponentTree.buildComponentTree( input, generator, tree, comparator ); + tree.pruneDuplicates(); + return tree; + } + + /** + * Create a variable of type T with value delta by copying and setting a + * value from the input {@link RandomAccessibleInterval}. + */ + private static < T extends RealType< T > > T getDeltaVariable( final RandomAccessibleInterval< T > input, final double delta ) + { + final RandomAccess< T > a = input.randomAccess(); + input.min( a ); + final T deltaT = a.get().createVariable(); + deltaT.setReal( delta ); + return deltaT; + } + + private final HashSet< Mser< T > > roots; + + private final ArrayList< Mser< T > > nodes; + + private final Comparator< T > comparator; + + private final ComputeDelta< T > delta; + + /** + * Minimum size (in pixels) of accepted MSER. + */ + private final long minSize; + + /** + * Maximum size (in pixels) of accepted MSER. + */ + private final long maxSize; + + /** + * Maximum instability score of accepted MSER. + */ + private final double maxVar; + + /** + * Minimal diversity of adjacent accepted MSER. + */ + private final double minDiversity; + + /** + * The number of minima found since the last {@link #pruneDuplicates()}. + * + * @see #foundNewMinimum(MserEvaluationNode) + */ + private int minimaFoundSinceLastPrune; + + private static final int pruneAfterNMinima = 1000; + + private MserTree( final Comparator< T > comparator, final ComputeDelta< T > delta, final long minSize, final long maxSize, final double maxVar, final double minDiversity ) + { + roots = new HashSet< Mser< T > >(); + nodes = new ArrayList< Mser< T > >(); + this.comparator = comparator; + this.delta = delta; + this.minSize = minSize; + this.maxSize = maxSize; + this.maxVar = maxVar; + this.minDiversity = minDiversity; + minimaFoundSinceLastPrune = 0; + } + + /** + * Remove from the tree candidates which are too similar to their parent. + * Let A, B be a region and its parent. Then A is + * discarded if |B - A| / |B| <= minDiversity. + */ + private void pruneDuplicates() + { + nodes.clear(); + for ( final Mser< T > mser : roots ) + pruneChildren( mser ); + nodes.addAll( roots ); + } + + private void pruneChildren( final Mser< T > mser ) + { + final ArrayList< Mser< T > > validChildren = new ArrayList< Mser< T > >(); + for ( int i = 0; i < mser.children.size(); ++i ) + { + final Mser< T > m = mser.children.get( i ); + final double div = ( mser.size() - m.size() ) / ( double ) mser.size(); + if ( div > minDiversity ) + { + validChildren.add( m ); + pruneChildren( m ); + } + else + { + mser.children.addAll( m.children ); + for ( final Mser< T > m2 : m.children ) + m2.parent = mser; + } + } + mser.children.clear(); + mser.children.addAll( validChildren ); + nodes.addAll( validChildren ); + } + + @Override + public void emit( final MserPartialComponent< T > component ) + { + new MserEvaluationNode< T >( component, comparator, delta, this ); + component.children.clear(); + } + + /** + * Called when a local minimal {@link MserEvaluationNode} (a MSER candidate) + * is found. + * + * @param node + * MSER candidate. + */ + void foundNewMinimum( final MserEvaluationNode< T > node ) + { + if ( node.size >= minSize && node.size <= maxSize && node.score <= maxVar ) + { + final Mser< T > mser = new Mser< T >( node ); + for ( final Mser< T > m : node.mserThisOrChildren ) + mser.children.add( m ); + node.mserThisOrChildren.clear(); + node.mserThisOrChildren.add( mser ); + + for ( final Mser< T > m : mser.children ) + roots.remove( m ); + roots.add( mser ); + nodes.add( mser ); + if ( ++minimaFoundSinceLastPrune == pruneAfterNMinima ) + { + minimaFoundSinceLastPrune = 0; + pruneDuplicates(); + } + } + } + + /** + * Get number of detected MSERs. + * + * @return number of detected MSERs. + */ + public int size() + { + return nodes.size(); + } + + /** + * Returns an iterator over all MSERs in the tree. + * + * @return iterator over all MSERss in the tree. + */ + @Override + public Iterator< Mser< T > > iterator() + { + return nodes.iterator(); + } + + /** + * Get the set of roots of the MSER tree (respectively forest...). + * + * @return set of roots. + */ + @Override + public HashSet< Mser< T > > roots() + { + return roots; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/pixellist/PixelList.java b/src/main/java/varun_algorithm_componenttree/pixellist/PixelList.java new file mode 100644 index 0000000..3378f58 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/pixellist/PixelList.java @@ -0,0 +1,211 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.pixellist; + +import java.util.Iterator; + +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.util.IntervalIndexer; + +/** + * A singly-linked list of pixel locations stored in a {@link RandomAccessible}. + * (the value at a given location in the {@link RandomAccessible} is the index + * of the next location in the list.) + * + * + * @author Tobias Pietzsch + */ +public final class PixelList implements Iterable< Localizable > +{ + /** + * RandomAccess into the index image to store the linked list. + */ + private final RandomAccess< LongType > locationsAccess; + + /** + * Dimensions of the index image. + */ + private final long[] dimensions; + + /** + * Index of first location in the list. + */ + private long headIndex; + + /** + * Last location in the list. + */ + private final long[] tailPos; + + /** + * length of the list. + */ + private long size; + + /** + * @param locationsAccess + * RandomAccess into the index image to store the linked list. + * @param dimensions + * Dimensions of the index image. + */ + public PixelList( final RandomAccess< LongType > locationsAccess, final long[] dimensions ) + { + this.locationsAccess = locationsAccess; + this.dimensions = dimensions; + headIndex = 0; + tailPos = new long[ dimensions.length ]; + size = 0; + } + + public PixelList( final PixelList l ) + { + this.locationsAccess = l.locationsAccess; + this.dimensions = l.dimensions; + this.headIndex = l.headIndex; + this.tailPos = null; + this.size = l.size; + } + + /** + * Append a pixel location to the list. + */ + public void addPosition( final Localizable position ) + { + if ( size == 0 ) + { + position.localize( tailPos ); + final long i = IntervalIndexer.positionToIndex( tailPos, dimensions ); + headIndex = i; + } + else + { + locationsAccess.setPosition( tailPos ); + position.localize( tailPos ); + final long i = IntervalIndexer.positionToIndex( tailPos, dimensions ); + locationsAccess.get().set( i ); + } + ++size; + } + + /** + * Append another {@link PixelList} to this one. + */ + public void merge( final PixelList l ) + { + if ( size == 0 ) + { + headIndex = l.headIndex; + for ( int i = 0; i < tailPos.length; ++i ) + tailPos[ i ] = l.tailPos[ i ]; + } + else + { + locationsAccess.setPosition( tailPos ); + locationsAccess.get().set( l.headIndex ); + for ( int i = 0; i < tailPos.length; ++i ) + tailPos[ i ] = l.tailPos[ i ]; + } + size += l.size; + } + + public class PixelListIterator implements Iterator< Localizable > + { + private long i; + + private long nextIndex; + + private final long[] tmp; + + private final Point pos; + + public PixelListIterator() + { + i = 0; + nextIndex = headIndex; + tmp = new long[ dimensions.length ]; + pos = new Point( dimensions.length ); + } + + @Override + public boolean hasNext() + { + return i < size; + } + + @Override + public Localizable next() + { + ++i; + IntervalIndexer.indexToPosition( nextIndex, dimensions, tmp ); + pos.setPosition( tmp ); + locationsAccess.setPosition( tmp ); + nextIndex = locationsAccess.get().get(); + return pos; + } + + @Override + public void remove() + {} + } + + @Override + public Iterator< Localizable > iterator() + { + return new PixelListIterator(); + } + + /** + * Get the size of the list. + * + * @return number of elements in this list. + */ + public long size() + { + return size; + } + + /** + * empty the list. + */ + public void clear() + { + size = 0; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponent.java b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponent.java new file mode 100644 index 0000000..afea684 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponent.java @@ -0,0 +1,151 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.pixellist; + +import java.util.ArrayList; +import java.util.Iterator; + +import net.imglib2.Localizable; + +import net.imglib2.type.Type; +import varun_algorithm_componenttree.Component; + +/** + * A connected component of the image thresholded at {@link #value()}. The set + * of pixels can be accessed by iterating ({@link #iterator()}) the component. + * + * This is a node in a {@link PixelListComponentTree}. The child and parent + * nodes can be accessed by {@link #getChildren()} and {@link #getParent()}. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public final class PixelListComponent< T extends Type< T > > implements Component< T, PixelListComponent< T > > +{ + /** + * child nodes in the {@link PixelListComponentTree}. + */ + private final ArrayList< PixelListComponent< T > > children; + + /** + * parent node in the {@link PixelListComponentTree}. + */ + private PixelListComponent< T > parent; + + /** + * Threshold value of the connected component. + */ + private final T value; + + /** + * Pixels in the component. + */ + private final PixelList pixelList; + + PixelListComponent( final PixelListPartialComponent< T > intermediate ) + { + children = new ArrayList< PixelListComponent< T > >(); + parent = null; + value = intermediate.getValue().copy(); + pixelList = new PixelList( intermediate.pixelList ); + if ( intermediate.emittedComponent != null ) + children.add( intermediate.emittedComponent ); + for ( final PixelListPartialComponent< T > c : intermediate.children ) + { + children.add( c.emittedComponent ); + c.emittedComponent.parent = this; + } + intermediate.emittedComponent = this; + intermediate.children.clear(); + } + + /** + * Get the image threshold that created the extremal region. + * + * @return the image threshold that created the extremal region. + */ + @Override + public T value() + { + return value; + } + + /** + * Get the number of pixels in the extremal region. + * + * @return number of pixels in the extremal region. + */ + @Override + public long size() + { + return pixelList.size(); + } + + /** + * Get an iterator over the pixel locations ({@link Localizable}) in this + * connected component. + * + * @return iterator over locations. + */ + @Override + public Iterator< Localizable > iterator() + { + return pixelList.iterator(); + } + + /** + * Get the children of this node in the {@link PixelListComponentTree}. + * + * @return the children of this node in the {@link PixelListComponentTree}. + */ + @Override + public ArrayList< PixelListComponent< T > > getChildren() + { + return children; + } + + /** + * Get the parent of this node in the {@link PixelListComponentTree}. + * + * @return the parent of this node in the {@link PixelListComponentTree}. + */ + @Override + public PixelListComponent< T > getParent() + { + return parent; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponentTree.java b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponentTree.java new file mode 100644 index 0000000..c1e4527 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListComponentTree.java @@ -0,0 +1,212 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.pixellist; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Set; + +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.util.Util; +import varun_algorithm_componenttree.BuildComponentTree; +import varun_algorithm_componenttree.ComponentTree; +import varun_algorithm_componenttree.PartialComponent; + +/** + * Component tree of an image stored as a tree of {@link PixelListComponent}s. + * This class is used both to represent and build the tree. + * + *

      + * TODO Add support for non-zero-min RandomAccessibleIntervals. + * (Currently, we assume that the input image is a zero-min interval.) + *

      + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +public final class PixelListComponentTree< T extends Type< T > > implements ComponentTree< PixelListComponent< T > >, Iterable< PixelListComponent< T > >, PartialComponent.Handler< PixelListPartialComponent< T > > +{ + /** + * Build a component tree from an input image. Calls + * {@link #buildComponentTree(RandomAccessibleInterval, RealType, ImgFactory, boolean)} + * using an {@link ArrayImgFactory} or {@link CellImgFactory} depending on + * input image size. + * + * @param input + * the input image. + * @param type + * a variable of the input image type. + * @param darkToBright + * whether to apply thresholds from dark to bright (true) or + * bright to dark (false) + * @return component tree of the image. + */ + public static < T extends RealType< T > > PixelListComponentTree< T > buildComponentTree( final RandomAccessibleInterval< T > input, final T type, final boolean darkToBright ) + { + final ImgFactory< LongType > factory = Util.getArrayOrCellImgFactory( input, new LongType() ); + return buildComponentTree( input, type, factory, darkToBright ); + } + + /** + * Build a component tree from an input image. + * + * @param input + * the input image. + * @param type + * a variable of the input image type. + * @param imgFactory + * used for creating the {@link PixelList} image (see + * {@link PixelListPartialComponentGenerator}). + * @param darkToBright + * whether to apply thresholds from dark to bright (true) or + * bright to dark (false) + * @return component tree of the image. + */ + public static < T extends RealType< T > > PixelListComponentTree< T > buildComponentTree( final RandomAccessibleInterval< T > input, final T type, final ImgFactory< LongType > imgFactory, final boolean darkToBright ) + { + final T max = type.createVariable(); + max.setReal( darkToBright ? type.getMaxValue() : type.getMinValue() ); + final PixelListPartialComponentGenerator< T > generator = new PixelListPartialComponentGenerator< T >( max, input, imgFactory ); + final PixelListComponentTree< T > tree = new PixelListComponentTree< T >(); + BuildComponentTree.buildComponentTree( input, generator, tree, darkToBright ); + return tree; + } + + /** + * Build a component tree from an input image. Calls + * {@link #buildComponentTree(RandomAccessibleInterval, Type, Comparator, ImgFactory)} + * using an {@link ArrayImgFactory} or {@link CellImgFactory} depending on + * input image size. + * + * @param input + * the input image. + * @param maxValue + * a value (e.g., grey-level) greater than any occurring in the + * input image. + * @param comparator + * determines ordering of threshold values. + * @return component tree of the image. + */ + public static < T extends Type< T > > PixelListComponentTree< T > buildComponentTree( final RandomAccessibleInterval< T > input, final T maxValue, final Comparator< T > comparator ) + { + final ImgFactory< LongType > factory = Util.getArrayOrCellImgFactory( input, new LongType() ); + return buildComponentTree( input, maxValue, comparator, factory ); + } + + /** + * Build a component tree from an input image. + * + * @param input + * the input image. + * @param maxValue + * a value (e.g., grey-level) greater than any occurring in the + * input image. + * @param comparator + * determines ordering of threshold values. + * @param imgFactory + * used for creating the {@link PixelList} image + * {@link PixelListPartialComponentGenerator}. + * @return component tree of the image. + */ + public static < T extends Type< T > > PixelListComponentTree< T > buildComponentTree( final RandomAccessibleInterval< T > input, final T maxValue, final Comparator< T > comparator, final ImgFactory< LongType > imgFactory ) + { + final PixelListPartialComponentGenerator< T > generator = new PixelListPartialComponentGenerator< T >( maxValue, input, imgFactory ); + final PixelListComponentTree< T > tree = new PixelListComponentTree< T >(); + BuildComponentTree.buildComponentTree( input, generator, tree, comparator ); + return tree; + } + + private PixelListComponent< T > root; + + private final ArrayList< PixelListComponent< T > > nodes; + + private PixelListComponentTree() + { + root = null; + nodes = new ArrayList< PixelListComponent< T > >(); + } + + @Override + public void emit( final PixelListPartialComponent< T > partialComponent ) + { + final PixelListComponent< T > component = new PixelListComponent< T >( partialComponent ); + root = component; + nodes.add( component ); + } + + /** + * Returns an iterator over all connected components in the tree. + * + * @return iterator over all connected components in the tree. + */ + @Override + public Iterator< PixelListComponent< T > > iterator() + { + return nodes.iterator(); + } + + /** + * Get the root component. + * + * @return root component. + */ + @Override + public PixelListComponent< T > root() + { + return root; + } + + /** + * Get the root component. + * + * @return a singleton set containing the root component. + */ + @Override + public Set< PixelListComponent< T > > roots() + { + return Collections.singleton( root ); + } +} diff --git a/src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponent.java b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponent.java new file mode 100644 index 0000000..504927c --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponent.java @@ -0,0 +1,138 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.pixellist; + +import java.util.ArrayList; + +import net.imglib2.Localizable; + +import net.imglib2.type.Type; +import varun_algorithm_componenttree.PartialComponent; + +/** + * Implementation of {@link PartialComponent} that stores a list of associated + * pixels in a {@link PixelList}. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +final class PixelListPartialComponent< T extends Type< T > > implements PartialComponent< T, PixelListPartialComponent< T > > +{ + /** + * Threshold value of the connected component. + */ + private final T value; + + /** + * Pixels in the component. + */ + final PixelList pixelList; + + /** + * A list of {@link PixelListPartialComponent} merged into this one since it + * was last emitted. (For building up component tree.) + */ + final ArrayList< PixelListPartialComponent< T > > children; + + /** + * The PixelListComponent assigned to this {@link PixelListPartialComponent} + * when it was last emitted. (For building up component tree.) + */ + PixelListComponent< T > emittedComponent; + + /** + * Create new empty component. + * + * @param value + * (initial) threshold value {@see #getValue()}. + * @param generator + * the {@link PixelListPartialComponentGenerator#linkedList} is + * used to store the {@link #pixelList}. + */ + PixelListPartialComponent( final T value, final PixelListPartialComponentGenerator< T > generator ) + { + pixelList = new PixelList( generator.linkedList.randomAccess(), generator.dimensions ); + this.value = value.copy(); + children = new ArrayList< PixelListPartialComponent< T > >(); + emittedComponent = null; + } + + @Override + public void addPosition( final Localizable position ) + { + pixelList.addPosition( position ); + } + + @Override + public T getValue() + { + return value; + } + + @Override + public void setValue( final T value ) + { + this.value.set( value ); + } + + @Override + public void merge( final PixelListPartialComponent< T > component ) + { + pixelList.merge( component.pixelList ); + children.add( component ); + } + + @Override + public String toString() + { + String s = "{" + value.toString() + " : "; + boolean first = true; + for ( final Localizable l : pixelList ) + { + if ( first ) + { + first = false; + } + else + { + s += ", "; + } + s += l.toString(); + } + return s + "}"; + } +} diff --git a/src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponentGenerator.java b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponentGenerator.java new file mode 100644 index 0000000..abccb67 --- /dev/null +++ b/src/main/java/varun_algorithm_componenttree/pixellist/PixelListPartialComponentGenerator.java @@ -0,0 +1,104 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_componenttree.pixellist; + +import java.util.LinkedList; + +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.integer.LongType; +import varun_algorithm_componenttree.PartialComponent; + +/** + * Implementation of {@link PartialComponent.Generator} for creating + * {@link PixelListPartialComponent} components. According to the dimensions of + * the input image it creates a {@link LongType} {@link Img} to store the + * {@link PixelList} used in the components. + * + * @param + * value type of the input image. + * + * @author Tobias Pietzsch + */ +final class PixelListPartialComponentGenerator< T extends Type< T > > implements PartialComponent.Generator< T, PixelListPartialComponent< T > > +{ + private final T maxValue; + + /** + * Dimensions of the {@link LinkedList} image. + */ + final long[] dimensions; + + /** + * Represents a singly-linked list of pixel locations {@see PixelList}. + */ + final Img< LongType > linkedList; + + /** + * According to the dimensions of the input image, create a {@link LongType} + * {@link Img} to store the {@link PixelList} used in the components + * generated {@link #createComponent(Type)}. + * + * @param maxValue + * a value (e.g., grey-level) greater than any occurring in the + * input image. + * @param input + * input image. + * @param imgFactory + * used to create PixelList image. + */ + PixelListPartialComponentGenerator( final T maxValue, final RandomAccessibleInterval< T > input, final ImgFactory< LongType > imgFactory ) + { + this.maxValue = maxValue; + dimensions = new long[ input.numDimensions() ]; + input.dimensions( dimensions ); + linkedList = imgFactory.create( dimensions, new LongType() ); + } + + @Override + public PixelListPartialComponent< T > createComponent( final T value ) + { + return new PixelListPartialComponent< T >( value, this ); + } + + @Override + public PixelListPartialComponent< T > createMaxComponent() + { + return new PixelListPartialComponent< T >( maxValue, this ); + } +} diff --git a/src/main/java/varun_algorithm_dog/DifferenceOfGaussian.java b/src/main/java/varun_algorithm_dog/DifferenceOfGaussian.java new file mode 100644 index 0000000..6047e69 --- /dev/null +++ b/src/main/java/varun_algorithm_dog/DifferenceOfGaussian.java @@ -0,0 +1,253 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_dog; + +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.img.Img; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_gauss3.Gauss3; + +/** + * Compute Difference-of-Gaussian of a {@link RandomAccessible}. + * + * @author Tobias Pietzsch + */ +public class DifferenceOfGaussian +{ + /** + * Compute the difference of Gaussian for the input. Input convolved with + * Gaussian of sigmaSmaller is subtracted from input convolved with Gaussian + * of sigmaLarger (where {@code sigmaLarger > sigmaSmaller}). + *

      + * Creates an appropriate temporary image and calls + * {@link #DoG(double[], double[], RandomAccessible, RandomAccessible, RandomAccessibleInterval, ExecutorService)} + * . + *

      + * + * @param sigmaSmaller + * stddev (in every dimension) of smaller Gaussian. + * @param sigmaLarger + * stddev (in every dimension) of larger Gaussian. + * @param input + * the input image extended to infinity (or at least covering the + * same interval as the dog result image, plus borders for + * convolution). + * @param dog + * the Difference-of-Gaussian result image. + * @param service + * service providing threads for multi-threading + */ + public static < I extends NumericType< I >, T extends NumericType< T > & NativeType< T > > void DoG( + final double[] sigmaSmaller, + final double[] sigmaLarger, + final RandomAccessible< I > input, + final RandomAccessibleInterval< T > dog, + final ExecutorService service ) + { + final T type = Util.getTypeFromInterval( dog ); + final Img< T > g1 = Util.getArrayOrCellImgFactory( dog, type ).create( dog, type ); + final long[] translation = new long[ dog.numDimensions() ]; + dog.min( translation ); + DoG( sigmaSmaller, sigmaLarger, input, Views.translate( g1, translation ), dog, service ); + } + + /** + * Compute the difference of Gaussian for the input. Input convolved with + * Gaussian of sigmaSmaller is subtracted from input convolved with Gaussian + * of sigmaLarger (where sigmaLarger > sigmaSmaller). + * + * @param sigmaSmaller + * stddev (in every dimension) of smaller Gaussian. + * @param sigmaLarger + * stddev (in every dimension) of larger Gaussian. + * @param input + * the input image extended to infinity (or at least covering the + * same interval as the dog result image, plus borders for + * convolution). + * @param tmp + * temporary image, must at least cover the same interval as the + * dog result image. + * @param dog + * the Difference-of-Gaussian result image. + * @param service + * how many threads to use for the computation. + */ + public static < I extends NumericType< I >, T extends NumericType< T > & NativeType< T > > void DoG( + final double[] sigmaSmaller, + final double[] sigmaLarger, + final RandomAccessible< I > input, + final RandomAccessible< T > tmp, + final RandomAccessibleInterval< T > dog, + final ExecutorService service ) + { + final IntervalView< T > tmpInterval = Views.interval( tmp, dog ); + try + { + Gauss3.gauss( sigmaSmaller, input, tmpInterval, service ); + Gauss3.gauss( sigmaLarger, input, dog, service ); + } + catch ( final IncompatibleTypeException e ) + { + e.printStackTrace(); + } + final IterableInterval< T > dogIterable = Views.iterable( dog ); + final IterableInterval< T > tmpIterable = Views.iterable( tmpInterval ); + final long size = dogIterable.size(); + // FIXME find better heuristic? + final int numThreads = Runtime.getRuntime().availableProcessors(); + final int numTasks = numThreads <= 1 ? 1 : numThreads * 20; + final long taskSize = size / numTasks; + final ArrayList< Future< Void > > futures = new ArrayList<>(); + for ( int taskNum = 0; taskNum < numTasks; ++taskNum ) + { + final long fromIndex = taskNum * taskSize; + final long thisTaskSize = ( taskNum == numTasks - 1 ) ? size - fromIndex : taskSize; + if ( dogIterable.iterationOrder().equals( tmpIterable.iterationOrder() ) ) + futures.add( service.submit( new Callable< Void >() + { + @Override + public Void call() + { + final Cursor< T > dogCursor = dogIterable.cursor(); + final Cursor< T > tmpCursor = tmpIterable.cursor(); + dogCursor.jumpFwd( fromIndex ); + tmpCursor.jumpFwd( fromIndex ); + for ( int i = 0; i < thisTaskSize; ++i ) + dogCursor.next().sub( tmpCursor.next() ); + return null; + } + } ) ); + else + futures.add( service.submit( new Callable< Void >() + { + @Override + public Void call() + { + final Cursor< T > dogCursor = dogIterable.localizingCursor(); + final RandomAccess< T > tmpAccess = tmpInterval.randomAccess(); + dogCursor.jumpFwd( fromIndex ); + for ( int i = 0; i < thisTaskSize; ++i ) + { + final T o = dogCursor.next(); + tmpAccess.setPosition( dogCursor ); + o.sub( tmpAccess.get() ); + } + return null; + } + } ) ); + } + for ( final Future< Void > f : futures ) + { + try + { + f.get(); + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + catch ( final ExecutionException e ) + { + e.printStackTrace(); + } + } + } + + /** + * Helper function to compute per-dimension sigmas in pixel coordinates. The + * parameters {@code sigma1} and {@code sigma2} specify desired + * sigmas (scale) in image coordinates. Taking into account the sigma of the + * input image as well as the image calibration, the resulting sigma arrays + * specifiy the smoothing that has to be applied to achieve the desired + * sigmas. + * + * @param imageSigma + * estimated sigma of the input image, in pixel coordinates. + * @param minf + * multiple of the {@code imageSigma} that smoothing with + * the resulting sigma must at least achieve. + * @param pixelSize + * calibration. Dimensions of a pixel in image units. + * @param sigma1 + * desired sigma in image coordinates. + * @param sigma2 + * desired sigma in image coordinates. + * @return {@code double[2][numDimensions]}, array of two arrays + * contains resulting sigmas for sigma1, sigma2. + */ + public static double[][] computeSigmas( final double imageSigma, final double minf, final double[] pixelSize, final double sigma1, final double sigma2 ) + { + final int n = pixelSize.length; + final double k = sigma2 / sigma1; + final double[] sigmas1 = new double[ n ]; + final double[] sigmas2 = new double[ n ]; + for ( int d = 0; d < n; ++d ) + { + final double s1 = Math.max( minf * imageSigma, sigma1 / pixelSize[ d ] ); + final double s2 = k * s1; + sigmas1[ d ] = Math.sqrt( s1 * s1 - imageSigma * imageSigma ); + sigmas2[ d ] = Math.sqrt( s2 * s2 - imageSigma * imageSigma ); + } + return new double[][] { sigmas1, sigmas2 }; + } + + /** + * Helper function to compute the minimum sigma that can be given to + * {@link #computeSigmas(double, double, double[], double, double)} while + * still achieving isotropic smoothed images. + */ + public static double computeMinIsotropicSigma( final double imageSigma, final double minf, final double[] pixelSize ) + { + final int n = pixelSize.length; + double s = pixelSize[ 0 ]; + for ( int d = 1; d < n; ++d ) + s = Math.max( s, pixelSize[ d ] ); + return minf * imageSigma * s; + } +} diff --git a/src/main/java/varun_algorithm_dog/DogDetection.java b/src/main/java/varun_algorithm_dog/DogDetection.java new file mode 100644 index 0000000..17c02c5 --- /dev/null +++ b/src/main/java/varun_algorithm_dog/DogDetection.java @@ -0,0 +1,415 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_dog; + +import java.util.ArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.imagej.space.LinearSpace; +import net.imglib2.Interval; +import net.imglib2.Point; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Util; +import net.imglib2.view.Views; +import varun_algorithm_localextrema.LocalExtrema; +import varun_algorithm_localextrema.RefinedPeak; +import varun_algorithm_localextrema.SubpixelLocalization; +import varun_algorithm_localextrema.LocalExtrema.LocalNeighborhoodCheck; + +/** + * @author Stephan Preibisch + * @author Tobias Pietzsch + */ +public class DogDetection< T extends RealType< T > & NativeType< T > > +{ + public static enum ExtremaType + { + /** + * Bright blobs on dark background. + */ + MINIMA, + + /** + * Dark blobs on bright background. + */ + MAXIMA + } + + /** + * {@link ExecutorService} used for Multi-Threading. If null, a new one will + * be created on demand + **/ + private ExecutorService executorService; + + public < I extends RandomAccessibleInterval< T > & LinearSpace< ? > > DogDetection( + final I input, + final double sigmaSmaller, + final double sigmaLarger, + final ExtremaType extremaType, + final double minPeakValue ) + { + this( Views.extendMirrorSingle( input ), input, getcalib( input ), sigmaSmaller, sigmaLarger, extremaType, minPeakValue, true ); + } + + public DogDetection( + final RandomAccessibleInterval< T > input, + final double[] calibration, + final double sigmaSmaller, + final double sigmaLarger, + final ExtremaType extremaType, + final double minPeakValue, + final boolean normalizeMinPeakValue ) + { + this( Views.extendMirrorSingle( input ), input, calibration, sigmaSmaller, sigmaLarger, extremaType, minPeakValue, true ); + } + + /** + * Sets up a {@link DogDetection} with the specified parameters (does not do + * any computation yet). If the input image is of type {@link DoubleType}, + * {@link DoubleType} will be used for computing the Difference-of-Gaussian. + * In all other cases, {@link FloatType} will be used). + * + * @param input + * the input image. + * @param interval + * which interval of the input image to process + * @param calibration + * The calibration, i.e., the voxel sizes in some unit for the + * input image. + * @param sigmaSmaller + * sigma for the smaller scale in the same units as calibration. + * @param sigmaLarger + * sigma for the larger scale in the same units as calibration. + * @param extremaType + * which type of extrema (minima, maxima) to detect. Note that + * minima in the Difference-of-Gaussian correspond to bright + * blobs on dark background. Maxima correspond to dark blobs on + * bright background. + * @param minPeakValue + * threshold value for detected extrema. Maxima below + * {@code minPeakValue} or minima above {@code -minPeakValue} + * will be disregarded. + * @param normalizeMinPeakValue + * Whether the peak value should be normalized. The + * Difference-of-Gaussian is an approximation of the + * scale-normalized Laplacian-of-Gaussian, with a factor of + * f = sigmaSmaller / (sigmaLarger - sigmaSmaller). If + * {@code normalizeMinPeakValue=true}, the {@code minPeakValue} + * will be divided by f (which is equivalent to scaling + * the DoG by f). + */ + public DogDetection( + final RandomAccessible< T > input, + final Interval interval, + final double[] calibration, + final double sigmaSmaller, + final double sigmaLarger, + final ExtremaType extremaType, + final double minPeakValue, + final boolean normalizeMinPeakValue ) + { + this( input, interval, calibration, sigmaSmaller, sigmaLarger, extremaType, minPeakValue, normalizeMinPeakValue, new DogComputationType<>( input, interval ).getType() ); + } + + /** + * Sets up a {@link DogDetection} with the specified parameters (does not do + * any computation yet). + * + * @param input + * the input image. + * @param interval + * which interval of the input image to process + * @param calibration + * The calibration, i.e., the voxel sizes in some unit for the + * input image. + * @param sigmaSmaller + * sigma for the smaller scale in the same units as calibration. + * @param sigmaLarger + * sigma for the larger scale in the same units as calibration. + * @param extremaType + * which type of extrema (minima, maxima) to detect. Note that + * minima in the Difference-of-Gaussian correspond to bright + * blobs on dark background. Maxima correspond to dark blobs on + * bright background. + * @param minPeakValue + * threshold value for detected extrema. Maxima below + * {@code minPeakValue} or minima above {@code -minPeakValue} + * will be disregarded. + * @param normalizeMinPeakValue + * Whether the peak value should be normalized. The + * Difference-of-Gaussian is an approximation of the + * scale-normalized Laplacian-of-Gaussian, with a factor of + * f = sigmaSmaller / (sigmaLarger - sigmaSmaller). If + * {@code normalizeMinPeakValue=true}, the {@code minPeakValue} + * will be divided by f (which is equivalent to scaling + * the DoG by f). + * @param computationType + * The type to use for computing the Difference-of-Gaussian. + */ + public < F extends RealType< F > & NativeType< F > > DogDetection( + final RandomAccessible< T > input, + final Interval interval, + final double[] calibration, + final double sigmaSmaller, + final double sigmaLarger, + final ExtremaType extremaType, + final double minPeakValue, + final boolean normalizeMinPeakValue, + final F computationType ) + { + this.input = input; + this.interval = interval; + this.sigmaSmaller = sigmaSmaller; + this.sigmaLarger = sigmaLarger; + this.pixelSize = calibration; + this.typedDogDetection = new TypedDogDetection<>( computationType ); + this.imageSigma = 0.5; + this.minf = 2; + this.extremaType = extremaType; + this.minPeakValue = minPeakValue; + this.normalizeMinPeakValue = normalizeMinPeakValue; + this.keepDoGImg = true; + this.numThreads = Runtime.getRuntime().availableProcessors(); + } + + /** + * If you want to get subpixel-localized peaks, call + * {@link #getSubpixelPeaks()} directly. + */ + public ArrayList< Point > getPeaks() + { + return typedDogDetection.getPeaks(); + } + + public ArrayList< RefinedPeak< Point > > getSubpixelPeaks() + { + return typedDogDetection.getSubpixelPeaks(); + } + + protected final RandomAccessible< T > input; + + protected final Interval interval; + + protected final double sigmaSmaller; + + protected final double sigmaLarger; + + protected final double[] pixelSize; + + protected final TypedDogDetection< ? > typedDogDetection; + + protected double imageSigma; + + protected double minf; + + protected ExtremaType extremaType; + + protected double minPeakValue; + + protected boolean normalizeMinPeakValue; + + protected boolean keepDoGImg; + + protected int numThreads; + + public void setImageSigma( final double imageSigma ) + { + this.imageSigma = imageSigma; + } + + public void setMinf( final double minf ) + { + this.minf = minf; + } + + public void setMinPeakValue( final double minPeakValue ) + { + this.minPeakValue = minPeakValue; + } + + public void setNormalizeMinPeakValue( final boolean normalizeMinPeakValue ) + { + this.normalizeMinPeakValue = normalizeMinPeakValue; + } + + public void setKeepDoGImg( final boolean keepDoGImg ) + { + this.keepDoGImg = keepDoGImg; + } + + public void setNumThreads( final int numThreads ) + { + this.numThreads = numThreads; + } + + public double getImageSigma() + { + return imageSigma; + } + + public double getMinf() + { + return minf; + } + + public double getMinPeakValue() + { + return minPeakValue; + } + + public boolean getNormalizeMinPeakValue() + { + return normalizeMinPeakValue; + } + + public boolean getKeepDoGImg() + { + return keepDoGImg; + } + + public int getNumThreads() + { + return numThreads; + } + + public void setExecutorService( final ExecutorService service ) + { + this.executorService = service; + } + + private static double[] getcalib( final LinearSpace< ? > calib ) + { + final double[] c = new double[ calib.numDimensions() ]; + for ( int d = 0; d < c.length; ++d ) + c[ d ] = calib.axis( d ).scale(); + return c; + } + + private static class DogComputationType< F extends RealType< F > & NativeType< F > > + { + private final F type; + + @SuppressWarnings( "unchecked" ) + public DogComputationType( + final RandomAccessible< ? > input, + final Interval interval ) + { + final Object t = Util.getTypeFromInterval( Views.interval( input, interval ) ); + if ( t instanceof DoubleType ) + type = ( F ) new DoubleType(); + else + type = ( F ) new FloatType(); + } + + public F getType() + { + return type; + } + } + + protected class TypedDogDetection< F extends RealType< F > & NativeType< F > > + { + protected final F type; + + protected RandomAccessibleInterval< F > dogImg; + + public TypedDogDetection( final F type ) + { + this.type = type; + } + + public ArrayList< Point > getPeaks() + { + final ExecutorService service; + if ( executorService == null ) + service = Executors.newFixedThreadPool( numThreads ); + else + service = executorService; + + dogImg = Util.getArrayOrCellImgFactory( interval, type ).create( interval, type ); + final long[] translation = new long[ interval.numDimensions() ]; + interval.min( translation ); + dogImg = Views.translate( dogImg, translation ); + + final double[][] sigmas = DifferenceOfGaussian.computeSigmas( imageSigma, minf, pixelSize, sigmaSmaller, sigmaLarger ); + DifferenceOfGaussian.DoG( sigmas[ 0 ], sigmas[ 1 ], input, dogImg, service ); + final F val = type.createVariable(); + final double minValueT = type.getMinValue(); + final double maxValueT = type.getMaxValue(); + final LocalNeighborhoodCheck< Point, F > localNeighborhoodCheck; + final double normalization = normalizeMinPeakValue ? ( sigmaLarger / sigmaSmaller - 1.0 ) : 1.0; + switch ( extremaType ) + { + case MINIMA: + val.setReal( Math.max( Math.min( -minPeakValue * normalization, maxValueT ), minValueT ) ); + localNeighborhoodCheck = new LocalExtrema.MinimumCheck<>( val ); + break; + case MAXIMA: + default: + val.setReal( Math.max( Math.min( minPeakValue * normalization, maxValueT ), minValueT ) ); + localNeighborhoodCheck = new LocalExtrema.MaximumCheck<>( val ); + } + final ArrayList< Point > peaks = LocalExtrema.findLocalExtrema( dogImg, localNeighborhoodCheck, service ); + if ( !keepDoGImg ) + dogImg = null; + + if ( executorService == null ) + service.shutdown(); + + return peaks; + } + + public ArrayList< RefinedPeak< Point > > getSubpixelPeaks() + { + final boolean savedKeepDoGImg = keepDoGImg; + keepDoGImg = true; + final ArrayList< Point > peaks = getPeaks(); + final SubpixelLocalization< Point, F > spl = new SubpixelLocalization<>( dogImg.numDimensions() ); + spl.setAllowMaximaTolerance( true ); + spl.setMaxNumMoves( 10 ); + final ArrayList< RefinedPeak< Point > > refined = spl.process( peaks, dogImg, dogImg ); + keepDoGImg = savedKeepDoGImg; + if ( !keepDoGImg ) + dogImg = null; + return refined; + } + } +} diff --git a/src/main/java/varun_algorithm_edge/Edgel.java b/src/main/java/varun_algorithm_edge/Edgel.java new file mode 100644 index 0000000..fbd5ed4 --- /dev/null +++ b/src/main/java/varun_algorithm_edge/Edgel.java @@ -0,0 +1,99 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_edge; + +import net.imglib2.AbstractRealLocalizable; + +/** + * An oriented point representing a sub-pixel localized segment of a + * (hyper-)edge. + * + * @author Tobias Pietzsch + */ +public class Edgel extends AbstractRealLocalizable +{ + + private final double[] gradient; + + private final double magnitude; + + /** + * Create an edgel. + * + * @param position + * the sub-pixel position of the edgel. + * @param gradient + * the gradient direction (a unit vector) at the edgel position. + * This is perpendicular to the edge direction. + * @param magnitude + * the gradient magnitude at the edgel position. + */ + public Edgel( final double[] position, final double[] gradient, final double magnitude ) + { + super(position.clone()); + this.gradient = gradient.clone(); + this.magnitude = magnitude; + } + + /** + * Get the gradient direction at the edgel position. This is a unit vector + * perpendicular to the edge direction. + * + * @return the gradient direction at the edgel position. + */ + public double[] getGradient() + { + return gradient; + } + + /** + * Get the gradient magnitude at the edgel position. + * + * @return the gradient magnitude at the edgel position. + */ + public double getMagnitude() + { + return magnitude; + } + + public String toString() + { + return String.format("Edgel: pos (%.2f,%.2f,%.2f) grad (%.2f,%.2f,%.2f) mag (%.2f)", + position[0], position[1], position[2], + gradient[0], gradient[1], gradient[2], + magnitude); + } + +} diff --git a/src/main/java/varun_algorithm_edge/SubpixelEdgelDetection.java b/src/main/java/varun_algorithm_edge/SubpixelEdgelDetection.java new file mode 100644 index 0000000..523019d --- /dev/null +++ b/src/main/java/varun_algorithm_edge/SubpixelEdgelDetection.java @@ -0,0 +1,229 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_edge; + +import java.util.ArrayList; + +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.RealRandomAccess; +import net.imglib2.algorithm.edge.Edgel; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.interpolation.randomaccess.NLinearInterpolatorFactory; +import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Intervals; +import net.imglib2.view.Views; +import varun_algorithm_gradient.PartialDerivative; + +/** + * Compute the set of sub-pixel edgels for the input image. A pixel contains an + * edgel if its gradient magnitude is a local maximum in the direction of the + * gradient (perpendicular to the edge). The sub-pixel position of the edgel is + * found as the maximum of a quadratic function fitted to the neighboring + * magnitudes in the gradient direction. The method is described in + *

      + * F. Devernay, + * A Non-Maxima Suppression Method for Edge Detection with Sub-Pixel Accuracy + * , RR 2724, INRIA, nov. 1995 + *

      + * + * @author Tobias Pietzsch + */ +public class SubpixelEdgelDetection +{ + /** + * Compute the set of sub-pixel edgels for the input image. A pixel contains + * an edgel if its gradient magnitude is a local maximum in the direction of + * the gradient (perpendicular to the edge). The sub-pixel position of the + * edgel is found as the maximum of a quadratic function fitted to the + * neighboring magnitudes in the gradient direction. + *

      + * Note: The input image type must be a signed type! Otherwise gradient + * computation will not work. + * + * @param input + * input image + * @param factory + * used to create an image to store the partial derivatives of + * the input image + * @param minGradientMagnitude + * Only consider a pixel an edgel if the gradient magnitude is + * greater than this + * @return set of edgels found in the input image + */ + public static < T extends RealType< T > > ArrayList< Edgel > getEdgels( + final RandomAccessibleInterval< T > input, + final ImgFactory< T > factory, + final double minGradientMagnitude ) + { + final ArrayList< Edgel > edgels = new ArrayList< Edgel >(); + + // number of dimensions of the input image. + final int n = input.numDimensions(); + + // For computing partial derivatives of input we require a border of 1 + // pixel. Then we want to find local maxima of the gradient which + // requires again a border of 1 pixel. So if the image size is smaller + // than 5 pixels in any dimension, we can abort because we will not find + // any gradient maxima. + for ( int d = 0; d < n; ++d ) + if ( input.dimension( d ) < 5 ) + return edgels; + + // Image of n+1 dimensions to store the partial derivatives of the input + // image. The (n+1)-th dimension is used to index the partial + // derivative. For example, the partial derivative by Y of pixel (a,b,c) + // is stored at position (a,b,c,1). + final long[] dim = new long[ n + 1 ]; + for ( int d = 0; d < n; ++d ) + dim[ d ] = input.dimension( d ); + dim[ n ] = n; + final Img< T > gradients = factory.create( dim, input.randomAccess().get() ); + + // Compute partial derivatives of input in all dimension. This requires + // a border of 1 pixel with respect to the input image + final Interval gradientComputationInterval = Intervals.expand( input, -1 ); + for ( int d = 0; d < n; ++d ) + PartialDerivative.gradientCentralDifference( input, Views.interval( Views.hyperSlice( gradients, n, d ), gradientComputationInterval ), d ); + + // Find gradient maxima. This requires a border of 2 pixels with respect + // to the input image + final Interval maximaComputationInterval = Intervals.expand( input, -2 ); + + final long[] min = new long[ n ]; + maximaComputationInterval.min( min ); + final long[] max = new long[ n ]; + maximaComputationInterval.max( max ); + final long[] shiftback = new long[ n ]; + for ( int d = 0; d < n; ++d ) + shiftback[ d ] = min[ d ] - max[ d ]; + + final NLinearInterpolatorFactory< T > interpolatorFactory = new NLinearInterpolatorFactory< T >(); + @SuppressWarnings( "unchecked" ) + final RealRandomAccess< T >[] gradientAccess = new RealRandomAccess[ n ]; + for ( int d = 0; d < n; ++d ) + gradientAccess[ d ] = interpolatorFactory.create( Views.hyperSlice( gradients, n, d ) ); + + final RandomAccess< T > src = gradients.randomAccess(); + + for ( int d = 0; d < n; ++d ) + src.setPosition( min[ d ], d ); + src.setPosition( 0, n ); + + final double g[] = new double[ n ]; + final double doublePos[] = new double[ n ]; + + final double minMagnitudeSquared = minGradientMagnitude * minGradientMagnitude; + final long max0 = max[ 0 ]; + while ( true ) + { + // process pixel... + // gradient direction + double len = 0; + for ( int d = 0; d < n; ++d ) + { + final double gg = src.get().getRealDouble(); + len += gg * gg; + g[ d ] = gg; + src.fwd( n ); + } + src.setPosition( 0, n ); + if ( len >= minMagnitudeSquared ) + { + len = Math.sqrt( len ); + + for ( int d = 0; d < n; ++d ) + { + g[ d ] /= len; + doublePos[ d ] = src.getDoublePosition( d ) + g[ d ]; + } + final double lighterMag = gradientMagnitudeInDirection( doublePos, g, gradientAccess ); + if ( len >= lighterMag ) + { + for ( int d = 0; d < n; ++d ) + doublePos[ d ] = src.getDoublePosition( d ) - g[ d ]; + + final double darkerMag = gradientMagnitudeInDirection( doublePos, g, gradientAccess ); + + if ( len >= darkerMag ) + { + // sub-pixel localization + final double m = ( darkerMag - lighterMag ) / ( 2 * ( darkerMag - 2 * len + lighterMag ) ); + for ( int d = 0; d < n; ++d ) + doublePos[ d ] = src.getDoublePosition( d ) + m * g[ d ]; + edgels.add( new Edgel( doublePos, g, len ) ); + } + } + } + + // move to next pixel + if ( src.getLongPosition( 0 ) == max0 ) + { + src.move( shiftback[ 0 ], 0 ); + if ( n == 1 ) + return edgels; + for ( int d = 1; d < n; ++d ) + { + if ( src.getLongPosition( d ) == max[ d ] ) + { + src.move( shiftback[ d ], d ); + if ( d == n - 1 ) + return edgels; + } + else + { + src.fwd( d ); + break; + } + } + } + else + src.fwd( 0 ); + } + } + + private static < T extends RealType< T > > double gradientMagnitudeInDirection( final double[] position, final double[] direction, final RealRandomAccess< T >[] gradientAccess ) + { + double len = 0; + for ( int d = 0; d < gradientAccess.length; ++d ) + { + gradientAccess[ d ].setPosition( position ); + len += gradientAccess[ d ].get().getRealDouble() * direction[ d ]; + } + return len; + } +} diff --git a/src/main/java/varun_algorithm_fill/Filter.java b/src/main/java/varun_algorithm_fill/Filter.java new file mode 100644 index 0000000..c9ab664 --- /dev/null +++ b/src/main/java/varun_algorithm_fill/Filter.java @@ -0,0 +1,50 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_fill; + +/** + * Interface for comparing {@link T} t and {@link U} u and accepting + * them as equivalent in a sense specified by implementation thereof. + * + * @author Philipp Hanslovsky + * @author Stephan Saalfeld + * + * @param + * @param + */ +public interface Filter< T, U > +{ + boolean accept( T t, U u ); +} diff --git a/src/main/java/varun_algorithm_fill/FloodFill.java b/src/main/java/varun_algorithm_fill/FloodFill.java new file mode 100644 index 0000000..91504ca --- /dev/null +++ b/src/main/java/varun_algorithm_fill/FloodFill.java @@ -0,0 +1,228 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_fill; + +import gnu.trove.list.TLongList; +import gnu.trove.list.array.TLongArrayList; +import net.imglib2.Cursor; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + +import net.imglib2.type.Type; +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Neighborhood; +import varun_algorithm_neighborhood.Shape; + +/** + * Iterative n-dimensional flood fill for arbitrary neighborhoods. + * + * @author Philipp Hanslovsky + * @author Stephan Saalfeld + */ +public class FloodFill +{ + // int or long? current TLongList cannot store more than Integer.MAX_VALUE + private static final int CLEANUP_THRESHOLD = ( int ) 1e5; + + /** + * Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting + * at seed location, write fillLabel into target at current location and + * continue for each pixel in neighborhood defined by shape if neighborhood + * pixel is in the same connected component and fillLabel has not been + * written into that location yet (comparator evaluates to 0). + * + * Convenience call to + * {@link FloodFill#fill(RandomAccessible, RandomAccessible, Localizable, Object, Type, Shape, Filter)} + * . seedLabel is extracted from source at seed location. + * + * @param source + * input + * @param target + * {@link RandomAccessible} to be written into. May be the same + * as input. + * @param seed + * Start flood fill at this location. + * @param fillLabel + * Immutable. Value to be written into valid flood fill + * locations. + * @param filter + * Returns true if pixel has not been visited yet and should be + * written into. Returns false if target pixel has been visited + * or source pixel is not part of the same connected component. + * @param + * T implements {@code Type}. + * @param + * U implements {@code Type}. + */ + public static < T extends Type< T >, U extends Type< U > > void fill( final RandomAccessible< T > source, final RandomAccessible< U > target, final Localizable seed, final U fillLabel, final Shape shape, final Filter< Pair< T, U >, Pair< T, U > > filter ) + { + final RandomAccess< T > access = source.randomAccess(); + access.setPosition( seed ); + fill( source, target, seed, access.get().copy(), fillLabel, shape, filter ); + } + + /** + * Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting + * at seed location, write fillLabel into target at current location and + * continue for each pixel in neighborhood defined by shape if neighborhood + * pixel is in the same connected component and fillLabel has not been + * written into that location yet (comparator evaluates to 0). + * + * Convenience call to + * {@link FloodFill#fill(RandomAccessible, RandomAccessible, Localizable, Object, Object, Shape, Filter, Writer)} + * with {@link TypeWriter} as writer. + * + * @param source + * input + * @param target + * {@link RandomAccessible} to be written into. May be the same + * as input. + * @param seed + * Start flood fill at this location. + * @param seedLabel + * Immutable. Reference value of input at seed location. + * @param fillLabel + * Immutable. Value to be written into valid flood fill + * locations. + * @param filter + * Returns true if pixel has not been visited yet and should be + * written into. Returns false if target pixel has been visited + * or source pixel is not part of the same connected component. + * @param + * No restrictions on {@link T}. + * @param + * {@link U} implements {@code Type}. + */ + public static < T, U extends Type< U > > void fill( final RandomAccessible< T > source, final RandomAccessible< U > target, final Localizable seed, final T seedLabel, final U fillLabel, final Shape shape, final Filter< Pair< T, U >, Pair< T, U > > filter ) + { + fill( source, target, seed, seedLabel, fillLabel, shape, filter, new TypeWriter< U >() ); + } + + /** + * + * Iterative n-dimensional flood fill for arbitrary neighborhoods: Starting + * at seed location, write fillLabel into target at current location and + * continue for each pixel in neighborhood defined by shape if neighborhood + * pixel is in the same connected component and fillLabel has not been + * written into that location yet (comparator evaluates to 0). + * + * @param source + * input + * @param target + * {@link RandomAccessible} to be written into. May be the same + * as input. + * @param seed + * Start flood fill at this location. + * @param seedLabel + * Immutable. Reference value of input at seed location. + * @param fillLabel + * Immutable. Value to be written into valid flood fill + * locations. + * @param shape + * Defines neighborhood that is considered for connected + * components, e.g. + * {@link varun_algorithm_neighborhood.DiamondShape} + * @param filter + * Returns true if pixel has not been visited yet and should be + * written into. Returns false if target pixel has been visited + * or source pixel is not part of the same connected component. + * @param writer + * Defines how fillLabel is written into target at current + * location. + * @param + * No restrictions on T. Appropriate comparator is the only + * requirement. + * @param + * No restrictions on U. Appropriate comparator and writer is the + * only requirement. + */ + public static < T, U > void fill( final RandomAccessible< T > source, final RandomAccessible< U > target, final Localizable seed, final T seedLabel, final U fillLabel, final Shape shape, final Filter< Pair< T, U >, Pair< T, U > > filter, final Writer< U > writer ) + { + final int n = source.numDimensions(); + + final ValuePair< T, U > reference = new ValuePair< T, U >( seedLabel, fillLabel ); + + final RandomAccessible< Pair< T, U > > paired = Views.pair( source, target ); + + final TLongList[] coordinates = new TLongList[ n ]; + for ( int d = 0; d < n; ++d ) + { + coordinates[ d ] = new TLongArrayList(); + coordinates[ d ].add( seed.getLongPosition( d ) ); + } + + final RandomAccessible< Neighborhood< Pair< T, U > > > neighborhood = shape.neighborhoodsRandomAccessible( paired ); + final RandomAccess< Neighborhood< Pair< T, U > > > neighborhoodAccess = neighborhood.randomAccess(); + + final RandomAccess< U > targetAccess = target.randomAccess(); + targetAccess.setPosition( seed ); + writer.write( fillLabel, targetAccess.get() ); + + for ( int i = 0; i < coordinates[ 0 ].size(); ++i ) + { + for ( int d = 0; d < n; ++d ) + neighborhoodAccess.setPosition( coordinates[ d ].get( i ), d ); + + final Cursor< Pair< T, U > > neighborhoodCursor = neighborhoodAccess.get().cursor(); + + while ( neighborhoodCursor.hasNext() ) + { + final Pair< T, U > p = neighborhoodCursor.next(); + if ( filter.accept( p, reference ) ) + { + writer.write( fillLabel, p.getB() ); + for ( int d = 0; d < n; ++d ) + coordinates[ d ].add( neighborhoodCursor.getLongPosition( d ) ); + } + } + + if ( i > CLEANUP_THRESHOLD ) + { + for ( int d = 0; d < coordinates.length; ++d ) + { + final TLongList c = coordinates[ d ]; + coordinates[ d ] = c.subList( i, c.size() ); + } + i = 0; + } + + } + + } + +} diff --git a/src/main/java/varun_algorithm_fill/TypeWriter.java b/src/main/java/varun_algorithm_fill/TypeWriter.java new file mode 100644 index 0000000..e0dbd41 --- /dev/null +++ b/src/main/java/varun_algorithm_fill/TypeWriter.java @@ -0,0 +1,52 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_fill; + +import net.imglib2.type.Type; + +/** + * Default implementation of {@link Writer} for {@link Type}. + * + * @author Philipp Hanslovsky + * @author Stephan Saalfeld + */ +public class TypeWriter< T extends Type< T > > implements Writer< T > +{ + @Override + public void write( final T source, final T target ) + { + target.set( source ); + } +} diff --git a/src/main/java/varun_algorithm_fill/Writer.java b/src/main/java/varun_algorithm_fill/Writer.java new file mode 100644 index 0000000..7899e40 --- /dev/null +++ b/src/main/java/varun_algorithm_fill/Writer.java @@ -0,0 +1,48 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_fill; + +/** + * Interface for writing from arbitrary source into target (same types). + * + * @author Philipp Hanslovsky + * @author Stephan Saalfeld + * + * @param + */ +public interface Writer< U > +{ + void write( final U source, final U target ); +} diff --git a/src/main/java/varun_algorithm_floydsteinberg/FloydSteinbergDithering.java b/src/main/java/varun_algorithm_floydsteinberg/FloydSteinbergDithering.java new file mode 100644 index 0000000..d1e564c --- /dev/null +++ b/src/main/java/varun_algorithm_floydsteinberg/FloydSteinbergDithering.java @@ -0,0 +1,287 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_floydsteinberg; + +import java.util.Random; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Intervals; +import net.imglib2.util.Util; +import varun_algorithm.Benchmark; +import varun_algorithm.OutputAlgorithm; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public class FloydSteinbergDithering< T extends RealType< T >> implements OutputAlgorithm< Img< BitType >>, Benchmark +{ + Img< BitType > result; + + final RandomAccessibleInterval< T > img; + + final Img< FloatType > errorDiffusionKernel; + + final long[] dim, tmp1, tmp2; + + final float ditheringThreshold; + + long processingTime; + + String errorMessage = ""; + + public FloydSteinbergDithering( final RandomAccessibleInterval< T > img, final float ditheringThreshold ) + { + this.img = img; + this.dim = Intervals.dimensionsAsLongArray( img ); + this.tmp1 = new long[ img.numDimensions() ]; + this.tmp2 = new long[ img.numDimensions() ]; + + this.errorDiffusionKernel = createErrorDiffusionKernel( img.numDimensions() ); + + this.ditheringThreshold = ditheringThreshold; + } + + /** Will estimate the dithering threshold by (max - min) / 2 */ + public FloydSteinbergDithering( final RandomAccessibleInterval< T > img ) + { + this( img, Float.NEGATIVE_INFINITY ); + } + + @Override + public boolean process() + { + final long startTime = System.currentTimeMillis(); + /* + * ComputeMinMax cmm = new ComputeMinMax(img); //TODO + * cmm.process(); final float minValue = cmm.getMin().getRealFloat(); + * final float maxValue = cmm.getMax().getRealFloat(); final long + * numDimensions = img.numDimensions(); final float ditheringThreshold = + * Float.NEGATIVE_INFINITY == this.ditheringThreshold ? (maxValue - + * minValue) / 2.0f : this.ditheringThreshold; + * + * // creates the output image of BitType using the same Storage + * Strategy as the input image try { result = new + * ArrayImgFactory().imgFactory(new BitType()).create( dim, new + * BitType() ); } catch (IncompatibleTypeException e) { throw new + * RuntimeException(e); } + * + * // we create a Cursor that traverses (top -> bottom) and (left -> + * right) in n dimensions, // which is a Cursor on a normal Array, + * therefore we use a FakeArray which just gives us position // + * information without allocating memory final ZeroMinIntervalIterator + * cursor = new ZeroMinIntervalIterator( dim ); + * + * // we also need a Cursors for the input, the output and the kernel + * image final RandomAccess cursorInput = new + * ExtendedRandomAccessibleInterval> (img, + * new OutOfBoundsConstantValueFactory> ( + * Views.iterable(img).firstElement().createVariable() )) .randomAccess( + * img ); + * + * //img.randomAccess( new OutOfBoundsConstantValueFactory>( + * img.firstElement().createVariable() ) ); final RandomAccess + * cursorOutput = result.randomAccess(); final Cursor + * cursorKernel = errorDiffusionKernel.cursor(); + * + * while( cursor.hasNext() ) { cursor.fwd(); + * + * // move input and output cursor to the current location + * cursorInput.setPosition( cursor ); cursorOutput.setPosition( cursor + * ); + * + * // set new value and compute error final float error; final float in + * = cursorInput.get().getRealFloat(); if ( in < ditheringThreshold ) { + * cursorOutput.get().setZero(); error = in - minValue; } else { + * cursorOutput.get().setOne(); error = in - maxValue; } + * + * if ( error != 0.0f ) { // distribute the error cursorKernel.reset(); + * cursorKernel.jumpFwd( errorDiffusionKernel.size()/2 ); + * cursor.localize( tmp1 ); + * + * while ( cursorKernel.hasNext() ) { cursorKernel.fwd(); + * + * final float value = error * cursorKernel.get().get(); + * cursorKernel.localize( tmp2 ); + * + * for ( int d = 0; d < numDimensions; ++d ) tmp2[ d ] += tmp1[ d ] - 1; + * + * cursorInput.move( tmp2 ); cursorInput.get().setReal( + * cursorInput.get().getRealFloat() + value ); } } } + * + * processingTime = System.currentTimeMillis() - startTime; + * + * // successfully computed the dithering + */ + return true; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public Img< BitType > getResult() + { + return result; + } + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } + + public Img< FloatType > createErrorDiffusionKernel( final int numDimensions ) + { + final ArrayImgFactory< FloatType > factory = new ArrayImgFactory< FloatType >(); + + // for 2d we take the values from the literature + if ( numDimensions == 2 ) + { + final Img< FloatType > kernel = factory.create( new long[] { 3, 3 }, new FloatType() ); + + final RandomAccess< FloatType > cursor = kernel.randomAccess(); + + // For the 2d-case as well: + // |- - -| + // |- # 7| + // |3 5 1| + // ( - means processed already, # means the one we are currently + // processing) + cursor.setPosition( 2, 0 ); + cursor.setPosition( 1, 1 ); + cursor.get().setReal( 7.0f / 16.0f ); + + cursor.move( 1, 1 ); + cursor.get().setReal( 1.0f / 16.0f ); + + cursor.move( -1, 0 ); + cursor.get().setReal( 5.0f / 16.0f ); + + cursor.move( -1, 0 ); + cursor.get().setReal( 3.0f / 16.0f ); + + return kernel; + } + final Img< FloatType > kernel = factory.create( Util.getArrayFromValue( 3L, numDimensions ), new FloatType() ); + final Cursor< FloatType > cursor = kernel.cursor(); + + final int numValues = ( int ) ( kernel.size() / 2 ); + final float[] rndValues = new float[ numValues ]; + float sum = 0; + final Random rnd = new Random( 435345 ); + + for ( int i = 0; i < numValues; ++i ) + { + rndValues[ i ] = rnd.nextFloat(); + sum += rndValues[ i ]; + } + + for ( int i = 0; i < numValues; ++i ) + rndValues[ i ] /= sum; + + int count = 0; + while ( cursor.hasNext() ) + { + cursor.fwd(); + + if ( count > numValues ) + cursor.get().setReal( rndValues[ count - numValues - 1 ] ); + + ++count; + } + + // + // Optimize + // + for ( int i = 0; i < 100; ++i ) + for ( int d = 0; d < numDimensions; ++d ) + { + cursor.reset(); + + float sumD = 0; + + while ( cursor.hasNext() ) + { + cursor.fwd(); + if ( cursor.getIntPosition( d ) != 1 ) + sumD += cursor.get().get(); + } + + cursor.reset(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + + if ( cursor.getIntPosition( d ) != 1 ) + cursor.get().set( cursor.get().get() / sumD ); + } + } + + sum = 0; + + cursor.reset(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + sum += cursor.get().get(); + } + + cursor.reset(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().set( cursor.get().get() / sum ); + } + return kernel; + } +} diff --git a/src/main/java/varun_algorithm_function/Function.java b/src/main/java/varun_algorithm_function/Function.java new file mode 100644 index 0000000..91b506d --- /dev/null +++ b/src/main/java/varun_algorithm_function/Function.java @@ -0,0 +1,47 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_function; + +import net.imglib2.type.Type; + +/** + * TODO + * + */ +public interface Function< S extends Type< S >, T extends Type< T >, U extends Type< U >> +{ + /** Perform an operation with inputs S and T and output U. */ + public void compute( S s, T t, U u ); +} diff --git a/src/main/java/varun_algorithm_function/NormMinMax.java b/src/main/java/varun_algorithm_function/NormMinMax.java new file mode 100644 index 0000000..bfe92a1 --- /dev/null +++ b/src/main/java/varun_algorithm_function/NormMinMax.java @@ -0,0 +1,60 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_function; + +import net.imglib2.converter.Converter; +import net.imglib2.type.numeric.RealType; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public class NormMinMax< A extends RealType< A > > implements Converter< A, A > +{ + final double min, tmp; + + public NormMinMax( final double min, final double max ) + { + this.min = min; + this.tmp = max - min; + } + + @Override + public void convert( final A input, final A output ) + { + output.setReal( ( input.getRealDouble() - min ) / tmp ); + } +} diff --git a/src/main/java/varun_algorithm_function/SubtractNorm.java b/src/main/java/varun_algorithm_function/SubtractNorm.java new file mode 100644 index 0000000..15f79b5 --- /dev/null +++ b/src/main/java/varun_algorithm_function/SubtractNorm.java @@ -0,0 +1,62 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_function; + +import net.imglib2.algorithm.function.Function; +import net.imglib2.type.numeric.NumericType; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public class SubtractNorm< A extends NumericType< A > > implements Function< A, A, A > +{ + final A normalizationFactor; + + public SubtractNorm( final A normalizationFactor ) + { + this.normalizationFactor = normalizationFactor; + } + + @Override + public void compute( final A input1, final A input2, final A output ) + { + output.set( input1 ); + output.sub( input2 ); + output.mul( normalizationFactor ); + } + +} diff --git a/src/main/java/varun_algorithm_function/SubtractNormReal.java b/src/main/java/varun_algorithm_function/SubtractNormReal.java new file mode 100644 index 0000000..6171656 --- /dev/null +++ b/src/main/java/varun_algorithm_function/SubtractNormReal.java @@ -0,0 +1,60 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_function; + +import net.imglib2.algorithm.function.Function; +import net.imglib2.type.numeric.RealType; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public class SubtractNormReal< A extends RealType< A >, B extends RealType< B >, C extends RealType< C > > implements Function< A, B, C > +{ + final double normalizationFactor; + + public SubtractNormReal( final double normalizationFactor ) + { + this.normalizationFactor = normalizationFactor; + } + + @Override + public void compute( final A input1, final B input2, final C output ) + { + output.setReal( ( input1.getRealDouble() - input2.getRealDouble() ) * normalizationFactor ); + } + +} diff --git a/src/main/java/varun_algorithm_function/SubtractReal.java b/src/main/java/varun_algorithm_function/SubtractReal.java new file mode 100644 index 0000000..5543101 --- /dev/null +++ b/src/main/java/varun_algorithm_function/SubtractReal.java @@ -0,0 +1,52 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_function; + +import net.imglib2.algorithm.function.Function; +import net.imglib2.type.numeric.RealType; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public class SubtractReal< A extends RealType< A >, B extends RealType< B >, C extends RealType< C > > implements Function< A, B, C > +{ + @Override + public void compute( final A input1, final B input2, final C output ) + { + output.setReal( input1.getRealDouble() - input2.getRealDouble() ); + } +} diff --git a/src/main/java/varun_algorithm_gauss/AbstractGauss.java b/src/main/java/varun_algorithm_gauss/AbstractGauss.java new file mode 100644 index 0000000..453284f --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/AbstractGauss.java @@ -0,0 +1,911 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import java.util.concurrent.atomic.AtomicInteger; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Iterator; +import net.imglib2.Localizable; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + +import net.imglib2.converter.Converter; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.iterator.LocalizingZeroMinIntervalIterator; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.util.Intervals; +import net.imglib2.util.Util; +import varun_multithreading.SimpleMultiThreading; + +/** + * + * @param + * - Defines the {@link Type} in which the actual computation is + * performed + * + * @author Stephan Preibisch + */ +public abstract class AbstractGauss< T extends NumericType< T > > +{ + final Interval inputInterval; + + final Localizable outputOffset; + + /* final */RandomAccessible< T > input, output; + + final ImgFactory< T > factory; + + final Img< T > tmp1, tmp2; + + final T type; + + final int numDimensions; + + final double[] sigma; + + final double[][] kernel; + + int numThreads; + + public AbstractGauss( final double[] sigma, final RandomAccessible< T > input, final Interval inputInterval, + final RandomAccessible< T > output, final Localizable outputOffset, + final ImgFactory< T > factory, final T type ) + { + this.numThreads = Runtime.getRuntime().availableProcessors(); + this.numDimensions = sigma.length; + this.input = input; + this.output = output; + this.factory = factory; + this.type = type; + + this.sigma = sigma; + this.kernel = new double[ numDimensions ][]; + this.inputInterval = inputInterval; + this.outputOffset = outputOffset; + + computeKernel(); + + // allocate the temp images + final Interval intervalTmp = getTemporaryImgSize(); + + if ( numDimensions > 1 ) + tmp1 = factory.create( intervalTmp, getProcessingType() ); + else + tmp1 = null; + + if ( numDimensions > 2 ) + tmp2 = factory.create( intervalTmp, getProcessingType() ); + else + tmp2 = null; + } + + public double[] getSigma() + { + return sigma; + } + + public double[][] getKernel() + { + return kernel; + } + + public int numDimensions() + { + return numDimensions; + } + + public RandomAccessible< T > getInput() + { + return input; + } + + public RandomAccessible< T > getOutput() + { + return output; + } + + public ImgFactory< T > getFactory() + { + return factory; + } + + public Interval getInputInterval() + { + return inputInterval; + } + + public Localizable getOutputOffset() + { + return outputOffset; + } + + public T type() + { + return type; + } + + public Img< T > getTmp1() + { + return tmp1; + } + + public Img< T > getTmp2() + { + return tmp2; + } + + protected T getProcessingType() + { + return type.createVariable(); + } + + protected abstract Img< T > getProcessingLine( final long size ); + + /** + * @return the result of the convolution + */ + public RandomAccessible< T > getResult() + { + return output; + } + + /** + * The area for the output/temp that needs to be convolved, always relative + * to the input of the next convolution operation. The area is larger than + * the input, defined by the size of the kernel in each dimension. + * + * @param dim + * - The dimension that is currently processed + * @return - the {@link Interval} defining the output size for the current + * dimension that is processed + */ + protected Interval getRange( final int dim ) + { + // this is a special case, only the area defined by the input interval + // needs to be convolved once + if ( numDimensions == 1 ) + return inputInterval; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + // the first convolution is relative to the input RandomAccessible + // which is not necessarily zero-bounded + if ( dim == 0 ) + { + min[ 0 ] = inputInterval.min( 0 ); + max[ 0 ] = inputInterval.max( 0 ); + + // all other dimensions except for the first one need to be + // convolved with an extra size defined by the kernel size + // the only exception would be if a OutOfBoundsMirrorStrategy + // on top of a Img would be used, this could be checked in a + // special Factory class + for ( int d = 1; d < numDimensions; ++d ) + { + min[ d ] = inputInterval.min( d ) - kernel[ d ].length / 2; + max[ d ] = inputInterval.max( d ) + kernel[ d ].length / 2; + } + } + else + { + // now everything is relative to the temp images that have to be + // used in the special implementations of the Gaussian convolutions + for ( int d = 0; d < numDimensions; ++d ) + { + // all dimensions that have been convolved already are processed + // with their normal size + if ( d < dim ) + { + min[ d ] = 0; + max[ d ] = inputInterval.dimension( d ) - 1; + } + else if ( d == dim ) // the current dimension starts at kernel/2 + // and has the size of the original + // input + { + min[ d ] = kernel[ d ].length / 2; + max[ d ] = inputInterval.dimension( d ) - 1 + kernel[ d ].length / 2; + } + else + // all other dimensions that have not been convolved yet have + // the size of image + half the kernel size on both ends + { + min[ d ] = 0; + max[ d ] = inputInterval.dimension( d ) - 1 + kernel[ d ].length - 1; + } + } + } + return new FinalInterval( min, max ); + } + + /** + * An {@link Iterator} that samples a one dimensional line of input data for + * the current dimension. + * + * @param dim + * - The current dimension + * @param range + * - the size of the output/temp image + * @return - A {@link SamplingLineIterator} which provides the input + */ + protected SamplingLineIterator< T > createInputLineSampler( final int dim, final Interval range ) + { + // the random access on the input data + final RandomAccess< T > randomAccess; + + if ( dim == 0 ) + randomAccess = input.randomAccess( Intervals.expand( range, kernel[ dim ].length / 2, dim ) ); + else if ( dim % 2 == 1 ) + randomAccess = tmp1.randomAccess(); // odd dimensions + else + randomAccess = tmp2.randomAccess(); // even dimensions + + // place the randomAccess at the right location and return the + // size of the SamplingLineIterator we need to compute the processLine + + // the size of the 1-d line + final long sizeProcessLine = range.dimension( dim ); + + // the size of the SamplingLineIterator we need to compute the + // processLine + final long sizeInputData = sizeProcessLine + kernel[ dim ].length - 1; + + // use the input image if it is the first dimension + if ( dim == 0 ) + { + // put the randomAccess into the correct location, the range is + // relative to the input for dim 0 + range.min( randomAccess ); + + // this also sticks out half because we need more input pixels in x + // due to the convolution (kernel size) + randomAccess.move( -( kernel[ 0 ].length / 2 ), 0 ); + } + else + { + // now put the randomAccess into the correct location, the range is + // relative to the temporary images + // and therefore zero-bounded + range.min( randomAccess ); + + // all dimensions start at 0 relative to the temp images, except the + // dimension that is currently convolved + randomAccess.move( -( kernel[ dim ].length / 2 ), dim ); + } + + // return a new SamplingLineIterator that also keeps the instance of the + // processing line, + // which is important for multithreading so that each + // SamplingLineIterator has its own + // temporary space + return new SamplingLineIterator< T >( dim, sizeInputData, randomAccess, getProcessingLine( sizeProcessLine ), getProcessingType(), getProcessingType() ); + } + + /** + * An {@link Iterator} that samples a one dimensional line of output data + * for the current dimension. + * + * @param dim + * - The current dimension + * @param range + * - the size of the output/temp image + * @param inputLineSampler + * - the input line sampler which knows all the stuff already + * @return - A {@link SamplingLineIterator} which provides the output + */ + protected WritableLineIterator< T > createOutputLineWriter( final int dim, final Interval range, final SamplingLineIterator< T > inputLineSampler ) + { + // the random access on the output data + final RandomAccess< T > randomAccess; + + if ( dim == numDimensions - 1 ) + randomAccess = output.randomAccess(); + else if ( dim % 2 == 0 ) + randomAccess = tmp1.randomAccess(); // even dimensions + else + randomAccess = tmp2.randomAccess(); // odd dimensions + + // the size of the 1-d line, same as the input + final long sizeProcessLine = inputLineSampler.getProcessLine().size(); + + if ( dim == numDimensions - 1 ) + { + // put the randomAccess into the correct location, the range is + // relative to the input for dim=numDimensions-1 + randomAccess.setPosition( outputOffset ); + } + else + { + if ( dim % 2 == 0 ) + tmp1.min( randomAccess ); + else + tmp2.min( randomAccess ); + } + + return new WritableLineIterator< T >( dim, sizeProcessLine, randomAccess ); + } + + /** + * Compute the current line. It is up to the implementation howto really do + * that. The idea is to only iterate over the input once (that's why it is + * an {@link Iterator}) as it is potentially an expensive operation (e.g. a + * {@link Converter} might be involved or we are computing on a rendered + * input) + * + * @param input + * - the {@link Iterator}/{@link Sampler} over the current input + * line. + */ + protected void processLine( final SamplingLineIterator< T > input, final double[] kernel ) + { + final int kernelSize = kernel.length; + final int kernelSizeMinus1 = kernelSize - 1; + final int kernelSizeHalf = kernelSize / 2; + final int kernelSizeHalfMinus1 = kernelSizeHalf - 1; + + // where the result is written to, at least a size of 1 + final RandomAccess< T > randomAccessLeft = input.randomAccessLeft; // processingLine.randomAccess(); + final RandomAccess< T > randomAccessRight = input.randomAccessRight; // processingLine.randomAccess(); + final T copy = input.copy; // getProcessingType(); + final T tmp = input.tmp; // getProcessingType(); + + final long imgSize = input.getProcessLine().size(); + + // do we have a "normal" convolution where the image is at least + // as big as the kernel, i.e. is the output big enough + // so that we have left and kernel.length-1 incomplete convolutions + // and at least one convolution in the middle where the input + // contributes + // to kernel.size pixels? + if ( imgSize >= kernelSize ) + { + /* + * This is how the convolution scheme works, we access every input + * only once as it is potentially expensive. We distribute each + * value according to the kernel over the output line. Iterating the + * output line is cheap as it is typically a one-dimensional array. + * Furthermore, we re-use + * + * Kernelsize = 5, e.g. [ 0.05 0.25 0.4 0.25 0.05 ] + * + * Input --- --- --- --- --- --- --- --- --- ----- ----- ----- ----- + * ----- ----- i |0| |1| |2| |3| |4| |5| |6| |7| |8| ... |102| |103| + * |104| |105| |106| |107| --- --- --- --- --- --- --- --- --- ----- + * ----- ----- ----- ----- ----- + * + * Output --- --- --- --- --- --- --- ----- ----- ----- ----- o |0| + * |1| |2| |3| |4| |5| |6| ... |100| |101| |102| |103| --- --- --- + * --- --- --- --- ----- ----- ----- ----- + * + * convolve the first pixels where the input influences less than + * kernel.size pixels ( -> referes to add() ) + * + * names of variables in algorithm: i k o | | | | | | i(0)*kernel[0] + * -> o(0) k=i-o + * + * i(1)*kernel[1] -> o(0) k=i-o i(1)*kernel[0] -> o(1) + * + * i(2)*kernel[2] -> o(0) k=i-o i(2)*kernel[1] -> o(1) + * i(2)*kernel[0] -> o(2) + * + * i(3)*kernel[3] -> o(0) k=i-o i(3)*kernel[2] -> o(1) + * i(3)*kernel[1] -> o(2) i(3)*kernel[0] -> o(3) + * + * From now on, it is the same till the end of the line (n=0 at the + * beginning) + * + * i(n+4)*kernel[0] -> o(n) <- same 0 i(n+4)*kernel[1] -> o(n+1) <- + * same 1 i(n+4)*kernel[2] -> o(n+2) i(n+4)*kernel[1] -> o(n+3) <- + * same 1 (re-use calculation) i(n+4)*kernel[0] -> o(n+4) <- same 0 + * (re-use calculation) + * + * Compute the last pixels + * + * i(104)*kernel[0] -> o(100) i(104)*kernel[1] -> o(101) + * i(104)*kernel[2] -> o(102) i(104)*kernel[3] -> o(103) + * + * i(105)*kernel[0] -> o(101) i(105)*kernel[1] -> o(102) + * i(105)*kernel[2] -> o(103) + * + * i(106)*kernel[0] -> o(102) i(106)*kernel[1] -> o(103) + * + * i(107)*kernel[0] -> o(103) + */ + + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPosition( 0, 0 ); + + // now add it to the one output values it contributes to + copy.mul( kernel[ 0 ] ); + randomAccessLeft.get().add( copy ); + + for ( int i = 1; i < kernelSizeMinus1; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPosition( -1, 0 ); + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + { + randomAccessLeft.fwd( 0 ); + + tmp.set( copy ); + tmp.mul( kernel[ i - o ] ); + + randomAccessLeft.get().add( tmp ); + } + } + + // convolve all values where the input value contributes to the full + // kernel + // + // we set one random access on the left side of the kernel and one + // on the right + // side and move them inwards, a bit like the scanner of KITT in + // Knight Rider(TM), + // but only one way. In this way we save half the calculations. + // The pixel in the center is done by the left random access. + // We perform one movement less than necessary, because in the last + // pixel before + // the center we only need to move the left one which is responsible + // for the center + final long length = imgSize - kernelSizeMinus1; + for ( long n = 0; n < length; ++n ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the left and the right random access to the right + // coordinates + // the left random access is always responsible for the center + randomAccessLeft.setPosition( n, 0 ); + randomAccessRight.setPosition( n + kernelSizeMinus1, 0 ); + + // move till the last pixel before the center of the kernel + for ( int k = 0; k < kernelSizeHalfMinus1; ++k ) + { + tmp.set( copy ); + tmp.mul( kernel[ k ] ); + + randomAccessLeft.get().add( tmp ); + randomAccessRight.get().add( tmp ); + + randomAccessLeft.fwd( 0 ); + randomAccessRight.bck( 0 ); + } + + // do the last pixel (same as a above, but right cursor doesn't + // move) + tmp.set( copy ); + tmp.mul( kernel[ kernelSizeHalfMinus1 ] ); + + randomAccessLeft.get().add( tmp ); + randomAccessRight.get().add( tmp ); + + randomAccessLeft.fwd( 0 ); + + // do the center pixel + tmp.set( copy ); + tmp.mul( kernel[ kernelSizeHalf ] ); + + randomAccessLeft.get().add( tmp ); + } + + /* + * Just to visualize it again for the output imgSize Input --- --- + * --- --- --- --- --- --- --- ----- ----- ----- ----- ----- ----- i + * |0| |1| |2| |3| |4| |5| |6| |7| |8| ... |102| |103| |104| |105| + * |106| |107| --- --- --- --- --- --- --- --- --- ----- ----- ----- + * ----- ----- ----- + * + * Output --- --- --- --- --- --- --- ----- ----- ----- ----- o |0| + * |1| |2| |3| |4| |5| |6| ... |100| |101| |102| |103| --- --- --- + * --- --- --- --- ----- ----- ----- ----- + */ + + // convolve the last pixels where the input influences less than + // kernel.size pixels + final long endLength = imgSize + kernelSizeMinus1; + for ( long i = imgSize; i < endLength; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPosition( i - kernelSize, 0 ); + + // now add it to all output values it contributes to + int k = 0; + for ( long o = i - kernelSize + 1; o < imgSize; ++o ) + { + randomAccessLeft.fwd( 0 ); + + tmp.set( copy ); + tmp.mul( kernel[ k++ ] ); + + randomAccessLeft.get().add( tmp ); + } + } + } + else + { + /* + * The area to be convolved is actually smaller than the kernel, so + * we face something like that, althought the kernel can potentially + * be significanly bigger... + * + * Kernelsize = 5, e.g. [ 0.05 0.25 0.4 0.25 0.05 ] + * + * Input --- --- --- --- --- --- --- i |0| |1| |2| |3| |4| |5| |6| + * --- --- --- --- --- --- --- + * + * Output --- --- --- o |0| |1| |2| --- --- --- + * + * Input --- --- --- --- --- i |0| |1| |2| |3| |4| --- --- --- --- + * --- + * + * Output --- o |0| --- + */ + + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPosition( 0, 0 ); + + // now add it to all output values it contributes to + copy.mul( kernel[ 0 ] ); + randomAccessLeft.get().add( copy ); + + for ( int i = 1; i < imgSize; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPosition( -1, 0 ); + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + { + randomAccessLeft.fwd( 0 ); + + tmp.set( copy ); + tmp.mul( kernel[ i - o ] ); + + randomAccessLeft.get().add( tmp ); + } + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + for ( long i = imgSize; i < imgSize + kernelSizeMinus1; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + // now add it to all output values it contributes to + long o = i - kernelSize + 1; + int k = 0; + + if ( o < 0 ) + { + k = -( int ) o; + o = 0; + } + + randomAccessLeft.setPosition( o - 1, 0 ); + + // now add it to all output values it contributes to + for ( ; o < imgSize; ++o ) + { + randomAccessLeft.fwd( 0 ); + + tmp.set( copy ); + tmp.mul( kernel[ k++ ] ); + + randomAccessLeft.get().add( tmp ); + } + } + } + } + + /** + * Writes the computed line back into the output/temp image. The idea is to + * only iterate over the output once (that's why it is an {@link Iterator}) + * as it is potentially an expensive operation (e.g. a {@link Converter} + * might be involved) + * + * @param a + * - the {@link Iterator}/{@link Sampler} over the current output + * line. + */ + protected void writeLine( final WritableLineIterator< T > a, final SamplingLineIterator< T > inputLineSampler ) + { + final Cursor< T > resultCursor = inputLineSampler.resultCursor; // inputLineSampler.getProcessLine().cursor(); + resultCursor.reset(); + + // the first pixel is special as we cannot move it to -1 (might not be + // defined) + if ( resultCursor.hasNext() ) + { + resultCursor.fwd(); + a.set( resultCursor.get() ); + } + + while ( resultCursor.hasNext() ) + { + resultCursor.fwd(); + a.fwd(); + + a.set( resultCursor.get() ); + } + } + + /** + * Updates the current {@link SamplingLineIterator} to the location of the + * new line that is processed and clears the processing line (set all values + * to zero) + * + * @param a + * - the {@link SamplingLineIterator} + * @param range + * - the size of the output/temp image + * @param offset + * - the new position + * @param originalLocation + * - the location where is was put initially, all movements are + * relative to that + */ + protected void updateInputLineSampler( final SamplingLineIterator< T > a, final Interval range, final long[] offset, final Localizable originalLocation ) + { + final Positionable positionable = a.getPositionable(); + + for ( int d = 0; d < numDimensions; ++d ) + positionable.setPosition( originalLocation.getLongPosition( d ) + offset[ d ], d ); + + // clear the processing line + for ( final T v : a.getProcessLine() ) + v.setZero(); + } + + /** + * Updates the current {@link WritableLineIterator} to the location of the + * new line that is processed. + * + * @param a + * - the {@link WritableLineIterator} + * @param range + * - the size of the output/temp image + * @param offset + * - the new position + * @param originalLocation + * - the location where is was put initially, all movements are + * relative to that + */ + protected void updateOutputLineWriter( final WritableLineIterator< T > a, final Interval range, final long[] offset, final Localizable originalLocation ) + { + final Positionable positionable = a.getPositionable(); + + for ( int d = 0; d < numDimensions; ++d ) + positionable.setPosition( originalLocation.getLongPosition( d ) + offset[ d ], d ); + } + + /** + * Compute the Gaussian Kernel for all dimensions + */ + protected void computeKernel() + { + for ( int d = 0; d < numDimensions; ++d ) + this.kernel[ d ] = Util.createGaussianKernel1DDouble( sigma[ d ], true ); + } + + /** + * @return - the size the input images need to have + */ + protected Interval getTemporaryImgSize() + { + return getRange( 0 ); + } + + public int getNumThreads() + { + return numThreads; + } + + public void setNumThreads( final int numThreads ) + { + this.numThreads = Math.max( 1, numThreads ); + } + + public void call() + { + if ( numDimensions > 1 ) + { + for ( int d = 0; d < numDimensions; ++d ) + { + final int dim = d; + final int numThreads = getNumThreads(); + + final AtomicInteger ai = new AtomicInteger(); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + for ( int ithread = 0; ithread < threads.length; ++ithread ) + threads[ ithread ] = new Thread( new Runnable() + { + @Override + public void run() + { + final int myNumber = ai.getAndIncrement(); + + final Interval range = getRange( dim ); + + /** + * Here create a virtual + * LocalizingZeroMinIntervalIterator to iterate + * through all dimensions except the one we are + * computing in + */ + final long[] fakeSize = new long[ numDimensions - 1 ]; + final long[] tmp = new long[ numDimensions ]; + + // get all dimensions except the one we are + // currently doing the gauss on + int countDim = 0; + for ( int d = 0; d < numDimensions; ++d ) + if ( d != dim ) + fakeSize[ countDim++ ] = range.dimension( d ); + + // create the iterator in the input image for the + // current dimension + final SamplingLineIterator< T > inputLineIterator = createInputLineSampler( dim, range ); + final Localizable offsetInput = inputLineIterator.getOffset(); + + // get the iterator in the output image for the + // current dimension position + final WritableLineIterator< T > outputLineIterator = createOutputLineWriter( dim, range, inputLineIterator ); + final Localizable offsetOutput = outputLineIterator.getOffset(); + + final LocalizingZeroMinIntervalIterator cursorDim = new LocalizingZeroMinIntervalIterator( fakeSize ); + + // iterate over all dimensions except the one we are + // computing in + while ( cursorDim.hasNext() ) + { + cursorDim.fwd(); + + if ( numThreads == 1 || cursorDim.getIntPosition( 0 ) % numThreads == myNumber ) + { + // update all positions except for the one + // we are currrently doing the gauss on + cursorDim.localize( fakeSize ); + + tmp[ dim ] = 0; + countDim = 0; + for ( int d = 0; d < numDimensions; ++d ) + if ( d != dim ) + tmp[ d ] = fakeSize[ countDim++ ]; + + // update the iterator in the input image + // for the current dimension position + updateInputLineSampler( inputLineIterator, range, tmp, offsetInput ); + + // compute the current line + processLine( inputLineIterator, kernel[ dim ] ); + + // update the iterator in the input image + // for the current dimension position + updateOutputLineWriter( outputLineIterator, range, tmp, offsetOutput ); + + // and write it back to the output/temp + // image + writeLine( outputLineIterator, inputLineIterator ); + } + } + } + } ); + + SimpleMultiThreading.startAndJoin( threads ); + } + } + else + { + final Interval range = getRange( 0 ); + + // create the iterator in the input image for the current dimension + final SamplingLineIterator< T > inputLineIterator = createInputLineSampler( 0, range ); + + // get the iterator in the output image for the current dimension + // position + final WritableLineIterator< T > outputLineIterator = createOutputLineWriter( 0, range, inputLineIterator ); + + // compute the current line + processLine( inputLineIterator, kernel[ 0 ] ); + + // and write it back to the output/temp image + writeLine( outputLineIterator, inputLineIterator ); + } + } +} diff --git a/src/main/java/varun_algorithm_gauss/AbstractLineIterator.java b/src/main/java/varun_algorithm_gauss/AbstractLineIterator.java new file mode 100644 index 0000000..fdcbfe0 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/AbstractLineIterator.java @@ -0,0 +1,158 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.Iterator; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.Positionable; +import net.imglib2.converter.Converter; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +public abstract class AbstractLineIterator implements Iterator +{ + long i; + + final int d; + + final long size; + + final Positionable positionable; + + final Localizable offset; + + /** + * Make a new LineIterator which iterates a 1d line of a certain length + * + * @param dim + * - which dimension to iterate (dimension id) + * @param size + * - number of pixels to iterate + * @param randomAccess + * - defines the right position (one pixel left of the starting + * pixel) and can be moved along the line + */ + public < A extends Localizable & Positionable > AbstractLineIterator( final int dim, final long size, final A randomAccess ) + { + this( dim, size, randomAccess, randomAccess ); + } + + /** + * Make a new LineIterator which iterates a 1d line of a certain length + * + * @param dim + * - which dimension to iterate (dimension id) + * @param size + * - number of pixels to iterate + * @param offset + * - defines the right position (one pixel left of the starting + * pixel) + * @param positionable + * - the {@link Positionable} + */ + public AbstractLineIterator( final int dim, final long size, final Localizable offset, final Positionable positionable ) + { + this.d = dim; + this.size = size; + this.positionable = positionable; + + // store the initial position + if ( positionable == offset ) + this.offset = new Point( offset ); + else + this.offset = offset; + + positionable.setPosition( offset ); + + reset(); + } + + /** + * In this way it is possible to reposition the {@link Positionable} from + * outside without having the need to keep the instance explicitly. This + * repositioning is not dependent wheather a {@link Converter} is used or + * not. + * + * @return - the {@link Localizable} defining the initial offset + */ + public Localizable getOffset() + { + return offset; + } + + /** + * In this way it is possible to reposition the {@link Positionable} from + * outside without having the need to keep the instance explicitly. This + * repositioning is not dependent wheather a {@link Converter} is used or + * not. + * + * @return - the positionable of the {@link AbstractLineIterator} + */ + public Positionable getPositionable() + { + return positionable; + } + + @Override + public void jumpFwd( final long steps ) + { + i += steps; + positionable.move( steps, d ); + } + + @Override + public void fwd() + { + ++i; + positionable.fwd( d ); + } + + @Override + public void reset() + { + i = -1; + } + + @Override + public boolean hasNext() + { + return i < size; + } +} diff --git a/src/main/java/varun_algorithm_gauss/Gauss.java b/src/main/java/varun_algorithm_gauss/Gauss.java new file mode 100644 index 0000000..5bde7aa --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/Gauss.java @@ -0,0 +1,949 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.EuclideanSpace; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.converter.readwrite.RealDoubleSamplerConverter; +import net.imglib2.converter.readwrite.RealFloatSamplerConverter; +import net.imglib2.converter.readwrite.WriteConvertedIterableRandomAccessibleInterval; +import net.imglib2.converter.readwrite.WriteConvertedRandomAccessible; +import net.imglib2.converter.readwrite.WriteConvertedRandomAccessibleInterval; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary; +import net.imglib2.type.NativeType; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +/** + * TODO + * + */ +@Deprecated +public class Gauss +{ + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img as {@link FloatType} + */ + public static < T extends RealType< T >> Img< FloatType > toFloat( final double sigma, final Img< T > img ) + { + return toFloat( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img as {@link FloatType} + */ + public static < T extends RealType< T >> Img< FloatType > toFloat( final double[] sigma, final Img< T > img ) + { + return toFloat( sigma, img, new OutOfBoundsMirrorFactory< FloatType, RandomAccessibleInterval< FloatType > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img as {@link FloatType} + */ + public static < T extends RealType< T >> Img< FloatType > toFloat( final double sigma, final Img< T > img, + final OutOfBoundsFactory< FloatType, RandomAccessibleInterval< FloatType > > outofbounds ) + { + return toFloat( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img as {@link FloatType} + */ + public static < T extends RealType< T >> Img< FloatType > toFloat( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< FloatType, RandomAccessibleInterval< FloatType > > outofbounds ) + { + GaussFloat gauss = null; + try + { + if ( FloatType.class.isInstance( img.firstElement() ) ) + { + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final Img< FloatType > img2 = ( Img ) img; + gauss = new GaussFloat( sigma, img2 ); + } + else + { + final RandomAccessibleInterval< FloatType > rIn = new WriteConvertedIterableRandomAccessibleInterval< T, FloatType, Img< T > >( img, new RealFloatSamplerConverter< T >() ); + gauss = new GaussFloat( sigma, Views.extend( rIn, outofbounds ), img, img.factory().imgFactory( new FloatType() ) ); + } + } + catch ( final IncompatibleTypeException e ) + { + return null; + } + + gauss.call(); + + return ( Img< FloatType > ) gauss.getResult(); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img with img precision + */ + public static < T extends RealType< T >> Img< T > inFloat( final double sigma, final Img< T > img ) + { + return inFloat( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img with img precision + */ + public static < T extends RealType< T >> Img< T > inFloat( final double[] sigma, final Img< T > img ) + { + return inFloat( sigma, img, new OutOfBoundsMirrorFactory< FloatType, RandomAccessibleInterval< FloatType > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img with img precision + */ + public static < T extends RealType< T >> Img< T > inFloat( final double sigma, final Img< T > img, + final OutOfBoundsFactory< FloatType, RandomAccessibleInterval< FloatType > > outofbounds ) + { + return inFloat( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img with img precision + */ + @SuppressWarnings( { "unchecked", "rawtypes" } ) + public static < T extends RealType< T >> Img< T > inFloat( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< FloatType, RandomAccessibleInterval< FloatType > > outofbounds ) + { + try + { + if ( FloatType.class.isInstance( img.firstElement() ) ) { return ( Img ) toFloat( sigma, img, outofbounds ); } + final Img< T > output = img.factory().create( img, img.firstElement() ); + + final RandomAccessible< FloatType > rIn = Views.extend( new WriteConvertedRandomAccessibleInterval< T, FloatType >( img, new RealFloatSamplerConverter< T >() ), outofbounds ); + final RandomAccessible< FloatType > rOut = new WriteConvertedRandomAccessible< T, FloatType >( output, new RealFloatSamplerConverter< T >() ); + + inFloat( sigma, rIn, img, rOut, new Point( sigma.length ), img.factory().imgFactory( new FloatType() ) ); + + return output; + } + catch ( final IncompatibleTypeException e ) + { + return null; + } + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with float precision on an entire {@link Img} using the + * {@link OutOfBoundsMirrorFactory} with single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + */ + public static < T extends RealType< T >> void inFloatInPlace( final double sigma, final Img< T > img ) + { + inFloatInPlace( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with float precision on an entire {@link Img} using the + * {@link OutOfBoundsMirrorFactory} with single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + */ + public static < T extends RealType< T >> void inFloatInPlace( final double[] sigma, final Img< T > img ) + { + inFloatInPlace( sigma, img, new OutOfBoundsMirrorFactory< FloatType, RandomAccessibleInterval< FloatType > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with float precision on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} that will be convolved in place + */ + public static < T extends RealType< T >> void inFloatInPlace( final double sigma, final Img< T > img, + final OutOfBoundsFactory< FloatType, RandomAccessibleInterval< FloatType > > outofbounds ) + { + inFloatInPlace( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with float precision on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} that will be convolved in place + */ + public static < T extends RealType< T >> void inFloatInPlace( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< FloatType, RandomAccessibleInterval< FloatType > > outofbounds ) + { + GaussFloat gauss = null; + try + { + if ( FloatType.class.isInstance( img.firstElement() ) ) + { + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final Img< FloatType > img2 = ( Img ) img; + gauss = new GaussFloat( sigma, Views.extend( img2, outofbounds ), img2, img2, new Point( sigma.length ), img2.factory().imgFactory( new FloatType() ) ); + } + else + { + final RandomAccessibleInterval< FloatType > rIn = new WriteConvertedIterableRandomAccessibleInterval< T, FloatType, Img< T > >( img, new RealFloatSamplerConverter< T >() ); + gauss = new GaussFloat( sigma, Views.extend( rIn, outofbounds ), img, rIn, new Point( sigma.length ), img.factory().imgFactory( new FloatType() ) ); + } + } + catch ( final IncompatibleTypeException e ) + { + System.out.println( e ); + return; + } + + gauss.call(); + } + + /** + * Computes a Gaussian convolution with float precision on an infinite + * {@link RandomAccessible} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link RandomAccessible} + * ({@code infinite -> Views.extend( ... )}) + * @param interval + * - the interval which should be convolved + * @param output + * - the output {@link RandomAccessible} (img and output can be + * the same) + * @param origin + * - the origin in the output where the result should be placed + * @param imgFactory + * - the {@link ImgFactory} for {@link FloatType} which is needed + * for temporary imgs + */ + public static < T extends RealType< T >> void inFloat( final double sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< FloatType > imgFactory ) + { + inFloat( getSigmaDim( sigma, img ), img, interval, output, origin, imgFactory ); + } + + /** + * Computes a Gaussian convolution with float precision on an infinite + * {@link RandomAccessible} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link RandomAccessible} + * ({@code infinite -> Views.extend( ... )}) + * @param interval + * - the interval which should be convolved + * @param output + * - the output {@link RandomAccessible} (img and output can be + * the same) + * @param origin + * - the origin in the output where the result should be placed + * @param imgFactory + * - the {@link ImgFactory} for {@link FloatType} which is needed + * for temporary imgs + */ + public static < T extends RealType< T >> void inFloat( final double[] sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< FloatType > imgFactory ) + { + // find out if it is a FloatType, therefore we get the first value + // defined by the + // interval in the img + final long[] tmpCoordinate = new long[ img.numDimensions() ]; + interval.min( tmpCoordinate ); + final RandomAccess< T > tmp = img.randomAccess(); + tmp.setPosition( tmpCoordinate ); + + if ( FloatType.class.isInstance( tmp.get() ) ) + { + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final RandomAccessible< FloatType > rIn = ( RandomAccessible ) img; + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final RandomAccessible< FloatType > rOut = ( RandomAccessible ) output; + + new GaussFloat( sigma, rIn, interval, rOut, origin, imgFactory ).call(); + } + else + { + final RandomAccessible< FloatType > rIn = new WriteConvertedRandomAccessible< T, FloatType >( img, new RealFloatSamplerConverter< T >() ); + final RandomAccessible< FloatType > rOut = new WriteConvertedRandomAccessible< T, FloatType >( output, new RealFloatSamplerConverter< T >() ); + + new GaussFloat( sigma, rIn, interval, rOut, origin, imgFactory ).call(); + } + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img in {@link DoubleType} + */ + public static < T extends RealType< T >> Img< DoubleType > toDouble( final double sigma, final Img< T > img ) + { + return toDouble( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img in {@link DoubleType} + */ + public static < T extends RealType< T >> Img< DoubleType > toDouble( final double[] sigma, final Img< T > img ) + { + return toDouble( sigma, img, new OutOfBoundsMirrorFactory< DoubleType, RandomAccessibleInterval< DoubleType > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img in {@link DoubleType} + */ + public static < T extends RealType< T >> Img< DoubleType > toDouble( final double sigma, final Img< T > img, + final OutOfBoundsFactory< DoubleType, RandomAccessibleInterval< DoubleType > > outofbounds ) + { + return toDouble( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img in {@link DoubleType} + */ + public static < T extends RealType< T >> Img< DoubleType > toDouble( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< DoubleType, RandomAccessibleInterval< DoubleType > > outofbounds ) + { + GaussDouble gauss = null; + try + { + if ( DoubleType.class.isInstance( img.firstElement() ) ) + { + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final Img< DoubleType > img2 = ( Img ) img; + gauss = new GaussDouble( sigma, img2 ); + } + else + { + final RandomAccessibleInterval< DoubleType > rIn = new WriteConvertedIterableRandomAccessibleInterval< T, DoubleType, Img< T > >( img, new RealDoubleSamplerConverter< T >() ); + gauss = new GaussDouble( sigma, Views.extend( rIn, outofbounds ), img, img.factory().imgFactory( new DoubleType() ) ); + } + } + catch ( final IncompatibleTypeException e ) + { + return null; + } + + gauss.call(); + + return ( Img< DoubleType > ) gauss.getResult(); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img having the input type + */ + public static < T extends RealType< T >> Img< T > inDouble( final double sigma, final Img< T > img ) + { + return inDouble( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img having the input type + */ + public static < T extends RealType< T >> Img< T > inDouble( final double[] sigma, final Img< T > img ) + { + return inDouble( sigma, img, new OutOfBoundsMirrorFactory< DoubleType, RandomAccessibleInterval< DoubleType > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img having the input type + */ + public static < T extends RealType< T >> Img< T > inDouble( final double sigma, final Img< T > img, + final OutOfBoundsFactory< DoubleType, RandomAccessibleInterval< DoubleType > > outofbounds ) + { + return inDouble( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img having the input type + */ + @SuppressWarnings( { "unchecked", "rawtypes" } ) + public static < T extends RealType< T >> Img< T > inDouble( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< DoubleType, RandomAccessibleInterval< DoubleType > > outofbounds ) + { + try + { + if ( DoubleType.class.isInstance( img.firstElement() ) ) { return ( Img ) toDouble( sigma, img, outofbounds ); } + final Img< T > output = img.factory().create( img, img.firstElement() ); + + final RandomAccessible< DoubleType > rIn = Views.extend( new WriteConvertedRandomAccessibleInterval< T, DoubleType >( img, new RealDoubleSamplerConverter< T >() ), outofbounds ); + final RandomAccessible< DoubleType > rOut = new WriteConvertedRandomAccessible< T, DoubleType >( output, new RealDoubleSamplerConverter< T >() ); + + inDouble( sigma, rIn, img, rOut, new Point( sigma.length ), img.factory().imgFactory( new DoubleType() ) ); + + return output; + } + catch ( final IncompatibleTypeException e ) + { + return null; + } + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with double precision on an entire {@link Img} using the + * {@link OutOfBoundsMirrorFactory} with single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + */ + public static < T extends RealType< T >> void inDoubleInPlace( final double sigma, final Img< T > img ) + { + inDoubleInPlace( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with double precision on an entire {@link Img} using the + * {@link OutOfBoundsMirrorFactory} with single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + */ + public static < T extends RealType< T >> void inDoubleInPlace( final double[] sigma, final Img< T > img ) + { + inDoubleInPlace( sigma, img, new OutOfBoundsMirrorFactory< DoubleType, RandomAccessibleInterval< DoubleType > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with double precision on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} that will be convolved in place + */ + public static < T extends RealType< T >> void inDoubleInPlace( final double sigma, final Img< T > img, + final OutOfBoundsFactory< DoubleType, RandomAccessibleInterval< DoubleType > > outofbounds ) + { + inDoubleInPlace( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with double precision on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} that will be convolved in place + */ + public static < T extends RealType< T >> void inDoubleInPlace( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< DoubleType, RandomAccessibleInterval< DoubleType > > outofbounds ) + { + GaussDouble gauss = null; + try + { + if ( DoubleType.class.isInstance( img.firstElement() ) ) + { + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final Img< DoubleType > img2 = ( Img ) img; + gauss = new GaussDouble( sigma, Views.extend( img2, outofbounds ), img2, img2, new Point( sigma.length ), img2.factory().imgFactory( new DoubleType() ) ); + } + else + { + final RandomAccessibleInterval< DoubleType > rIn = new WriteConvertedIterableRandomAccessibleInterval< T, DoubleType, Img< T > >( img, new RealDoubleSamplerConverter< T >() ); + gauss = new GaussDouble( sigma, Views.extend( rIn, outofbounds ), img, rIn, new Point( sigma.length ), img.factory().imgFactory( new DoubleType() ) ); + } + } + catch ( final IncompatibleTypeException e ) + { + return; + } + + gauss.call(); + } + + /** + * Computes a Gaussian convolution with double precision on an infinite + * {@link RandomAccessible} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link RandomAccessible} + * ({@code infinite -> Views.extend( ... )}) + * @param interval + * - the interval which should be convolved + * @param output + * - the output {@link RandomAccessible} (img and output can be + * the same) + * @param origin + * - the origin in the output where the result should be placed + * @param imgFactory + * - the {@link ImgFactory} for {@link DoubleType} which is + * needed for temporary imgs + */ + public static < T extends RealType< T >> void inDouble( final double sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< DoubleType > imgFactory ) + { + inDouble( getSigmaDim( sigma, img ), img, interval, output, origin, imgFactory ); + } + + /** + * Computes a Gaussian convolution with double precision on an infinite + * {@link RandomAccessible} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link RandomAccessible} + * ({@code infinite -> Views.extend( ... )}) + * @param interval + * - the interval which should be convolved + * @param output + * - the output {@link RandomAccessible} (img and output can be + * the same) + * @param origin + * - the origin in the output where the result should be placed + * @param imgFactory + * - the {@link ImgFactory} for {@link DoubleType} which is + * needed for temporary imgs + */ + public static < T extends RealType< T >> void inDouble( final double[] sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< DoubleType > imgFactory ) + { + // find out if it is a DoubleType, therefore we get the first value + // defined by the + // interval in the img + final long[] tmpCoordinate = new long[ img.numDimensions() ]; + interval.min( tmpCoordinate ); + final RandomAccess< T > tmp = img.randomAccess(); + tmp.setPosition( tmpCoordinate ); + + if ( DoubleType.class.isInstance( tmp.get() ) ) + { + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final RandomAccessible< DoubleType > rIn = ( RandomAccessible ) img; + @SuppressWarnings( { "rawtypes", "unchecked" } ) + final RandomAccessible< DoubleType > rOut = ( RandomAccessible ) output; + + new GaussDouble( sigma, rIn, interval, rOut, origin, imgFactory ).call(); + } + else + { + final RandomAccessible< DoubleType > rIn = new WriteConvertedRandomAccessible< T, DoubleType >( img, new RealDoubleSamplerConverter< T >() ); + final RandomAccessible< DoubleType > rOut = new WriteConvertedRandomAccessible< T, DoubleType >( output, new RealDoubleSamplerConverter< T >() ); + + new GaussDouble( sigma, rIn, interval, rOut, origin, imgFactory ).call(); + } + } + + /** + * Computes a Gaussian convolution with the precision of the type provided + * on an entire {@link Img} using the {@link OutOfBoundsMirrorFactory} with + * single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img + */ + public static < T extends NumericType< T >> Img< T > inNumericType( final double sigma, final Img< T > img ) + { + return inNumericType( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution with the precision of the type provided + * on an entire {@link Img} using the {@link OutOfBoundsMirrorFactory} with + * single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @return the convolved img + */ + public static < T extends NumericType< T >> Img< T > inNumericType( final double[] sigma, final Img< T > img ) + { + return inNumericType( sigma, img, new OutOfBoundsMirrorFactory< T, RandomAccessibleInterval< T > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution with the precision of the type provided + * on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img + */ + public static < T extends NumericType< T >> Img< T > inNumericType( final double sigma, final Img< T > img, + final OutOfBoundsFactory< T, RandomAccessibleInterval< T > > outofbounds ) + { + return inNumericType( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution with the precision of the type provided + * on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + * @param outofbounds + * - the {@link OutOfBoundsFactory} + * @return the convolved img + */ + public static < T extends NumericType< T >> Img< T > inNumericType( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< T, RandomAccessibleInterval< T > > outofbounds ) + { + final Img< T > output = img.factory().create( img, img.firstElement() ); + inNumericType( sigma, Views.extend( img, outofbounds ), img, output, new Point( sigma.length ), img.factory() ); + return output; + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with the precision of the type provided on an entire {@link Img} using + * the {@link OutOfBoundsMirrorFactory} with single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + */ + public static < T extends NumericType< T >> void inNumericTypeInPlace( final double sigma, final Img< T > img ) + { + inNumericTypeInPlace( getSigmaDim( sigma, img ), img ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with the precision of the type provided on an entire {@link Img} using + * the {@link OutOfBoundsMirrorFactory} with single boundary + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} + */ + public static < T extends NumericType< T >> void inNumericTypeInPlace( final double[] sigma, final Img< T > img ) + { + inNumericTypeInPlace( sigma, img, new OutOfBoundsMirrorFactory< T, RandomAccessibleInterval< T > >( Boundary.SINGLE ) ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with the precision of the type provided on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} that will be convolved in place + */ + public static < T extends NumericType< T >> void inNumericTypeInPlace( final double sigma, final Img< T > img, + final OutOfBoundsFactory< T, RandomAccessibleInterval< T > > outofbounds ) + { + inNumericTypeInPlace( getSigmaDim( sigma, img ), img, outofbounds ); + } + + /** + * Computes a Gaussian convolution in-place (temporary imgs are necessary) + * with the precision of the type provided on an entire {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link Img} that will be convolved in place + */ + public static < T extends NumericType< T >> void inNumericTypeInPlace( final double[] sigma, final Img< T > img, + final OutOfBoundsFactory< T, RandomAccessibleInterval< T > > outofbounds ) + { + inNumericType( sigma, Views.extend( img, outofbounds ), img, img, new Point( sigma.length ), img.factory() ); + } + + /** + * Computes a Gaussian convolution with the precision of the type provided + * on an infinite {@link RandomAccessible} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link RandomAccessible} + * ({@code infinite -> Views.extend( ... )}) + * @param interval + * - the interval which should be convolved + * @param output + * - the output {@link RandomAccessible} (img and output can be + * the same) + * @param origin + * - the origin in the output where the result should be placed + * @param imgFactory + * - the {@link ImgFactory} for T which is needed for temporary + * imgs + */ + public static < T extends NumericType< T >> void inNumericType( final double sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< T > imgFactory ) + { + inNumericType( getSigmaDim( sigma, img ), img, interval, output, origin, imgFactory ); + } + + /** + * Computes a Gaussian convolution with the precision of the type provided + * on an infinite {@link RandomAccessible} + * + * @param sigma + * - the sigma for the convolution + * @param img + * - the img {@link RandomAccessible} + * ({@code infinite -> Views.extend( ... )}) + * @param interval + * - the interval which should be convolved + * @param output + * - the output {@link RandomAccessible} (img and output can be + * the same) + * @param origin + * - the origin in the output where the result should be placed + * @param imgFactory + * - the {@link ImgFactory} for T which is needed for temporary + * imgs + */ + @SuppressWarnings( "rawtypes" ) + public static < T extends NumericType< T >> void inNumericType( final double[] sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< T > imgFactory ) + { + // find out if it is a NativeType, therefore we get the first value + // defined by the + // interval in the img - we also need it in this case for the + // convolution + final long[] tmpCoordinate = new long[ img.numDimensions() ]; + interval.min( tmpCoordinate ); + final RandomAccess< T > tmp = img.randomAccess(); + tmp.setPosition( tmpCoordinate ); + + if ( NativeType.class.isInstance( tmp.get() ) ) + { + // we have to call it in an extra method because we cannot cast to + // (NumericType & NativeType) + computeInNativeType( sigma, ( RandomAccessible ) img, interval, ( RandomAccessible ) output, origin, ( ImgFactory ) imgFactory, tmp.get() ); + } + else + { + final GaussGeneral< T > gauss = new GaussGeneral< T >( sigma, img, interval, output, origin, imgFactory, tmp.get() ); + gauss.call(); + } + } + + private static final < T extends NumericType< T > & NativeType< T > > void computeInNativeType( final double[] sigma, final RandomAccessible< T > img, final Interval interval, + final RandomAccessible< T > output, final Localizable origin, final ImgFactory< T > imgFactory, @SuppressWarnings( "rawtypes" ) final Type type ) + { + @SuppressWarnings( "unchecked" ) + final GaussNativeType< T > gauss = new GaussNativeType< T >( sigma, img, interval, output, origin, imgFactory, ( T ) type ); + gauss.call(); + } + + private static final double[] getSigmaDim( final double sigma, final EuclideanSpace img ) + { + final double s[] = new double[ img.numDimensions() ]; + + for ( int d = 0; d < img.numDimensions(); ++d ) + s[ d ] = sigma; + + return s; + } +} diff --git a/src/main/java/varun_algorithm_gauss/GaussDouble.java b/src/main/java/varun_algorithm_gauss/GaussDouble.java new file mode 100644 index 0000000..8fe8c94 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/GaussDouble.java @@ -0,0 +1,371 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.Interval; +import net.imglib2.Iterator; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; +import net.imglib2.converter.Converter; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.NativeImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.basictypeaccess.array.DoubleArray; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.view.Views; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +final public class GaussDouble extends AbstractGauss< DoubleType > +{ + protected boolean isArray; + + /** + * Computes a Gaussian convolution with double precision on a + * {@link RandomAccessible} of {@link DoubleType} in a certain + * {@link Interval} and returns an {@link Img} defined by the + * {@link ImgFactory} containing the result + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param factory + * - the {@link ImgFactory} that defines the temporary and output + * images to be used + */ + public GaussDouble( final double[] sigma, final RandomAccessible< DoubleType > input, final Interval interval, final ImgFactory< DoubleType > factory ) + { + super( sigma, input, interval, factory.create( interval, new DoubleType() ), new Point( sigma.length ), factory, new DoubleType() ); + } + + /** + * Computes a Gaussian convolution with double precision on a + * {@link RandomAccessible} of {@link DoubleType} in a certain + * {@link Interval} and writes it into a given {@link RandomAccessible} at a + * specific Point + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param output + * - the {@link RandomAccessible} where the output will be + * written to + * @param outputOffset + * - the offset that corresponds to the first pixel in output + * {@link RandomAccessible} + * @param factory + * - the {@link ImgFactory} for creating temporary images + */ + public GaussDouble( final double[] sigma, final RandomAccessible< DoubleType > input, final Interval interval, final RandomAccessible< DoubleType > output, final Localizable outputOffset, final ImgFactory< DoubleType > factory ) + { + super( sigma, input, interval, output, outputOffset, factory, new DoubleType() ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + */ + public GaussDouble( final double[] sigma, final Img< DoubleType > input ) + { + this( sigma, Views.extend( input, new OutOfBoundsMirrorFactory< DoubleType, Img< DoubleType > >( Boundary.SINGLE ) ), input, input.factory() ); + } + + /** + * Computes a Gaussian convolution with double precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + * @param outOfBounds + * - the {@link OutOfBoundsFactory} to use + */ + public GaussDouble( final double[] sigma, final Img< DoubleType > input, final OutOfBoundsFactory< DoubleType, Img< DoubleType > > outOfBounds ) + { + this( sigma, Views.extend( input, outOfBounds ), input, input.factory() ); + } + + public static Img< DoubleType > gauss( final double[] sigma, final Img< DoubleType > input ) + { + final GaussDouble gauss = new GaussDouble( sigma, input ); + gauss.call(); + return ( Img< DoubleType > ) gauss.getResult(); + } + + public static Img< DoubleType > gauss( final double[] sigma, final Img< DoubleType > input, final OutOfBoundsFactory< DoubleType, Img< DoubleType > > outOfBounds ) + { + final GaussDouble gauss = new GaussDouble( sigma, input, outOfBounds ); + gauss.call(); + return ( Img< DoubleType > ) gauss.getResult(); + } + + public static Img< DoubleType > gauss( final double[] sigma, final RandomAccessible< DoubleType > input, final Interval interval, final ImgFactory< DoubleType > factory ) + { + final GaussDouble gauss = new GaussDouble( sigma, input, interval, factory ); + gauss.call(); + return ( Img< DoubleType > ) gauss.getResult(); + } + + public static void gauss( final double[] sigma, final RandomAccessible< DoubleType > input, final Interval interval, final RandomAccessible< DoubleType > output, final Localizable outputOffset, final ImgFactory< DoubleType > factory ) + { + final GaussDouble gauss = new GaussDouble( sigma, input, interval, output, outputOffset, factory ); + gauss.call(); + } + + @Override + protected Img< DoubleType > getProcessingLine( final long sizeProcessLine ) + { + final Img< DoubleType > processLine; + + // try to use array if each individual line is not too long + if ( sizeProcessLine <= Integer.MAX_VALUE ) + { + isArray = true; + processLine = new ArrayImgFactory< DoubleType >().create( new long[] { sizeProcessLine }, new DoubleType() ); + } + else + { + isArray = false; + processLine = new CellImgFactory< DoubleType >( Integer.MAX_VALUE / 16 ).create( new long[] { sizeProcessLine }, new DoubleType() ); + } + + return processLine; + } + + /** + * Compute the current line. It is up to the implementation howto really do + * that. The idea is to only iterate over the input once (that's why it is + * an {@link Iterator}) as it is potentially an expensive operation (e.g. a + * {@link Converter} might be involved or we are computing on a rendered + * input) + * + * @param input + * - the {@link Iterator}/{@link Sampler} over the current input + * line. + */ + @Override + protected void processLine( final SamplingLineIterator< DoubleType > input, final double[] kernel ) + { + if ( !isArray() ) + { + super.processLine( input, kernel ); + return; + } + + final int kernelSize = kernel.length; + final int kernelSizeMinus1 = kernelSize - 1; + final int kernelSizeHalf = kernelSize / 2; + final int kernelSizeHalfMinus1 = kernelSizeHalf - 1; + + final double[] v = ( ( DoubleArray ) ( ( NativeImg< ?, ? > ) input.getProcessLine() ).update( null ) ).getCurrentStorageArray(); + + final int imgSize = v.length; + + int indexLeft = 0; + int indexRight = 0; + + if ( imgSize >= kernelSize ) + { + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + v[ 0 ] += input.get().get() * kernel[ 0 ]; + + for ( int i = 1; i < kernelSizeMinus1; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final double copy = input.get().get(); + + // set the random access in the processing line to the right + // position + indexLeft = -1; + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + v[ ++indexLeft ] += ( copy * kernel[ i - o ] ); + } + + // convolve all values where the input value contributes to the full + // kernel + final int length = imgSize - kernelSizeMinus1; + for ( int n = 0; n < length; ++n ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final double copy = input.get().get(); + + // set the left and the right random access to the right + // coordinates + // the left random access is always responsible for the center + indexLeft = n; + indexRight = n + kernelSizeMinus1; + + // move till the last pixel before the center of the kernel + for ( int k = 0; k < kernelSizeHalfMinus1; ++k ) + { + final double tmp = ( copy * kernel[ k ] ); + + v[ indexLeft++ ] += tmp; + v[ indexRight-- ] += tmp; + } + + // do the last pixel (same as a above, but right cursor doesn't + // move) + final double tmp = ( copy * kernel[ kernelSizeHalfMinus1 ] ); + + v[ indexLeft++ ] += tmp; + v[ indexRight ] += tmp; + + // do the center pixel + v[ indexLeft ] += ( copy * kernel[ kernelSizeHalf ] ); + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + final int endLength = imgSize + kernelSizeMinus1; + for ( int i = imgSize; i < endLength; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final double copy = input.get().get(); + + // set the random access in the processing line to the right + // position + indexLeft = i - kernelSize; + + // now add it to all output values it contributes to + int k = 0; + for ( long o = i - kernelSize + 1; o < imgSize; ++o ) + v[ ++indexLeft ] += ( copy * kernel[ k++ ] ); + } + } + else + { + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + v[ 0 ] += input.get().get() * kernel[ 0 ]; + + for ( int i = 1; i < imgSize; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final double copy = input.get().get(); + + // set the random access in the processing line to the right + // position + indexLeft = -1; + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + v[ ++indexLeft ] += ( copy * kernel[ i - o ] ); + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + for ( int i = imgSize; i < imgSize + kernelSizeMinus1; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final double copy = input.get().get(); + + // set the random access in the processing line to the right + // position + // now add it to all output values it contributes to + int o = i - kernelSize + 1; + int k = 0; + + if ( o < 0 ) + { + k = -o; + o = 0; + } + + for ( ; o < imgSize; ++o ) + v[ o ] += ( copy * kernel[ k++ ] ); + } + } + } + + protected boolean isArray() + { + return isArray; + } +} diff --git a/src/main/java/varun_algorithm_gauss/GaussFloat.java b/src/main/java/varun_algorithm_gauss/GaussFloat.java new file mode 100644 index 0000000..0838921 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/GaussFloat.java @@ -0,0 +1,371 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.Interval; +import net.imglib2.Iterator; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; +import net.imglib2.converter.Converter; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.NativeImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.basictypeaccess.array.FloatArray; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +final public class GaussFloat extends AbstractGauss< FloatType > +{ + protected boolean isArray; + + /** + * Computes a Gaussian convolution with float precision on a + * {@link RandomAccessible} of {@link FloatType} in a certain + * {@link Interval} and returns an {@link Img} defined by the + * {@link ImgFactory} containing the result + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param factory + * - the {@link ImgFactory} that defines the temporary and output + * images to be used + */ + public GaussFloat( final double[] sigma, final RandomAccessible< FloatType > input, final Interval interval, final ImgFactory< FloatType > factory ) + { + super( sigma, input, interval, factory.create( interval, new FloatType() ), new Point( sigma.length ), factory, new FloatType() ); + } + + /** + * Computes a Gaussian convolution with float precision on a + * {@link RandomAccessible} of {@link FloatType} in a certain + * {@link Interval} and writes it into a given {@link RandomAccessible} at a + * specific Point + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param output + * - the {@link RandomAccessible} where the output will be + * written to + * @param outputOffset + * - the offset that corresponds to the first pixel in output + * {@link RandomAccessible} + * @param factory + * - the {@link ImgFactory} for creating temporary images + */ + public GaussFloat( final double[] sigma, final RandomAccessible< FloatType > input, final Interval interval, final RandomAccessible< FloatType > output, final Localizable outputOffset, final ImgFactory< FloatType > factory ) + { + super( sigma, input, interval, output, outputOffset, factory, new FloatType() ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + */ + public GaussFloat( final double[] sigma, final Img< FloatType > input ) + { + this( sigma, Views.extend( input, new OutOfBoundsMirrorFactory< FloatType, Img< FloatType > >( Boundary.SINGLE ) ), input, input.factory() ); + } + + /** + * Computes a Gaussian convolution with float precision on an entire + * {@link Img} + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + * @param outOfBounds + * - the {@link OutOfBoundsFactory} to use + */ + public GaussFloat( final double[] sigma, final Img< FloatType > input, final OutOfBoundsFactory< FloatType, Img< FloatType > > outOfBounds ) + { + this( sigma, Views.extend( input, outOfBounds ), input, input.factory() ); + } + + public static Img< FloatType > gauss( final double[] sigma, final Img< FloatType > input ) + { + final GaussFloat gauss = new GaussFloat( sigma, input ); + gauss.call(); + return ( Img< FloatType > ) gauss.getResult(); + } + + public static Img< FloatType > gauss( final double[] sigma, final Img< FloatType > input, final OutOfBoundsFactory< FloatType, Img< FloatType > > outOfBounds ) + { + final GaussFloat gauss = new GaussFloat( sigma, input, outOfBounds ); + gauss.call(); + return ( Img< FloatType > ) gauss.getResult(); + } + + public static Img< FloatType > gauss( final double[] sigma, final RandomAccessible< FloatType > input, final Interval interval, final ImgFactory< FloatType > factory ) + { + final GaussFloat gauss = new GaussFloat( sigma, input, interval, factory ); + gauss.call(); + return ( Img< FloatType > ) gauss.getResult(); + } + + public static void gauss( final double[] sigma, final RandomAccessible< FloatType > input, final Interval interval, final RandomAccessible< FloatType > output, final Localizable outputOffset, final ImgFactory< FloatType > factory ) + { + final GaussFloat gauss = new GaussFloat( sigma, input, interval, output, outputOffset, factory ); + gauss.call(); + } + + @Override + protected Img< FloatType > getProcessingLine( final long sizeProcessLine ) + { + final Img< FloatType > processLine; + + // try to use array if each individual line is not too long + if ( sizeProcessLine <= Integer.MAX_VALUE ) + { + isArray = true; + processLine = new ArrayImgFactory< FloatType >().create( new long[] { sizeProcessLine }, new FloatType() ); + } + else + { + isArray = false; + processLine = new CellImgFactory< FloatType >( Integer.MAX_VALUE / 16 ).create( new long[] { sizeProcessLine }, new FloatType() ); + } + + return processLine; + } + + /** + * Compute the current line. It is up to the implementation howto really do + * that. The idea is to only iterate over the input once (that's why it is + * an {@link Iterator}) as it is potentially an expensive operation (e.g. a + * {@link Converter} might be involved or we are computing on a rendered + * input) + * + * @param input + * - the {@link Iterator}/{@link Sampler} over the current input + * line. + */ + @Override + protected void processLine( final SamplingLineIterator< FloatType > input, final double[] kernel ) + { + if ( !isArray() ) + { + super.processLine( input, kernel ); + return; + } + + final int kernelSize = kernel.length; + final int kernelSizeMinus1 = kernelSize - 1; + final int kernelSizeHalf = kernelSize / 2; + final int kernelSizeHalfMinus1 = kernelSizeHalf - 1; + + final float[] v = ( ( FloatArray ) ( ( NativeImg< ?, ? > ) input.getProcessLine() ).update( null ) ).getCurrentStorageArray(); + + final int imgSize = v.length; + + int indexLeft = 0; + int indexRight = 0; + + if ( imgSize >= kernelSize ) + { + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + v[ 0 ] += ( float ) ( input.get().get() * kernel[ 0 ] ); + + for ( int i = 1; i < kernelSizeMinus1; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final float copy = input.get().get(); + + // set the random access in the processing line to the right + // position + indexLeft = -1; + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + v[ ++indexLeft ] += ( float ) ( copy * kernel[ i - o ] ); + } + + // convolve all values where the input value contributes to the full + // kernel + final int length = imgSize - kernelSizeMinus1; + for ( int n = 0; n < length; ++n ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final float copy = input.get().get(); + + // set the left and the right random access to the right + // coordinates + // the left random access is always responsible for the center + indexLeft = n; + indexRight = n + kernelSizeMinus1; + + // move till the last pixel before the center of the kernel + for ( int k = 0; k < kernelSizeHalfMinus1; ++k ) + { + final float tmp = ( float ) ( copy * kernel[ k ] ); + + v[ indexLeft++ ] += tmp; + v[ indexRight-- ] += tmp; + } + + // do the last pixel (same as a above, but right cursor doesn't + // move) + final float tmp = ( float ) ( copy * kernel[ kernelSizeHalfMinus1 ] ); + + v[ indexLeft++ ] += tmp; + v[ indexRight ] += tmp; + + // do the center pixel + v[ indexLeft ] += ( float ) ( copy * kernel[ kernelSizeHalf ] ); + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + final int endLength = imgSize + kernelSizeMinus1; + for ( int i = imgSize; i < endLength; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final float copy = input.get().get(); + + // set the random access in the processing line to the right + // position + indexLeft = i - kernelSize; + + // now add it to all output values it contributes to + int k = 0; + for ( long o = i - kernelSize + 1; o < imgSize; ++o ) + v[ ++indexLeft ] += ( float ) ( copy * kernel[ k++ ] ); + } + } + else + { + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + v[ 0 ] += ( float ) ( input.get().get() * kernel[ 0 ] ); + + for ( int i = 1; i < imgSize; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final float copy = input.get().get(); + + // set the random access in the processing line to the right + // position + indexLeft = -1; + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + v[ ++indexLeft ] += ( float ) ( copy * kernel[ i - o ] ); + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + for ( int i = imgSize; i < imgSize + kernelSizeMinus1; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + final float copy = input.get().get(); + + // set the random access in the processing line to the right + // position + // now add it to all output values it contributes to + int o = i - kernelSize + 1; + int k = 0; + + if ( o < 0 ) + { + k = -o; + o = 0; + } + + for ( ; o < imgSize; ++o ) + v[ o ] += ( float ) ( copy * kernel[ k++ ] ); + } + } + } + + protected boolean isArray() + { + return isArray; + } +} diff --git a/src/main/java/varun_algorithm_gauss/GaussGeneral.java b/src/main/java/varun_algorithm_gauss/GaussGeneral.java new file mode 100644 index 0000000..d77d4fe --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/GaussGeneral.java @@ -0,0 +1,168 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.list.ListImgFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.view.Views; + +/** + * TODO + * + */ +@Deprecated +public class GaussGeneral< T extends NumericType< T > > extends AbstractGauss< T > +{ + /** + * Computes a Gaussian convolution on a {@link RandomAccessible} in a + * certain {@link Interval} and returns an {@link Img} defined by the + * {@link ImgFactory} containing the result. + * + * WARNING: This is a very slow implementation as it is not written for + * {@link NativeType}. If your type is {@link NativeType}, use + * {@link GaussNativeType} instead! + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param factory + * - the {@link ImgFactory} that defines the temporary and output + * images to be used + */ + public GaussGeneral( final double[] sigma, final RandomAccessible< T > input, final Interval interval, final ImgFactory< T > factory, final T type ) + { + super( sigma, input, interval, factory.create( interval, type ), new Point( sigma.length ), factory, type ); + } + + /** + * Computes a Gaussian convolution with any precision on a + * {@link RandomAccessible} in a certain {@link Interval} and writes it into + * a given {@link RandomAccessible} at a specific Point + * + * WARNING: This is a very slow implementation as it is not written for + * {@link NativeType}. If your type is {@link NativeType}, use + * {@link GaussNativeType} instead! + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param output + * - the {@link RandomAccessible} where the output will be + * written to + * @param outputOffset + * - the offset that corresponds to the first pixel in output + * {@link RandomAccessible} + * @param factory + * - the {@link ImgFactory} for creating temporary images + */ + public GaussGeneral( final double[] sigma, final RandomAccessible< T > input, final Interval interval, final RandomAccessible< T > output, final Localizable outputOffset, final ImgFactory< T > factory, final T type ) + { + super( sigma, input, interval, output, outputOffset, factory, type ); + } + + /** + * Computes a Gaussian convolution with any precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * WARNING: This is a very slow implementation as it is not written for + * {@link NativeType}. If your type is {@link NativeType}, use + * {@link GaussNativeType} instead! + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + */ + public GaussGeneral( final double[] sigma, final Img< T > input ) + { + this( sigma, Views.extend( input, new OutOfBoundsMirrorFactory< T, Img< T > >( Boundary.SINGLE ) ), input, input.factory(), input.firstElement().createVariable() ); + } + + /** + * Computes a Gaussian convolution with any precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * WARNING: This is a very slow implementation as it is not written for + * {@link NativeType}. If your type is {@link NativeType}, use + * {@link GaussNativeType} instead! + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + */ + public GaussGeneral( final double[] sigma, final Img< T > input, final OutOfBoundsFactory< T, Img< T > > outOfBounds ) + { + this( sigma, Views.extend( input, outOfBounds ), input, input.factory(), input.firstElement().createVariable() ); + } + + @Override + protected Img< T > getProcessingLine( final long sizeProcessLine ) + { + final Img< T > processLine; + + // try to use array if each individual line is not too long + if ( sizeProcessLine <= Integer.MAX_VALUE ) + { + processLine = new ListImgFactory< T >().create( new long[] { sizeProcessLine }, getProcessingType() ); + } + else + { + System.out.println( "Individual dimension size is too large for ListImg, sorry. We need a CellListImg..." ); + processLine = null; + } + + return processLine; + } +} diff --git a/src/main/java/varun_algorithm_gauss/GaussNativeType.java b/src/main/java/varun_algorithm_gauss/GaussNativeType.java new file mode 100644 index 0000000..116bcdd --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/GaussNativeType.java @@ -0,0 +1,403 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.Interval; +import net.imglib2.Iterator; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + +import net.imglib2.converter.Converter; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.array.ArrayRandomAccess; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory; +import net.imglib2.outofbounds.OutOfBoundsMirrorFactory.Boundary; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.view.Views; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +public class GaussNativeType< T extends NumericType< T > & NativeType< T > > extends AbstractGauss< T > +{ + protected boolean isArray; + + /** + * Computes a Gaussian convolution on a {@link RandomAccessible} in a + * certain {@link Interval} and returns an {@link Img} defined by the + * {@link ImgFactory} containing the result. + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param factory + * - the {@link ImgFactory} that defines the temporary and output + * images to be used + */ + public GaussNativeType( final double[] sigma, final RandomAccessible< T > input, final Interval interval, final ImgFactory< T > factory, final T type ) + { + super( sigma, input, interval, factory.create( interval, type ), new Point( sigma.length ), factory, type ); + } + + /** + * Computes a Gaussian convolution with any precision on a + * {@link RandomAccessible} in a certain {@link Interval} and writes it into + * a given {@link RandomAccessible} at a specific Point + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the {@link RandomAccessible} to work on + * @param interval + * - the area that is convolved + * @param output + * - the {@link RandomAccessible} where the output will be + * written to + * @param outputOffset + * - the offset that corresponds to the first pixel in output + * {@link RandomAccessible} + * @param factory + * - the {@link ImgFactory} for creating temporary images + */ + public GaussNativeType( final double[] sigma, final RandomAccessible< T > input, final Interval interval, final RandomAccessible< T > output, final Localizable outputOffset, final ImgFactory< T > factory, final T type ) + { + super( sigma, input, interval, output, outputOffset, factory, type ); + } + + /** + * Computes a Gaussian convolution with any precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + */ + public GaussNativeType( final double[] sigma, final Img< T > input ) + { + this( sigma, Views.extend( input, new OutOfBoundsMirrorFactory< T, Img< T > >( Boundary.SINGLE ) ), input, input.factory(), input.firstElement().createVariable() ); + } + + /** + * Computes a Gaussian convolution with any precision on an entire + * {@link Img} using the {@link OutOfBoundsMirrorFactory} with single + * boundary + * + * @param sigma + * - the sigma for the convolution + * @param input + * - the input {@link Img} + */ + public GaussNativeType( final double[] sigma, final Img< T > input, final OutOfBoundsFactory< T, Img< T > > outOfBounds ) + { + this( sigma, Views.extend( input, outOfBounds ), input, input.factory(), input.firstElement().createVariable() ); + } + + protected boolean isArray() + { + return isArray; + } + + @Override + protected Img< T > getProcessingLine( final long sizeProcessLine ) + { + final Img< T > processLine; + + // try to use array if each individual line is not too long + if ( sizeProcessLine <= Integer.MAX_VALUE ) + { + isArray = true; + processLine = new ArrayImgFactory< T >().create( new long[] { sizeProcessLine }, getProcessingType() ); + } + else + { + isArray = false; + processLine = new CellImgFactory< T >( Integer.MAX_VALUE / 16 ).create( new long[] { sizeProcessLine }, getProcessingType() ); + } + + return processLine; + } + + /** + * Compute the current line. It is up to the implementation howto really do + * that. The idea is to only iterate over the input once (that's why it is + * an {@link Iterator}) as it is potentially an expensive operation (e.g. a + * {@link Converter} might be involved or we are computing on a rendered + * input) + * + * @param input + * - the {@link Iterator}/{@link Sampler} over the current input + * line. + */ + @Override + protected void processLine( final SamplingLineIterator< T > input, final double[] kernel ) + { + if ( !isArray() ) + { + super.processLine( input, kernel ); + return; + } + + final int kernelSize = kernel.length; + final int kernelSizeMinus1 = kernelSize - 1; + final int kernelSizeHalf = kernelSize / 2; + final int kernelSizeHalfMinus1 = kernelSizeHalf - 1; + + final ArrayRandomAccess< T > randomAccessLeft = ( ArrayRandomAccess< T > ) input.randomAccessLeft; + final ArrayRandomAccess< T > randomAccessRight = ( ArrayRandomAccess< T > ) input.randomAccessRight; + final T copy = input.copy; + final T tmp = input.tmp; + + final long imgSize = input.getProcessLine().size(); + + if ( imgSize >= kernelSize ) + { + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + + // copy input into a temp variable, it might be expensive to get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPositionDim0( 0 ); + + // now add it to all output values it contributes to + copy.mul( kernel[ 0 ] ); + randomAccessLeft.get().add( copy ); + + for ( int i = 1; i < kernelSizeMinus1; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPositionDim0( -1 ); + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + { + randomAccessLeft.fwdDim0(); + + tmp.set( copy ); + tmp.mul( kernel[ i - o ] ); + + randomAccessLeft.get().add( tmp ); + } + } + + // convolve all values where the input value contributes to the full + // kernel + final long length = imgSize - kernelSizeMinus1; + for ( long n = 0; n < length; ++n ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the left and the right random access to the right + // coordinates + // the left random access is always responsible for the center + randomAccessLeft.setPositionDim0( n ); + randomAccessRight.setPositionDim0( n + kernelSizeMinus1 ); + + // move till the last pixel before the center of the kernel + for ( int k = 0; k < kernelSizeHalfMinus1; ++k ) + { + tmp.set( copy ); + tmp.mul( kernel[ k ] ); + + randomAccessLeft.get().add( tmp ); + randomAccessRight.get().add( tmp ); + + randomAccessLeft.fwdDim0(); + randomAccessRight.bckDim0(); + } + + // do the last pixel (same as a above, but right cursor doesn't + // move) + tmp.set( copy ); + tmp.mul( kernel[ kernelSizeHalfMinus1 ] ); + + randomAccessLeft.get().add( tmp ); + randomAccessRight.get().add( tmp ); + + randomAccessLeft.fwdDim0(); + + // do the center pixel + tmp.set( copy ); + tmp.mul( kernel[ kernelSizeHalf ] ); + + randomAccessLeft.get().add( tmp ); + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + final long endLength = imgSize + kernelSizeMinus1; + for ( long i = imgSize; i < endLength; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPositionDim0( i - kernelSize ); + + // now add it to all output values it contributes to + int k = 0; + for ( long o = i - kernelSize + 1; o < imgSize; ++o ) + { + randomAccessLeft.fwdDim0(); + + tmp.set( copy ); + tmp.mul( kernel[ k++ ] ); + + randomAccessLeft.get().add( tmp ); + } + } + } + else + { + // convolve the first pixels where the input influences less than + // kernel.size pixels + + // the FIRST pixel is a special case as we cannot set the cursor to + // -1 (might not be defined) + // copy input into a temp variable, it might be expensive to get() + + // copy input into a temp variable, it might be expensive to get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPositionDim0( 0 ); + + // now add it to all output values it contributes to + copy.mul( kernel[ 0 ] ); + randomAccessLeft.get().add( copy ); + + for ( int i = 1; i < imgSize; ++i ) + { + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + randomAccessLeft.setPositionDim0( -1 ); + + // now add it to all output values it contributes to + for ( int o = 0; o <= i; ++o ) + { + randomAccessLeft.fwdDim0(); + + tmp.set( copy ); + tmp.mul( kernel[ i - o ] ); + + randomAccessLeft.get().add( tmp ); + } + } + + // convolve the last pixels where the input influences less than + // kernel.size pixels + for ( long i = imgSize; i < imgSize + kernelSizeMinus1; ++i ) + { + // after the fwd() call the random access is at position imgSize + // as pictured above + input.fwd(); + + // copy input into a temp variable, it might be expensive to + // get() + copy.set( input.get() ); + + // set the random access in the processing line to the right + // position + // now add it to all output values it contributes to + long o = i - kernelSize + 1; + int k = 0; + + if ( o < 0 ) + { + k = -( int ) o; + o = 0; + } + + randomAccessLeft.setPositionDim0( o - 1 ); + + // now add it to all output values it contributes to + for ( ; o < imgSize; ++o ) + { + randomAccessLeft.fwdDim0(); + + tmp.set( copy ); + tmp.mul( kernel[ k++ ] ); + + randomAccessLeft.get().add( tmp ); + } + } + } + } +} diff --git a/src/main/java/varun_algorithm_gauss/SamplingLineIterator.java b/src/main/java/varun_algorithm_gauss/SamplingLineIterator.java new file mode 100644 index 0000000..f2b2af8 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/SamplingLineIterator.java @@ -0,0 +1,120 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.Sampler; + +import net.imglib2.img.Img; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +public class SamplingLineIterator< T > extends AbstractLineIterator implements Sampler< T > +{ + final Img< T > processLine; + + final RandomAccess< T > randomAccess; + + final Cursor< T > resultCursor; + + final RandomAccess< T > randomAccessLeft, randomAccessRight; + + final T copy, tmp; + + /** + * Make a new SamplingLineIterator which iterates a 1d line of a certain + * length and is used as the input for the convolution operation + * + * @param dim + * - which dimension to iterate (dimension id) + * @param size + * - number of pixels to iterate + * @param randomAccess + * - the {@link RandomAccess} which is moved along the line and + * is placed at the right location (one pixel left of the + * starting pixel) + * @param processLine + * - the line that will be used for processing and is associated + * with this {@link SamplingLineIterator}, this is important for + * multithreading so that each SamplingLineIterator has its own + * temporary space for computing the gaussian convolution + */ + public SamplingLineIterator( final int dim, final long size, final RandomAccess< T > randomAccess, final Img< T > processLine, final T copy, final T tmp ) + { + super( dim, size, randomAccess, randomAccess ); + + this.processLine = processLine; + this.randomAccess = randomAccess; + + this.randomAccessLeft = processLine.randomAccess(); + this.randomAccessRight = processLine.randomAccess(); + this.copy = copy; + this.tmp = tmp; + + this.resultCursor = processLine.cursor(); + } + + /** + * @return - the line that is used for processing and is associated with + * this {@link SamplingLineIterator} + */ + public Img< T > getProcessLine() + { + return processLine; + } + + @Override + public T get() + { + return randomAccess.get(); + } + + @Override + public SamplingLineIterator< T > copy() + { + // new instance with same properties + final SamplingLineIterator< T > c = new SamplingLineIterator< T >( d, size, randomAccess, getProcessLine(), copy, tmp ); + + // update current status + c.i = i; + + return c; + } +} diff --git a/src/main/java/varun_algorithm_gauss/WritableLineIterator.java b/src/main/java/varun_algorithm_gauss/WritableLineIterator.java new file mode 100644 index 0000000..300ef30 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss/WritableLineIterator.java @@ -0,0 +1,80 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss; + +import net.imglib2.RandomAccess; +import net.imglib2.type.Type; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +public class WritableLineIterator< T extends Type< T > > extends AbstractLineIterator +{ + final RandomAccess< T > randomAccess; + + /** + * Make a new AbstractWritableLineIterator which iterates a 1d line of a + * certain length and is used as the input for the convolution operation + * + * @param dim + * - which dimension to iterate (dimension id) + * @param size + * - number of pixels to iterate + * @param randomAccess + * - the {@link RandomAccess} which is moved along the line and + * is placed at the right location (one pixel left of the + * starting pixel) + */ + public WritableLineIterator( final int dim, final long size, final RandomAccess< T > randomAccess ) + { + super( dim, size, randomAccess, randomAccess ); + + this.randomAccess = randomAccess; + } + + /** + * Sets the possibly converted value at the current location + * + * @param type + * - the value + */ + public void set( final T type ) + { + randomAccess.get().set( type ); + } +} diff --git a/src/main/java/varun_algorithm_gauss3/ConvolverFactory.java b/src/main/java/varun_algorithm_gauss3/ConvolverFactory.java new file mode 100644 index 0000000..8cdb03a --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/ConvolverFactory.java @@ -0,0 +1,75 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; + +/** + * {@link ConvolverFactory} creates 1-dimensional line convolvers. See + * {@link #create(double[], RandomAccess, RandomAccess, int, long)}. + * + * @author Tobias Pietzsch + * + * @param + * @param + */ +public interface ConvolverFactory< S, T > +{ + /** + * Create a 1-dimensional line convolver. A line convolver has an input and + * an output {@link RandomAccess}. They are moved forwards a long a line in + * dimension d, reading source values from the input and writing convolved + * values to the output. The line convolver is a Runnable. The idea is to + * put the input and an output {@link RandomAccess} to the start of a line, + * then call {@link Runnable#run()} to do the convolution. Then the input + * and an output {@link RandomAccess} are moved to the next line, + * {@link Runnable#run()} is called again, and so on. + * + * @param halfkernel + * the upper half (starting at the center pixel) of the symmetric + * convolution kernel. + * @param in + * {@link RandomAccess} on the source values. + * @param out + * {@link RandomAccess} on the target (convolved) values. + * @param d + * dimension in which to convolve. + * @param lineLength + * how many convolved values to produce in one + * {@link Runnable#run()}. + * @return a line convolver. + */ + public Runnable create( final double[] halfkernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ); +} diff --git a/src/main/java/varun_algorithm_gauss3/ConvolverNativeType.java b/src/main/java/varun_algorithm_gauss3/ConvolverNativeType.java new file mode 100644 index 0000000..e8ec051 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/ConvolverNativeType.java @@ -0,0 +1,179 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; + +import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; + +/** + * A 1-dimensional line convolver that operates on all {@link NumericType} with + * {@link NativeType} representation. It implemented using a shifting window + * buffer that is stored in a small {@link NativeType} image. This is intented + * for very large images, where a single line has more than + * {@link Integer#MAX_VALUE} elements. For smaller images, the faster + * {@link ConvolverNativeTypeBuffered} should be used. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input and output type + */ +public final class ConvolverNativeType< T extends NumericType< T > & NativeType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing {@link ConvolverNativeType}. + */ + public static < T extends NumericType< T > & NativeType< T > > ConvolverFactoryNativeType< T > factory( final T type ) + { + return new ConvolverFactoryNativeType< T >( type ); + } + + public static final class ConvolverFactoryNativeType< T extends NumericType< T > & NativeType< T > > implements ConvolverFactory< T, T > + { + final private T type; + + public ConvolverFactoryNativeType( final T type ) + { + this.type = type; + } + + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< T > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new ConvolverNativeType< T >( halfkernel, in, out, d, lineLength, type ); + } + } + + final private double[] kernel; + + final private RandomAccess< T > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private long linelen; + + final T b1; + + final T b2; + + final T tmp; + + private ConvolverNativeType( final double[] kernel, final RandomAccess< T > in, final RandomAccess< T > out, final int d, final long lineLength, final T type ) + { + this.kernel = kernel; + this.in = in; + this.out = out; + this.d = d; + + k = kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + linelen = lineLength; + + final int buflen = 2 * k - 1; + final ArrayImg< T, ? > buf = new ArrayImgFactory< T >().create( new long[] { buflen }, type ); + b1 = buf.randomAccess().get(); + b2 = buf.randomAccess().get(); + + tmp = type.createVariable(); + } + + private void next() + { + // move buf contents down + for ( int i = 0; i < k1k1; ++i ) + { + b2.updateIndex( i + 1 ); + b1.updateIndex( i ); + b1.set( b2 ); + } + + // add new values + final T w = in.get(); + + // center + tmp.set( w ); + tmp.mul( kernel[ 0 ] ); + b1.updateIndex( k1 ); + b1.add( tmp ); + + // loop + for ( int j = 1; j < k1; ++j ) + { + tmp.set( w ); + tmp.mul( kernel[ j ] ); + b1.updateIndex( k1 + j ); + b1.add( tmp ); + b1.updateIndex( k1 - j ); + b1.add( tmp ); + } + + // outer-most + tmp.set( w ); + tmp.mul( kernel[ k1 ] ); + b1.updateIndex( k1k1 ); + b1.set( tmp ); + + in.fwd( d ); + } + + @Override + public void run() + { + for ( int i = 0; i < k1k1; ++i ) + next(); + for ( long i = 0; i < linelen; ++i ) + { + next(); + b1.updateIndex( 0 ); + b1.add( tmp ); + out.get().set( b1 ); + out.fwd( d ); + } + } +} diff --git a/src/main/java/varun_algorithm_gauss3/ConvolverNativeTypeBuffered.java b/src/main/java/varun_algorithm_gauss3/ConvolverNativeTypeBuffered.java new file mode 100644 index 0000000..7cd7dd5 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/ConvolverNativeTypeBuffered.java @@ -0,0 +1,171 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; +import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; + +/** + * A 1-dimensional line convolver that operates on all {@link NumericType} with + * {@link NativeType} representation. It implemented using a line buffer that is + * stored in a {@link NativeType} image that is (approximately) as big as one + * output line. This works for images, where a single line has no more than + * {@link Integer#MAX_VALUE} elements. For larger images + * {@link ConvolverNativeType} can be used. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input and output type + */ +public final class ConvolverNativeTypeBuffered< T extends NumericType< T > & NativeType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing + * {@link ConvolverNativeTypeBuffered}. + */ + public static < T extends NumericType< T > & NativeType< T > > ConvolverFactoryNativeTypeBuffered< T > factory( final T type ) + { + return new ConvolverFactoryNativeTypeBuffered< T >( type ); + } + + public static final class ConvolverFactoryNativeTypeBuffered< T extends NumericType< T > & NativeType< T > > implements ConvolverFactory< T, T > + { + final private T type; + + public ConvolverFactoryNativeTypeBuffered( final T type ) + { + this.type = type; + } + + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< T > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new ConvolverNativeTypeBuffered< T >( halfkernel, in, out, d, lineLength, type ); + } + } + + final private double[] kernel; + + final private RandomAccess< T > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private int buflen; + + final T b1; + + final T tmp; + + private ConvolverNativeTypeBuffered( final double[] kernel, final RandomAccess< T > in, final RandomAccess< T > out, final int d, final long lineLength, final T type ) + { + this.kernel = kernel; + this.in = in; + this.out = out; + this.d = d; + + k = kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + + buflen = ( int ) lineLength + 2 * k1k1; + final ArrayImg< T, ? > buf = new ArrayImgFactory< T >().create( new long[] { buflen }, type ); + b1 = buf.randomAccess().get(); + + tmp = type.createVariable(); + } + + @Override + public void run() + { + final int max = buflen - k1; + for ( int i = k1; i < max; ++i ) + { + final T w = in.get(); + + // center + tmp.set( w ); + tmp.mul( kernel[ 0 ] ); + b1.updateIndex( i ); + b1.add( tmp ); + + // loop + for ( int j = 1; j < k1; ++j ) + { + tmp.set( w ); + tmp.mul( kernel[ j ] ); + b1.updateIndex( i + j ); + b1.add( tmp ); + b1.updateIndex( i - j ); + b1.add( tmp ); + } + + // outer-most + tmp.set( w ); + tmp.mul( kernel[ k1 ] ); + b1.updateIndex( i - k1 ); + b1.add( tmp ); + b1.updateIndex( i + k1 ); + b1.set( tmp ); + + in.fwd( d ); + } + + writeLine(); + } + + private void writeLine() + { + final int max = buflen - k1k1; + for ( int i = k1k1; i < max; ++i ) + { + b1.updateIndex( i ); + out.get().set( b1 ); + out.fwd( d ); + } + } +} diff --git a/src/main/java/varun_algorithm_gauss3/ConvolverNumericType.java b/src/main/java/varun_algorithm_gauss3/ConvolverNumericType.java new file mode 100644 index 0000000..885b5ff --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/ConvolverNumericType.java @@ -0,0 +1,167 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import java.lang.reflect.Array; + +import net.imglib2.RandomAccess; + +import net.imglib2.type.numeric.NumericType; + +/** + * A 1-dimensional line convolver that operates on all {@link NumericType}. It + * implemented using a shifting window buffer that is stored in a T[] array. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input and output type + */ +public class ConvolverNumericType< T extends NumericType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing {@link ConvolverNumericType} + * . + */ + public static < T extends NumericType< T > > ConvolverNumericTypeFactory< T > factory( final T type ) + { + return new ConvolverNumericTypeFactory< T >( type ); + } + + public static final class ConvolverNumericTypeFactory< T extends NumericType< T > > implements ConvolverFactory< T, T > + { + final private T type; + + public ConvolverNumericTypeFactory( final T type ) + { + this.type = type; + } + + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< T > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new ConvolverNumericType< T >( halfkernel, in, out, d, lineLength, type ); + } + } + + final private double[] kernel; + + final private RandomAccess< T > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private long linelen; + + final T[] buf; + + final T tmp; + + @SuppressWarnings( "unchecked" ) + private ConvolverNumericType( final double[] kernel, final RandomAccess< T > in, final RandomAccess< T > out, final int d, final long lineLength, final T type ) + { + this.kernel = kernel; + this.in = in; + this.out = out; + this.d = d; + + k = kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + linelen = lineLength; + + final int buflen = 2 * k - 1; + buf = ( T[] ) Array.newInstance( type.getClass(), buflen ); + for ( int i = 0; i < buflen; ++i ) + buf[ i ] = type.createVariable(); + + tmp = type.createVariable(); + } + + private void next() + { + // move buf contents down + final T first = buf[ 0 ]; + for ( int i = 0; i < k1k1; ++i ) + buf[ i ] = buf[ i + 1 ]; + buf[ k1k1 ] = first; + + // add new values + final T w = in.get(); + + // center + tmp.set( w ); + tmp.mul( kernel[ 0 ] ); + buf[ k1 ].add( tmp ); + + // loop + for ( int j = 1; j < k1; ++j ) + { + tmp.set( w ); + tmp.mul( kernel[ j ] ); + buf[ k1 + j ].add( tmp ); + buf[ k1 - j ].add( tmp ); + } + + // outer-most + tmp.set( w ); + tmp.mul( kernel[ k1 ] ); + buf[ k1k1 ].set( tmp ); + + in.fwd( d ); + } + + @Override + public void run() + { + for ( int i = 0; i < k1k1; ++i ) + next(); + for ( long i = 0; i < linelen; ++i ) + { + next(); + tmp.add( buf[ 0 ] ); + out.get().set( tmp ); + out.fwd( d ); + } + } +} diff --git a/src/main/java/varun_algorithm_gauss3/DoubleConvolverRealType.java b/src/main/java/varun_algorithm_gauss3/DoubleConvolverRealType.java new file mode 100644 index 0000000..0cd85d7 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/DoubleConvolverRealType.java @@ -0,0 +1,195 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; + +import net.imglib2.type.numeric.RealType; + +/** + * A 1-dimensional line convolver that operates on all {@link RealType}. It + * implemented using a shifting window buffer that is stored in a small double[] + * array. This is intented for very large images, where a single line has more + * than {@link Integer#MAX_VALUE} elements. For smaller images, the faster + * {@link DoubleConvolverRealTypeBuffered} should be used. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input type + * @param + * output type + */ +public final class DoubleConvolverRealType< S extends RealType< S >, T extends RealType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing + * {@link DoubleConvolverRealType}. + */ + public static < S extends RealType< S >, T extends RealType< T > > ConvolverFactory< S, T > factory() + { + return new ConvolverFactory< S, T >() + { + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new DoubleConvolverRealType< S, T >( halfkernel, in, out, d, lineLength ); + } + }; + } + + final private double[] kernel; + + final private RandomAccess< S > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private int k1k; + + final private long fill2; + + final private boolean fillAdditional; + + final private double[] buf1; + + final private double[] buf2; + + private DoubleConvolverRealType( final double[] kernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + this.kernel = kernel; + this.in = in; + this.out = out; + this.d = d; + + k = this.kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + k1k = k1 + k; + fill2 = lineLength / 2; + fillAdditional = ( lineLength % 2 == 1 ); + + final int l = 2 * k; + buf1 = new double[ l ]; + buf2 = new double[ l ]; + } + + private void prefill1() + { + final double w = in.get().getRealDouble(); + buf1[ k1 ] = w * kernel[ 0 ] + buf2[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final double wk = w * kernel[ i ]; + buf1[ k1 + i ] = wk + buf2[ k + i ]; + buf1[ k1 - i ] = wk + buf2[ k - i ]; + } + buf1[ k1k1 ] = w * kernel[ k1 ] + buf2[ k1k ]; + in.fwd( d ); + } + + private void prefill2() + { + final double w = in.get().getRealDouble(); + buf2[ k1 ] = w * kernel[ 0 ] + buf1[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final double wk = w * kernel[ i ]; + buf2[ k1 + i ] = wk + buf1[ k + i ]; + buf2[ k1 - i ] = wk + buf1[ k - i ]; + } + buf2[ k1k1 ] = w * kernel[ k1 ] + buf1[ k1k ]; + in.fwd( d ); + } + + private void next2() + { + final double w = in.get().getRealDouble(); + buf2[ k1 ] = w * kernel[ 0 ] + buf1[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final double wk = w * kernel[ i ]; + buf2[ k1 + i ] = wk + buf1[ k + i ]; + buf2[ k1 - i ] = wk + buf1[ k - i ]; + } + final double wk = w * kernel[ k1 ]; + buf2[ k1k1 ] = wk + buf1[ k1k ]; + out.get().setReal( wk + buf1[ 1 ] ); + in.fwd( d ); + out.fwd( d ); + } + + private void next1() + { + final double w = in.get().getRealDouble(); + buf1[ k1 ] = w * kernel[ 0 ] + buf2[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final double wk = w * kernel[ i ]; + buf1[ k1 + i ] = wk + buf2[ k + i ]; + buf1[ k1 - i ] = wk + buf2[ k - i ]; + } + final double wk = w * kernel[ k1 ]; + buf1[ k1k1 ] = wk + buf2[ k1k ]; + out.get().setReal( wk + buf2[ 1 ] ); + in.fwd( d ); + out.fwd( d ); + } + + @Override + public void run() + { + for ( int i = 0; i < k1; ++i ) + { + prefill1(); + prefill2(); + } + for ( long i = 0; i < fill2; ++i ) + { + next1(); + next2(); + } + if ( fillAdditional ) + next1(); + } +} diff --git a/src/main/java/varun_algorithm_gauss3/DoubleConvolverRealTypeBuffered.java b/src/main/java/varun_algorithm_gauss3/DoubleConvolverRealTypeBuffered.java new file mode 100644 index 0000000..a4f5d10 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/DoubleConvolverRealTypeBuffered.java @@ -0,0 +1,146 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; + +import net.imglib2.type.numeric.RealType; + +/** + * A 1-dimensional line convolver that operates on all {@link RealType}. It + * implemented using a line buffer that is stored in a double[] array that is + * (approximately) as big as one output line. This works for images, where a + * single line has no more than {@link Integer#MAX_VALUE} elements. For larger + * images {@link DoubleConvolverRealType} can be used. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input type + * @param + * output type + */ +public final class DoubleConvolverRealTypeBuffered< S extends RealType< S >, T extends RealType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing + * {@link DoubleConvolverRealTypeBuffered}. + */ + public static < S extends RealType< S >, T extends RealType< T > > ConvolverFactory< S, T > factory() + { + return new ConvolverFactory< S, T >() + { + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new DoubleConvolverRealTypeBuffered< S, T >( halfkernel, in, out, d, lineLength ); + } + }; + } + + final private double[] kernel; + + final private RandomAccess< S > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private int buflen; + + final private double[] buf; + + private DoubleConvolverRealTypeBuffered( final double[] kernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + this.kernel = kernel; + this.in = in; + this.out = out; + this.d = d; + + k = this.kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + + buflen = ( int ) lineLength + 2 * k1k1; + buf = new double[ buflen ]; + } + + @Override + public void run() + { + final int max = buflen - k1; + for ( int i = k1; i < max; ++i ) + { + final double w = in.get().getRealDouble(); + + // center + buf[ i ] += w * kernel[ 0 ]; + + // loop + for ( int j = 1; j < k1; ++j ) + { + final double wk = w * kernel[ j ]; + buf[ i + j ] += wk; + buf[ i - j ] += wk; + } + + // outer-most + final double wk = w * kernel[ k1 ]; + buf[ i - k1 ] += wk; + buf[ i + k1 ] = wk; + + in.fwd( d ); + } + + writeLine(); + } + + private void writeLine() + { + final int max = buflen - k1k1; + for ( int i = k1k1; i < max; ++i ) + { + out.get().setReal( buf[ i ] ); + out.fwd( d ); + } + } +} diff --git a/src/main/java/varun_algorithm_gauss3/FloatConvolverRealType.java b/src/main/java/varun_algorithm_gauss3/FloatConvolverRealType.java new file mode 100644 index 0000000..8e36f11 --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/FloatConvolverRealType.java @@ -0,0 +1,197 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; + +import net.imglib2.type.numeric.RealType; + +/** + * A 1-dimensional line convolver that operates on all {@link RealType}. It + * implemented using a shifting window buffer that is stored in a small float[] + * array. This is intented for very large images, where a single line has more + * than {@link Integer#MAX_VALUE} elements. For smaller images, the faster + * {@link DoubleConvolverRealTypeBuffered} should be used. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input type + * @param + * output type + */ +public final class FloatConvolverRealType< S extends RealType< S >, T extends RealType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing + * {@link FloatConvolverRealType}. + */ + public static < S extends RealType< S >, T extends RealType< T > > ConvolverFactory< S, T > factory() + { + return new ConvolverFactory< S, T >() + { + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new FloatConvolverRealType< S, T >( halfkernel, in, out, d, lineLength ); + } + }; + } + + final private float[] kernel; + + final private RandomAccess< S > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private int k1k; + + final private long fill2; + + final private boolean fillAdditional; + + final private float[] buf1; + + final private float[] buf2; + + private FloatConvolverRealType( final double[] kernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + this.kernel = new float[ kernel.length ]; + for ( int i = 0; i < kernel.length; ++i ) + this.kernel[ i ] = ( float ) kernel[ i ]; + this.in = in; + this.out = out; + this.d = d; + + k = this.kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + k1k = k1 + k; + fill2 = lineLength / 2; + fillAdditional = ( lineLength % 2 == 1 ); + + final int l = 2 * k; + buf1 = new float[ l ]; + buf2 = new float[ l ]; + } + + private void prefill1() + { + final float w = in.get().getRealFloat(); + buf1[ k1 ] = w * kernel[ 0 ] + buf2[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final float wk = w * kernel[ i ]; + buf1[ k1 + i ] = wk + buf2[ k + i ]; + buf1[ k1 - i ] = wk + buf2[ k - i ]; + } + buf1[ k1k1 ] = w * kernel[ k1 ] + buf2[ k1k ]; + in.fwd( d ); + } + + private void prefill2() + { + final float w = in.get().getRealFloat(); + buf2[ k1 ] = w * kernel[ 0 ] + buf1[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final float wk = w * kernel[ i ]; + buf2[ k1 + i ] = wk + buf1[ k + i ]; + buf2[ k1 - i ] = wk + buf1[ k - i ]; + } + buf2[ k1k1 ] = w * kernel[ k1 ] + buf1[ k1k ]; + in.fwd( d ); + } + + private void next2() + { + final float w = in.get().getRealFloat(); + buf2[ k1 ] = w * kernel[ 0 ] + buf1[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final float wk = w * kernel[ i ]; + buf2[ k1 + i ] = wk + buf1[ k + i ]; + buf2[ k1 - i ] = wk + buf1[ k - i ]; + } + final float wk = w * kernel[ k1 ]; + buf2[ k1k1 ] = wk + buf1[ k1k ]; + out.get().setReal( wk + buf1[ 1 ] ); + in.fwd( d ); + out.fwd( d ); + } + + private void next1() + { + final float w = in.get().getRealFloat(); + buf1[ k1 ] = w * kernel[ 0 ] + buf2[ k ]; + for ( int i = 1; i < k1; ++i ) + { + final float wk = w * kernel[ i ]; + buf1[ k1 + i ] = wk + buf2[ k + i ]; + buf1[ k1 - i ] = wk + buf2[ k - i ]; + } + final float wk = w * kernel[ k1 ]; + buf1[ k1k1 ] = wk + buf2[ k1k ]; + out.get().setReal( wk + buf2[ 1 ] ); + in.fwd( d ); + out.fwd( d ); + } + + @Override + public void run() + { + for ( int i = 0; i < k1; ++i ) + { + prefill1(); + prefill2(); + } + for ( long i = 0; i < fill2; ++i ) + { + next1(); + next2(); + } + if ( fillAdditional ) + next1(); + } +} diff --git a/src/main/java/varun_algorithm_gauss3/FloatConvolverRealTypeBuffered.java b/src/main/java/varun_algorithm_gauss3/FloatConvolverRealTypeBuffered.java new file mode 100644 index 0000000..61dd1db --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/FloatConvolverRealTypeBuffered.java @@ -0,0 +1,148 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import net.imglib2.RandomAccess; + +import net.imglib2.type.numeric.RealType; + +/** + * A 1-dimensional line convolver that operates on all {@link RealType}. It + * implemented using a line buffer that is stored in a float[] array that is + * (approximately) as big as one output line. This works for images, where a + * single line has no more than {@link Integer#MAX_VALUE} elements. For larger + * images {@link DoubleConvolverRealType} can be used. + * + * @author Tobias Pietzsch + * @see ConvolverFactory + * + * @param + * input type + * @param + * output type + */ +public final class FloatConvolverRealTypeBuffered< S extends RealType< S >, T extends RealType< T > > implements Runnable +{ + /** + * @return a {@link ConvolverFactory} producing + * {@link FloatConvolverRealTypeBuffered}. + */ + public static < S extends RealType< S >, T extends RealType< T > > ConvolverFactory< S, T > factory() + { + return new ConvolverFactory< S, T >() + { + @Override + public Runnable create( final double[] halfkernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + return new FloatConvolverRealTypeBuffered< S, T >( halfkernel, in, out, d, lineLength ); + } + }; + } + + final private float[] kernel; + + final private RandomAccess< S > in; + + final private RandomAccess< T > out; + + final private int d; + + final private int k; + + final private int k1; + + final private int k1k1; + + final private int buflen; + + final private float[] buf; + + private FloatConvolverRealTypeBuffered( final double[] kernel, final RandomAccess< S > in, final RandomAccess< T > out, final int d, final long lineLength ) + { + this.kernel = new float[ kernel.length ]; + for ( int i = 0; i < kernel.length; ++i ) + this.kernel[ i ] = ( float ) kernel[ i ]; + this.in = in; + this.out = out; + this.d = d; + + k = this.kernel.length; + k1 = k - 1; + k1k1 = k1 + k1; + + buflen = ( int ) lineLength + 2 * k1k1; + buf = new float[ buflen ]; + } + + @Override + public void run() + { + final int max = buflen - k1; + for ( int i = k1; i < max; ++i ) + { + final float w = in.get().getRealFloat(); + + // center + buf[ i ] += w * kernel[ 0 ]; + + // loop + for ( int j = 1; j < k1; ++j ) + { + final float wk = w * kernel[ j ]; + buf[ i + j ] += wk; + buf[ i - j ] += wk; + } + + // outer-most + final float wk = w * kernel[ k1 ]; + buf[ i - k1 ] += wk; + buf[ i + k1 ] = wk; + + in.fwd( d ); + } + + writeLine(); + } + + private void writeLine() + { + final int max = buflen - k1k1; + for ( int i = k1k1; i < max; ++i ) + { + out.get().setReal( buf[ i ] ); + out.fwd( d ); + } + } +} diff --git a/src/main/java/varun_algorithm_gauss3/Gauss3.java b/src/main/java/varun_algorithm_gauss3/Gauss3.java new file mode 100644 index 0000000..6430b2f --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/Gauss3.java @@ -0,0 +1,247 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.view.Views; + +/** + * Gaussian convolution. + * + * @author Tobias Pietzsch + */ +public final class Gauss3 +{ + /** + * Apply Gaussian convolution to source and write the result to output. + * In-place operation (source==target) is supported. + * + *

      + * If the target type T is {@link DoubleType}, all calculations are done in + * double precision. For all other target {@link RealType RealTypes} float + * precision is used. General {@link NumericType NumericTypes} are computed + * in their own precision. The source type S and target type T are either + * both {@link RealType RealTypes} or both the same type. + * + * @param sigma + * standard deviation of isotropic Gaussian. + * @param source + * source image, must be sufficiently padded (e.g. + * {@link Views#extendMirrorSingle(RandomAccessibleInterval)}) to + * provide values for the target interval plus a border of half + * the kernel size. + * @param target + * target image + * @param + * source type + * @param + * target type + * @throws IncompatibleTypeException + * if source and target type are not compatible (they must be + * either both {@link RealType RealTypes} or the same type). + */ + public static < S extends NumericType< S >, T extends NumericType< T > > void gauss( final double sigma, final RandomAccessible< S > source, final RandomAccessibleInterval< T > target ) throws IncompatibleTypeException + { + final int n = source.numDimensions(); + final double[] s = new double[ n ]; + for ( int d = 0; d < n; ++d ) + s[ d ] = sigma; + gauss( s, source, target ); + } + + /** + * Apply Gaussian convolution to source and write the result to output. + * In-place operation (source==target) is supported. + * + *

      + * If the target type T is {@link DoubleType}, all calculations are done in + * double precision. For all other target {@link RealType RealTypes} float + * precision is used. General {@link NumericType NumericTypes} are computed + * in their own precision. The source type S and target type T are either + * both {@link RealType RealTypes} or both the same type. + * + *

      + * Computation is multi-threaded with as many threads as processors + * available. + * + * @param sigma + * standard deviation in every dimension. + * @param source + * source image, must be sufficiently padded (e.g. + * {@link Views#extendMirrorSingle(RandomAccessibleInterval)}) to + * provide values for the target interval plus a border of half + * the kernel size. + * @param target + * target image + * @param + * source type + * @param + * target type + * @throws IncompatibleTypeException + * if source and target type are not compatible (they must be + * either both {@link RealType RealTypes} or the same type). + */ + public static < S extends NumericType< S >, T extends NumericType< T > > void gauss( final double[] sigma, final RandomAccessible< S > source, final RandomAccessibleInterval< T > target ) throws IncompatibleTypeException + { + final int numthreads = Runtime.getRuntime().availableProcessors(); + final ExecutorService service = Executors.newFixedThreadPool( numthreads ); + gauss( sigma, source, target, service ); + service.shutdown(); + } + + /** + * Apply Gaussian convolution to source and write the result to output. + * In-place operation (source==target) is supported. + * + *

      + * If the target type T is {@link DoubleType}, all calculations are done in + * double precision. For all other target {@link RealType RealTypes} float + * precision is used. General {@link NumericType NumericTypes} are computed + * in their own precision. The source type S and target type T are either + * both {@link RealType RealTypes} or both the same type. + * + * @param sigma + * standard deviation in every dimension. + * @param source + * source image, must be sufficiently padded (e.g. + * {@link Views#extendMirrorSingle(RandomAccessibleInterval)}) to + * provide values for the target interval plus a border of half + * the kernel size. + * @param target + * target image + * @param numThreads + * how many threads to use for the computation. + * @param + * source type + * @param + * target type + * @throws IncompatibleTypeException + * if source and target type are not compatible (they must be + * either both {@link RealType RealTypes} or the same type). + */ + public static < S extends NumericType< S >, T extends NumericType< T > > void gauss( final double[] sigma, final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, final int numThreads ) throws IncompatibleTypeException + { + final double[][] halfkernels = halfkernels( sigma ); + final ExecutorService service = Executors.newFixedThreadPool( numThreads ); + SeparableSymmetricConvolution.convolve( halfkernels, source, target, service ); + service.shutdown(); + } + + /** + * Apply Gaussian convolution to source and write the result to output. + * In-place operation (source==target) is supported. + * + *

      + * If the target type T is {@link DoubleType}, all calculations are done in + * double precision. For all other target {@link RealType RealTypes} float + * precision is used. General {@link NumericType NumericTypes} are computed + * in their own precision. The source type S and target type T are either + * both {@link RealType RealTypes} or both the same type. + * + * @param sigma + * standard deviation in every dimension. + * @param source + * source image, must be sufficiently padded (e.g. + * {@link Views#extendMirrorSingle(RandomAccessibleInterval)}) to + * provide values for the target interval plus a border of half + * the kernel size. + * @param target + * target image + * @param service + * service providing threads for multi-threading + * @param + * source type + * @param + * target type + * @throws IncompatibleTypeException + * if source and target type are not compatible (they must be + * either both {@link RealType RealTypes} or the same type). + */ + public static < S extends NumericType< S >, T extends NumericType< T > > void gauss( final double[] sigma, final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, final ExecutorService service ) throws IncompatibleTypeException + { + final double[][] halfkernels = halfkernels( sigma ); + SeparableSymmetricConvolution.convolve( halfkernels, source, target, service ); + } + + public static double[][] halfkernels( final double[] sigma ) + { + final int n = sigma.length; + final double[][] halfkernels = new double[ n ][]; + final int[] size = halfkernelsizes( sigma ); + for ( int i = 0; i < n; ++i ) + halfkernels[ i ] = halfkernel( sigma[ i ], size[ i ], true ); + return halfkernels; + } + + public static int[] halfkernelsizes( final double[] sigma ) + { + final int n = sigma.length; + final int[] size = new int[ n ]; + for ( int i = 0; i < n; ++i ) + size[ i ] = Math.max( 2, ( int ) ( 3 * sigma[ i ] + 0.5 ) + 1 ); + return size; + } + + public static double[] halfkernel( final double sigma, final int size, final boolean normalize ) + { + final double two_sq_sigma = 2 * sigma * sigma; + final double[] kernel = new double[ size ]; + + kernel[ 0 ] = 1; + for ( int x = 1; x < size; ++x ) + kernel[ x ] = Math.exp( -( x * x ) / two_sq_sigma ); + + if ( normalize ) + { + double sum = 0.5; + for ( int x = 1; x < size; ++x ) + sum += kernel[ x ]; + sum *= 2; + + for ( int x = 0; x < size; ++x ) + kernel[ x ] /= sum; + } + + return kernel; + } +} diff --git a/src/main/java/varun_algorithm_gauss3/SeparableSymmetricConvolution.java b/src/main/java/varun_algorithm_gauss3/SeparableSymmetricConvolution.java new file mode 100644 index 0000000..d79b61e --- /dev/null +++ b/src/main/java/varun_algorithm_gauss3/SeparableSymmetricConvolution.java @@ -0,0 +1,444 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gauss3; + +import java.util.ArrayList; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import net.imglib2.Dimensions; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.img.list.ListImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.IntervalIndexer; +import net.imglib2.util.Util; +import net.imglib2.view.Views; + +/** + * Convolution with a separable symmetric kernel. + * + * @author Tobias Pietzsch + */ +public final class SeparableSymmetricConvolution +{ + /** + * Convolve source with a separable symmetric kernel and write the result to + * output. In-place operation (source==target) is supported. + * + *

      + * If the target type T is {@link DoubleType}, all calculations are done in + * double precision. For all other target {@link RealType RealTypes} float + * precision is used. General {@link NumericType NumericTypes} are computed + * in their own precision. The source type S and target type T are either + * both {@link RealType RealTypes} or both the same type. + * + * @param halfkernels + * an array containing half-kernels for every dimension. A + * half-kernel is the upper half (starting at the center pixel) + * of the symmetric convolution kernel for a given dimension. + * @param source + * source image, must be sufficiently padded (e.g. + * {@link Views#extendMirrorSingle(RandomAccessibleInterval)}) to + * provide values for the target interval plus a border of half + * the kernel size. + * @param target + * target image. + * @param service + * service providing threads for multi-threading + * @param + * source type + * @param + * target type + * @throws IncompatibleTypeException + * if source and target type are not compatible (they must be + * either both {@link RealType RealTypes} or the same type). + */ + @SuppressWarnings( { "rawtypes", "unchecked" } ) + public static < S extends NumericType< S >, T extends NumericType< T > > void convolve( final double[][] halfkernels, final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, final ExecutorService service ) throws IncompatibleTypeException + { + final T targetType = Util.getTypeFromInterval( target ); + final S sourceType = getType( source, target ); + if ( targetType instanceof RealType ) + { + if ( !( sourceType instanceof RealType ) ) + throw new IncompatibleTypeException( sourceType, "RealType source required for convolving into a RealType target" ); + // NB: Casting madness thanks to a long standing javac bug; + // see e.g. http://bugs.sun.com/view_bug.do?bug_id=6548436 + // TODO: remove casting madness as soon as the bug is fixed + final Object oTargetType = targetType; + if ( oTargetType instanceof DoubleType ) + convolveRealTypeDouble( halfkernels, ( RandomAccessible ) source, ( RandomAccessibleInterval ) target, service ); + else + convolveRealTypeFloat( halfkernels, ( RandomAccessible ) source, ( RandomAccessibleInterval ) target, service ); + } + else + { + if ( !targetType.getClass().isInstance( sourceType ) ) + throw new IncompatibleTypeException( sourceType, targetType.getClass().getCanonicalName() + " source required for convolving into a " + targetType.getClass().getCanonicalName() + " target" ); + if ( targetType instanceof NativeType ) + convolveNativeType( halfkernels, ( RandomAccessible ) source, ( RandomAccessibleInterval ) target, service ); + else + convolveNumericType( halfkernels, ( RandomAccessible ) source, ( RandomAccessibleInterval ) target, service ); + } + } + + private static < S extends RealType< S >, T extends RealType< T > > void convolveRealTypeFloat( final double[][] halfkernels, + final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, final ExecutorService service ) + { + final FloatType type = new FloatType(); + final ImgFactory< FloatType > imgfac = getImgFactory( target, halfkernels, type ); + if ( canUseBufferedConvolver( target, halfkernels ) ) + convolve( halfkernels, source, target, + FloatConvolverRealTypeBuffered.< S, FloatType >factory(), + FloatConvolverRealTypeBuffered.< FloatType, FloatType >factory(), + FloatConvolverRealTypeBuffered.< FloatType, T >factory(), + FloatConvolverRealTypeBuffered.< S, T >factory(), imgfac, type, service ); + else + convolve( halfkernels, source, target, + FloatConvolverRealType.< S, FloatType >factory(), + FloatConvolverRealType.< FloatType, FloatType >factory(), + FloatConvolverRealType.< FloatType, T >factory(), + FloatConvolverRealType.< S, T >factory(), imgfac, type, service ); + } + + private static < S extends RealType< S >, T extends RealType< T > > void convolveRealTypeDouble( final double[][] halfkernels, + final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, final ExecutorService service ) + { + final DoubleType type = new DoubleType(); + final ImgFactory< DoubleType > imgfac = getImgFactory( target, halfkernels, type ); + if ( canUseBufferedConvolver( target, halfkernels ) ) + convolve( halfkernels, source, target, + DoubleConvolverRealTypeBuffered.< S, DoubleType >factory(), + DoubleConvolverRealTypeBuffered.< DoubleType, DoubleType >factory(), + DoubleConvolverRealTypeBuffered.< DoubleType, T >factory(), + DoubleConvolverRealTypeBuffered.< S, T >factory(), imgfac, type, service ); + else + convolve( halfkernels, source, target, + DoubleConvolverRealType.< S, DoubleType >factory(), + DoubleConvolverRealType.< DoubleType, DoubleType >factory(), + DoubleConvolverRealType.< DoubleType, T >factory(), + DoubleConvolverRealType.< S, T >factory(), imgfac, type, service ); + } + + private static < T extends NumericType< T > & NativeType< T > > void convolveNativeType( final double[][] halfkernels, + final RandomAccessible< T > source, final RandomAccessibleInterval< T > target, final ExecutorService service ) + { + final T type = Util.getTypeFromInterval( target ); + final ConvolverFactory< T, T > convfac; + if ( canUseBufferedConvolver( target, halfkernels ) ) + convfac = ConvolverNativeTypeBuffered.factory( type ); + else + convfac = ConvolverNativeType.factory( type ); + final ImgFactory< T > imgfac = getImgFactory( target, halfkernels, type ); + convolve( halfkernels, source, target, convfac, convfac, convfac, convfac, imgfac, type, service ); + } + + private static < T extends NumericType< T > > void convolveNumericType( final double[][] halfkernels, + final RandomAccessible< T > source, final RandomAccessibleInterval< T > target, final ExecutorService service ) + { + final T type = Util.getTypeFromInterval( target ); + final ConvolverFactory< T, T > convfac = ConvolverNumericType.factory( type ); + convolve( halfkernels, source, target, convfac, convfac, convfac, convfac, new ListImgFactory< T >(), type, service ); + } + + /** + * Get an instance of type T from a {@link RandomAccess} on accessible that + * is positioned at the min of interval. + * + * @param accessible + * @param interval + * @return type instance + */ + private static < T extends NumericType< T > > T getType( final RandomAccessible< T > accessible, final Interval interval ) + { + final RandomAccess< T > a = accessible.randomAccess(); + interval.min( a ); + return a.get(); + } + + public static < S, T > void convolve1d( final double[] halfkernel, + final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, + final ConvolverFactory< S, T > convolverFactoryST, + final ExecutorService service ) + { + final long[] sourceOffset = new long[] { 1 - halfkernel.length }; + convolveOffset( halfkernel, source, sourceOffset, target, target, 0, convolverFactoryST, service, 1 ); + } + + /** + * Convolve source with a separable symmetric kernel and write the result to + * output. In-place operation (source==target) is supported. Calculations + * are done in the intermediate type determined by the + * {@link ConvolverFactory ConvolverFactories}. + * + * @param halfkernels + * an array containing half-kernels for every dimension. A + * half-kernel is the upper half (starting at the center pixel) + * of the symmetric convolution kernel for a given dimension. + * @param source + * source image, must be sufficiently padded (e.g. + * {@link Views#extendMirrorSingle(RandomAccessibleInterval)}) to + * provide values for the target interval plus a border of half + * the kernel size. + * @param target + * target image. + * @param convolverFactorySI + * produces line convolvers reading source type and writing + * temporary type. + * @param convolverFactoryII + * produces line convolvers reading temporary type and writing + * temporary type. + * @param convolverFactoryIT + * produces line convolvers reading temporary type and writing + * target type. + * @param convolverFactoryST + * produces line convolvers reading source type and writing + * target type. + * @param imgFactory + * factory to create temporary images. + * @param type + * instance of the temporary image type. + * @param service + * service providing threads for multi-threading + */ + public static < S, I, T > void convolve( final double[][] halfkernels, + final RandomAccessible< S > source, final RandomAccessibleInterval< T > target, + final ConvolverFactory< S, I > convolverFactorySI, + final ConvolverFactory< I, I > convolverFactoryII, + final ConvolverFactory< I, T > convolverFactoryIT, + final ConvolverFactory< S, T > convolverFactoryST, + final ImgFactory< I > imgFactory, final I type, + final ExecutorService service ) + { + final int n = source.numDimensions(); + if ( n == 1 ) + { + convolve1d( halfkernels[ 0 ], source, target, convolverFactoryST, service ); + } + else + { + // FIXME: is there a better way to determine the number of threads + final int numThreads = Runtime.getRuntime().availableProcessors(); + final int numTasks = numThreads > 1 ? numThreads * 4 : 1; + final long[] sourceOffset = new long[ n ]; + final long[] targetOffset = new long[ n ]; + target.min( sourceOffset ); + for ( int d = 0; d < n; ++d ) + { + targetOffset[ d ] = -sourceOffset[ d ]; + sourceOffset[ d ] += 1 - halfkernels[ d ].length; + } + + final long[][] tmpdims = getTempImageDimensions( target, halfkernels ); + Img< I > tmp1 = imgFactory.create( tmpdims[ 0 ], type ); + if ( n == 2 ) + { + convolveOffset( halfkernels[ 0 ], source, sourceOffset, tmp1, tmp1, 0, convolverFactorySI, service, numTasks ); + convolveOffset( halfkernels[ 1 ], tmp1, targetOffset, target, target, 1, convolverFactoryIT, service, numTasks ); + } + else + { + Img< I > tmp2 = imgFactory.create( tmpdims[ 1 ], type ); + final long[] zeroOffset = new long[ n ]; + convolveOffset( halfkernels[ 0 ], source, sourceOffset, tmp1, new FinalInterval( tmpdims[ 0 ] ), 0, convolverFactorySI, service, numTasks ); + for ( int d = 1; d < n - 1; ++d ) + { + convolveOffset( halfkernels[ d ], tmp1, zeroOffset, tmp2, new FinalInterval( tmpdims[ d ] ), d, convolverFactoryII, service, numTasks ); + final Img< I > tmp = tmp2; + tmp2 = tmp1; + tmp1 = tmp; + } + convolveOffset( halfkernels[ n - 1 ], tmp1, targetOffset, target, target, n - 1, convolverFactoryIT, service, numTasks ); + } + } + } + + /** + * 1D convolution in dimension d. + */ + static < S, T > void convolveOffset( final double[] halfkernel, final RandomAccessible< S > source, final long[] sourceOffset, final RandomAccessible< T > target, final Interval targetInterval, final int d, final ConvolverFactory< S, T > factory, final ExecutorService service, final int numTasks ) + { + final int n = source.numDimensions(); + final int k1 = halfkernel.length - 1; + long tmp = 1; + for ( int i = 0; i < n; ++i ) + if ( i != d ) + tmp *= targetInterval.dimension( i ); + final long endIndex = tmp; + final long taskSize = tmp / numTasks; + + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + final long[] dim = new long[ n ]; + targetInterval.min( min ); + targetInterval.max( max ); + targetInterval.dimensions( dim ); + dim[ d ] = 1; + + final long[] srcmin = new long[ n ]; + final long[] srcmax = new long[ n ]; + for ( int i = 0; i < n; ++i ) + { + srcmin[ i ] = min[ i ] + sourceOffset[ i ]; + srcmax[ i ] = max[ i ] + sourceOffset[ i ]; + } + srcmax[ d ] += 2 * k1; + + final ArrayList< Future< Void > > futures = new ArrayList< Future< Void > >(); + + for ( int taskNum = 0; taskNum < numTasks; ++taskNum ) + { + final long myStartIndex = taskNum * taskSize; + final long myEndIndex = ( taskNum == numTasks - 1 ) ? + endIndex : + myStartIndex + taskSize; + final Callable< Void > r = new Callable< Void >() + { + @Override + public Void call() + { + final RandomAccess< S > in = source.randomAccess( new FinalInterval( srcmin, srcmax ) ); + final RandomAccess< T > out = target.randomAccess( targetInterval ); + final Runnable convolver = factory.create( halfkernel, in, out, d, targetInterval.dimension( d ) ); + + out.setPosition( min ); + in.setPosition( srcmin ); + + final long[] moveToStart = new long[ n ]; + IntervalIndexer.indexToPosition( myStartIndex, dim, moveToStart ); + out.move( moveToStart ); + in.move( moveToStart ); + + for ( long index = myStartIndex; index < myEndIndex; ++index ) + { + convolver.run(); + out.setPosition( min[ d ], d ); + in.setPosition( srcmin[ d ], d ); + for ( int i = 0; i < n; ++i ) + { + if ( i != d ) + { + out.fwd( i ); + if ( out.getLongPosition( i ) > max[ i ] ) + { + out.setPosition( min[ i ], i ); + in.setPosition( srcmin[ i ], i ); + } + else + { + in.fwd( i ); + break; + } + } + } + } + return null; + } + }; + futures.add( service.submit( r ) ); + } + for ( final Future< Void > future : futures ) + { + try + { + future.get(); + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + catch ( final ExecutionException e ) + { + e.printStackTrace(); + } + } + } + + static long[][] getTempImageDimensions( final Dimensions targetsize, final double[][] halfkernels ) + { + final int n = targetsize.numDimensions(); + final long[][] tmpdims = new long[ n ][]; + tmpdims[ n - 1 ] = new long[ n ]; + targetsize.dimensions( tmpdims[ n - 1 ] ); + for ( int d = n - 2; d >= 0; --d ) + { + tmpdims[ d ] = tmpdims[ d + 1 ].clone(); + tmpdims[ d ][ d + 1 ] += 2 * halfkernels[ d + 1 ].length - 2; + } + return tmpdims; + } + + static boolean canUseBufferedConvolver( final Dimensions targetsize, final double[][] halfkernels ) + { + final int n = targetsize.numDimensions(); + for ( int d = 0; d < n; ++d ) + if ( targetsize.dimension( d ) + 4 * halfkernels[ d ].length - 4 > Integer.MAX_VALUE ) + return false; + return true; + } + + static boolean canUseArrayImgFactory( final Dimensions targetsize, final double[][] halfkernels ) + { + final int n = targetsize.numDimensions(); + long size = targetsize.dimension( 0 ); + for ( int d = 1; d < n; ++d ) + size *= targetsize.dimension( d ) + 2 * halfkernels[ d ].length; + return size <= Integer.MAX_VALUE; + } + + static < T extends NativeType< T > > ImgFactory< T > getImgFactory( final Dimensions targetsize, final double[][] halfkernels, final T type ) + { + if ( canUseArrayImgFactory( targetsize, halfkernels ) ) + return new ArrayImgFactory< T >(); + final int cellSize = ( int ) Math.pow( Integer.MAX_VALUE / type.getEntitiesPerPixel().getRatio(), 1.0 / targetsize.numDimensions() ); + return new CellImgFactory< T >( cellSize ); + } +} diff --git a/src/main/java/varun_algorithm_gradient/HessianMatrix.java b/src/main/java/varun_algorithm_gradient/HessianMatrix.java new file mode 100644 index 0000000..5c39bc9 --- /dev/null +++ b/src/main/java/varun_algorithm_gradient/HessianMatrix.java @@ -0,0 +1,390 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gradient; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.stream.IntStream; + +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.View; + +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.MixedTransformView; +import net.imglib2.view.Views; +import varun_algorithm_gauss3.Gauss3; + +/** + * + * Compute entries of n-dimensional Hessian matrix. + * + * @author Philipp Hanslovsky + * + */ +public class HessianMatrix +{ + + /** + * @param source + * n-dimensional input {@link RandomAccessibleInterval} + * @param gaussian + * n-dimensional output parameter + * {@link RandomAccessibleInterval} + * @param gradient + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * the gradients along all axes of the smoothed source (size of + * last dimension is n) + * @param hessianMatrix + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * all second partial derivatives as a linear representation + * (size of last dimension is n * ( n + 1 ) / 2) of upper + * triangular Hessian matrix: For n-dimensional input, + * hessianMatrix (m) will be populated along the nth + * dimension like this: [m11, m12, ... , m1n, m22, m23, ... , + * mnn] + * @param outOfBounds + * {@link OutOfBoundsFactory} that specifies how out of bound + * pixels of intermediate results should be handled (necessary + * for gradient computation). + * @param sigma + * Scale for Gaussian smoothing. + * + * @return Hessian matrix that was passed as output parameter. + * @throws IncompatibleTypeException + */ + public static < T extends RealType< T >, U extends RealType< U > > RandomAccessibleInterval< U > calculateMatrix( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > gaussian, + final RandomAccessibleInterval< U > gradient, + final RandomAccessibleInterval< U > hessianMatrix, + final OutOfBoundsFactory< U, ? super RandomAccessibleInterval< U > > outOfBounds, + final double... sigma ) throws IncompatibleTypeException + { + + if ( sigma.length == 1 ) + Gauss3.gauss( sigma[ 0 ], source, gaussian ); + else + Gauss3.gauss( sigma, source, gaussian ); + return calculateMatrix( Views.extend( gaussian, outOfBounds ), gradient, hessianMatrix, outOfBounds ); + } + + /** + * @param source + * n-dimensional pre-smoothed {@link RandomAccessible}. It is the + * callers responsibility to smooth the input at the desired + * scales. + * @param gradient + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * the gradients along all axes of the smoothed source (size of + * last dimension is n) + * @param hessianMatrix + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * all second partial derivatives as a linear representation + * (size of last dimension is n * ( n + 1 ) / 2) of upper + * triangular Hessian matrix: For n-dimensional input, + * hessianMatrix (m) will be populated along the nth + * dimension like this: [m11, m12, ... , m1n, m22, m23, ... , + * mnn] + * + * @param outOfBounds + * {@link OutOfBoundsFactory} that specifies how out of bound + * pixels of intermediate results should be handled (necessary + * for gradient computation). + * + * @return Hessian matrix that was passed as output parameter. + */ + public static < T extends RealType< T > > RandomAccessibleInterval< T > calculateMatrix( + final RandomAccessible< T > source, + final RandomAccessibleInterval< T > gradient, + final RandomAccessibleInterval< T > hessianMatrix, + final OutOfBoundsFactory< T, ? super RandomAccessibleInterval< T > > outOfBounds ) + { + + final int nDim = gradient.numDimensions() - 1; + + for ( long d = 0; d < nDim; ++d ) + PartialDerivative.gradientCentralDifference( source, Views.hyperSlice( gradient, nDim, d ), ( int ) d ); + + return calculateMatrix( Views.extend( gradient, outOfBounds ), hessianMatrix ); + } + + /** + * + * @param gradients + * n+1-dimensional {@link RandomAccessible} containing the + * gradients along all axes of the smoothed source (size of last + * dimension is n) + * @param hessianMatrix + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * all second partial derivatives as a linear representation + * (size of last dimension is n * ( n + 1 ) / 2) of upper + * triangular Hessian matrix: For n-dimensional input, + * hessianMatrix (m) will be populated along the nth + * dimension like this: [m11, m12, ... , m1n, m22, m23, ... , + * mnn] + * + * @return Hessian matrix that was passed as output parameter. + */ + public static < T extends RealType< T > > RandomAccessibleInterval< T > calculateMatrix( + final RandomAccessible< T > gradients, + final RandomAccessibleInterval< T > hessianMatrix ) + { + + final int nDim = gradients.numDimensions() - 1; + + long count = 0; + for ( long d1 = 0; d1 < nDim; ++d1 ) + { + final MixedTransformView< T > hs1 = Views.hyperSlice( gradients, nDim, d1 ); + for ( long d2 = d1; d2 < nDim; ++d2 ) + { + final IntervalView< T > hs2 = Views.hyperSlice( hessianMatrix, nDim, count ); + PartialDerivative.gradientCentralDifference( hs1, hs2, ( int ) d2 ); + ++count; + } + } + return hessianMatrix; + } + + // parallel + + /** + * @param source + * n-dimensional input {@link RandomAccessibleInterval} + * @param gaussian + * n-dimensional output parameter + * {@link RandomAccessibleInterval} + * @param gradient + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * the gradients along all axes of the smoothed source (size of + * last dimension is n) + * @param hessianMatrix + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * all second partial derivatives as a linear representation + * (size of last dimension is n * ( n + 1 ) / 2) of upper + * triangular Hessian matrix: For n-dimensional input, + * hessianMatrix (m) will be populated along the nth + * dimension like this: [m11, m12, ... , m1n, m22, m23, ... , + * mnn] + * @param outOfBounds + * {@link OutOfBoundsFactory} that specifies how out of bound + * pixels of intermediate results should be handled (necessary + * for gradient computation). + * @param nTasks + * Number of tasks used for parallel computation of eigenvalues. + * @param es + * {@link ExecutorService} providing workers for parallel + * computation. Service is managed (created, shutdown) by caller. + * @param sigma + * Scale for Gaussian smoothing. + * + * @return Hessian matrix that was passed as output parameter. + * @throws IncompatibleTypeException + * @throws ExecutionException + * @throws InterruptedException + */ + public static < T extends RealType< T >, U extends RealType< U > > RandomAccessibleInterval< U > calculateMatrix( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > gaussian, + final RandomAccessibleInterval< U > gradient, + final RandomAccessibleInterval< U > hessianMatrix, + final OutOfBoundsFactory< U, ? super RandomAccessibleInterval< U > > outOfBounds, + final int nTasks, + final ExecutorService es, + final double... sigma ) throws IncompatibleTypeException, InterruptedException, ExecutionException + { + + if ( sigma.length == 1 ) + Gauss3.gauss( IntStream.range( 0, source.numDimensions() ).mapToDouble( i -> sigma[ 0 ] ).toArray(), source, gaussian, es ); + else + Gauss3.gauss( sigma, source, gaussian, es ); + return calculateMatrix( Views.extend( gaussian, outOfBounds ), gradient, hessianMatrix, outOfBounds, nTasks, es ); + } + + /** + * @param source + * n-dimensional pre-smoothed {@link RandomAccessible}. It is the + * callers responsibility to smooth the input at the desired + * scales. + * @param gradient + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * the gradients along all axes of the smoothed source (size of + * last dimension is n) + * @param hessianMatrix + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * all second partial derivatives as a linear representation + * (size of last dimension is n * ( n + 1 ) / 2) of upper + * triangular Hessian matrix: For n-dimensional input, + * hessianMatrix (m) will be populated along the nth + * dimension like this: [m11, m12, ... , m1n, m22, m23, ... , + * mnn] + * @param outOfBounds + * {@link OutOfBoundsFactory} that specifies how out of bound + * pixels of intermediate results should be handled (necessary + * for gradient computation). + * @param nTasks + * Number of tasks used for parallel computation of eigenvalues. + * @param es + * {@link ExecutorService} providing workers for parallel + * computation. Service is managed (created, shutdown) by caller. + * + * @return Hessian matrix that was passed as output parameter. + * @throws IncompatibleTypeException + * @throws ExecutionException + * @throws InterruptedException + */ + public static < T extends RealType< T > > RandomAccessibleInterval< T > calculateMatrix( + final RandomAccessible< T > source, + final RandomAccessibleInterval< T > gradient, + final RandomAccessibleInterval< T > hessianMatrix, + final OutOfBoundsFactory< T, ? super RandomAccessibleInterval< T > > outOfBounds, + final int nTasks, + final ExecutorService es ) throws IncompatibleTypeException, InterruptedException, ExecutionException + { + + final int nDim = gradient.numDimensions() - 1; + + for ( long d = 0; d < nDim; ++d ) + PartialDerivative.gradientCentralDifferenceParallel( source, Views.hyperSlice( gradient, nDim, d ), ( int ) d, nTasks, es ); + + return calculateMatrix( Views.extend( gradient, outOfBounds ), hessianMatrix, nTasks, es ); + } + + /** + * + * @param gradient + * n+1-dimensional {@link RandomAccessible} containing the + * gradients along all axes of the smoothed source (size of last + * dimension is n) + * @param hessianMatrix + * n+1-dimensional {@link RandomAccessibleInterval} for storing + * all second partial derivatives as a linear representation + * (size of last dimension is n * ( n + 1 ) / 2) of upper + * triangular Hessian matrix: For n-dimensional input, + * hessianMatrix (m) will be populated along the nth + * dimension like this: [m11, m12, ... , m1n, m22, m23, ... , + * mnn] + * @param nTasks + * Number of tasks used for parallel computation of eigenvalues. + * @param es + * {@link ExecutorService} providing workers for parallel + * computation. Service is managed (created, shutdown) by caller. + * + * @return Hessian matrix that was passed as output parameter. + * @throws IncompatibleTypeException + * @throws ExecutionException + * @throws InterruptedException + */ + public static < T extends RealType< T > > RandomAccessibleInterval< T > calculateMatrix( + final RandomAccessible< T > gradient, + final RandomAccessibleInterval< T > hessianMatrix, + final int nTasks, + final ExecutorService es ) throws IncompatibleTypeException, InterruptedException, ExecutionException + { + + final int nDim = gradient.numDimensions() - 1; + + long count = 0; + for ( long d1 = 0; d1 < nDim; ++d1 ) + { + final MixedTransformView< T > hs1 = Views.hyperSlice( gradient, nDim, d1 ); + for ( long d2 = d1; d2 < nDim; ++d2 ) + { + final IntervalView< T > hs2 = Views.hyperSlice( hessianMatrix, nDim, count ); + PartialDerivative.gradientCentralDifferenceParallel( hs1, hs2, ( int ) d2, nTasks, es ); + ++count; + } + } + return hessianMatrix; + } + + + + /** + * + * The {@link HessianMatrix#calculateMatrix} methods assume that the voxels + * of the input data are isotropic. This is not always the case and the + * finite differences would need to be normalized accordingly. As the + * derivative is a linear transformation re-normalization (scaling) of the + * Hessian matrix produces the desired result. In general, the normalization + * is arbitrary and we choose to normalize such that the second derivative + * remains unchanged along the dimension for which the extent of a voxel is + * the smallest. + * + * Note that the returned object is a {@link View} on the Hessian matrix and + * values are re-normalized as they are queried. For repeated use of the + * re-normalized Hessian matrix, it might be beneficial to persist the + * re-normalized values by copying into a {@link RandomAccessibleInterval}. + * + * @param hessian + * Hessian matrix to be re-normalized (scaled). + * hessian is an n+1 dimensional + * {@link RandomAccessibleInterval} that stores a linear + * representation of the upper triangular n-dimensional Hessian + * matrix along the last dimension as specified in + * {@link HessianMatrix#calculateMatrix(RandomAccessible, RandomAccessibleInterval)}. + * @param sigma + * Specify the voxel size for each dimension. + * @return Scaled {@link View} of the hessian matrix stored in + * hessian + */ + public static < T extends RealType< T > > IntervalView< T > scaleHessianMatrix( final RandomAccessibleInterval< T > hessian, final double[] sigma ) + { + + assert sigma.length == hessian.numDimensions() - 1; + assert sigma.length * ( sigma.length + 1 ) / 2 == hessian.dimension( sigma.length ); + final int maxD = sigma.length; + + final double minSigma = Util.min( sigma ); + final double minSigmaSq = minSigma * minSigma; + final double[] sigmaSquared = new double[ sigma.length * ( sigma.length + 1 ) / 2 ]; + for ( int i1 = 0, k = 0; i1 < sigma.length; ++i1 ) + for ( int i2 = i1; i2 < sigma.length; ++i2, ++k ) + sigmaSquared[ k ] = sigma[ i1 ] * sigma[ i2 ] / minSigmaSq; + + final ScaleAsFunctionOfPosition< T > scaledMatrix = new ScaleAsFunctionOfPosition<>( hessian, l -> { + return sigmaSquared[ l.getIntPosition( maxD ) ]; + } ); + + return Views.interval( scaledMatrix, hessian ); + } + + +} diff --git a/src/main/java/varun_algorithm_gradient/PartialDerivative.java b/src/main/java/varun_algorithm_gradient/PartialDerivative.java new file mode 100644 index 0000000..1edce28 --- /dev/null +++ b/src/main/java/varun_algorithm_gradient/PartialDerivative.java @@ -0,0 +1,237 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gradient; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.util.Intervals; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; + +/** + * @author Tobias Pietzsch + * @author Philipp Hanslovsky + * + */ +public class PartialDerivative +{ + // nice version... + /** + * Compute the partial derivative of source in a particular dimension. + * + * @param source + * source image, has to provide valid data in the interval of the + * gradient image plus a one pixel border in dimension. + * @param gradient + * output image + * @param dimension + * along which dimension the partial derivatives are computed + */ + public static < T extends NumericType< T > > void gradientCentralDifference2( final RandomAccessible< T > source, final RandomAccessibleInterval< T > gradient, final int dimension ) + { + final Cursor< T > front = Views.flatIterable( Views.interval( source, Intervals.translate( gradient, 1, dimension ) ) ).cursor(); + final Cursor< T > back = Views.flatIterable( Views.interval( source, Intervals.translate( gradient, -1, dimension ) ) ).cursor(); + + for ( final T t : Views.flatIterable( gradient ) ) + { + t.set( front.next() ); + t.sub( back.next() ); + t.mul( 0.5 ); + } + } + + // parallel version... + /** + * Compute the partial derivative of source in a particular dimension. + * + * @param source + * source image, has to provide valid data in the interval of the + * gradient image plus a one pixel border in dimension. + * @param gradient + * output image + * @param dimension + * along which dimension the partial derivatives are computed + * @param nTasks + * Number of tasks for gradient computation. + * @param es + * {@link ExecutorService} providing workers for gradient + * computation. Service is managed (created, shutdown) by caller. + */ + public static < T extends NumericType< T > > void gradientCentralDifferenceParallel( + final RandomAccessible< T > source, + final RandomAccessibleInterval< T > gradient, + final int dimension, + final int nTasks, + final ExecutorService es ) throws InterruptedException, ExecutionException + { + final int nDim = source.numDimensions(); + if ( nDim < 2 ) + { + gradientCentralDifference( source, gradient, dimension ); + return; + } + + long dimensionMax = Long.MIN_VALUE; + int dimensionArgMax = -1; + + for ( int d = 0; d < nDim; ++d ) + { + final long size = gradient.dimension( d ); + if ( d != dimension && size > dimensionMax ) + { + dimensionMax = size; + dimensionArgMax = d; + } + } + + final long stepSize = Math.max( dimensionMax / nTasks, 1 ); + final long stepSizeMinusOne = stepSize - 1; + final long min = gradient.min( dimensionArgMax ); + final long max = gradient.max( dimensionArgMax ); + + final ArrayList< Callable< Void > > tasks = new ArrayList<>(); + for ( long currentMin = min, minZeroBase = 0; minZeroBase < dimensionMax; currentMin += stepSize, minZeroBase += stepSize ) + { + final long currentMax = Math.min( currentMin + stepSizeMinusOne, max ); + final long[] mins = new long[ nDim ]; + final long[] maxs = new long[ nDim ]; + gradient.min( mins ); + gradient.max( maxs ); + mins[ dimensionArgMax ] = currentMin; + maxs[ dimensionArgMax ] = currentMax; + final IntervalView< T > currentInterval = Views.interval( gradient, new FinalInterval( mins, maxs ) ); + tasks.add( () -> { + gradientCentralDifference( source, currentInterval, dimension ); + return null; + } ); + } + + final List< Future< Void > > futures = es.invokeAll( tasks ); + + for ( final Future< Void > f : futures ) + f.get(); + } + + // fast version + /** + * Compute the partial derivative of source in a particular dimension. + * + * @param source + * source image, has to provide valid data in the interval of the + * gradient image plus a one pixel border in dimension. + * @param gradient + * output image + * @param dimension + * along which dimension the partial derivatives are computed + */ + public static < T extends NumericType< T > > void gradientCentralDifference( final RandomAccessible< T > source, final RandomAccessibleInterval< T > gradient, final int dimension ) + { + final int n = gradient.numDimensions(); + + final long[] min = new long[ n ]; + gradient.min( min ); + final long[] max = new long[ n ]; + gradient.max( max ); + final long[] shiftback = new long[ n ]; + for ( int d = 0; d < n; ++d ) + shiftback[ d ] = min[ d ] - max[ d ]; + + final RandomAccess< T > result = gradient.randomAccess(); + final RandomAccess< T > back = source.randomAccess( Intervals.translate( gradient, -1, dimension ) ); + final RandomAccess< T > front = source.randomAccess( Intervals.translate( gradient, 1, dimension ) ); + + result.setPosition( min ); + back.setPosition( min ); + back.bck( dimension ); + front.setPosition( min ); + front.fwd( dimension ); + + final long max0 = max[ 0 ]; + while ( true ) + { + // process pixel + final T t = result.get(); + t.set( front.get() ); + t.sub( back.get() ); + t.mul( 0.5 ); + + // move to next pixel + // check dimension 0 separately to avoid the loop over d in most + // iterations + if ( result.getLongPosition( 0 ) == max0 ) + { + if ( n == 1 ) + return; + result.move( shiftback[ 0 ], 0 ); + back.move( shiftback[ 0 ], 0 ); + front.move( shiftback[ 0 ], 0 ); + // now check the remaining dimensions + for ( int d = 1; d < n; ++d ) + if ( result.getLongPosition( d ) == max[ d ] ) + { + result.move( shiftback[ d ], d ); + back.move( shiftback[ d ], d ); + front.move( shiftback[ d ], d ); + if ( d == n - 1 ) + return; + } + else + { + result.fwd( d ); + back.fwd( d ); + front.fwd( d ); + break; + } + } + else + { + result.fwd( 0 ); + back.fwd( 0 ); + front.fwd( 0 ); + } + } + } +} diff --git a/src/main/java/varun_algorithm_gradient/ScaleAsFunctionOfPosition.java b/src/main/java/varun_algorithm_gradient/ScaleAsFunctionOfPosition.java new file mode 100644 index 0000000..080097c --- /dev/null +++ b/src/main/java/varun_algorithm_gradient/ScaleAsFunctionOfPosition.java @@ -0,0 +1,110 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_gradient; + +import java.util.function.ToDoubleFunction; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.AbstractConvertedRandomAccess; +import net.imglib2.converter.AbstractConvertedRandomAccessible; +import net.imglib2.type.Type; +import net.imglib2.type.operators.MulFloatingPoint; + +/** + * + * @author Philipp Hanslovsky + * + * Multiply the value of a {@link RandomAccessible} depending on the + * position of the access. + * + */ +public class ScaleAsFunctionOfPosition< T extends Type< T > & MulFloatingPoint > extends AbstractConvertedRandomAccessible< T, T > +{ + + private final ToDoubleFunction< Localizable > scalingFunction; + + public ScaleAsFunctionOfPosition( final RandomAccessibleInterval< T > source, final ToDoubleFunction< Localizable > scalingFunction ) + { + super( source ); + this.scalingFunction = scalingFunction; + } + + @Override + public ScaledRandomAccess< T > randomAccess() + { + return new ScaledRandomAccess<>( source.randomAccess(), scalingFunction ); + } + + @Override + public AbstractConvertedRandomAccess< T, T > randomAccess( final Interval interval ) + { + return randomAccess(); + } + + public static class ScaledRandomAccess< T extends Type< T > & MulFloatingPoint > extends AbstractConvertedRandomAccess< T, T > + { + + private final T t; + + private final ToDoubleFunction< Localizable > scalingFunction; + + public ScaledRandomAccess( final RandomAccess< T > source, final ToDoubleFunction< Localizable > scalingFunction ) + { + super( source ); + this.t = source.get().createVariable(); + this.scalingFunction = scalingFunction; + } + + @Override + public T get() + { + t.set( source.get() ); + t.mul( scalingFunction.applyAsDouble( source ) ); + return t; + } + + @Override + public ScaledRandomAccess< T > copy() + { + return new ScaledRandomAccess<>( source.copyRandomAccess(), scalingFunction ); + } + + } + +} diff --git a/src/main/java/varun_algorithm_integral/IntegralImg.java b/src/main/java/varun_algorithm_integral/IntegralImg.java new file mode 100644 index 0000000..1b1d7fe --- /dev/null +++ b/src/main/java/varun_algorithm_integral/IntegralImg.java @@ -0,0 +1,332 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_integral; + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.converter.Converter; +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.iterator.LocalizingZeroMinIntervalIterator; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.NumericType; +import varun_algorithm.OutputAlgorithm; + +/** + * n-dimensional integral image that stores sums using type {@code }. Care + * must be taken that sums do not overflow the capacity of type {@code }. + * + * The integral image will be one pixel larger in each dimension as for easy + * computation of sums it has to contain "zeros" at the beginning of each + * dimension + * + * The {@link Converter} defines howto convert from Type {@code } to {@code + * }. + * + * Sums are done with the precision of {@code } and then set to the integral + * image type, which may crop the values according to the type's capabilities. + * + * @param + * The type of the input image. + * @param + * The type of the integral image. + * + * @author Stephan Preibisch + * @author Albert Cardona + */ +public class IntegralImg< R extends NumericType< R >, T extends NumericType< T > & NativeType< T > > implements OutputAlgorithm< Img< T > > +{ + protected final RandomAccessibleInterval< R > img; + + protected final T type; + + protected Img< T > integral; + + protected final Converter< R, T > converter; + + protected final int order; + + public IntegralImg( final RandomAccessibleInterval< R > img, final T type, final Converter< R, T > converter ) + { + this(img, type, converter, 1); + } + + public IntegralImg( final RandomAccessibleInterval< R > img, final T type, final Converter< R, T > converter, final int order ) + { + this.img = img; + this.type = type; + this.converter = converter; + this.order = order; + } + + @Override + public boolean process() + { + final int numDimensions = img.numDimensions(); + final long[] integralSize = new long[ numDimensions ]; + + // the size of the first dimension is changed + for ( int d = 0; d < numDimensions; ++d ) + integralSize[ d ] = ( int ) img.dimension( d ) + 1; + + final Img< T > integral = new ArrayImgFactory< T >().create( integralSize, type.createVariable() ); + + // not enough RAM or disc space + if ( integral == null ) + return false; + this.integral = integral; + + if ( numDimensions > 1 ) + { + /** + * Here we "misuse" a ArrayLocalizableCursor to iterate through all + * dimensions except the one we are computing the integral image in + */ + final long[] fakeSize = new long[ numDimensions - 1 ]; + + // location for the input location + final long[] tmpIn = new long[ numDimensions ]; + + // location for the integral location + final long[] tmpOut = new long[ numDimensions ]; + + // the size of dimension 0 + final long size = integralSize[ 0 ]; + + for ( int d = 1; d < numDimensions; ++d ) + fakeSize[ d - 1 ] = integralSize[ d ]; + + final LocalizingZeroMinIntervalIterator cursorDim = new LocalizingZeroMinIntervalIterator( fakeSize ); + + final RandomAccess< R > cursorIn = img.randomAccess(); + final RandomAccess< T > cursorOut = integral.randomAccess(); + + final T tmpVar = type.createVariable(); + final T sum = type.createVariable(); + + // iterate over all dimensions except the one we are computing the + // integral in, which is dim=0 here + main: while ( cursorDim.hasNext() ) + { + cursorDim.fwd(); + + // get all dimensions except the one we are currently doing the + // integral on + cursorDim.localize( fakeSize ); + + tmpIn[ 0 ] = 0; + tmpOut[ 0 ] = 1; + + for ( int d = 1; d < numDimensions; ++d ) + { + tmpIn[ d ] = fakeSize[ d - 1 ] - 1; + tmpOut[ d ] = fakeSize[ d - 1 ]; + + // all entries of position 0 are 0 + if ( tmpOut[ d ] == 0 ) + continue main; + } + + // set the cursor to the beginning of the correct line + cursorIn.setPosition( tmpIn ); + + // set the cursor in the integral image to the right position + cursorOut.setPosition( tmpOut ); + + // integrate over the line + integrateLineDim0( converter, cursorIn, cursorOut, sum, tmpVar, size ); + /* + * // compute the first pixel converter.convert( + * cursorIn.getType(), sum ); cursorOut.getType().set( sum ); + * + * for ( int i = 2; i < size; ++i ) { cursorIn.fwd( 0 ); + * cursorOut.fwd( 0 ); + * + * converter.convert( cursorIn.getType(), tmpVar ); sum.add( + * tmpVar ); cursorOut.getType().set( sum ); } + */ + } + } + else + { + final T tmpVar = type.createVariable(); + final T sum = type.createVariable(); + + // the size of dimension 0 + final long size = integralSize[ 0 ]; + + final RandomAccess< R > cursorIn = img.randomAccess(); + final RandomAccess< T > cursorOut = integral.randomAccess(); + + cursorIn.setPosition( 0, 0 ); + cursorOut.setPosition( 1, 0 ); + + // compute the first pixel + converter.convert( cursorIn.get(), sum ); + cursorOut.get().set( sum ); + + for ( int i = 2; i < size; ++i ) + { + cursorIn.fwd( 0 ); + cursorOut.fwd( 0 ); + + converter.convert( cursorIn.get(), tmpVar ); + sum.add( tmpVar ); + cursorOut.get().set( sum ); + } + + return true; + } + + for ( int d = 1; d < numDimensions; ++d ) + { + /** + * Here we "misuse" a ArrayLocalizableCursor to iterate through all + * dimensions except the one we are computing the fft in + */ + final long[] fakeSize = new long[ numDimensions - 1 ]; + final long[] tmp = new long[ numDimensions ]; + + // the size of dimension d + final long size = integralSize[ d ]; + + // get all dimensions except the one we are currently doing the + // integral on + int countDim = 0; + for ( int e = 0; e < numDimensions; ++e ) + if ( e != d ) + fakeSize[ countDim++ ] = integralSize[ e ]; + + final LocalizingZeroMinIntervalIterator cursorDim = new LocalizingZeroMinIntervalIterator( fakeSize ); + + final RandomAccess< T > cursor = integral.randomAccess(); + final T sum = type.createVariable(); + + while ( cursorDim.hasNext() ) + { + cursorDim.fwd(); + + // get all dimensions except the one we are currently doing the + // integral on + cursorDim.localize( fakeSize ); + + tmp[ d ] = 1; + countDim = 0; + for ( int e = 0; e < numDimensions; ++e ) + if ( e != d ) + tmp[ e ] = fakeSize[ countDim++ ]; + + // update the cursor in the input image to the current dimension + // position + cursor.setPosition( tmp ); + + // sum up line + integrateLine( d, cursor, sum, size ); + /* + * // init sum on first pixel that is not zero sum.set( + * cursor.getType() ); + * + * for ( int i = 2; i < size; ++i ) { cursor.fwd( d ); + * + * sum.add( cursor.getType() ); cursor.getType().set( sum ); } + */ + } + } + + return true; + } + + protected void integrateLineDim0( final Converter< R, T > converter, final RandomAccess< R > cursorIn, final RandomAccess< T > cursorOut, final T sum, final T tmpVar, final long size ) + { + // compute the first pixel + converter.convert( cursorIn.get(), sum ); + // compute Math.pow(tmpVar, order) imglib-style + for ( int j = 0; j < order-1; ++j) + { + sum.mul(sum); + } + + cursorOut.get().set( sum ); + + for ( int i = 2; i < size; ++i ) + { + cursorIn.fwd( 0 ); + cursorOut.fwd( 0 ); + + converter.convert( cursorIn.get(), tmpVar ); + + // compute Math.pow(tmpVar, order) imglib-style + for ( int j = 0; j < order-1; ++j) + { + tmpVar.mul(tmpVar); + } + + sum.add( tmpVar ); + cursorOut.get().set( sum ); + } + } + + protected void integrateLine( final int d, final RandomAccess< T > cursor, final T sum, final long size ) + { + // init sum on first pixel that is not zero + sum.set( cursor.get() ); + + for ( int i = 2; i < size; ++i ) + { + cursor.fwd( d ); + + sum.add( cursor.get() ); + cursor.get().set( sum ); + } + } + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public String getErrorMessage() + { + return null; + } + + @Override + public Img< T > getResult() + { + return integral; + } +} diff --git a/src/main/java/varun_algorithm_integral/IntegralImgDouble.java b/src/main/java/varun_algorithm_integral/IntegralImgDouble.java new file mode 100644 index 0000000..2539f3d --- /dev/null +++ b/src/main/java/varun_algorithm_integral/IntegralImgDouble.java @@ -0,0 +1,94 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_integral; + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.integral.IntegralImg; +import net.imglib2.converter.Converter; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.real.DoubleType; + +/** + * Special implementation for double using the basic type to sum up the + * individual lines. + * + * @param + * @author Stephan Preibisch + */ +public class IntegralImgDouble< R extends NumericType< R > > extends IntegralImg< R, DoubleType > +{ + + public IntegralImgDouble( final RandomAccessibleInterval< R > img, final DoubleType type, final Converter< R, DoubleType > converter ) + { + super( img, type, converter ); + } + + @Override + protected void integrateLineDim0( final Converter< R, DoubleType > converter, final RandomAccess< R > cursorIn, final RandomAccess< DoubleType > cursorOut, final DoubleType sum, final DoubleType tmpVar, final long size ) + { + // compute the first pixel + converter.convert( cursorIn.get(), sum ); + cursorOut.get().set( sum ); + + double sum2 = sum.get(); + + for ( int i = 2; i < size; ++i ) + { + cursorIn.fwd( 0 ); + cursorOut.fwd( 0 ); + + converter.convert( cursorIn.get(), tmpVar ); + sum2 += tmpVar.get(); + cursorOut.get().set( sum2 ); + } + } + + @Override + protected void integrateLine( final int d, final RandomAccess< DoubleType > cursor, final DoubleType sum, final long size ) + { + // init sum on first pixel that is not zero + double sum2 = cursor.get().get(); + + for ( int i = 2; i < size; ++i ) + { + cursor.fwd( d ); + + sum2 += cursor.get().get(); + cursor.get().set( sum2 ); + } + } + +} diff --git a/src/main/java/varun_algorithm_integral/IntegralImgLong.java b/src/main/java/varun_algorithm_integral/IntegralImgLong.java new file mode 100644 index 0000000..0081c1e --- /dev/null +++ b/src/main/java/varun_algorithm_integral/IntegralImgLong.java @@ -0,0 +1,93 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_integral; + +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.integral.IntegralImg; +import net.imglib2.converter.Converter; +import net.imglib2.type.numeric.NumericType; +import net.imglib2.type.numeric.integer.LongType; + +/** + * Special implementation for long using the basic type to sum up the individual + * lines. + * + * @param + * @author Stephan Preibisch + */ +public class IntegralImgLong< R extends NumericType< R > > extends IntegralImg< R, LongType > +{ + + public IntegralImgLong( final RandomAccessibleInterval< R > img, final LongType type, final Converter< R, LongType > converter ) + { + super( img, type, converter ); + } + + @Override + protected void integrateLineDim0( final Converter< R, LongType > converter, final RandomAccess< R > cursorIn, final RandomAccess< LongType > cursorOut, final LongType sum, final LongType tmpVar, final long size ) + { + // compute the first pixel + converter.convert( cursorIn.get(), sum ); + cursorOut.get().set( sum ); + + long sum2 = sum.get(); + + for ( int i = 2; i < size; ++i ) + { + cursorIn.fwd( 0 ); + cursorOut.fwd( 0 ); + + converter.convert( cursorIn.get(), tmpVar ); + sum2 += tmpVar.get(); + cursorOut.get().set( sum2 ); + } + } + + @Override + protected void integrateLine( final int d, final RandomAccess< LongType > cursor, final LongType sum, final long size ) + { + // init sum on first pixel that is not zero + long sum2 = cursor.get().get(); + + for ( int i = 2; i < size; ++i ) + { + cursor.fwd( d ); + + sum2 += cursor.get().get(); + cursor.get().set( sum2 ); + } + } +} diff --git a/src/main/java/varun_algorithm_integral/ScaleAreaAveraging2d.java b/src/main/java/varun_algorithm_integral/ScaleAreaAveraging2d.java new file mode 100644 index 0000000..4e10e45 --- /dev/null +++ b/src/main/java/varun_algorithm_integral/ScaleAreaAveraging2d.java @@ -0,0 +1,293 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_integral; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.algorithm.integral.IntegralImg; +import net.imglib2.converter.Converter; +import net.imglib2.converter.TypeIdentity; +import net.imglib2.exception.IncompatibleTypeException; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.list.ListImgFactory; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.Views; +import varun_algorithm.OutputAlgorithm; + +/** + * TODO + * + */ +public class ScaleAreaAveraging2d< T extends RealType< T >, R extends RealType< R >> implements OutputAlgorithm< Img< R > > +{ + protected ImgFactory< R > imgFactory; + + protected Img< R > scaled; + + protected RandomAccessibleInterval< T > integralImg; + + protected String error; + + protected final long[] size; + + final R targetType; + + final Converter< T, R > converter; + + /** + * @param integralImg + * The instance of {@link IntegralImg} or equivalent. + * @param targetType + * The desired type of the scaled image. + * @param size + * The target dimensions of the desired scaled image. + */ + @Deprecated + public ScaleAreaAveraging2d( final Img< T > integralImg, final R targetType, final long[] size ) + { + this( integralImg, targetType, size, null ); + + try + { + this.imgFactory = integralImg.factory().imgFactory( targetType ); + } + catch ( final IncompatibleTypeException e ) + { + this.imgFactory = new ListImgFactory< R >(); + } + } + + /** + * @param integralImg + * The instance of {@link IntegralImg} or equivalent. + * @param targetType + * The desired type of the scaled image. + * @param size + * The target dimensions of the desired scaled image. + */ + @SuppressWarnings( "unchecked" ) + public ScaleAreaAveraging2d( final RandomAccessibleInterval< T > integralImg, final R targetType, final long[] size, final ImgFactory< R > imgFactory ) + { + this.size = size; + this.targetType = targetType; + this.integralImg = integralImg; + this.imgFactory = imgFactory; + + if ( targetType.getClass().isInstance( Views.iterable( integralImg ).firstElement().createVariable() ) ) + { + // double cast to workaround javac error + converter = ( Converter< T, R > ) ( Converter< ?, ? > ) new TypeIdentity< T >(); + } + else + { + converter = new Converter< T, R >() + { + @Override + public void convert( final T input, final R output ) + { + output.setReal( input.getRealDouble() ); + } + }; + } + } + + @Deprecated + public ScaleAreaAveraging2d( final Img< T > integralImg, final R targetType, final Converter< T, R > converter, final long[] size ) + { + this( integralImg, targetType, converter, size, null ); + + try + { + this.imgFactory = integralImg.factory().imgFactory( targetType ); + } + catch ( final IncompatibleTypeException e ) + { + this.imgFactory = new ListImgFactory< R >(); + } + } + + public ScaleAreaAveraging2d( final RandomAccessibleInterval< T > integralImg, final R targetType, final Converter< T, R > converter, final long[] size, final ImgFactory< R > imgFactory ) + { + this.size = size; + this.targetType = targetType; + this.integralImg = integralImg; + this.converter = converter; + this.imgFactory = imgFactory; + } + + /** + * Set the desired dimensions of the scaled image obtainable after invoking + * {@link #process()} via {@link #getResult()}. + * + * @param width + * @param height + */ + public void setOutputDimensions( final long width, final long height ) + { + size[ 0 ] = width; + size[ 1 ] = height; + } + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public boolean process() + { + scaled = imgFactory.create( size, targetType ); + + final Cursor< R > cursor = scaled.cursor(); + final RandomAccess< T > c2 = integralImg.randomAccess(); + + final T sum = Views.iterable( integralImg ).firstElement().createVariable(); + final T area = sum.createVariable(); + + if ( isIntegerDivision( integralImg, scaled ) ) + { + final long stepSizeX = ( integralImg.dimension( 0 ) - 1 ) / size[ 0 ]; + final long stepSizeY = ( integralImg.dimension( 1 ) - 1 ) / size[ 1 ]; + area.setReal( stepSizeX * stepSizeY ); + + // final int vX = stepSizeX; + // final int vY = stepSizeY; + + while ( cursor.hasNext() ) + { + cursor.fwd(); + +// final int px = cursor.getPosition( 0 ); +// final int py = cursor.getPosition( 1 ); +// +// final int startX = px * stepSizeX; +// final int startY = py * stepSizeY; +// +// computeAverage( startX, startY, vX, vY, c2, sum ); + + // Same as above, without intermediary variables: + computeSum( + cursor.getLongPosition( 0 ) * stepSizeX, + cursor.getLongPosition( 1 ) * stepSizeY, + stepSizeX, stepSizeY, // vX, vY, + c2, sum ); + + sum.div( area ); + +// System.out.println( sum ); +// System.exit( 0 ); + + converter.convert( sum, cursor.get() ); + } + } + else + { + final double stepSizeX = ( ( double ) integralImg.dimension( 0 ) - 1 ) / size[ 0 ]; + final double stepSizeY = ( ( double ) integralImg.dimension( 1 ) - 1 ) / size[ 1 ]; + + while ( cursor.hasNext() ) + { + cursor.fwd(); + + final long px = cursor.getLongPosition( 0 ); + final long py = cursor.getLongPosition( 1 ); + + final double tmp1 = px * stepSizeX + 0.5; + final long startX = ( long ) ( tmp1 ); + final long vX = ( long ) ( tmp1 + stepSizeX ) - startX; + + final double tmp2 = py * stepSizeY + 0.5; + final long startY = ( long ) ( tmp2 ); + final long vY = ( long ) ( tmp2 + stepSizeY ) - startY; + + area.setReal( vX * vY ); + + computeSum( startX, startY, vX, vY, c2, sum ); + + sum.div( area ); + + converter.convert( sum, cursor.get() ); + } + } + + return true; + } + + final private static < T extends RealType< T >> void computeSum( final long startX, final long startY, final long vX, final long vY, + final RandomAccess< T > c2, final T sum ) + { + c2.setPosition( startX, 0 ); + c2.setPosition( startY, 1 ); + sum.set( c2.get() ); + + c2.move( vX, 0 ); + sum.sub( c2.get() ); + + c2.move( vY, 1 ); + sum.add( c2.get() ); + + c2.move( -vX, 0 ); + sum.sub( c2.get() ); + } + + /** + * The dimensions of the integral image are always +1 from the integrated + * image. + */ + protected static final boolean isIntegerDivision( final RandomAccessibleInterval< ? > integralImg, final RandomAccessibleInterval< ? > scaled ) + { + for ( int d = 0; d < scaled.numDimensions(); ++d ) + if ( 0 != ( integralImg.dimension( d ) - 1 ) % scaled.dimension( d ) ) + return false; + + return true; + } + + @Override + public String getErrorMessage() + { + return error; + } + + @Override + public Img< R > getResult() + { + return scaled; + } + +} diff --git a/src/main/java/varun_algorithm_kdtree/ClipConvexPolytopeKDTree.java b/src/main/java/varun_algorithm_kdtree/ClipConvexPolytopeKDTree.java new file mode 100644 index 0000000..928058d --- /dev/null +++ b/src/main/java/varun_algorithm_kdtree/ClipConvexPolytopeKDTree.java @@ -0,0 +1,295 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_kdtree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import net.imglib2.KDTree; +import net.imglib2.KDTreeNode; +import net.imglib2.algorithm.kdtree.ConvexPolytope; +import net.imglib2.algorithm.kdtree.HyperPlane; +import net.imglib2.algorithm.kdtree.KDTreeNodeIterable; + +/** + * Partition nodes in a {@link KDTree} into disjoint sets of nodes that are + * inside and outside a given convex polytope, respectively. + * + *

      + * Construct with the {@link KDTree}. Call {@link #clip(ConvexPolytope)} to + * partition with respect to a {@link ConvexPolytope}. Then call + * {@link #getInsideNodes()} and {@link #getOutsideNodes()} to get the sets of + * nodes inside and outside the polytope, respectively. + * + *

      + * The algorithm is described in this note. + * + * @param + * type of values stored in the tree. + * + * @author Tobias Pietzsch + */ +public class ClipConvexPolytopeKDTree< T > +{ + private final KDTree< T > tree; + + private final int n; + + private int nPlanes; + + private double[][] normals; + + private double[] ms; + + private final double[] xmin; + + private final double[] xmax; + + private boolean[] qR; + + private boolean[] qL; + + private final ArrayList< boolean[] > activeStack; + + private final ArrayList< boolean[] > psStack; + + private final ArrayList< KDTreeNode< T > > inNodes; + + private final ArrayList< KDTreeNode< T > > inSubtrees; + + private final ArrayList< KDTreeNode< T > > outNodes; + + private final ArrayList< KDTreeNode< T > > outSubtrees; + + public ClipConvexPolytopeKDTree( final KDTree< T > tree ) + { + this.tree = tree; + n = tree.numDimensions(); + xmin = new double[ n ]; + xmax = new double[ n ]; + activeStack = new ArrayList< boolean[] >(); + psStack = new ArrayList< boolean[] >(); + inNodes = new ArrayList< KDTreeNode< T > >(); + inSubtrees = new ArrayList< KDTreeNode< T > >(); + outNodes = new ArrayList< KDTreeNode< T > >(); + outSubtrees = new ArrayList< KDTreeNode< T > >(); + } + + public int numDimensions() + { + return n; + } + + public void clip( final ConvexPolytope polytope ) + { + final Collection< ? extends HyperPlane > planes = polytope.getHyperplanes(); + initNewSearch( planes.size() ); + int i = 0; + for ( final HyperPlane plane : planes ) + { + final double[] normal = normals[ i ]; + System.arraycopy( plane.getNormal(), 0, normal, 0, n ); + ms[ i ] = plane.getDistance(); + for ( int d = 0; d < n; ++d ) + { + qL[ d * nPlanes + i ] = normal[ d ] < 0; + qR[ d * nPlanes + i ] = normal[ d ] >= 0; + } + ++i; + } + clip( tree.getRoot(), 0 ); + } + + public void clip( final double[][] planes ) + { + initNewSearch( planes.length ); + for ( int i = 0; i < nPlanes; ++i ) + { + final double[] normal = normals[ i ]; + System.arraycopy( planes[ i ], 0, normal, 0, n ); + ms[ i ] = planes[ i ][ n ]; + for ( int d = 0; d < n; ++d ) + { + qL[ d * nPlanes + i ] = normal[ d ] < 0; + qR[ d * nPlanes + i ] = normal[ d ] >= 0; + } + } + clip( tree.getRoot(), 0 ); + } + + public Iterable< KDTreeNode< T > > getInsideNodes() + { + return new KDTreeNodeIterable< T >( inNodes, inSubtrees ); + } + + public Iterable< KDTreeNode< T > > getOutsideNodes() + { + return new KDTreeNodeIterable< T >( outNodes, outSubtrees ); + } + + private void initNewSearch( final int nPlanes ) + { + this.nPlanes = nPlanes; + normals = new double[ nPlanes ][]; + for ( int i = 0; i < nPlanes; ++i ) + normals[ i ] = new double[ n ]; + ms = new double[ nPlanes ]; + qR = new boolean[ n * nPlanes ]; + qL = new boolean[ n * nPlanes ]; + inNodes.clear(); + inSubtrees.clear(); + outNodes.clear(); + outSubtrees.clear(); + activeStack.clear(); + psStack.clear(); + tree.realMin( xmin ); + tree.realMax( xmax ); + Arrays.fill( getActiveArray( 0 ), true ); + } + + private boolean[] getActiveArray( final int i ) + { + if ( i >= activeStack.size() ) + { + activeStack.add( new boolean[ nPlanes ] ); + psStack.add( new boolean[ nPlanes ] ); + } + return activeStack.get( i ); + } + + private boolean[] getPsArray( final int i ) + { + return psStack.get( i ); + } + + private void addAll( final KDTreeNode< T > node, final ArrayList< KDTreeNode< T > > list ) + { + list.add( node ); + } + + private boolean allAbove( final int i ) + { + final double[] normal = normals[ i ]; + double dot = 0; + for ( int d = 0; d < n; ++d ) + dot += normal[ d ] * ( normal[ d ] >= 0 ? xmin[ d ] : xmax[ d ] ); + return dot >= ms[ i ]; + } + + private boolean allBelow( final int i ) + { + final double[] normal = normals[ i ]; + double dot = 0; + for ( int d = 0; d < n; ++d ) + dot += normal[ d ] * ( normal[ d ] < 0 ? xmin[ d ] : xmax[ d ] ); + return dot < ms[ i ]; + } + + private void clipSubtree( final KDTreeNode< T > current, final boolean[] ps, final boolean[] qs, final int qoff, final int recursionDepth ) + { + final boolean[] active = getActiveArray( recursionDepth ); + final boolean[] stillActive = getActiveArray( recursionDepth + 1 ); + System.arraycopy( active, 0, stillActive, 0, nPlanes ); + boolean noneActive = true; + for ( int i = 0; i < nPlanes; ++i ) + { + if ( active[ i ] ) + { + if ( ps[ i ] && qs[ qoff + i ] && allAbove( i ) ) + stillActive[ i ] = false; + else + { + noneActive = false; + if ( !ps[ i ] && !qs[ qoff + i ] && allBelow( i ) ) + { + addAll( current, outSubtrees ); + return; + } + } + } + } + if ( noneActive ) + addAll( current, inSubtrees ); + else + clip( current, recursionDepth + 1 ); + } + + private void clip( final KDTreeNode< T > current, final int recursionDepth ) + { + final int sd = current.getSplitDimension(); + final double sc = current.getSplitCoordinate(); + + final boolean[] active = getActiveArray( recursionDepth ); + final boolean[] ps = getPsArray( recursionDepth ); + + boolean p = true; + for ( int i = 0; i < nPlanes; ++i ) + { + if ( active[ i ] ) + { + final double[] normal = normals[ i ]; + double dot = 0; + for ( int d = 0; d < n; ++d ) + dot += current.getDoublePosition( d ) * normal[ d ]; + ps[ i ] = dot >= ms[ i ]; + p &= ps[ i ]; + } + } + + if ( p ) + inNodes.add( current ); + else + outNodes.add( current ); + + final int qoff = sd * nPlanes; + if ( current.left != null ) + { + final double max = xmax[ sd ]; + xmax[ sd ] = sc; + clipSubtree( current.left, ps, qL, qoff, recursionDepth ); + xmax[ sd ] = max; + } + + if ( current.right != null ) + { + final double min = xmin[ sd ]; + xmin[ sd ] = sc; + clipSubtree( current.right, ps, qR, qoff, recursionDepth ); + xmin[ sd ] = min; + } + } +} diff --git a/src/main/java/varun_algorithm_kdtree/ConvexPolytope.java b/src/main/java/varun_algorithm_kdtree/ConvexPolytope.java new file mode 100644 index 0000000..b1928df --- /dev/null +++ b/src/main/java/varun_algorithm_kdtree/ConvexPolytope.java @@ -0,0 +1,100 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_kdtree; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.realtransform.AffineGet; +import net.imglib2.util.LinAlgHelpers; + +public class ConvexPolytope extends AbstractEuclideanSpace +{ + private final Collection< ? extends HyperPlane > hyperplanes; + + public ConvexPolytope( final Collection< ? extends HyperPlane > hyperplanes ) + { + super( hyperplanes.iterator().next().numDimensions() ); + this.hyperplanes = hyperplanes; + } + + public ConvexPolytope( final HyperPlane... hyperplanes ) + { + this( Arrays.asList( hyperplanes ) ); + } + + public Collection< ? extends HyperPlane > getHyperplanes() + { + return hyperplanes; + } + + /** + * Apply an {@link AffineGet affine transformation} to a {@link HyperPlane}. + * + * @param polytope + * a polytope. + * @param transform + * affine transformation to apply to the polytope. + * @return the transformed polytope. + */ + public static ConvexPolytope transform( final ConvexPolytope polytope, final AffineGet transform ) + { + assert polytope.numDimensions() == transform.numDimensions(); + + final int n = transform.numDimensions(); + + final double[] O = new double[ n ]; + final double[] tO = new double[ n ]; + final double[] tN = new double[ n ]; + final double[][] m = new double[n][n]; + for ( int r = 0; r < n; ++r ) + for ( int c = 0; c < n; ++c ) + m[r][c] = transform.inverse().get( c, r ); + + final ArrayList< HyperPlane > transformedPlanes = new ArrayList<>(); + for ( final HyperPlane plane : polytope.getHyperplanes() ) + { + LinAlgHelpers.scale( plane.getNormal(), plane.getDistance(), O ); + transform.apply( O, tO ); + LinAlgHelpers.mult( m, plane.getNormal(), tN ); + LinAlgHelpers.normalize( tN ); + final double td = LinAlgHelpers.dot( tN, tO ); + transformedPlanes.add( new HyperPlane( tN, td ) ); + } + return new ConvexPolytope( transformedPlanes ); + } +} diff --git a/src/main/java/varun_algorithm_kdtree/HyperPlane.java b/src/main/java/varun_algorithm_kdtree/HyperPlane.java new file mode 100644 index 0000000..6791304 --- /dev/null +++ b/src/main/java/varun_algorithm_kdtree/HyperPlane.java @@ -0,0 +1,105 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_kdtree; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.realtransform.AffineGet; +import net.imglib2.util.LinAlgHelpers; + +public class HyperPlane extends AbstractEuclideanSpace +{ + private final double[] normal; + + private final double distance; + + public HyperPlane( final double[] normal, final double distance ) + { + super( normal.length ); + this.normal = normal.clone(); + this.distance = distance; + LinAlgHelpers.normalize( this.normal ); + } + + public HyperPlane( final double... normalAndDistance ) + { + super( normalAndDistance.length - 1 ); + this.normal = new double[ n ]; + System.arraycopy( normalAndDistance, 0, normal, 0, n ); + this.distance = normalAndDistance[ n ]; + LinAlgHelpers.normalize( this.normal ); + } + + public double[] getNormal() + { + return normal; + } + + public double getDistance() + { + return distance; + } + + /** + * Apply an {@link AffineGet affine transformation} to a {@link HyperPlane}. + * + * @param plane + * a plane. + * @param transform + * affine transformation to apply to the plane. + * @return the transformed plane. + */ + public static HyperPlane transform( final HyperPlane plane, final AffineGet transform ) + { + assert plane.numDimensions() == transform.numDimensions(); + + final int n = transform.numDimensions(); + + final double[] O = new double[ n ]; + final double[] tO = new double[ n ]; + LinAlgHelpers.scale( plane.getNormal(), plane.getDistance(), O ); + transform.apply( O, tO ); + + final double[][] m = new double[ n ][ n ]; + for ( int r = 0; r < n; ++r ) + for ( int c = 0; c < n; ++c ) + m[ r ][ c ] = transform.inverse().get( c, r ); + final double[] tN = new double[ n ]; + LinAlgHelpers.mult( m, plane.getNormal(), tN ); + LinAlgHelpers.normalize( tN ); + final double td = LinAlgHelpers.dot( tN, tO ); + + return new HyperPlane( tN, td ); + } +} diff --git a/src/main/java/varun_algorithm_kdtree/KDTreeNodeIterable.java b/src/main/java/varun_algorithm_kdtree/KDTreeNodeIterable.java new file mode 100644 index 0000000..6861de5 --- /dev/null +++ b/src/main/java/varun_algorithm_kdtree/KDTreeNodeIterable.java @@ -0,0 +1,122 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_kdtree; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Iterator; + +import net.imglib2.KDTreeNode; + +public class KDTreeNodeIterable< T > implements Iterable< KDTreeNode< T > > +{ + private final ArrayList< KDTreeNode< T > > singleNodes; + + private final ArrayList< KDTreeNode< T > > subtrees; + + public KDTreeNodeIterable( final ArrayList< KDTreeNode< T > > singleNodes, final ArrayList< KDTreeNode< T > > subtrees ) + { + this.singleNodes = singleNodes; + this.subtrees = subtrees; + + } + + @Override + public Iterator< KDTreeNode< T > > iterator() + { + return new KDTreeNodeIterable.Iter< T >( singleNodes, subtrees ); + } + + private static class Iter< T > implements Iterator< KDTreeNode< T > > + { + private final ArrayList< KDTreeNode< T > > nodes; + + private int nextNodeIndex; + + private final ArrayList< KDTreeNode< T > > subtrees; + + private int nextSubtreeIndex; + + private final ArrayDeque< KDTreeNode< T > > stack; + + private Iter( final ArrayList< KDTreeNode< T > > nodes, final ArrayList< KDTreeNode< T > > subtrees ) + { + this.nodes = nodes; + this.subtrees = subtrees; + nextNodeIndex = 0; + nextSubtreeIndex = 0; + stack = new ArrayDeque< KDTreeNode< T > >(); + } + + @Override + public boolean hasNext() + { + return !stack.isEmpty() || nextSubtreeIndex < subtrees.size() || nextNodeIndex < nodes.size(); + } + + @Override + public KDTreeNode< T > next() + { + if ( !stack.isEmpty() ) + { + final KDTreeNode< T > current = stack.pop(); + if ( current.left != null ) + stack.push( current.left ); + if ( current.right != null ) + stack.push( current.right ); + return current; + } + else if ( nextSubtreeIndex < subtrees.size() ) + { + final KDTreeNode< T > current = subtrees.get( nextSubtreeIndex++ ); + if ( current.left != null ) + stack.push( current.left ); + if ( current.right != null ) + stack.push( current.right ); + return current; + } + else if ( nextNodeIndex < nodes.size() ) + { + return nodes.get( nextNodeIndex++ ); + } + else + return null; + } + + @Override + public void remove() + {} + } +} diff --git a/src/main/java/varun_algorithm_kdtree/SplitHyperPlaneKDTree.java b/src/main/java/varun_algorithm_kdtree/SplitHyperPlaneKDTree.java new file mode 100644 index 0000000..32f6caf --- /dev/null +++ b/src/main/java/varun_algorithm_kdtree/SplitHyperPlaneKDTree.java @@ -0,0 +1,203 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_kdtree; + +import java.util.ArrayList; + +import net.imglib2.KDTree; +import net.imglib2.KDTreeNode; + + +/** + * Partition nodes in a {@link KDTree} into disjoint sets of nodes that are + * above and below a given hyperplane, respectively. + * + *

      + * Construct with the {@link KDTree}. Call {@link #split(HyperPlane)} to + * partition with respect to a {@link HyperPlane}. Then call + * {@link #getAboveNodes()} and {@link #getBelowNodes()} to get the sets of + * nodes above and below the hyperplane, respectively. + * + *

      + * The algorithm is described in this note. + * + * @param + * type of values stored in the tree. + * + * @author Tobias Pietzsch + */ +public class SplitHyperPlaneKDTree< T > +{ + private final KDTree< T > tree; + + private final int n; + + private final double[] normal; + + private double m; + + private final double[] xmin; + + private final double[] xmax; + + private final ArrayList< KDTreeNode< T > > aboveNodes; + + private final ArrayList< KDTreeNode< T > > aboveSubtrees; + + private final ArrayList< KDTreeNode< T > > belowNodes; + + private final ArrayList< KDTreeNode< T > > belowSubtrees; + + public SplitHyperPlaneKDTree( final KDTree< T > tree ) + { + n = tree.numDimensions(); + xmin = new double[ n ]; + xmax = new double[ n ]; + normal = new double[ n ]; + this.tree = tree; + aboveNodes = new ArrayList< KDTreeNode< T > >(); + aboveSubtrees = new ArrayList< KDTreeNode< T > >(); + belowNodes = new ArrayList< KDTreeNode< T > >(); + belowSubtrees = new ArrayList< KDTreeNode< T > >(); + } + + public int numDimensions() + { + return n; + } + + public void split( final HyperPlane plane ) + { + initNewSearch(); + System.arraycopy( plane.getNormal(), 0, normal, 0, n ); + m = plane.getDistance(); + split( tree.getRoot() ); + } + + public void split( final double[] plane ) + { + initNewSearch(); + System.arraycopy( plane, 0, normal, 0, n ); + m = plane[ n ]; + split( tree.getRoot() ); + } + + private void initNewSearch() + { + aboveNodes.clear(); + aboveSubtrees.clear(); + belowNodes.clear(); + belowSubtrees.clear(); + tree.realMin( xmin ); + tree.realMax( xmax ); + } + + public Iterable< KDTreeNode< T > > getAboveNodes() + { + return new KDTreeNodeIterable< T >( aboveNodes, aboveSubtrees ); + } + + public Iterable< KDTreeNode< T > > getBelowNodes() + { + return new KDTreeNodeIterable< T >( belowNodes, belowSubtrees ); + } + + private static < T > void addAll( final KDTreeNode< T > node, final ArrayList< KDTreeNode< T > > list ) + { + list.add( node ); + } + + private boolean allAbove() + { + double dot = 0; + for ( int d = 0; d < n; ++d ) + dot += normal[ d ] * ( normal[ d ] >= 0 ? xmin[ d ] : xmax[ d ] ); + return dot >= m; + } + + private boolean allBelow() + { + double dot = 0; + for ( int d = 0; d < n; ++d ) + dot += normal[ d ] * ( normal[ d ] < 0 ? xmin[ d ] : xmax[ d ] ); + return dot < m; + } + + private void splitSubtree( final KDTreeNode< T > current, final boolean p, final boolean q ) + { + if ( p && q && allAbove() ) + addAll( current, aboveSubtrees ); + else if ( !p && !q && allBelow() ) + addAll( current, belowSubtrees ); + else + split( current ); + } + + private void split( final KDTreeNode< T > current ) + { + final int sd = current.getSplitDimension(); + final double sc = current.getSplitCoordinate(); + + double dot = 0; + for ( int d = 0; d < n; ++d ) + dot += current.getDoublePosition( d ) * normal[ d ]; + final boolean p = dot >= m; + + // current + if ( p ) + aboveNodes.add( current ); + else + belowNodes.add( current ); + + // left + if ( current.left != null ) + { + final double max = xmax[ sd ]; + xmax[ sd ] = sc; + splitSubtree( current.left, p, normal[ sd ] < 0 ); + xmax[ sd ] = max; + } + + // right + if ( current.right != null ) + { + final double min = xmin[ sd ]; + xmin[ sd ] = sc; + splitSubtree( current.right, p, normal[ sd ] >= 0 ); + xmin[ sd ] = min; + } + } +} diff --git a/src/main/java/varun_algorithm_kdtree/VolumetricSearch.java b/src/main/java/varun_algorithm_kdtree/VolumetricSearch.java new file mode 100644 index 0000000..942c50b --- /dev/null +++ b/src/main/java/varun_algorithm_kdtree/VolumetricSearch.java @@ -0,0 +1,257 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_kdtree; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +import net.imglib2.Interval; +import net.imglib2.KDTree; +import net.imglib2.KDTreeNode; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RealInterval; +import net.imglib2.RealLocalizable; + + +/** + * The volumetric search uses a K-D tree to search for all hyper-rectangular + * nodes that contain a given point. + * + * You can use this via the {@code RandomAccessible>} interface: + *

        + *
      • Get the {@code RandomAccess>} interface
      • + *
      • Localize it to your point,
      • + *
      • {@code get()} performs the search, returning the list.
      • + *
      + * + * @author Lee Kamentsky + * @author Tobias Pietzsch + */ +public class VolumetricSearch< I extends RealInterval > implements RandomAccessible< List< I >> +{ + final int numDimensions; + + final KDTree< IntervalWrapper< I > > kdtree; + + public VolumetricSearch( final List< I > intervals ) + { + if ( intervals.isEmpty() ) + { + numDimensions = 0; + } + else + { + numDimensions = intervals.get( 0 ).numDimensions(); + } + final ArrayList< IntervalWrapper< I > > wrappers = new ArrayList< IntervalWrapper< I > >( intervals.size() ); + for ( final I interval : intervals ) + wrappers.add( new IntervalWrapper< I >( interval ) ); + kdtree = new KDTree< IntervalWrapper< I > >( wrappers, wrappers ); + } + + private static class IntervalWrapper< I extends RealInterval > implements RealLocalizable + { + final I interval; + + final int n; + + public IntervalWrapper( final I interval ) + { + this.interval = interval; + this.n = interval.numDimensions(); + } + + public I get() + { + return interval; + } + + @Override + public int numDimensions() + { + return 2 * n; + } + + @Override + public void localize( final float[] position ) + { + for ( int d = 0; d < n; ++d ) + { + position[ d ] = ( float ) interval.realMin( d ); + position[ d + n ] = ( float ) interval.realMax( d ); + } + } + + @Override + public void localize( final double[] position ) + { + for ( int d = 0; d < n; ++d ) + { + position[ d ] = interval.realMin( d ); + position[ d + n ] = interval.realMax( d ); + } + } + + @Override + public float getFloatPosition( final int d ) + { + return ( float ) getDoublePosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return ( d < n ) ? interval.realMin( d ) : interval.realMax( d - n ); + } + + } + + /** + * Find all intervals that contain a given point + * + * @param pt + * the point in question + * @return list of all intervals containing the point. + */ + public List< I > find( final RealLocalizable pt ) + { + final double[] position = new double[ numDimensions ]; + pt.localize( position ); + final LinkedList< I > list = new LinkedList< I >(); + if ( kdtree.getRoot() == null ) + return list; + + final Stack< KDTreeNode< IntervalWrapper< I > > > toDo = new Stack< KDTreeNode< IntervalWrapper< I > > >(); + toDo.push( kdtree.getRoot() ); + while ( toDo.size() > 0 ) + { + final KDTreeNode< IntervalWrapper< I > > node = toDo.pop(); + final int k = node.getSplitDimension(); + + // check this interval + final I interval = node.get().get(); + boolean good = true; + for ( int i = 0; i < numDimensions; i++ ) + { + if ( ( position[ i ] < interval.realMin( i ) ) || ( position[ i ] > interval.realMax( i ) ) ) + { + good = false; + break; + } + } + if ( good ) + list.add( interval ); + + // possibly add children + if ( k < numDimensions ) + { + // The coordinate is a minimum. + // If it is greater than the position, only take the left branch + // which still could be lower. + // Otherwise (coordinate is smaller/equal position, take the + // right branch as well + if ( node.left != null ) + toDo.push( node.left ); + if ( node.right != null && node.getSplitCoordinate() <= position[ k ] ) + toDo.push( node.right ); + } + else + { + // The coordinate is a maximum. + // If it is smaller than the position, only take the right + // branch + // which still could be higher. + // Otherwise (coordinate is larger/equal position, take the left + // branch as well + if ( node.right != null ) + toDo.push( node.right ); + if ( node.left != null && node.getSplitCoordinate() >= position[ k - numDimensions ] ) + toDo.push( node.left ); + } + } + + return list; + } + + @Override + public int numDimensions() + { + return numDimensions; + } + + private class VolumetricSearchRandomAccess extends Point implements RandomAccess< List< I > > + { + VolumetricSearchRandomAccess() + { + super( numDimensions ); + } + + @Override + public List< I > get() + { + return find( this ); + } + + @Override + public VolumetricSearchRandomAccess copy() + { + final VolumetricSearchRandomAccess myCopy = new VolumetricSearchRandomAccess(); + myCopy.setPosition( this ); + return myCopy; + } + + @Override + public VolumetricSearchRandomAccess copyRandomAccess() + { + return copy(); + } + } + + @Override + public RandomAccess< List< I > > randomAccess() + { + return new VolumetricSearchRandomAccess(); + } + + @Override + public RandomAccess< List< I > > randomAccess( final Interval interval ) + { + return randomAccess(); + } +} diff --git a/src/main/java/varun_algorithm_labeling/AllConnectedComponents.java b/src/main/java/varun_algorithm_labeling/AllConnectedComponents.java new file mode 100644 index 0000000..0e54000 --- /dev/null +++ b/src/main/java/varun_algorithm_labeling/AllConnectedComponents.java @@ -0,0 +1,297 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_labeling; + +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingOutOfBoundsRandomAccessFactory; +import net.imglib2.labeling.LabelingType; +import net.imglib2.outofbounds.OutOfBounds; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.type.logic.BitType; +import net.imglib2.view.Views; + +/** + * Label all 8-connected components of a binary image + * + * @author Lee Kamentsky + */ +@Deprecated +public class AllConnectedComponents +{ + protected static class PositionStack + { + private final int dimensions; + + private long[] storage; + + private int position = 0; + + public PositionStack( final int dimensions ) + { + this.dimensions = dimensions; + storage = new long[ 100 * dimensions ]; + } + + public void push( final long[] position ) + { + final int insertPoint = this.position * dimensions; + if ( storage.length == insertPoint ) + { + final long[] newStorage = new long[ ( this.position * 3 / 2 ) * dimensions ]; + System.arraycopy( storage, 0, newStorage, 0, storage.length ); + storage = newStorage; + } + System.arraycopy( position, 0, storage, insertPoint, dimensions ); + this.position++; + } + + public void pop( final long[] position ) + { + this.position--; + System.arraycopy( storage, this.position * dimensions, position, 0, dimensions ); + } + + public boolean isEmpty() + { + return position == 0; + } + } + + /** + * Label all connected components in the given image using an 8-connected + * structuring element or it's N-dimensional analog (connect if touching + * along diagonals as well as +/- one element in any direction). + * + * @param + * the type of the labels to apply + * @param labeling + * Assign labels to this labeling space + * @param img + * a binary image where true indicates parts of components + * @param names + * supplies names for the different components as needed + * @throws NoSuchElementException + * if there are not enough names + */ + public static < T extends Comparable< T >> void labelAllConnectedComponents( final Labeling< T > labeling, final RandomAccessibleInterval< BitType > img, final Iterator< T > names ) throws NoSuchElementException + { + final long[][] offsets = getStructuringElement( img.numDimensions() ); + labelAllConnectedComponents( labeling, img, names, offsets ); + } + + /** + * Label all connected components in the given image using an arbitrary + * structuring element. + * + * @param + * the type of the labels to apply + * @param labeling + * Assign labels to this labeling space + * @param img + * a binary image where true indicates parts of components + * @param names + * supplies names for the different components as needed + * @param structuringElement + * an array of offsets to a pixel of the pixels which are + * considered connected. For instance, a 4-connected structuring + * element would be "new int [][] {{-1,0},{1,0},{0,-1},{0,1}}". + * @throws NoSuchElementException + * if there are not enough names + */ + public static < T extends Comparable< T >> void labelAllConnectedComponents( final Labeling< T > labeling, final RandomAccessibleInterval< BitType > img, final Iterator< T > names, final long[][] structuringElement ) throws NoSuchElementException + { + final Cursor< BitType > c = Views.iterable( img ).localizingCursor(); + final RandomAccess< BitType > raSrc = img.randomAccess(); + final OutOfBoundsFactory< LabelingType< T >, Labeling< T >> factory = new LabelingOutOfBoundsRandomAccessFactory< T, Labeling< T >>(); + final OutOfBounds< LabelingType< T >> raDest = factory.create( labeling ); + final long[] srcPosition = new long[ img.numDimensions() ]; + final long[] destPosition = new long[ labeling.numDimensions() ]; + final long[] dimensions = new long[ labeling.numDimensions() ]; + labeling.dimensions( dimensions ); + final PositionStack toDoList = new PositionStack( img.numDimensions() ); + while ( c.hasNext() ) + { + final BitType t = c.next(); + if ( t.get() ) + { + c.localize( srcPosition ); + boolean outOfBounds = false; + for ( int i = 0; i < dimensions.length; i++ ) + { + if ( srcPosition[ i ] >= dimensions[ i ] ) + { + outOfBounds = true; + break; + } + } + if ( outOfBounds ) + continue; + + raDest.setPosition( srcPosition ); + /* + * Assign a label if no label has yet been assigned. + */ + LabelingType< T > label = raDest.get(); + if ( label.getLabeling().isEmpty() ) + { + final List< T > currentLabel = label.intern( names.next() ); + label.setLabeling( currentLabel ); + toDoList.push( srcPosition ); + while ( !toDoList.isEmpty() ) + { + /* + * Find neighbors at the position + */ + toDoList.pop( srcPosition ); + for ( final long[] offset : structuringElement ) + { + outOfBounds = false; + for ( int i = 0; i < offset.length; i++ ) + { + destPosition[ i ] = srcPosition[ i ] + offset[ i ]; + if ( ( destPosition[ i ] < 0 ) || ( destPosition[ i ] >= dimensions[ i ] ) ) + { + outOfBounds = true; + break; + } + } + if ( outOfBounds ) + continue; + raSrc.setPosition( destPosition ); + if ( raSrc.get().get() ) + { + raDest.setPosition( destPosition ); + label = raDest.get(); + if ( label.getLabeling().isEmpty() ) + { + label.setLabeling( currentLabel ); + toDoList.push( destPosition ); + } + } + } + } + } + } + } + } + + /** + * Return an array of offsets to the 8-connected (or N-d equivalent) + * structuring element for the dimension space. The structuring element is + * the list of offsets from the center to the pixels to be examined. + * + * @param dimensions + * @return the structuring element. + */ + static public long[][] getStructuringElement( final int dimensions ) + { + int nElements = 1; + for ( int i = 0; i < dimensions; i++ ) + nElements *= 3; + nElements--; + final long[][] result = new long[ nElements ][ dimensions ]; + final long[] position = new long[ dimensions ]; + Arrays.fill( position, -1 ); + for ( int i = 0; i < nElements; i++ ) + { + System.arraycopy( position, 0, result[ i ], 0, dimensions ); + /* + * Special case - skip the center element. + */ + if ( i == nElements / 2 - 1 ) + { + position[ 0 ] += 2; + } + else + { + for ( int j = 0; j < dimensions; j++ ) + { + if ( position[ j ] == 1 ) + { + position[ j ] = -1; + } + else + { + position[ j ]++; + break; + } + } + } + } + return result; + } + + /** + * Return an iterator that (endlessly) dispenses increasing integer values + * for labeling components. + * + * @param start + * @return an iterator dispensing Integers + */ + static public Iterator< Integer > getIntegerNames( final int start ) + { + return new Iterator< Integer >() + { + int current = start; + + @Override + public boolean hasNext() + { + return true; + } + + @Override + public Integer next() + { + return current++; + } + + @Override + public void remove() + { + + } + }; + } +} diff --git a/src/main/java/varun_algorithm_labeling/ConnectedComponents.java b/src/main/java/varun_algorithm_labeling/ConnectedComponents.java new file mode 100644 index 0000000..6063427 --- /dev/null +++ b/src/main/java/varun_algorithm_labeling/ConnectedComponents.java @@ -0,0 +1,715 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_labeling; + +import gnu.trove.list.array.TIntArrayList; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.iterator.IntervalIterator; +import net.imglib2.roi.labeling.ImgLabeling; +import net.imglib2.roi.labeling.LabelingMapping; +import net.imglib2.type.numeric.IntegerType; +import net.imglib2.view.Views; + +/** + * Label all connected components of a binary image. + * + * @author Tobias Pietzsch + */ +public final class ConnectedComponents +{ + public static enum StructuringElement + { + FOUR_CONNECTED( Collect4NeighborLabels.factory ), + EIGHT_CONNECTED( Collect8NeighborLabels.factory ); + + private final CollectNeighborLabelsFactory factory; + + private StructuringElement( final CollectNeighborLabelsFactory factory ) + { + this.factory = factory; + } + + public CollectNeighborLabelsFactory getFactory() + { + return factory; + } + } + + /** + * Label all connected components in the given input image. In the output + * image, all background pixels will be labeled to {} and foreground + * components labeled as {1}, {2}, {3}, etc. where 1, 2, 3 are labels + * returned by {@code labelGenerator.next()}. {@code labelGenerator.next()} + * is called exactly n times if the input contains + * n connected components. + * + * @param input + * input image with pixels != 0 belonging to foreground. + * @param labeling + * output labeling in which the connected components will be + * labeled. + * @param labelGenerator + * produces labels for the connected components. + * @param se + * structuring element to use. 8-connected or 4-connected + * (respectively n-dimensional analog) + */ + public static < T extends IntegerType< T >, L, I extends IntegerType< I > > void labelAllConnectedComponents( + final RandomAccessible< T > input, + final ImgLabeling< L, I > labeling, + final Iterator< L > labelGenerator, + final StructuringElement se ) + { + final int numThreads = Runtime.getRuntime().availableProcessors(); + final ExecutorService service = Executors.newFixedThreadPool( numThreads ); + labelAllConnectedComponents( input, labeling, labelGenerator, se, service ); + service.shutdown(); + } + + /** + * Label all connected components in the given input image. In the output + * image, all background pixels will be labeled to {} and foreground + * components labeled as {1}, {2}, {3}, etc. where 1, 2, 3 are labels + * returned by {@code labelGenerator.next()}. {@code labelGenerator.next()} + * is called exactly n times if the input contains + * n connected components. + * + * @param input + * input image with pixels != 0 belonging to foreground. + * @param labeling + * output labeling in which the connected components will be + * labeled. + * @param labelGenerator + * produces labels for the connected components. + * @param se + * structuring element to use. 8-connected or 4-connected + * (respectively n-dimensional analog) + * @param service + * service providing threads for multi-threading + */ + public static < T extends IntegerType< T >, L, I extends IntegerType< I > > void labelAllConnectedComponents( + final RandomAccessible< T > input, + final ImgLabeling< L, I > labeling, + final Iterator< L > labelGenerator, + final StructuringElement se, + final ExecutorService service ) + { + final RandomAccessibleInterval< I > output = labeling.getIndexImg(); + for ( final I i : Views.iterable( output ) ) + i.setZero(); + + final int numLabels = labelAllConnectedComponents( input, output, se ) + 1; + + final ArrayList< Set< L > > labelSets = new ArrayList< Set< L > >(); + labelSets.add( new HashSet< L >() ); + for ( int i = 1; i < numLabels; ++i ) + { + final HashSet< L > set = new HashSet< L >(); + set.add( labelGenerator.next() ); + labelSets.add( set ); + } + new LabelingMapping.SerialisationAccess< L >( labeling.getMapping() ) + { + { + super.setLabelSets( labelSets ); + } + }; + } + + /** + * "Label" all connected components in the given input image. In the output + * image, all background pixels will be set to 0 and foreground components + * set to 1, 2, 3, etc. + * + *

      + * Note, that the {@code output} image must be cleared to 0! + *

      + * + * @param input + * input image with pixels > 0 belonging to foreground. + * @param output + * output image, must be filled with 0. + * @param se + * structuring element to use. 8-connected or 4-connected + * (respectively n-dimensional analog) + * @return the number of connected components (that is, the highest value + * occurring in the output image. + */ + public static < T extends IntegerType< T >, L extends IntegerType< L > > int labelAllConnectedComponents( + final RandomAccessible< T > input, + final RandomAccessibleInterval< L > output, + final StructuringElement se ) + { + final int numThreads = Runtime.getRuntime().availableProcessors(); + final ExecutorService service = Executors.newFixedThreadPool( numThreads ); + final int result = labelAllConnectedComponents( input, output, se, service ); + service.shutdown(); + return result; + } + + /** + * "Label" all connected components in the given input image. In the output + * image, all background pixels will be set to 0 and foreground components + * set to 1, 2, 3, etc. + * + *

      + * Note, that the {@code output} image must be cleared to 0! + * + * @param input + * input image with pixels > 0 belonging to foreground. + * @param output + * output image, must be filled with 0. + * @param se + * structuring element to use. 8-connected or 4-connected + * (respectively n-dimensional analog) + * @param service + * service providing threads for multi-threading + * @return the number of connected components (that is, the highest value + * occurring in the output image. + */ + public static < T extends IntegerType< T >, L extends IntegerType< L > > int labelAllConnectedComponents( + final RandomAccessible< T > input, + final RandomAccessibleInterval< L > output, + final StructuringElement se, + final ExecutorService service ) + { + final int n = output.numDimensions(); + final int splitDim = n - 1; + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + output.min( min ); + output.max( max ); + final long splitDimMax = max[ splitDim ]; + + final int numThreads = Runtime.getRuntime().availableProcessors(); + int numTasks = numThreads > 1 ? numThreads * 2 : 1; + numTasks = ( int ) Math.max( 1, Math.min( numTasks, output.dimension( splitDim ) / 4 ) ); + final long taskSize = output.dimension( splitDim ) / numTasks; + + @SuppressWarnings( "unchecked" ) + final Fragment< T, L >[] fragments = new Fragment[ numTasks ]; + final CollectNeighborLabels< L > collectNeighborLabels = se.getFactory().< L >newInstance( n ); + for ( int i = 0; i < numTasks; ++i ) + { + max[ splitDim ] = ( i == numTasks - 1 ) ? splitDimMax : min[ splitDim ] + taskSize - 1; + fragments[ i ] = new Fragment< T, L >( input, Views.interval( output, min, max ), collectNeighborLabels ); + min[ splitDim ] += taskSize; + } + + final ArrayList< Future< ? > > futures = new ArrayList< Future< ? > >(); + for ( final Fragment< T, L > fragment : fragments ) + { + futures.add( service.submit( new Runnable() + { + @Override + public void run() + { + fragment.mark(); + } + } ) ); + } + getAllFutures( futures ); + + final TIntArrayList merged = mergeCanonicalLists( fragments ); + for ( int i = 1; i < numTasks; ++i ) + fragments[ i ].linkToPreviousFragment( fragments[ i - 1 ], merged ); + final int numComponents = splitCanonicalLists( fragments, merged ); + + for ( final Fragment< T, L > fragment : fragments ) + { + futures.add( service.submit( new Runnable() + { + @Override + public void run() + { + fragment.relabel(); + } + } ) ); + } + getAllFutures( futures ); + + return numComponents; + } + + private static final class Fragment< T extends IntegerType< T >, L extends IntegerType< L > > + { + private final int n; + + private final TIntArrayList canonicalLabels; + + private final RandomAccessible< T > input; + + private final RandomAccessibleInterval< L > output; + + private final CollectNeighborLabels< L > collectNeighborLabels; + + private int offset; + + public Fragment( + final RandomAccessible< T > input, + final RandomAccessibleInterval< L > output, + final CollectNeighborLabels< L > collectNeighborLabels ) + { + n = output.numDimensions(); + this.input = input; + this.output = output; + this.collectNeighborLabels = collectNeighborLabels; + canonicalLabels = new TIntArrayList( 1000 ); + canonicalLabels.add( 0 ); + } + + public void mark() + { + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + output.min( min ); + output.max( max ); + + // a list to collect labels of non-zero neighbors of a pixel + final TIntArrayList neighborLabels = new TIntArrayList( n ); + + final TIntArrayList updateLabels = new TIntArrayList( 10 ); + + final Cursor< T > in = Views.flatIterable( Views.interval( input, output ) ).localizingCursor(); + final RandomAccess< L > la = output.randomAccess(); + + while ( in.hasNext() ) + { + if ( in.next().getInteger() > 0 ) + { + la.setPosition( in ); + collectNeighborLabels.collect( la, neighborLabels, min, max ); + final int numLabeledNeighbors = neighborLabels.size(); + if ( numLabeledNeighbors == 0 ) + { + // create new Label + final int label = canonicalLabels.size(); + canonicalLabels.add( label ); + la.get().setInteger( label ); + } + else if ( numLabeledNeighbors == 1 ) + { + la.get().setInteger( canonicalLabels.get( neighborLabels.get( 0 ) ) ); + } + else + { + // assign canonical label + int canonical = canonicalLabels.get( neighborLabels.get( 0 ) ); + boolean makeCanonical = false; + for ( int i = 1; i < neighborLabels.size(); ++i ) + { + if ( canonicalLabels.get( neighborLabels.get( i ) ) != canonical ) + { + makeCanonical = true; + break; + } + } + if ( makeCanonical ) + { + updateLabels.clear(); + canonical = Integer.MAX_VALUE; + for ( int i = 0; i < neighborLabels.size(); ++i ) + { + int label = neighborLabels.get( i ); + while ( canonicalLabels.get( label ) != label ) + { + updateLabels.add( label ); + canonical = Math.min( canonical, label ); + label = canonicalLabels.get( label ); + } + updateLabels.add( label ); + canonical = Math.min( canonical, label ); + } + for ( int i = 0; i < updateLabels.size(); ++i ) + canonicalLabels.set( updateLabels.get( i ), canonical ); + } + la.get().setInteger( canonical ); + } + } + } + } + + public void linkToPreviousFragment( final Fragment< T, L > previous, final TIntArrayList merged ) + { + final int previousOffset = previous.offset; + final int splitDim = n - 1; + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + output.min( min ); + output.max( max ); + max[ splitDim ] = min[ splitDim ]; + + // a list to collect labels of labeled neighbors of a pixel + final TIntArrayList neighborLabels = new TIntArrayList( n ); + + final TIntArrayList updateLabels = new TIntArrayList( 10 ); + + final Cursor< L > in = Views.iterable( Views.interval( output, min, max ) ).localizingCursor(); + min[ splitDim ] -= 1; + final RandomAccess< L > la = output.randomAccess( new FinalInterval( min, max ) ); + + while ( in.hasNext() ) + { + int label = in.next().getInteger(); + if ( label != 0 ) + { + label += offset; + la.setPosition( in ); + collectNeighborLabels.collectAtPreviousFragmentBorder( la, neighborLabels, min, max ); + final int numLabeledNeighbors = neighborLabels.size(); + if ( numLabeledNeighbors != 0 ) + { + for ( int i = 0; i < numLabeledNeighbors; ++i ) + neighborLabels.set( i, neighborLabels.get( i ) + previousOffset ); + int canonical = merged.get( label ); + boolean makeCanonical = false; + for ( int i = 0; i < neighborLabels.size(); ++i ) + { + if ( merged.get( neighborLabels.get( i ) ) != canonical ) + { + neighborLabels.add( label ); + makeCanonical = true; + break; + } + } + if ( makeCanonical ) + { + updateLabels.clear(); + canonical = Integer.MAX_VALUE; + for ( int i = 0; i < neighborLabels.size(); ++i ) + { + label = neighborLabels.get( i ); + while ( merged.get( label ) != label ) + { + updateLabels.add( label ); + canonical = Math.min( canonical, label ); + label = merged.get( label ); + } + updateLabels.add( label ); + canonical = Math.min( canonical, label ); + } + for ( int i = 0; i < updateLabels.size(); ++i ) + merged.set( updateLabels.get( i ), canonical ); + } + } + } + } + } + + public void relabel() + { + for ( final L label : Views.iterable( output ) ) + label.setInteger( canonicalLabels.get( label.getInteger() ) ); + } + } + + private static < T extends IntegerType< T >, L extends IntegerType< L > > TIntArrayList mergeCanonicalLists( final Fragment< T, L >[] fragments ) + { + int size = 0; + for ( final Fragment< T, L > fragment : fragments ) + { + fragment.offset = size; + size += fragment.canonicalLabels.size() - 1; // -1 is for background + } + final TIntArrayList merged = new TIntArrayList( size + 1 ); + merged.add( 0 ); // background + for ( final Fragment< T, L > fragment : fragments ) + { + final TIntArrayList fl = fragment.canonicalLabels; + final int o = fragment.offset; + for ( int i = 1; i < fl.size(); ++i ) + merged.add( fl.get( i ) + o ); + } + return merged; + } + + private static < T extends IntegerType< T >, L extends IntegerType< L > > int splitCanonicalLists( final Fragment< T, L >[] fragments, final TIntArrayList merged ) + { + int nextLabel = 1; + for ( int i = 1; i < merged.size(); ++i ) + if ( merged.get( i ) == i ) + merged.set( i, nextLabel++ ); + else + merged.set( i, merged.get( merged.get( i ) ) ); + + for ( final Fragment< T, L > fragment : fragments ) + { + final TIntArrayList fl = fragment.canonicalLabels; + final int o = fragment.offset; + for ( int i = 1; i < fl.size(); ++i ) + fl.set( i, merged.get( i + o ) ); + } + + return nextLabel - 1; + } + + private static void getAllFutures( final List< Future< ? > > futures ) + { + for ( final Future< ? > future : futures ) + { + try + { + future.get(); + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + catch ( final ExecutionException e ) + { + e.printStackTrace(); + } + } + futures.clear(); + } + + private static interface CollectNeighborLabels< L extends IntegerType< L > > + { + public void collect( RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ); + + public void collectAtPreviousFragmentBorder( RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ); + } + + private static interface CollectNeighborLabelsFactory + { + public < L extends IntegerType< L > > CollectNeighborLabels< L > newInstance( final int n ); + } + + private static final class Collect4NeighborLabels< L extends IntegerType< L > > implements CollectNeighborLabels< L > + { + private final int n; + + private Collect4NeighborLabels( final int n ) + { + this.n = n; + } + + @Override + public void collect( final RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ) + { + neighborLabels.clear(); + for ( int d = 0; d < n; ++d ) + { + if ( la.getLongPosition( d ) > labelsMin[ d ] ) + { + la.bck( d ); + final int l = la.get().getInteger(); + if ( l != 0 ) + neighborLabels.add( l ); + la.fwd( d ); + } + } + } + + private static final CollectNeighborLabelsFactory factory = new CollectNeighborLabelsFactory() + { + @Override + public < L extends IntegerType< L > > CollectNeighborLabels< L > newInstance( final int n ) + { + return new Collect4NeighborLabels< L >( n ); + } + }; + + @Override + public void collectAtPreviousFragmentBorder( final RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ) + { + neighborLabels.clear(); + la.bck( n - 1 ); + final int l = la.get().getInteger(); + if ( l != 0 ) + neighborLabels.add( l ); + la.fwd( n - 1 ); + } + } + + private static final class Collect8NeighborLabels< L extends IntegerType< L > > implements CollectNeighborLabels< L > + { + private final int n; + + private final long[][] offsets; + + private final long[] pos; + + private final long[] previousFragmentPos; + + private final int numPreviousFragmentOffsets; + + private Collect8NeighborLabels( final int n ) + { + this.n = n; + int nOffsets = 0; + for ( int d = 0; d < n; ++d ) + nOffsets = 3 * nOffsets + 1; + numPreviousFragmentOffsets = ( int ) Math.pow( 3, n - 1 ); + offsets = new long[ nOffsets ][]; + pos = new long[ n ]; + previousFragmentPos = new long[ n ]; + final long[] min = new long[ n ]; + Arrays.fill( min, -1 ); + final long[] max = new long[ n ]; + Arrays.fill( max, 1 ); + final IntervalIterator idx = new IntervalIterator( new FinalInterval( min, max ) ); + for ( int i = 0; i < offsets.length; ++i ) + { + offsets[ i ] = new long[ n ]; +A: while ( true ) + { + idx.fwd(); + idx.localize( offsets[ i ] ); + for ( int d = n - 1; d >= 0; --d ) + if ( offsets[ i ][ d ] < 0 ) + break A; + } + for ( int d = 0; d < n; ++d ) + { + offsets[ i ][ d ] -= pos[ d ]; + pos[ d ] += offsets[ i ][ d ]; + } + if ( i == numPreviousFragmentOffsets - 1 ) + for ( int d = 0; d < n; ++d ) + previousFragmentPos[ d ] = -pos[ d ]; + } + for ( int d = 0; d < n; ++d ) + pos[ d ] = -pos[ d ]; + } + + @Override + public void collect( final RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ) + { + for ( int d = 0; d < n; ++d ) + if ( la.getLongPosition( d ) <= labelsMin[ d ] || la.getLongPosition( d ) >= labelsMax[ d ] ) + { + collectChecked( la, neighborLabels, labelsMin, labelsMax ); + return; + } + collectUnchecked( la, neighborLabels ); + } + + private void collectChecked( final RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ) + { + neighborLabels.clear(); +A: for ( int i = 0; i < offsets.length; ++i ) + { + la.move( offsets[ i ] ); + for ( int d = 0; d < n; ++d ) + if ( la.getLongPosition( d ) < labelsMin[ d ] || la.getLongPosition( d ) > labelsMax[ d ] ) + continue A; + final int l = la.get().getInteger(); + if ( l != 0 ) + neighborLabels.add( l ); + } + la.move( pos ); + } + + private void collectUnchecked( final RandomAccess< L > la, final TIntArrayList neighborLabels ) + { + neighborLabels.clear(); + for ( int i = 0; i < offsets.length; ++i ) + { + la.move( offsets[ i ] ); + final int l = la.get().getInteger(); + if ( l != 0 ) + neighborLabels.add( l ); + } + la.move( pos ); + } + + @Override + public void collectAtPreviousFragmentBorder( final RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ) + { + for ( int d = 0; d < n - 1; ++d ) + if ( la.getLongPosition( d ) <= labelsMin[ d ] || la.getLongPosition( d ) >= labelsMax[ d ] ) + { + collectAtPreviousFragmentBorderChecked( la, neighborLabels, labelsMin, labelsMax ); + return; + } + collectAtPreviousFragmentBorderUnchecked( la, neighborLabels ); + } + + private void collectAtPreviousFragmentBorderChecked( final RandomAccess< L > la, final TIntArrayList neighborLabels, final long[] labelsMin, final long[] labelsMax ) + { + neighborLabels.clear(); +A: for ( int i = 0; i < numPreviousFragmentOffsets; ++i ) + { + la.move( offsets[ i ] ); + for ( int d = 0; d < n - 1; ++d ) + if ( la.getLongPosition( d ) < labelsMin[ d ] || la.getLongPosition( d ) > labelsMax[ d ] ) + continue A; + final int l = la.get().getInteger(); + if ( l != 0 ) + neighborLabels.add( l ); + } + la.move( previousFragmentPos ); + } + + private void collectAtPreviousFragmentBorderUnchecked( final RandomAccess< L > la, final TIntArrayList neighborLabels ) + { + neighborLabels.clear(); + for ( int i = 0; i < numPreviousFragmentOffsets; ++i ) + { + la.move( offsets[ i ] ); + final int l = la.get().getInteger(); + if ( l != 0 ) + neighborLabels.add( l ); + } + la.move( previousFragmentPos ); + } + + private static final CollectNeighborLabelsFactory factory = new CollectNeighborLabelsFactory() + { + @Override + public < L extends IntegerType< L > > CollectNeighborLabels< L > newInstance( final int n ) + { + return new Collect8NeighborLabels< L >( n ); + } + }; + } +} diff --git a/src/main/java/varun_algorithm_labeling/Watershed.java b/src/main/java/varun_algorithm_labeling/Watershed.java new file mode 100644 index 0000000..b128999 --- /dev/null +++ b/src/main/java/varun_algorithm_labeling/Watershed.java @@ -0,0 +1,354 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_labeling; + +import java.util.List; +import java.util.PriorityQueue; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.labeling.Labeling; +import net.imglib2.labeling.LabelingOutOfBoundsRandomAccessFactory; +import net.imglib2.labeling.LabelingType; +import net.imglib2.labeling.NativeImgLabeling; +import net.imglib2.outofbounds.OutOfBounds; +import net.imglib2.outofbounds.OutOfBoundsConstantValueFactory; +import net.imglib2.outofbounds.OutOfBoundsFactory; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.view.Views; +import varun_algorithm.OutputAlgorithm; + +/** + * Watershed algorithms. The watershed algorithm segments and labels an image + * using an analogy to a landscape. The image intensities are turned into the + * z-height of the landscape and the landscape is "filled with water" and the + * bodies of water label the landscape's pixels. Here is the reference for the + * original paper: + * + * Lee Vincent, Pierre Soille, Watersheds in digital spaces: An efficient + * algorithm based on immersion simulations, IEEE Trans. Pattern Anal. Machine + * Intell., 13(6) 583-598 (1991) + * + * Watersheds are often performed on the gradient of an intensity image or one + * where the edges of the object boundaries have been enhanced. The resulting + * image has a depressed object interior and a ridge which constrains the + * watershed boundary. + * + * @author Lee Kamentsky + */ +public class Watershed< T extends RealType< T >, L extends Comparable< L >> implements OutputAlgorithm< Labeling< L >> +{ + + protected static class PixelIntensity< U extends Comparable< U >> implements Comparable< PixelIntensity< U >> + { + protected final long index; + + protected final long age; + + protected final double intensity; + + protected final List< U > labeling; + + public PixelIntensity( final long[] position, final long[] dimensions, final double intensity, final long age, final List< U > labeling ) + { + long index = position[ 0 ]; + long multiplier = dimensions[ 0 ]; + for ( int i = 1; i < dimensions.length; i++ ) + { + index += position[ i ] * multiplier; + multiplier *= dimensions[ i ]; + } + + this.index = index; + this.intensity = intensity; + this.labeling = labeling; + this.age = age; + } + + @Override + public int compareTo( final PixelIntensity< U > other ) + { + int result = Double.compare( intensity, other.intensity ); + if ( result == 0 ) + result = Double.compare( age, other.age ); + return result; + } + + void getPosition( final long[] position, final long[] dimensions ) + { + long idx = index; + for ( int i = 0; i < dimensions.length; i++ ) + { + position[ i ] = ( int ) ( idx % dimensions[ i ] ); + idx /= dimensions[ i ]; + } + } + + List< U > getLabeling() + { + return labeling; + } + } + + protected RandomAccessibleInterval< T > image; + + protected Labeling< L > seeds; + + long[][] structuringElement; + + protected Labeling< L > output; + + String errorMessage; + + /** + * Provide the intensity image to be watershedded. + * + * @param image + * the intensity image that defines the watershed landscape. + * Lower values will be labeled first. + */ + public void setIntensityImage( final RandomAccessibleInterval< T > image ) + { + this.image = image; + } + + /** + * Provide the seeds that mark the watersheds. + * + * @param seeds + * a labeling of the space, defining the first pixels in the + * space to be labeled. The seeded pixels will be similarly + * labeled in the output as will be their watershed neighbors. + */ + public void setSeeds( final Labeling< L > seeds ) + { + this.seeds = seeds; + } + + /** + * Set the structuring element that defines the connectivity + * + * @param structuringElement + * an array of offsets where each element of the array gives the + * offset of a connected pixel from a pixel of interest. You can + * use AllConnectedComponents.getStructuringElement to get an + * 8-connected (or N-dimensional equivalent) structuring element + * (all adjacent pixels + diagonals). + */ + public void setStructuringElement( final long[][] structuringElement ) + { + this.structuringElement = structuringElement; + } + + /** + * Set the output labeling where the results will be stored. The class will + * provide one if none is supplied. + * + * @param outputLabeling + */ + public void setOutputLabeling( final Labeling< L > outputLabeling ) + { + output = outputLabeling; + } + + /** + * The seeded watershed uses a pre-existing labeling of the space where the + * labels act as seeds for the output watershed. The analogy would be to use + * dyed liquids emanating from the seeded pixels, flowing to the local + * minima and then filling individual watersheds until the liquids meet at + * the boundaries. + * + * This implementation breaks ties by assigning the pixel to the label that + * occupied an adjacent pixel first. + */ + @Override + public boolean process() + { + if ( !checkInput() ) + return false; + + if ( output == null ) + { + final long[] dimensions = new long[ seeds.numDimensions() ]; + seeds.dimensions( dimensions ); + final NativeImgLabeling< L, IntType > o = new NativeImgLabeling< L, IntType >( new ArrayImgFactory< IntType >().create( dimensions, new IntType() ) ); + output = o; + } + /* + * Make an OutOfBounds for the labels that returns empty labels if out + * of bounds. Make an OutOfBounds for the intensities that returns the + * maximum intensity if out of bounds so that in-bounds will be in a + * deep valley. + */ + final OutOfBoundsFactory< LabelingType< L >, Labeling< L >> factory = new LabelingOutOfBoundsRandomAccessFactory< L, Labeling< L >>(); + final OutOfBounds< LabelingType< L >> outputAccess = factory.create( output ); + + final T maxVal = Views.iterable( image ).firstElement().createVariable(); + maxVal.setReal( maxVal.getMaxValue() ); + final OutOfBoundsFactory< T, RandomAccessibleInterval< T >> oobImageFactory = new OutOfBoundsConstantValueFactory< T, RandomAccessibleInterval< T >>( maxVal ); + final OutOfBounds< T > imageAccess = oobImageFactory.create( image ); + + /* + * Start by loading up a priority queue with the seeded pixels + */ + final PriorityQueue< PixelIntensity< L >> pq = new PriorityQueue< PixelIntensity< L >>(); + final Cursor< LabelingType< L >> c = seeds.localizingCursor(); + + final long[] dimensions = new long[ image.numDimensions() ]; + output.dimensions( dimensions ); + final long[] position = new long[ image.numDimensions() ]; + final long[] destPosition = new long[ image.numDimensions() ]; + long age = 0; + + while ( c.hasNext() ) + { + final LabelingType< L > tSrc = c.next(); + List< L > l = tSrc.getLabeling(); + if ( l.isEmpty() ) + continue; + + c.localize( position ); + imageAccess.setPosition( position ); + if ( imageAccess.isOutOfBounds() ) + continue; + outputAccess.setPosition( position ); + if ( outputAccess.isOutOfBounds() ) + continue; + final LabelingType< L > tDest = outputAccess.get(); + l = tDest.intern( l ); + tDest.setLabeling( l ); + final double intensity = imageAccess.get().getRealDouble(); + pq.add( new PixelIntensity< L >( position, dimensions, intensity, age++, l ) ); + } + /* + * Rework the structuring element into a series of consecutive offsets + * so we can use Positionable.move to scan the image array. + */ + final long[][] strelMoves = new long[ structuringElement.length ][]; + final long[] currentOffset = new long[ image.numDimensions() ]; + for ( int i = 0; i < structuringElement.length; i++ ) + { + strelMoves[ i ] = new long[ image.numDimensions() ]; + for ( int j = 0; j < image.numDimensions(); j++ ) + { + strelMoves[ i ][ j ] = structuringElement[ i ][ j ] - currentOffset[ j ]; + if ( i > 0 ) + currentOffset[ j ] += structuringElement[ i ][ j ] - structuringElement[ i - 1 ][ j ]; + else + currentOffset[ j ] += structuringElement[ i ][ j ]; + } + } + /* + * Pop the head of the priority queue, label and push all unlabeled + * connected pixels. + */ + while ( !pq.isEmpty() ) + { + final PixelIntensity< L > currentPI = pq.remove(); + final List< L > l = currentPI.getLabeling(); + currentPI.getPosition( position, dimensions ); + outputAccess.setPosition( position ); + imageAccess.setPosition( position ); + for ( final long[] offset : strelMoves ) + { + outputAccess.move( offset ); + imageAccess.move( offset ); + if ( outputAccess.isOutOfBounds() ) + continue; + if ( imageAccess.isOutOfBounds() ) + continue; + final LabelingType< L > outputLabelingType = outputAccess.get(); + if ( !outputLabelingType.getLabeling().isEmpty() ) + continue; + outputLabelingType.setLabeling( l ); + final double intensity = imageAccess.get().getRealDouble(); + outputAccess.localize( destPosition ); + pq.add( new PixelIntensity< L >( destPosition, dimensions, intensity, age++, l ) ); + } + } + return true; + } + + @Override + public boolean checkInput() + { + if ( seeds == null ) + { + errorMessage = "The seed labeling was not provided. Call \"setSeeds\" to do this"; + return false; + } + if ( image == null ) + { + errorMessage = "The intensity image was not provided. Call \"setIntensityImage\" to do this"; + return false; + } + if ( seeds.numDimensions() != image.numDimensions() ) + { + errorMessage = String.format( "The dimensionality of the seed labeling (%dD) does not match that of the intensity image (%dD)", seeds.numDimensions(), image.numDimensions() ); + return false; + } + if ( ( output != null ) && ( seeds.numDimensions() != output.numDimensions() ) ) + { + errorMessage = String.format( "The dimensionality of the seed labeling (%dD) does not match that of the output labeling (%dD)", seeds.numDimensions(), output.numDimensions() ); + return false; + } + if ( structuringElement == null ) + structuringElement = AllConnectedComponents.getStructuringElement( image.numDimensions() ); + for ( int i = 0; i < structuringElement.length; i++ ) + { + if ( structuringElement[ i ].length != seeds.numDimensions() ) + { + errorMessage = "Some or all of the structuring element offsets do not have the same number of dimensions as the image"; + return false; + } + } + return true; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } + + @Override + public Labeling< L > getResult() + { + return output; + } +} diff --git a/src/main/java/varun_algorithm_linalg_eigen/EigenValues.java b/src/main/java/varun_algorithm_linalg_eigen/EigenValues.java new file mode 100644 index 0000000..5b4f834 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/EigenValues.java @@ -0,0 +1,88 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +/** + * + * Interface for handling different cases, e.g. square, symmetric, or 2 + * dimensional tensors. + * + */ +public interface EigenValues< T extends RealType< T >, U extends ComplexType< U > > +{ + public void compute( final Composite< T > matrix, final Composite< U > evs ); + + public default EigenValues< T, U > copy() + { + return this; + } + + public static < T extends RealType< T >, U extends ComplexType< U > > EigenValues1D< T, U > oneDimensional() + { + return new EigenValues1D<>(); + } + + public static < T extends RealType< T >, U extends ComplexType< U > > EigenValues2DSquare< T, U > square2D() + { + return new EigenValues2DSquare<>(); + } + + public static < T extends RealType< T >, U extends ComplexType< U > > EigenValues2DSymmetric< T, U > symmetric2D() + { + return new EigenValues2DSymmetric<>(); + } + + public static < T extends RealType< T >, U extends ComplexType< U > > EigenValuesSquare< T, U > square( final int nDim ) + { + return new EigenValuesSquare<>( nDim ); + } + + public static < T extends RealType< T >, U extends ComplexType< U > > EigenValuesSymmetric< T, U > symmetric( final int nDim ) + { + return new EigenValuesSymmetric<>( nDim ); + } + + public static < T extends RealType< T >, U extends ComplexType< U > > EigenValues< T, U > invalid() + { + return ( m, evs ) -> { + throw new UnsupportedOperationException( "EigenValues not implemented yet!" ); + }; + } + +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_linalg_eigen/EigenValues1D.java b/src/main/java/varun_algorithm_linalg_eigen/EigenValues1D.java new file mode 100644 index 0000000..5613324 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/EigenValues1D.java @@ -0,0 +1,48 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +public class EigenValues1D< T extends RealType< T >, U extends ComplexType< U > > implements EigenValues< T, U > +{ + @Override + public void compute( final Composite< T > tensor, final Composite< U > evs ) + { + evs.get( 0 ).setReal( tensor.get( 0 ).getRealDouble() ); + } +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSquare.java b/src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSquare.java new file mode 100644 index 0000000..eb41356 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSquare.java @@ -0,0 +1,67 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +public class EigenValues2DSquare< T extends RealType< T >, U extends ComplexType< U > > implements EigenValues< T, U > +{ + @Override + public void compute( final Composite< T > tensor, final Composite< U > evs ) + { + final double x11 = tensor.get( 0 ).getRealDouble(); + final double x12 = tensor.get( 1 ).getRealDouble(); + final double x21 = tensor.get( 2 ).getRealDouble(); + final double x22 = tensor.get( 3 ).getRealDouble(); + final double sum = x11 + x22; + final double diff = x11 - x22; + final double radicand = 4 * x12 * x21 + diff * diff; + if ( radicand < 0.0d ) + { + final double halfSqrt = 0.5 * Math.sqrt( -radicand ); + final double halfSum = 0.5 * sum; + evs.get( 0 ).setComplexNumber( halfSum, halfSqrt ); + evs.get( 1 ).setComplexNumber( halfSum, -halfSqrt ); + } + else + { + final double sqrt = Math.sqrt( radicand ); + evs.get( 0 ).setComplexNumber( 0.5 * ( sum + sqrt ), 0.0d ); + evs.get( 1 ).setComplexNumber( 0.5 * ( sum - sqrt ), 0.0d ); + } + } +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSymmetric.java b/src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSymmetric.java new file mode 100644 index 0000000..0f23c3b --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/EigenValues2DSymmetric.java @@ -0,0 +1,55 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +public class EigenValues2DSymmetric< T extends RealType< T >, U extends ComplexType< U > > implements EigenValues< T, U > +{ + @Override + public void compute( final Composite< T > tensor, final Composite< U > evs ) + { + final double x11 = tensor.get( 0 ).getRealDouble(); + final double x12 = tensor.get( 1 ).getRealDouble(); + final double x22 = tensor.get( 2 ).getRealDouble(); + final double sum = x11 + x22; + final double diff = x11 - x22; + final double sqrt = Math.sqrt( 4 * x12 * x12 + diff * diff ); + evs.get( 0 ).setReal( 0.5 * ( sum + sqrt ) ); + evs.get( 1 ).setReal( 0.5 * ( sum - sqrt ) ); + } +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_linalg_eigen/EigenValuesSquare.java b/src/main/java/varun_algorithm_linalg_eigen/EigenValuesSquare.java new file mode 100644 index 0000000..c8f0817 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/EigenValuesSquare.java @@ -0,0 +1,83 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import java.util.Optional; + +import org.ojalgo.matrix.decomposition.Eigenvalue; + + +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; +import varun_algorithm_linalg_matrix.RealCompositeSquareMatrix; + +public class EigenValuesSquare< T extends RealType< T >, U extends ComplexType< U > > implements EigenValues< T, U > +{ + private final int nDim; + + private final double[] evs; + + private final RealCompositeSquareMatrix< T > m; + + private final Eigenvalue< Double > ed; + + private final Optional< double[] > emptyOptional = Optional.empty(); + + public EigenValuesSquare( final int nDim ) + { + super(); + this.nDim = nDim; + this.evs = new double[ nDim ]; + this.m = new RealCompositeSquareMatrix<>( null, nDim ); + this.ed = Eigenvalue.PRIMITIVE.make( this.m, false ); + } + + @Override + public void compute( final Composite< T > tensor, final Composite< U > evs ) + { + m.setData( tensor ); + ed.computeValuesOnly( this.m ); + ed.getEigenvalues( this.evs, emptyOptional ); + for ( int d = 0; d < this.evs.length; ++d ) + evs.get( d ).setReal( this.evs[ d ] ); + } + + @Override + public EigenValuesSquare< T, U > copy() + { + return new EigenValuesSquare<>( nDim ); + } +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_linalg_eigen/EigenValuesSymmetric.java b/src/main/java/varun_algorithm_linalg_eigen/EigenValuesSymmetric.java new file mode 100644 index 0000000..2f7ad9f --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/EigenValuesSymmetric.java @@ -0,0 +1,83 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import java.util.Optional; + +import org.ojalgo.matrix.decomposition.Eigenvalue; + + +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; +import varun_algorithm_linalg_matrix.RealCompositeSymmetricMatrix; + +public class EigenValuesSymmetric< T extends RealType< T >, U extends ComplexType< U > > implements EigenValues< T, U > +{ + private final int nDim; + + private final double[] evs; + + private final RealCompositeSymmetricMatrix< T > m; + + private final Eigenvalue< Double > ed; + + private final Optional< double[] > emptyOptional = Optional.empty(); + + public EigenValuesSymmetric( final int nDim ) + { + super(); + this.nDim = nDim; + this.evs = new double[ nDim ]; + this.m = new RealCompositeSymmetricMatrix<>( null, nDim ); + this.ed = Eigenvalue.PRIMITIVE.make( this.m, true ); + } + + @Override + public void compute( final Composite< T > tensor, final Composite< U > evs ) + { + m.setData( tensor ); + ed.computeValuesOnly( m ); + ed.getEigenvalues( this.evs, emptyOptional ); + for ( int d = 0; d < this.evs.length; ++d ) + evs.get( d ).setReal( this.evs[ d ] ); + } + + @Override + public EigenValuesSymmetric< T, U > copy() + { + return new EigenValuesSymmetric<>( nDim ); + } +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_linalg_eigen/TensorEigenValues.java b/src/main/java/varun_algorithm_linalg_eigen/TensorEigenValues.java new file mode 100644 index 0000000..cd6486f --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_eigen/TensorEigenValues.java @@ -0,0 +1,393 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_eigen; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.numeric.ComplexType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import net.imglib2.view.composite.NumericComposite; +import net.imglib2.view.composite.RealComposite; + +/** + * + * @author Philipp Hanslovsky + * + * Compute eigenvalues of rank 2 tensors. + * + */ +public class TensorEigenValues +{ + + // static methods + + // symmetric + + /** + * + * @param tensor + * Input that holds linear representation of upper triangular + * tensor in last dimension, i.e. if tensor (t) has n+1 + * dimensions, the last dimension must be of size n * ( n + 1 ) / + * 2, and the entries in the last dimension are arranged like + * this: [t11, t12, ... , t1n, t22, t23, ... , tnn] + * + * @param eigenvalues + * Target {@link RandomAccessibleInterval} for storing the + * resulting tensor eigenvalues. Number of dimensions must be the + * same as for input. For an n+1 dimensional input, the size of + * the last dimension must be n. + * + */ + public static < T extends RealType< T >, U extends RealType< U > > RandomAccessibleInterval< U > calculateEigenValuesSymmetric( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues ) + { + + final int nDim = tensor.numDimensions() - 1; + assert eigenvalues.dimension( nDim ) * ( eigenvalues.dimension( nDim ) + 1 ) / 2 == tensor.dimension( nDim ); + + final EigenValues< T, U > ev; + if ( nDim == 1 ) + ev = EigenValues.oneDimensional(); + else if ( nDim == 2 ) + ev = EigenValues.symmetric2D(); + else if ( nDim > 2 ) + ev = EigenValues.symmetric( nDim ); + else + ev = EigenValues.invalid(); + + return calculateEigenValuesImpl( tensor, eigenvalues, ev ); + } + + /** + * + * @param tensor + * Input that holds linear representation of upper triangular + * tensor in last dimension, i.e. if tensor (t) has n+1 + * dimensions, the last dimension must be of size n * ( n + 1 ) / + * 2, and the entries in the last dimension are arranged like + * this: [t11, t12, ... , t1n, t22, t23, ... , tnn] + * + * @param eigenvalues + * Target {@link RandomAccessibleInterval} for storing the + * resulting tensor eigenvalues. Number of dimensions must be the + * same as for input. For an n+1 dimensional input, the size of + * the last dimension must be n. + * @param nTasks + * Number of tasks used for parallel computation of eigenvalues. + * @param es + * {@link ExecutorService} providing workers for parallel + * computation. Service is managed (created, shutdown) by caller. + * + */ + public static < T extends RealType< T >, U extends RealType< U > > RandomAccessibleInterval< U > calculateEigenValuesSymmetric( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues, + final int nTasks, + final ExecutorService es ) + { + + final int nDim = tensor.numDimensions() - 1; + assert eigenvalues.dimension( nDim ) * ( eigenvalues.dimension( nDim ) + 1 ) / 2 == tensor.dimension( nDim ); + + final EigenValues< T, U > ev; + if ( nDim == 1 ) + ev = EigenValues.oneDimensional(); + else if ( nDim == 2 ) + ev = EigenValues.symmetric2D(); + else if ( nDim > 2 ) + ev = EigenValues.symmetric( nDim ); + else + ev = EigenValues.invalid(); + + return calculateEigenValues( tensor, eigenvalues, ev, nTasks, es ); + } + + // square + + /** + * + * @param tensor + * Input that holds linear representation of tensor in last + * dimension, i.e. if tensor (t) has n+1 dimensions, the last + * dimension must be of size n * n, and the entries in the last + * dimension are arranged like this: [t11, t12, ... , t1n, t21, + * t22, t23, ... , tn1, ... , tnn] + * + * @param eigenvalues + * Target {@link RandomAccessibleInterval} for storing the + * resulting tensor eigenvalues. Number of dimensions must be the + * same as for input. For an n+1 dimensional input, the size of + * + */ + public static < T extends RealType< T >, U extends ComplexType< U > > RandomAccessibleInterval< U > calculateEigenValuesSquare( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues ) + { + + final int nDim = tensor.numDimensions() - 1; + assert eigenvalues.dimension( nDim ) * ( eigenvalues.dimension( nDim ) + 1 ) / 2 == tensor.dimension( nDim ); + + final EigenValues< T, U > ev; + if ( nDim == 1 ) + ev = EigenValues.oneDimensional(); + else if ( nDim == 2 ) + ev = EigenValues.square2D(); + else if ( nDim > 2 ) + ev = EigenValues.square( nDim ); + else + ev = EigenValues.invalid(); + + return calculateEigenValuesImpl( tensor, eigenvalues, ev ); + } + + /** + * + * @param tensor + * Input that holds linear representation of tensor in last + * dimension, i.e. if tensor (t) has n+1 dimensions, the last + * dimension must be of size n * n, and the entries in the last + * dimension are arranged like this: [t11, t12, ... , t1n, t21, + * t22, t23, ... , tn1, ... , tnn] + * + * @param eigenvalues + * Target {@link RandomAccessibleInterval} for storing the + * resulting tensor eigenvalues. Number of dimensions must be the + * same as for input. For an n+1 dimensional input, the size of + * the last dimension must be n. + * @param nTasks + * Number of threads/workers used for parallel computation of + * eigenvalues. + * @param es + * {@link ExecutorService} providing workers for parallel + * computation. Service is managed (created, shutdown) by caller. + * + */ + public static < T extends RealType< T >, U extends ComplexType< U > > RandomAccessibleInterval< U > calculateEigenValuesSquare( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues, + final int nTasks, + final ExecutorService es ) + { + final int nDim = tensor.numDimensions() - 1; + assert eigenvalues.dimension( nDim ) * eigenvalues.dimension( nDim ) == tensor.dimension( nDim ); + + final EigenValues< T, U > ev; + if ( nDim == 1 ) + ev = EigenValues.oneDimensional(); + else if ( nDim == 2 ) + ev = EigenValues.square2D(); + else if ( nDim > 2 ) + ev = EigenValues.square( nDim ); + else + ev = EigenValues.invalid(); + + return calculateEigenValues( tensor, eigenvalues, ev, nTasks, es ); + } + + // general + + /** + * + * @param tensor + * Input that holds linear representation of tensor in last + * dimension. Parameter ev specifies representation. + * + * @param eigenvalues + * Target {@link RandomAccessibleInterval} for storing the + * resulting tensor eigenvalues. Number of dimensions must be the + * same as for input. For an n+1 dimensional input, the size of + * the last dimension must be n. + * @param ev + * Implementation that specifies how to calculate eigenvalues + * from last dimension of input. + * + */ + public static < T extends RealType< T >, U extends ComplexType< U > > RandomAccessibleInterval< U > calculateEigenValues( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues, + final EigenValues< T, U > ev ) + { + return calculateEigenValuesImpl( tensor, eigenvalues, ev ); + } + + /** + * + * @param tensor + * Input that holds linear representation of tensor in last + * dimension. Parameter ev specifies representation. + * + * @param eigenvalues + * Target {@link RandomAccessibleInterval} for storing the + * resulting tensor eigenvalues. Number of dimensions must be the + * same as for input. For an n+1 dimensional input, the size of + * the last dimension must be n. + * @param ev + * Implementation that specifies how to calculate eigenvalues + * from last dimension of input. + * @param nTasks + * Number of threads/workers used for parallel computation of + * eigenvalues. + * @param es + * {@link ExecutorService} providing workers for parallel + * computation. Service is managed (created, shutdown) by caller. + * + */ + public static < T extends RealType< T >, U extends ComplexType< U > > RandomAccessibleInterval< U > calculateEigenValues( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues, + final EigenValues< T, U > ev, + final int nTasks, + final ExecutorService es ) + { + + assert nTasks > 0: "Passed nTasks < 1"; + + final int tensorDims = tensor.numDimensions(); + + long dimensionMax = Long.MIN_VALUE; + int dimensionArgMax = -1; + + for ( int d = 0; d < tensorDims - 1; ++d ) + { + final long size = tensor.dimension( d ); + if ( size > dimensionMax ) + { + dimensionMax = size; + dimensionArgMax = d; + } + } + + final long stepSize = Math.max( dimensionMax / nTasks, 1 ); + final long stepSizeMinusOne = stepSize - 1; + final long max = dimensionMax - 1; + + final ArrayList< Callable< RandomAccessibleInterval< U > > > tasks = new ArrayList<>(); + for ( long currentMin = 0; currentMin < dimensionMax; currentMin += stepSize ) + { + final long currentMax = Math.min( currentMin + stepSizeMinusOne, max ); + final long[] minT = new long[ tensorDims ]; + final long[] maxT = new long[ tensorDims ]; + final long[] minE = new long[ tensorDims ]; + final long[] maxE = new long[ tensorDims ]; + tensor.min( minT ); + tensor.max( maxT ); + eigenvalues.min( minE ); + eigenvalues.max( maxE ); + minE[ dimensionArgMax ] = minT[ dimensionArgMax ] = currentMin; + maxE[ dimensionArgMax ] = maxT[ dimensionArgMax ] = currentMax; + final IntervalView< T > currentTensor = Views.interval( tensor, new FinalInterval( minT, maxT ) ); + final IntervalView< U > currentEigenvalues = Views.interval( eigenvalues, new FinalInterval( minE, maxE ) ); + tasks.add( () -> calculateEigenValuesImpl( currentTensor, currentEigenvalues, ev.copy() ) ); + } + + + try + { + final List< Future< RandomAccessibleInterval< U > > > futures = es.invokeAll( tasks ); + for ( final Future< RandomAccessibleInterval< U > > f : futures ) + try + { + f.get(); + } + catch ( final ExecutionException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + catch ( final InterruptedException e ) + { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return eigenvalues; + + + + } + + private static < T extends RealType< T >, U extends ComplexType< U > > RandomAccessibleInterval< U > calculateEigenValuesImpl( + final RandomAccessibleInterval< T > tensor, + final RandomAccessibleInterval< U > eigenvalues, + final EigenValues< T, U > ev ) + { + final Cursor< RealComposite< T > > m = Views.iterable( Views.collapseReal( tensor ) ).cursor(); + final Cursor< NumericComposite< U > > e = Views.iterable( Views.collapseNumeric( eigenvalues ) ).cursor(); + while ( m.hasNext() ) + ev.compute( m.next(), e.next() ); + return eigenvalues; + } + + /** + * + * Create appropriately sized image for tensor input. + * + * @param tensor + * n+1 dimensional {@link RandomAccessibleInterval}. + * @param factory + * {@link ImgFactory} used for creating the result image. + * @param u + * Variable necessary for creating the result image. + * @return n+1 dimensional {@link Img} with size n in the last dimension. + */ + public static < T extends RealType< T >, U extends RealType< U > > Img< U > createAppropriateResultImg( + final RandomAccessibleInterval< T > tensor, + final ImgFactory< U > factory, + final U u ) + { + final int nDim = tensor.numDimensions(); + final long[] dimensions = new long[ nDim ]; + tensor.dimensions( dimensions ); + dimensions[ nDim - 1 ] = nDim - 1; + return factory.create( dimensions, u ); + } + + +} diff --git a/src/main/java/varun_algorithm_linalg_matrix/RealCompositeMatrix.java b/src/main/java/varun_algorithm_linalg_matrix/RealCompositeMatrix.java new file mode 100644 index 0000000..7289a78 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_matrix/RealCompositeMatrix.java @@ -0,0 +1,127 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_matrix; + +import org.ojalgo.access.Access2D; +import org.ojalgo.matrix.store.PhysicalStore; + +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +/** + * + * @author Philipp Hanslovsky + * + * {@link Access2D} that reads data from {@link Composite} (non-copy). + * + * @param + */ +public class RealCompositeMatrix< T extends RealType< T > > implements Access2D< Double >, Access2D.Collectable< Double, PhysicalStore< Double > > +{ + + protected Composite< T > data; + + protected final int nRows; + + protected final int nCols; + + protected final int length; + + public RealCompositeMatrix( final Composite< T > data, final int nRows, final int nCols ) + { + this( data, nRows, nCols, nRows * nCols ); + } + + protected RealCompositeMatrix( final Composite< T > data, final int nRows, final int nCols, final int length ) + { + super(); + + this.data = data; + this.nRows = nRows; + this.nCols = nCols; + this.length = length; + } + + public void setData( final Composite< T > data ) + { + this.data = data; + } + + public long rowAndColumnToLinear( final long row, final long col ) + { + return row * nCols + col; + } + + @Override + public long countColumns() + { + return nCols; + } + + @Override + public long countRows() + { + return nRows; + } + + @Override + public void supplyTo( final PhysicalStore< Double > receiver ) + { + final long tmpLimRows = Math.min( this.countRows(), receiver.countRows() ); + final long tmpLimCols = Math.min( this.countColumns(), receiver.countColumns() ); + + for ( long j = 0L; j < tmpLimCols; j++ ) + for (long i = 0L; i < tmpLimRows; i++) + receiver.set(i, j, this.doubleValue(i, j)); + } + + @Override + public double doubleValue( final long row, final long col ) + { + assert row >= 0 && row < this.nRows; + assert col >= 0 && col < this.nCols; + + final double val = data.get( rowAndColumnToLinear( row, col ) ).getRealDouble(); + + return val; + } + + @Override + public Double get( final long row, final long col ) + { + return doubleValue( row, col ); + } + +} diff --git a/src/main/java/varun_algorithm_linalg_matrix/RealCompositeSquareMatrix.java b/src/main/java/varun_algorithm_linalg_matrix/RealCompositeSquareMatrix.java new file mode 100644 index 0000000..75d58e8 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_matrix/RealCompositeSquareMatrix.java @@ -0,0 +1,62 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_matrix; + + +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +/** + * + * @author Philipp Hanslovsky + * + * Square {@link RealCompositeMatrix}. + * + * @param + */ +public class RealCompositeSquareMatrix< T extends RealType< T > > extends RealCompositeMatrix< T > +{ + + public RealCompositeSquareMatrix( final Composite< T > data, final int nRowsOrCols ) + { + this( data, nRowsOrCols, nRowsOrCols * nRowsOrCols ); + } + + protected RealCompositeSquareMatrix( final Composite< T > data, final int nRowsOrCols, final int length ) + { + super( data, nRowsOrCols, nRowsOrCols, length ); + } + +} diff --git a/src/main/java/varun_algorithm_linalg_matrix/RealCompositeSymmetricMatrix.java b/src/main/java/varun_algorithm_linalg_matrix/RealCompositeSymmetricMatrix.java new file mode 100644 index 0000000..2e2a4c7 --- /dev/null +++ b/src/main/java/varun_algorithm_linalg_matrix/RealCompositeSymmetricMatrix.java @@ -0,0 +1,86 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_linalg_matrix; + + +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.composite.Composite; + +/** + * + * @author Philipp Hanslovsky + * + * Symmetric {@link RealCompositeMatrix} + * + * @param + */ +public class RealCompositeSymmetricMatrix< T extends RealType< T > > extends RealCompositeSquareMatrix< T > +{ + + public RealCompositeSymmetricMatrix( final Composite< T > data, final int nRowsOrCols ) + { + this( data, nRowsOrCols, nRowsOrCols * ( nRowsOrCols + 1 ) / 2 ); + } + + protected RealCompositeSymmetricMatrix( final Composite< T > data, final int nRowsOrCols, final int length ) + { + super( data, nRowsOrCols, length ); + } + + @Override + public long rowAndColumnToLinear( final long row, final long col ) + { + + // total number of elements: length = nRows * ( nRows + 1 ) / 2 + // row - 1 complete rows + // number elements in non-complete rows: n = ( nRows - ( row - 1 ) ) * ( + // nRows -row ) / 2 + // number of elements total: length - n + ( col - row ) + + if ( row < col ) + { + final long rowDiff = nRows - row; + final long n = rowDiff * ( rowDiff + 1 ) / 2; + return length - n + col - row; + } + else + { + final long rowDiff = nRows - col; + final long n = rowDiff * ( rowDiff + 1 ) / 2; + return length - n + row - col; + } + } + +} diff --git a/src/main/java/varun_algorithm_localextrema/LocalExtrema.java b/src/main/java/varun_algorithm_localextrema/LocalExtrema.java new file mode 100644 index 0000000..1830725 --- /dev/null +++ b/src/main/java/varun_algorithm_localextrema/LocalExtrema.java @@ -0,0 +1,240 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_localextrema; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.Sampler; +import net.imglib2.util.Intervals; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Neighborhood; +import varun_algorithm_neighborhood.RectangleShape; + +/** + * Provides {@link #findLocalExtrema} to find pixels that are extrema in their + * local neighborhood. + * + * @author Tobias Pietzsch + */ +public class LocalExtrema +{ + /** + * A local extremum check. + * + * @param

      + * A representation of the extremum. For example, this could be + * just a {@link Point} describing the location of the extremum. + * It could contain additional information such as the value at + * the extremum or an extremum type. + * @param + * pixel type. + */ + public interface LocalNeighborhoodCheck< P, T extends Comparable< T > > + { + /** + * Determine whether a pixel is a local extremum. If so, return a + * {@code P} that represents the maximum. Otherwise return + * {@code null}. + * + * @param center + * an access located on the pixel to test + * @param neighborhood + * iterable neighborhood of the pixel, not containing the + * pixel itself. + * @return null if the center not a local extremum, a P if it is. + */ + public < C extends Localizable & Sampler< T > > P check( C center, Neighborhood< T > neighborhood ); + } + + /** + * Find pixels that are extrema in their local neighborhood. The specific + * test for being an extremum can is specified as an implementation of the + * {@link LocalNeighborhoodCheck} interface. + * + * TODO: Make neighborhood shape configurable. This will require that Shape + * can give a bounding box. + * + * @param img + * @param localNeighborhoodCheck + * @param service + * @return + */ + public static < P, T extends Comparable< T > > ArrayList< P > findLocalExtrema( final RandomAccessibleInterval< T > img, final LocalNeighborhoodCheck< P, T > localNeighborhoodCheck, final ExecutorService service ) + { + final ArrayList< P > allExtrema = new ArrayList< P >(); + + final Interval full = Intervals.expand( img, -1 ); + final int n = img.numDimensions(); + final int splitd = n - 1; + // FIXME is there a better way to determine number of threads + final int numThreads = Runtime.getRuntime().availableProcessors(); + final int numTasks = Math.max( Math.min( ( int ) full.dimension( splitd ), numThreads * 20 ), 1 ); + final long dsize = full.dimension( splitd ) / numTasks; + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + full.min( min ); + full.max( max ); + + final RectangleShape shape = new RectangleShape( 1, true ); + + final ArrayList< Future< Void > > futures = new ArrayList< Future< Void > >(); + final List< P > synchronizedAllExtrema = Collections.synchronizedList( allExtrema ); + for ( int taskNum = 0; taskNum < numTasks; ++taskNum ) + { + min[ splitd ] = full.min( splitd ) + taskNum * dsize; + max[ splitd ] = ( taskNum == numTasks - 1 ) ? full.max( splitd ) : min[ splitd ] + dsize - 1; + final RandomAccessibleInterval< T > source = Views.interval( img, new FinalInterval( min, max ) ); + final ArrayList< P > extrema = new ArrayList< P >( 128 ); + final Callable< Void > r = new Callable< Void >() + { + @Override + public Void call() + { + final Cursor< T > center = Views.flatIterable( source ).cursor(); + for ( final Neighborhood< T > neighborhood : shape.neighborhoods( source ) ) + { + center.fwd(); + final P p = localNeighborhoodCheck.check( center, neighborhood ); + if ( p != null ) + extrema.add( p ); + } + synchronizedAllExtrema.addAll( extrema ); + return null; + } + }; + futures.add( service.submit( r ) ); + } + for ( final Future< Void > f : futures ) + { + try + { + f.get(); + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + catch ( final ExecutionException e ) + { + e.printStackTrace(); + } + } + + return allExtrema; + } + + /** + * A {@link LocalNeighborhoodCheck} to test whether a pixel is a local + * maximum. A pixel is considered a maximum if its value is greater than or + * equal to a specified minimum allowed value, and no pixel in the + * neighborhood has a greater value. That means that maxima are non-strict. + * Intensity plateaus may result in multiple maxima. + * + * @param + * pixel type. + * + * @author Tobias Pietzsch + */ + public static class MaximumCheck< T extends Comparable< T > > implements LocalNeighborhoodCheck< Point, T > + { + final T minPeakValue; + + public MaximumCheck( final T minPeakValue ) + { + this.minPeakValue = minPeakValue; + } + + @Override + public < C extends Localizable & Sampler< T > > Point check( final C center, final Neighborhood< T > neighborhood ) + { + final T c = center.get(); + if ( minPeakValue.compareTo( c ) > 0 ) + return null; + + for ( final T t : neighborhood ) + if ( t.compareTo( c ) > 0 ) + return null; + + return new Point( center ); + } + } + + /** + * A {@link LocalNeighborhoodCheck} to test whether a pixel is a local + * minimum. A pixel is considered a minimum if its value is less than or + * equal to a specified maximum allowed value, and no pixel in the + * neighborhood has a smaller value. That means that minima are non-strict. + * Intensity plateaus may result in multiple minima. + * + * @param + * pixel type. + * + * @author Tobias Pietzsch + */ + public static class MinimumCheck< T extends Comparable< T > > implements LocalNeighborhoodCheck< Point, T > + { + final T maxPeakValue; + + public MinimumCheck( final T maxPeakValue ) + { + this.maxPeakValue = maxPeakValue; + } + + @Override + public < C extends Localizable & Sampler< T > > Point check( final C center, final Neighborhood< T > neighborhood ) + { + final T c = center.get(); + if ( maxPeakValue.compareTo( c ) < 0 ) + return null; + + for ( final T t : neighborhood ) + if ( t.compareTo( c ) < 0 ) + return null; + + return new Point( center ); + } + } +} diff --git a/src/main/java/varun_algorithm_localextrema/RefinedPeak.java b/src/main/java/varun_algorithm_localextrema/RefinedPeak.java new file mode 100644 index 0000000..4ccf033 --- /dev/null +++ b/src/main/java/varun_algorithm_localextrema/RefinedPeak.java @@ -0,0 +1,79 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_localextrema; + +import net.imglib2.Localizable; +import net.imglib2.RealLocalizable; +import net.imglib2.RealPoint; + +/** + * A {@link RealPoint} representing a sub-pixel-localized peak. Contains the + * original non-refined peak, a value, and a boolean validity flag. See + * {@link SubpixelLocalization}. + * + * @param

      + * + * @author Tobias Pietzsch + */ +public class RefinedPeak< P extends Localizable > extends RealPoint +{ + protected final P originalPeak; + + protected final double value; + + protected final boolean valid; + + public RefinedPeak( final P originalPeak, final RealLocalizable refinedLocation, final double refinedValue, final boolean valid ) + { + super( refinedLocation ); + this.originalPeak = originalPeak; + this.value = refinedValue; + this.valid = valid; + } + + public P getOriginalPeak() + { + return originalPeak; + } + + public double getValue() + { + return value; + } + + public boolean isValid() + { + return valid; + } +} diff --git a/src/main/java/varun_algorithm_localextrema/SubpixelLocalization.java b/src/main/java/varun_algorithm_localextrema/SubpixelLocalization.java new file mode 100644 index 0000000..84cd53e --- /dev/null +++ b/src/main/java/varun_algorithm_localextrema/SubpixelLocalization.java @@ -0,0 +1,536 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_localextrema; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.Point; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RealPoint; +import net.imglib2.RealPositionable; +import net.imglib2.type.numeric.RealType; +import net.imglib2.util.Intervals; +import Jama.LUDecomposition; +import Jama.Matrix; + +/** + * Refine a set of peaks to subpixel coordinates. This class provides the static + * {@link #refinePeaks(List, RandomAccessible, Interval, boolean, int, boolean, float, boolean[], int)} + * method to do this, but this has a lot of parameters. Therefore, this class + * can also be instantiated to encapsulate the parameter settings. + * + *

      + * A List {@link RefinedPeak} for the given list of {@link Localizable} is + * computed by, for each peak, fitting a quadratic function to the image and + * computing the subpixel coordinates of the extremum. This is an iterative + * procedure. If the extremum is shifted more than 0.5 in one or more the fit is + * repeated at the corresponding integer coordinates. This is repeated to + * convergence, for a maximum number of iterations, or until the integer + * coordinates move out of the valid image. + * + * @author Stephan Preibisch + * @author Tobias Pietzsch + */ +public class SubpixelLocalization< P extends Localizable, T extends RealType< T > > +{ + protected int maxNumMoves = 4; + + protected boolean allowMaximaTolerance = false; + + protected boolean canMoveOutside = false; + + protected float maximaTolerance = 0.01f; + + protected boolean[] allowedToMoveInDim; + + protected boolean returnInvalidPeaks = false; + + protected int numThreads; + + public SubpixelLocalization( final int numDimensions ) + { + // principally one can move in any dimension + allowedToMoveInDim = new boolean[ numDimensions ]; + Arrays.fill( allowedToMoveInDim, true ); + + numThreads = Runtime.getRuntime().availableProcessors(); + } + + public void setAllowMaximaTolerance( final boolean allowMaximaTolerance ) + { + this.allowMaximaTolerance = allowMaximaTolerance; + } + + public void setCanMoveOutside( final boolean canMoveOutside ) + { + this.canMoveOutside = canMoveOutside; + } + + public void setMaximaTolerance( final float maximaTolerance ) + { + this.maximaTolerance = maximaTolerance; + } + + public void setMaxNumMoves( final int maxNumMoves ) + { + this.maxNumMoves = maxNumMoves; + } + + public void setAllowedToMoveInDim( final boolean[] allowedToMoveInDim ) + { + this.allowedToMoveInDim = allowedToMoveInDim.clone(); + } + + public void setReturnInvalidPeaks( final boolean returnInvalidPeaks ) + { + this.returnInvalidPeaks = returnInvalidPeaks; + } + + public void setNumThreads( final int numThreads ) + { + this.numThreads = numThreads; + } + + public boolean getAllowMaximaTolerance() + { + return allowMaximaTolerance; + } + + public boolean getCanMoveOutside() + { + return canMoveOutside; + } + + public float getMaximaTolerance() + { + return maximaTolerance; + } + + public int getMaxNumMoves() + { + return maxNumMoves; + } + + public boolean[] getAllowedToMoveInDim() + { + return allowedToMoveInDim.clone(); + } + + public boolean getReturnInvalidPeaks() + { + return returnInvalidPeaks; + } + + public int getNumThreads() + { + return numThreads; + } + + /** + * Refine a set of peaks to subpixel coordinates. Calls + * {@link #refinePeaks(List, RandomAccessible, Interval, boolean, int, boolean, float, boolean[], int)} + * with the parameters set to this object. + * + * @param peaks + * List of integer peaks. + * @param img + * Pixel values. + * @param validInterval + * In which interval the {@code img} contains valid pixels. + * If null, an infinite {@code img} is assumed. Integer + * peaks must lie within a 1-pixel border of this interval. + * @return refined list of peaks. + */ + public ArrayList< RefinedPeak< P > > process( final List< P > peaks, final RandomAccessible< T > img, final Interval validInterval ) + { + return refinePeaks( peaks, img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance, maximaTolerance, allowedToMoveInDim, numThreads ); + } + + /** + * Refine a set of peaks to subpixel coordinates. Multi-threaded version. + *

      + * A List {@link RefinedPeak} for the given list of {@link Localizable} is + * computed by, for each peak, fitting a quadratic function to the image and + * computing the subpixel coordinates of the extremum. This is an iterative + * procedure. If the extremum is shifted more than 0.5 in one or more the + * fit is repeated at the corresponding integer coordinates. This is + * repeated to convergence, for a maximum number of iterations, or until the + * integer coordinates move out of the valid image. + * + * @param peaks + * List of integer peaks. + * @param img + * Pixel values. + * @param validInterval + * In which interval the {@code img} contains valid pixels. + * If null, an infinite {@code img} is assumed. Integer + * peaks must lie within a 1-pixel border of this interval. + * @param returnInvalidPeaks + * Whether (invalid) {@link RefinedPeak} should be created for + * peaks where the fitting procedure did not converge. + * @param maxNumMoves + * maximum number of iterations for each peak. + * @param allowMaximaTolerance + * If we allow an increasing maxima tolerance we will not change + * the base position that easily. Sometimes it simply jumps from + * left to right and back, because it is 4.51 (i.e. goto 5), then + * 4.49 (i.e. goto 4) Then we say, ok, lets keep the base + * position even if the subpixel location is 0.6... + * @param maximaTolerance + * By how much to increase the tolerance per iteration. + * @param allowedToMoveInDim + * specifies, per dimension, whether the base location is allowed + * to be moved in the iterative procedure. + * @param numThreads + * How many threads to use for the computation. + * @return refined list of peaks. + */ + public static < T extends RealType< T >, P extends Localizable > ArrayList< RefinedPeak< P > > refinePeaks( + final List< P > peaks, final RandomAccessible< T > img, final Interval validInterval, final boolean returnInvalidPeaks, + final int maxNumMoves, final boolean allowMaximaTolerance, final float maximaTolerance, final boolean[] allowedToMoveInDim, + final int numThreads ) + { + final int numPeaks = peaks.size(); + final ArrayList< RefinedPeak< P > > allRefinedPeaks = new ArrayList< RefinedPeak< P > >( numPeaks ); + + if ( numPeaks == 0 ) + return allRefinedPeaks; + + final int numTasks = numThreads <= 1 ? 1 : ( int ) Math.min( numPeaks, numThreads * 20 ); + final int taskSize = numPeaks / numTasks; + + final ExecutorService ex = Executors.newFixedThreadPool( numThreads ); + final List< RefinedPeak< P > > synchronizedAllRefinedPeaks = Collections.synchronizedList( allRefinedPeaks ); + for ( int taskNum = 0; taskNum < numTasks; ++taskNum ) + { + final int fromIndex = taskNum * taskSize; + final int toIndex = ( taskNum == numTasks - 1 ) ? numPeaks : fromIndex + taskSize; + final Runnable r = new Runnable() + { + @Override + public void run() + { + final ArrayList< RefinedPeak< P > > refinedPeaks = refinePeaks( + peaks.subList( fromIndex, toIndex ), + img, validInterval, returnInvalidPeaks, maxNumMoves, allowMaximaTolerance, maximaTolerance, allowedToMoveInDim ); + synchronizedAllRefinedPeaks.addAll( refinedPeaks ); + } + }; + ex.execute( r ); + } + ex.shutdown(); + try + { + ex.awaitTermination( 1000, TimeUnit.DAYS ); + } + catch ( final InterruptedException e ) + { + e.printStackTrace(); + } + + return allRefinedPeaks; + } + + /** + * Refine a set of peaks to subpixel coordinates. Single-threaded version. + *

      + * A List {@link RefinedPeak} for the given list of {@link Localizable} is + * computed by, for each peak, fitting a quadratic function to the image and + * computing the subpixel coordinates of the extremum. This is an iterative + * procedure. If the extremum is shifted more than 0.5 in one or more the + * fit is repeated at the corresponding integer coordinates. This is + * repeated to convergence, for a maximum number of iterations, or until the + * integer coordinates move out of the valid image. + * + * @param peaks + * List of integer peaks. + * @param img + * Pixel values. + * @param validInterval + * In which interval the {@code img} contains valid pixels. + * If null, an infinite {@code img} is assumed. Integer + * peaks must lie within a 1-pixel border of this interval. + * @param returnInvalidPeaks + * Whether (invalid) {@link RefinedPeak} should be created for + * peaks where the fitting procedure did not converge. + * @param maxNumMoves + * maximum number of iterations for each peak. + * @param allowMaximaTolerance + * If we allow an increasing maxima tolerance we will not change + * the base position that easily. Sometimes it simply jumps from + * left to right and back, because it is 4.51 (i.e. goto 5), then + * 4.49 (i.e. goto 4) Then we say, ok, lets keep the base + * position even if the subpixel location is 0.6... + * @param maximaTolerance + * By how much to increase the tolerance per iteration. + * @param allowedToMoveInDim + * specifies, per dimension, whether the base location is allowed + * to be moved in the iterative procedure. + * @return refined list of peaks. + */ + public static < T extends RealType< T >, P extends Localizable > ArrayList< RefinedPeak< P > > refinePeaks( + final List< P > peaks, final RandomAccessible< T > img, final Interval validInterval, final boolean returnInvalidPeaks, + final int maxNumMoves, final boolean allowMaximaTolerance, final float maximaTolerance, final boolean[] allowedToMoveInDim ) + { + final ArrayList< RefinedPeak< P >> refinedPeaks = new ArrayList< RefinedPeak< P > >(); + + final int n = img.numDimensions(); + + // the current position for the quadratic fit + final Point currentPosition = new Point( n ); + + // gradient vector and Hessian matrix at the current position + final Matrix g = new Matrix( n, 1 ); + final Matrix H = new Matrix( n, n ); + + // the current subpixel offset extimate + final RealPoint subpixelOffset = new RealPoint( n ); + + // bounds checking necessary? + final boolean canMoveOutside = ( validInterval == null ); + final Interval interval = canMoveOutside ? null : Intervals.expand( validInterval, -1 ); + + // the cursor for the computation + final RandomAccess< T > access = canMoveOutside ? img.randomAccess() : img.randomAccess( validInterval ); + + for ( final P p : peaks ) + { + currentPosition.setPosition( p ); + + // fit n-dimensional quadratic function to the extremum and + // if the extremum is shifted more than 0.5 in one or more + // directions we test whether it is better there + // until we + // - converge (find a stable extremum) + // - move out of the Img + // - achieved the maximal number of moves + boolean foundStableMaxima = false; + for ( int numMoves = 0; numMoves < maxNumMoves; ++numMoves ) + { + // check validity of the current location + if ( !( canMoveOutside || Intervals.contains( interval, currentPosition ) ) ) + { + break; + } + + quadraticFitOffset( currentPosition, access, g, H, subpixelOffset ); + + // test all dimensions for their change + // if the absolute value of the subpixel location + // is bigger than 0.5 we move into that direction + // + // Normally, above an offset of 0.5 the base position + // has to be changed, e.g. a subpixel location of 4.7 + // would mean that the new base location is 5 with an offset of + // -0.3 + // + // If we allow an increasing maxima tolerance we will + // not change the base position that easily. Sometimes + // it simply jumps from left to right and back, because + // it is 4.51 (i.e. goto 5), then 4.49 (i.e. goto 4) + // Then we say, ok, lets keep the base position even if + // the subpixel location is 0.6... + foundStableMaxima = true; + final double threshold = allowMaximaTolerance ? 0.5 + numMoves * maximaTolerance : 0.5; + for ( int d = 0; d < n; ++d ) + { + final double diff = subpixelOffset.getDoublePosition( d ); + if ( Math.abs( diff ) > threshold ) + { + if ( allowedToMoveInDim[ d ] ) + { + // move to another base location + currentPosition.move( diff > 0 ? 1 : -1, d ); + foundStableMaxima = false; + } + } + } + if ( foundStableMaxima ) + { + break; + } + } + + if ( foundStableMaxima ) + { + // compute the function value (intensity) of the fit + double value = 0; + for ( int d = 0; d < n; ++d ) + { + value += g.get( d, 0 ) * subpixelOffset.getDoublePosition( d ); + } + value *= 0.5; + access.setPosition( currentPosition ); + value += access.get().getRealDouble(); + + // set the results if everything went well + subpixelOffset.move( currentPosition ); + refinedPeaks.add( new RefinedPeak< P >( p, subpixelOffset, value, true ) ); + } + else if ( returnInvalidPeaks ) + { + refinedPeaks.add( new RefinedPeak< P >( p, p, 0, false ) ); + } + } + + return refinedPeaks; + } + + /** + * Estimate subpixel {@code offset} of extremum of quadratic function + * fitted at {@code p}. + * + * @param p + * integer position at which to fit quadratic. + * @param access + * access to the image values. + * @param g + * a n vector where n is the dimensionality of + * the image. (This is a temporary variable to store the + * gradient). + * @param H + * a n × n matrix where n is the + * dimensionality of the image. (This is a temporary variable to + * store the Hessian). + * @param offset + * subpixel offset of extremum wrt. {@code p} is stored + * here. + */ + protected static < T extends RealType< T > > void quadraticFitOffset( final Localizable p, final RandomAccess< T > access, final Matrix g, final Matrix H, final RealPositionable offset ) + { + final int n = p.numDimensions(); + + access.setPosition( p ); + final double a1 = access.get().getRealDouble(); + for ( int d = 0; d < n; ++d ) + { + // @formatter:off + // gradient + // we compute the derivative for dimension d like this + // + // | a0 | a1 | a2 | + // ^ + // | + // Original position of access + // + // g(d) = (a2 - a0)/2 + // we divide by 2 because it is a jump over two pixels + // @formatter:on + access.bck( d ); + final double a0 = access.get().getRealDouble(); + access.move( 2, d ); + final double a2 = access.get().getRealDouble(); + g.set( d, 0, ( a2 - a0 ) * 0.5 ); + + // Move back to center point + access.bck( d ); + + // @formatter:off + // Hessian + // diagonal element for dimension d + // computed from the row a in the input + // + // | a0 | a1 | a2 | + // ^ + // | + // Original position of access + // + // H(dd) = (a2-a1) - (a1-a0) + // = a2 - 2*a1 + a0 + // @formatter:on + H.set( d, d, a2 - 2 * a1 + a0 ); + + // off-diagonal Hessian elements H(de) = H(ed) are computed as a + // combination of dimA (dimension a) and dimB (dimension b), i.e. we + // always operate in a two-dimensional plane + // ______________________ + // | a0b0 | a1b0 | a2b0 | + // | a0b1 | a1b1 | a2b1 | + // | a0b2 | a1b2 | a2b2 | + // ---------------------- + // where a1b1 is the original position of the access + // + // H(ab) = ( (a2b2-a0b2)/2 - (a2b0 - a0b0)/2 )/2 + // + // we divide by 2 because these are always jumps over two pixels + for ( int e = d + 1; e < n; ++e ) + { + // We start from center point. + access.fwd( d ); + access.fwd( e ); + final double a2b2 = access.get().getRealDouble(); + access.move( -2, d ); + final double a0b2 = access.get().getRealDouble(); + access.move( -2, e ); + final double a0b0 = access.get().getRealDouble(); + access.move( 2, d ); + final double a2b0 = access.get().getRealDouble(); + // back to the original position + access.bck( d ); + access.fwd( e ); + final double v = ( a2b2 - a0b2 - a2b0 + a0b0 ) * 0.25; + H.set( d, e, v ); + H.set( e, d, v ); + } + } + + // Do not move in a plane if the matrix is singular. + final LUDecomposition decomp = new LUDecomposition( H ); + if ( decomp.isNonsingular() ) + { + final Matrix minusOffset = decomp.solve( g ); + for ( int d = 0; d < n; ++d ) + { + offset.setPosition( -minusOffset.get( d, 0 ), d ); + } + } + else + { + for ( int d = 0; d < n; d++ ) + { + offset.setPosition( 0l, d ); + } + } + } +} diff --git a/src/main/java/varun_algorithm_morphology/BlackTopHat.java b/src/main/java/varun_algorithm_morphology/BlackTopHat.java new file mode 100644 index 0000000..3ba94e4 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/BlackTopHat.java @@ -0,0 +1,634 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.List; + +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.operators.Sub; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Shape; + +/** + * Black top-hat (bottom-hat) transform for ImgLib2. + * + * See Top-hat + * transform. + * + * @author Jean-Yves Tinevez - 2014 + */ +public class BlackTopHat +{ + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, on a + * {@link RealType} {@link Img} using a list of {@link Shape}s as a flat + * structuring element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} pixels, + * contrary to grayscale structuring elements. This allows to simply use a + * {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > blackTopHat( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + final Img< T > closed = Closing.close( source, strels, numThreads ); + MorphologyUtils.subAAB( closed, source, numThreads ); + return closed; + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, using + * a list of {@link Shape}s as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} pixels, + * contrary to grayscale structuring elements. This allows to simply use a + * {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, because we + * want to be able to compare pixels between themselves and to + * subtract them. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T > > Img< T > blackTopHat( final Img< T > source, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > closed = Closing.close( source, strels, minVal, maxVal, numThreads ); + MorphologyUtils.subAAB( closed, source, numThreads ); + return closed; + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, on a + * {@link RealType} {@link Img} using a {@link Shape} as a flat structuring + * element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} pixels, + * contrary to grayscale structuring elements. This allows to simply use a + * {@link Shape} as a type for these structuring elements. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > blackTopHat( final Img< T > source, final Shape strel, final int numThreads ) + { + final Img< T > closed = Closing.close( source, strel, numThreads ); + MorphologyUtils.subAAB( closed, source, numThreads ); + return closed; + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, using + * a {@link Shape} as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} pixels, + * contrary to grayscale structuring elements. This allows to simply use a + * {@link Shape} as a type for these structuring elements. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on {@code T extends Comparable & Sub}. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, because we + * want to be able to compare pixels between themselves and to + * subtract them. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T > > Img< T > blackTopHat( final Img< T > source, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > closed = Closing.close( source, strel, minVal, maxVal, numThreads ); + MorphologyUtils.subAAB( closed, source, numThreads ); + return closed; + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation on a + * {@link RealType} source {@link RandomAccessible}, using a list of + * {@link Shape}s as a structuring element, and writes the result on a + * specified target which must be an {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target receives a copy of the source. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T > > void blackTopHat( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final int numThreads ) + { + Closing.close( source, target, strels, numThreads ); + MorphologyUtils.subAAB2( target, source, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation on a + * source {@link RandomAccessible}, using a list of {@link Shape}s as a + * structuring element, and writes the result on a specified target which + * must be an {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target receives a copy of the source. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on {@code T extends Comparable & Sub}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, because we + * want to be able to compare pixels between themselves and to + * subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void blackTopHat( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + Closing.close( source, target, strels, minVal, maxVal, numThreads ); + MorphologyUtils.subAAB2( target, source, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation on a + * {@link RealType} source {@link RandomAccessible}, using a {@link Shape} + * as a structuring element, and writes the result on a specified target + * which must be an {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T > > void blackTopHat( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final int numThreads ) + { + Closing.close( source, target, strel, numThreads ); + MorphologyUtils.subAAB2( target, source, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation on a + * source {@link RandomAccessible}, using a {@link Shape} as a structuring + * element, and writes the result on a specified target which must be an + * {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on {@code T extends Comparable & Sub}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, because we + * want to be able to compare pixels between themselves and to + * subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void blackTopHat( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + Closing.close( source, target, strel, minVal, maxVal, numThreads ); + MorphologyUtils.subAAB2( target, source, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, on a + * {@link RealType} {@link Img} using a list of {@link Shape}s as a flat + * structuring element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)}. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is left untouched. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T >> void blackTopHatInPlace( final RandomAccessible< T > source, final Interval interval, final List< Shape > strels, final int numThreads ) + { + // Prepare tmp holder + final T minVal = MorphologyUtils.createVariable( source, interval ); + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Closing.close( source, translated, strels, numThreads ); + MorphologyUtils.subABA( source, translated, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, using + * a list of {@link Shape}s as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is left untouched. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on {@code T extends Comparable & Sub}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, because we + * want to be able to compare pixels between themselves and to + * subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void blackTopHatInPlace( final RandomAccessible< T > source, final Interval interval, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + // Prepare tmp holder + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Closing.close( source, translated, strels, minVal, maxVal, numThreads ); + MorphologyUtils.subABA( source, translated, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, on a + * {@link RealType} {@link Img} using a {@link Shape} as a flat structuring + * element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T >> void blackTopHatInPlace( final RandomAccessible< T > source, final Interval interval, final Shape strel, final int numThreads ) + { + // Prepare tmp holder + final T minVal = MorphologyUtils.createVariable( source, interval ); + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Closing.close( source, translated, strel, numThreads ); + MorphologyUtils.subABA( source, translated, numThreads ); + } + + /** + * Performs the black top-hat (or bottom-hat) morphological operation, using + * a {@link Shape} as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on {@code T extends Comparable & Sub}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, because we + * want to be able to compare pixels between themselves and to + * subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void blackTopHatInPlace( final RandomAccessible< T > source, final Interval interval, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + // Prepare tmp holder + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Closing.close( source, translated, strel, minVal, maxVal, numThreads ); + MorphologyUtils.subABA( source, translated, numThreads ); + } + + /** + * Private default constructor. + */ + private BlackTopHat() + {} +} diff --git a/src/main/java/varun_algorithm_morphology/Closing.java b/src/main/java/varun_algorithm_morphology/Closing.java new file mode 100644 index 0000000..1ea1975 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/Closing.java @@ -0,0 +1,639 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.List; + +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.ExtendedRandomAccessibleInterval; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Shape; + +public class Closing +{ + /** + * Performs the morphological closing operation on a {@link RealType} + * {@link Img}, using a list of {@link Shape}s as a structuring element. See + * Closing_( + * morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + * + * @param source + * the {@link Img} to operate on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result image. Must + * extends {@link RealType}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends RealType< T >> Img< T > close( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + final Img< T > dilated = Dilation.dilate( source, strels, numThreads ); + final Img< T > eroded = Erosion.erode( dilated, strels, numThreads ); + return eroded; + } + + /** + * Performs the morphological closing operation on an {@link Img} of + * {@link Comparable} , using a list of {@link Shape}s as a structuring + * element. See Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link Img} to operate on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends Type< T > & Comparable< T > > Img< T > close( final Img< T > source, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > dilated = Dilation.dilate( source, strels, minVal, numThreads ); + final Img< T > eroded = Erosion.erode( dilated, strels, maxVal, numThreads ); + return eroded; + } + + /** + * Performs the morphological closing operation on a {@link RealType} + * {@link Img}, using a {@link Shape} as a structuring element. See Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + * + * @param source + * the {@link Img} to operate on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result image. Must + * extends {@link RealType}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends RealType< T >> Img< T > close( final Img< T > source, final Shape strel, final int numThreads ) + { + final Img< T > dilated = Dilation.dilate( source, strel, numThreads ); + final Img< T > eroded = Erosion.erode( dilated, strel, numThreads ); + return eroded; + } + + + /** + * Performs the morphological closing operation on an {@link Img} of + * {@link Comparable} , using a {@link Shape} as a structuring element. See + * Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link Img} to operate on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends Type< T > & Comparable< T > > Img< T > close( final Img< T > source, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > dilated = Dilation.dilate( source, strel, minVal, numThreads ); + final Img< T > eroded = Erosion.erode( dilated, strel, maxVal, numThreads ); + return eroded; + } + + /** + * Performs the morphological closing operation on a {@link RealType} source + * {@link RandomAccessible}, using a list of {@link Shape}s as a structuring + * element, and writes the result on a specified target which must be an + * {@link IterableInterval}. See Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T > > void close( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, target ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, target ); + minVal.setReal( minVal.getMinValue() ); + close( source, target, strels, minVal, maxVal, numThreads ); + } + + /** + * Performs the morphological closing operation on a source + * {@link RandomAccessible}, using a list of {@link Shape}s as a structuring + * element, and writes the result on a specified target which must be an + * {@link IterableInterval}. See Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform closing, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@code Compparable}. + */ + public static < T extends Type< T > & Comparable< T > > void close( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + // Create temp image + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( target, maxVal ); + final Img< T > img = factory.create( target, maxVal ); + final long[] min = new long[ target.numDimensions() ]; + target.min( min ); + + final IntervalView< T > translated = Views.translate( img, min ); + Dilation.dilate( source, translated, strels, minVal, numThreads ); + + final ExtendedRandomAccessibleInterval< T, IntervalView< T >> extended = Views.extendValue( translated, maxVal ); + Erosion.erode( extended, target, strels, maxVal, numThreads ); + } + + /** + * Performs the morphological closing operation on a {@link RealType} source + * {@link RandomAccessible}, using a {@link Shape} as a structuring element, + * and writes the result on a specified target which must be an + * {@link IterableInterval}. See Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T > > void close( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, target ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, target ); + minVal.setReal( minVal.getMinValue() ); + close( source, target, strel, minVal, maxVal, numThreads ); + } + + /** + * Performs the morphological closing operation on a source + * {@link RandomAccessible}, using a {@link Shape} as a structuring element, + * and writes the result on a specified target which must be an + * {@link IterableInterval}. See Closing_(morphology). + *

      + * The closing operation is simply a dilation followed by an erosion. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform closing, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@code Comparable}. + */ + public static < T extends Type< T > & Comparable< T > > void close( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + // Create temp image + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( target, minVal ); + final Img< T > img = factory.create( target, minVal ); + final long[] min = new long[ target.numDimensions() ]; + target.min( min ); + + final IntervalView< T > translated = Views.translate( img, min ); + Dilation.dilate( source, translated, strel, minVal, numThreads ); + + final ExtendedRandomAccessibleInterval< T, IntervalView< T >> extended = Views.extendValue( translated, maxVal ); + Erosion.erode( extended, target, strel, maxVal, numThreads ); + } + + /** + * Performs the closing morphological operation, on a source + * {@link RandomAccessibleInterval} using a list of {@link Shape}s as a flat + * structuring element. + * + * See + * Closing_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void closeInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, interval ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, interval ); + minVal.setReal( minVal.getMinValue() ); + + closeInPlace( source, interval, strels, minVal, maxVal, numThreads ); + } + + /** + * Performs the closing morphological operation, on a + * {@link RandomAccessibleInterval} using a list of {@link Shape}s as a flat + * structuring element. + * + * See + * Closing_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform closing, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable}. + */ + public static < T extends Type< T > & Comparable< T >> void closeInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + for ( final Shape strel : strels ) + { + closeInPlace( source, interval, strel, minVal, maxVal, numThreads ); + } + } + + /** + * Performs the closing morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Closing_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)}. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform closing, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void closeInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, interval ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, interval ); + minVal.setReal( minVal.getMinValue() ); + + closeInPlace( source, interval, strel, minVal, maxVal, numThreads ); + } + + /** + * Performs the closing morphological operation, on a + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Closing_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform closing, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable}. + */ + public static < T extends Type< T > & Comparable< T >> void closeInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + final ExtendedRandomAccessibleInterval< T, RandomAccessibleInterval< T >> extended = Views.extendValue( source, maxVal ); + + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, maxVal ); + final Img< T > img = factory.create( interval, maxVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + close( extended, translated, strel, minVal, maxVal, numThreads ); + MorphologyUtils.copy( translated, extended, numThreads ); + } + + /** + * Private constructor. Unused. + */ + private Closing() + {} +} diff --git a/src/main/java/varun_algorithm_morphology/Dilation.java b/src/main/java/varun_algorithm_morphology/Dilation.java new file mode 100644 index 0000000..6474c79 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/Dilation.java @@ -0,0 +1,1012 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.List; +import java.util.Vector; + +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.ExtendedRandomAccessibleInterval; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Neighborhood; +import varun_algorithm_neighborhood.Shape; +import varun_multithreading.Chunk; +import varun_multithreading.SimpleMultiThreading; + +public class Dilation +{ + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link Img} using a list of {@link Shape}s as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T > > Img< T > dilate( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = dilateFull( target, strel, numThreads ); + } + return MorphologyUtils.copyCropped( target, source, numThreads ); + } + + /** + * Performs the dilation morphological operation, on an {@link Img} using a + * list of {@link Shape}s as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical dilation. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > dilate( final Img< T > source, final List< Shape > strels, final T minVal, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = dilateFull( target, strel, minVal, numThreads ); + } + return MorphologyUtils.copyCropped( target, source, numThreads ); + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link Img} using a {@link Shape} as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > dilate( final Img< T > source, final Shape strel, final int numThreads ) + { + final Img< T > target = source.factory().create( source, source.firstElement().copy() ); + final T minVal = source.firstElement().createVariable(); + minVal.setReal( minVal.getMinValue() ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, minVal ); + dilate( extended, target, strel, numThreads ); + return target; + } + + /** + * Performs the dilation morphological operation, on an {@link Img} using a + * {@link Shape} as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical dilation. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > dilate( final Img< T > source, final Shape strel, final T minVal, final int numThreads ) + { + final Img< T > target = source.factory().create( source, source.firstElement().copy() ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, minVal ); + dilate( extended, target, strel, minVal, numThreads ); + return target; + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Dilation_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + */ + public static < T extends RealType< T >> void dilate( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final int numThreads ) + { + final T minVal = MorphologyUtils.createVariable( source, target ); + minVal.setReal( minVal.getMinValue() ); + dilate( source, target, strels, minVal, numThreads ); + } + + /** + * Performs the dilation morphological operation, using a + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Dilation_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. This is required to + * perform a proper mathematical dilation. Because we operate on + * a generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void dilate( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final T minVal, final int numThreads ) + { + if ( strels.isEmpty() ) { return; } + if ( strels.size() == 1 ) + { + dilate( source, target, strels.get( 0 ), minVal, numThreads ); + return; + } + + // Compute inflated temp size and offset. + final long[] targetDims = new long[ target.numDimensions() ]; + final long[] translation = new long[ target.numDimensions() ]; + for ( int d = 0; d < targetDims.length; d++ ) + { + targetDims[ d ] = target.dimension( d ); + translation[ d ] = target.min( d ); + } + for ( final Shape strel : strels ) + { + final Neighborhood< BitType > nh = MorphologyUtils.getNeighborhood( strel, target ); + for ( int d = 0; d < translation.length; d++ ) + { + translation[ d ] -= nh.dimension( d ) / 2; + targetDims[ d ] += nh.dimension( d ) - 1; + } + } + + // First shape -> write to enlarged temp. + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( targetDims, minVal ); + Img< T > temp = factory.create( targetDims, minVal ); + final IntervalView< T > translated = Views.translate( temp, translation ); + dilate( source, translated, strels.get( 0 ), minVal, numThreads ); + + // Middle and last shapes -> do erosion. + for ( int i = 1; i < strels.size(); i++ ) + { + temp = dilate( temp, strels.get( i ), minVal, numThreads ); + } + + // Copy-crop back on target, focusing on the center part. + final long[] offset = new long[ target.numDimensions() ]; + for ( int d = 0; d < offset.length; d++ ) + { + offset[ d ] = target.min( d ) - ( ( temp.dimension( d ) - target.dimension( d ) ) / 2 ); + } + MorphologyUtils.copy2( Views.translate( temp, offset ), target, numThreads ); + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Dilation_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strel + * the structuring element, as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + */ + public static < T extends RealType< T >> void dilate( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final int numThreads ) + { + final T minVal = MorphologyUtils.createVariable( source, target ); + minVal.setReal( minVal.getMinValue() ); + dilate( source, target, strel, minVal, numThreads ); + } + + /** + * Performs the dilation morphological operation, using a + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Dilation_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strel + * the structuring element, as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. This is required to + * perform a proper mathematical dilation. Because we operate on + * a generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void dilate( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T minVal, int numThreads ) + { + numThreads = Math.max( 1, numThreads ); + + /* + * Prepare iteration. + */ + + final RandomAccessible< Neighborhood< T >> accessible = strel.neighborhoodsRandomAccessible( source ); + + /* + * Multithread + */ + + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + + final Object tmp = minVal; + if ( tmp instanceof BitType ) + { + /* + * Optimization for BitType + */ + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology dilate thread " + i ) + { + @Override + public void run() + { + final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target ); + final Object tmp2 = target.cursor(); + @SuppressWarnings( "unchecked" ) + final Cursor< BitType > cursorDilated = ( Cursor< BitType > ) tmp2; + cursorDilated.jumpFwd( chunk.getStartPosition() ); + + for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) + { + cursorDilated.fwd(); + randomAccess.setPosition( cursorDilated ); + final Neighborhood< T > neighborhood = randomAccess.get(); + final Object tmp3 = neighborhood.cursor(); + @SuppressWarnings( "unchecked" ) + final Cursor< BitType > nc = ( Cursor< BitType > ) tmp3; + + while ( nc.hasNext() ) + { + nc.fwd(); + final BitType val = nc.get(); + if ( val.get() ) + { + cursorDilated.get().set( true ); + break; + } + } + } + + } + }; + } + } + else + { + /* + * All other comparable type. + */ + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology dilate thread " + i ) + { + @Override + public void run() + { + final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target ); + final Cursor< T > cursorDilated = target.cursor(); + cursorDilated.jumpFwd( chunk.getStartPosition() ); + + final T max = MorphologyUtils.createVariable( source, target ); + for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) + { + cursorDilated.fwd(); + randomAccess.setPosition( cursorDilated ); + final Neighborhood< T > neighborhood = randomAccess.get(); + final Cursor< T > nc = neighborhood.cursor(); + + /* + * Look for max in the neighborhood. + */ + + max.set( minVal ); + while ( nc.hasNext() ) + { + nc.fwd(); + final T val = nc.get(); + // We need only Comparable to do this: + if ( val.compareTo( max ) > 0 ) + { + max.set( val ); + } + } + cursorDilated.get().set( max ); + } + + } + }; + } + } + + /* + * Launch calculation + */ + + SimpleMultiThreading.startAndJoin( threads ); + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link Img} using a list of {@link Shape}s as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * This method performs what is called the 'full' dilation. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * Warning: Current implementation does not do stricto sensu + * the full dilation. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * dilated). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full dilation results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends RealType}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends RealType< T > > Img< T > dilateFull( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = dilateFull( target, strel, numThreads ); + } + return target; + } + + /** + * Performs the dilation morphological operation, on an {@link Img} using a + * list of {@link Shape}s as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * This method performs what is called the 'full' dilation. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + *

      + * Warning: Current implementation does not do stricto sensu + * the full dilation. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * dilated). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full dilation results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical dilation. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > dilateFull( final Img< T > source, final List< Shape > strels, final T minVal, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = dilateFull( target, strel, minVal, numThreads ); + } + return target; + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link Img} using a list of {@link Shape}s as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * This method performs what is called the 'full' dilation. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * Warning: Current implementation does not do stricto sensu + * the full dilation. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * dilated). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full dilation results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strel + * the structuring element as {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends RealType}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > dilateFull( final Img< T > source, final Shape strel, final int numThreads ) + { + final long[][] dimensionsAndOffset = MorphologyUtils.computeTargetImageDimensionsAndOffset( source, strel ); + + final long[] targetDims = dimensionsAndOffset[ 0 ]; + final long[] offset = dimensionsAndOffset[ 1 ]; + + final Img< T > target = source.factory().create( targetDims, source.firstElement().copy() ); + final IntervalView< T > offsetTarget = Views.offset( target, offset ); + final T minVal = MorphologyUtils.createVariable( source, source ); + minVal.setReal( minVal.getMinValue() ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, minVal ); + + dilate( extended, offsetTarget, strel, numThreads ); + return target; + } + + /** + * Performs the dilation morphological operation, on an {@link Img} using a + * list of {@link Shape}s as a flat structuring element. + * + * See + * Dilation_(morphology). + *

      + * This method performs what is called the 'full' dilation. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + *

      + * Warning: Current implementation does not do stricto sensu + * the full dilation. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * dilated). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full dilation results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical dilation. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the dilation result. Must be + * a sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > dilateFull( final Img< T > source, final Shape strel, final T minVal, final int numThreads ) + { + + final long[][] dimensionsAndOffset = MorphologyUtils.computeTargetImageDimensionsAndOffset( source, strel ); + final long[] targetDims = dimensionsAndOffset[ 0 ]; + final long[] offset = dimensionsAndOffset[ 1 ]; + + final Img< T > target = source.factory().create( targetDims, source.firstElement().copy() ); + final IntervalView< T > offsetTarget = Views.offset( target, offset ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, minVal ); + + dilate( extended, offsetTarget, strel, minVal, numThreads ); + return target; + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} * + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void dilateInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final int numThreads ) + { + for ( final Shape strel : strels ) + { + dilateInPlace( source, interval, strel, numThreads ); + } + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} * + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. * + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. This is required to + * perform a proper mathematical dilation. Because we operate on + * a generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void dilateInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final T minVal, final int numThreads ) + { + for ( final Shape strel : strels ) + { + dilateInPlace( source, interval, strel, minVal, numThreads ); + } + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void dilateInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final int numThreads ) + { + final T minVal = MorphologyUtils.createVariable( source, interval ); + minVal.setReal( minVal.getMinValue() ); + final ExtendedRandomAccessibleInterval< T, RandomAccessibleInterval< T >> extended = Views.extendValue( source, minVal ); + + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + dilate( extended, translated, strel, numThreads ); + MorphologyUtils.copy( translated, extended, numThreads ); + } + + /** + * Performs the dilation morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Dilation_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} * + *

      + * This method relies on a specified minimal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * dilation, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image. This normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}. This is required to + * perform a proper mathematical dilation. Because we operate on + * a generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void dilateInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final T minVal, final int numThreads ) + { + // Any chance we could do something similar with a RandomAccessible? + // Doing the following with a RandomAccessible as source generated an + // java.lang.ArrayIndexOutOfBoundsException when the cursor neighborhood + // meets the central point of the neighborhood, if this point is out of + // bounds. + // final ExtendedRandomAccessibleInterval< T, IntervalView< T >> + // extended = Views.extendValue( + // Views.interval( source, interval ), + // minVal ); + + final ExtendedRandomAccessibleInterval< T, RandomAccessibleInterval< T >> extended = Views.extendValue( source, minVal ); + + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + dilate( extended, translated, strel, minVal, numThreads ); + MorphologyUtils.copy( translated, extended, numThreads ); + } + + /** + * Private constructor. Unused. + */ + private Dilation() + {} +} diff --git a/src/main/java/varun_algorithm_morphology/Erosion.java b/src/main/java/varun_algorithm_morphology/Erosion.java new file mode 100644 index 0000000..9788bfb --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/Erosion.java @@ -0,0 +1,1001 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.List; +import java.util.Vector; + +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.ExtendedRandomAccessibleInterval; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Neighborhood; +import varun_algorithm_neighborhood.Shape; +import varun_multithreading.Chunk; +import varun_multithreading.SimpleMultiThreading; + +public class Erosion +{ + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link Img} using a list of {@link Shape}s as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T > > Img< T > erode( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = erodeFull( target, strel, numThreads ); + } + return MorphologyUtils.copyCropped( target, source, numThreads ); + } + + /** + * Performs the erosion morphological operation, on an {@link Img} using a + * list of {@link Shape}s as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical erosion. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > erode( final Img< T > source, final List< Shape > strels, final T maxVal, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = erodeFull( target, strel, maxVal, numThreads ); + } + return MorphologyUtils.copyCropped( target, source, numThreads ); + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link Img} using a {@link Shape} as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > erode( final Img< T > source, final Shape strel, final int numThreads ) + { + final Img< T > target = source.factory().create( source, source.firstElement().copy() ); + final T maxVal = source.firstElement().createVariable(); + maxVal.setReal( maxVal.getMaxValue() ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, maxVal ); + erode( extended, target, strel, numThreads ); + return target; + } + + /** + * Performs the erosion morphological operation, on an {@link Img} using a + * {@link Shape} as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical erosion. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > erode( final Img< T > source, final Shape strel, final T maxVal, final int numThreads ) + { + final Img< T > target = source.factory().create( source, source.firstElement().copy() ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, maxVal ); + erode( extended, target, strel, maxVal, numThreads ); + return target; + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Erosion_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + */ + public static < T extends RealType< T >> void erode( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, target ); + maxVal.setReal( maxVal.getMaxValue() ); + erode( source, target, strels, maxVal, numThreads ); + } + + /** + * Performs the erosion morphological operation, using a + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Erosion_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. This is required to + * perform a proper mathematical erosion. Because we operate on a + * generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void erode( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final T maxVal, final int numThreads ) + { + if ( strels.isEmpty() ) { return; } + if ( strels.size() == 1 ) + { + erode( source, target, strels.get( 0 ), maxVal, numThreads ); + return; + } + + // Compute inflated temp size and offset. + final long[] targetDims = new long[ target.numDimensions() ]; + final long[] translation = new long[ target.numDimensions() ]; + for ( int d = 0; d < targetDims.length; d++ ) + { + targetDims[ d ] = target.dimension( d ); + translation[ d ] = target.min( d ); + } + for ( final Shape strel : strels ) + { + final Neighborhood< BitType > nh = MorphologyUtils.getNeighborhood( strel, target ); + for ( int d = 0; d < translation.length; d++ ) + { + translation[ d ] -= nh.dimension( d ) / 2; + targetDims[ d ] += nh.dimension( d ) - 1; + } + } + + // First shape -> write to enlarged temp. + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( targetDims, maxVal ); + Img< T > temp = factory.create( targetDims, maxVal ); + final IntervalView< T > translated = Views.translate( temp, translation ); + erode( source, translated, strels.get( 0 ), maxVal, numThreads ); + + // Middle and last shapes -> do erosion. + for ( int i = 1; i < strels.size(); i++ ) + { + temp = erode( temp, strels.get( i ), maxVal, numThreads ); + } + + // Copy-crop back on target, focusing on the center part. + final long[] offset = new long[ target.numDimensions() ]; + for ( int d = 0; d < offset.length; d++ ) + { + offset[ d ] = target.min( d ) - ( ( temp.dimension( d ) - target.dimension( d ) ) / 2 ); + } + MorphologyUtils.copy2( Views.translate( temp, offset ), target, numThreads ); + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Erosion_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strel + * the structuring element, as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + */ + public static < T extends RealType< T >> void erode( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, target ); + maxVal.setReal( maxVal.getMaxValue() ); + erode( source, target, strel, maxVal, numThreads ); + } + + /** + * Performs the erosion morphological operation, using a + * {@link RandomAccessible} as a source and writing results in an + * {@link IterableInterval}. + * + * See + * Erosion_(morphology). + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source {@link RandomAccessible}, must be sufficiently + * padded. + * @param target + * the target image. + * @param strel + * the structuring element, as a {@link Shape}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. This is required to + * perform a proper mathematical erosion. Because we operate on a + * generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void erode( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T maxVal, int numThreads ) + { + numThreads = Math.max( 1, numThreads ); + + /* + * Prepare iteration. + */ + + final RandomAccessible< Neighborhood< T >> accessible = strel.neighborhoodsRandomAccessible( source ); + + /* + * Multithread + */ + + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + final Object tmp = maxVal; + if ( tmp instanceof BitType ) + { + /* + * Optimization for BitType + */ + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology erode thread " + i ) + { + @Override + public void run() + { + final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target ); + final Object tmp2 = target.cursor(); + @SuppressWarnings( "unchecked" ) + final Cursor< BitType > cursorTarget = ( Cursor< BitType > ) tmp2; + cursorTarget.jumpFwd( chunk.getStartPosition() ); + + for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) + { + cursorTarget.fwd(); + randomAccess.setPosition( cursorTarget ); + final Object tmp3 = randomAccess.get(); + @SuppressWarnings( "unchecked" ) + final Neighborhood< BitType > neighborhood = (varun_algorithm_neighborhood.Neighborhood< BitType > ) tmp3; + final Cursor< BitType > nc = neighborhood.cursor(); + + cursorTarget.get().set( true ); + while ( nc.hasNext() ) + { + nc.fwd(); + final BitType val = nc.get(); + if ( !val.get() ) + { + cursorTarget.get().set( false ); + break; + } + } + } + + } + }; + } + } + else + { + /* + * All other comparable type. + */ + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology erode thread " + i ) + { + @Override + public void run() + { + final RandomAccess< Neighborhood< T >> randomAccess = accessible.randomAccess( target ); + final Cursor< T > cursorTarget = target.cursor(); + cursorTarget.jumpFwd( chunk.getStartPosition() ); + + final T max = MorphologyUtils.createVariable( source, target ); + for ( long steps = 0; steps < chunk.getLoopSize(); steps++ ) + { + cursorTarget.fwd(); + randomAccess.setPosition( cursorTarget ); + final Neighborhood< T > neighborhood = randomAccess.get(); + final Cursor< T > nc = neighborhood.cursor(); + + /* + * Look for max in the neighborhood. + */ + + max.set( maxVal ); + while ( nc.hasNext() ) + { + nc.fwd(); + final T val = nc.get(); + // We need only Comparable to do this: + if ( val.compareTo( max ) < 0 ) + { + max.set( val ); + } + } + cursorTarget.get().set( max ); + } + + } + }; + } + } + + /* + * Launch calculation + */ + + SimpleMultiThreading.startAndJoin( threads ); + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link Img} using a list of {@link Shape}s as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * This method performs what is called the 'full' erosion. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * Warning: Current implementation does not do stricto sensu + * the full erosion. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * eroded). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full erosion results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends RealType< T > > Img< T > erodeFull( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = erodeFull( target, strel, numThreads ); + } + return target; + } + + /** + * Performs the erosion morphological operation, on an {@link Img} using a + * list of {@link Shape}s as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * This method performs what is called the 'full' erosion. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + *

      + * Warning: Current implementation does not do stricto sensu + * the full erosion. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * eroded). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full erosion results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical erosion. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > erodeFull( final Img< T > source, final List< Shape > strels, final T maxVal, final int numThreads ) + { + Img< T > target = source; + for ( final Shape strel : strels ) + { + target = erodeFull( target, strel, maxVal, numThreads ); + } + return target; + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link Img} using a list of {@link Shape}s as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * This method performs what is called the 'full' erosion. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * Warning: Current implementation does not do stricto sensu + * the full erosion. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * eroded). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full erosion results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strel + * the structuring element as {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > erodeFull( final Img< T > source, final Shape strel, final int numThreads ) + { + final long[][] dimensionsAndOffset = MorphologyUtils.computeTargetImageDimensionsAndOffset( source, strel ); + + final long[] targetDims = dimensionsAndOffset[ 0 ]; + final long[] offset = dimensionsAndOffset[ 1 ]; + + final Img< T > target = source.factory().create( targetDims, source.firstElement().copy() ); + final IntervalView< T > offsetTarget = Views.offset( target, offset ); + final T maxVal = MorphologyUtils.createVariable( source, source ); + maxVal.setReal( maxVal.getMaxValue() ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, maxVal ); + + erode( extended, offsetTarget, strel, numThreads ); + return target; + } + + /** + * Performs the erosion morphological operation, on an {@link Img} using a + * list of {@link Shape}s as a flat structuring element. + * + * See + * Erosion_(morphology). + *

      + * This method performs what is called the 'full' erosion. That is: the + * result image has its dimension enlarged by the structuring element, with + * respect to the source image. It is limited to flat structuring elements, + * only having {@code on/off} pixels, contrary to grayscale structuring + * elements. This allows to simply use a {@link Shape} as a type for these + * structuring elements. + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + *

      + * Warning: Current implementation does not do stricto sensu + * the full erosion. Indeed, if the structuring element has more dimensions + * than the source {@link Img}, they are ignored, and the returned + * {@link Img} has the same number of dimensions that of the source (but + * eroded). This is due to the fact that we use a {@link Shape} for + * structuring elements, and that it does not return a number of dimensions. + * The neighborhood created have therefore at most as many dimensions as the + * source image. The real, full erosion results should have a number of + * dimensions equals to the maximum of the number of dimension of both + * source and structuring element. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}. This is + * required to perform a proper mathematical erosion. Because we + * operate on a generic {@link Type}, it has to be provided + * manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the erosion result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return a new {@link Img}, possibly of larger dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > > Img< T > erodeFull( final Img< T > source, final Shape strel, final T maxVal, final int numThreads ) + { + + final long[][] dimensionsAndOffset = MorphologyUtils.computeTargetImageDimensionsAndOffset( source, strel ); + final long[] targetDims = dimensionsAndOffset[ 0 ]; + final long[] offset = dimensionsAndOffset[ 1 ]; + + final Img< T > target = source.factory().create( targetDims, source.firstElement().copy() ); + final IntervalView< T > offsetTarget = Views.offset( target, offset ); + final ExtendedRandomAccessibleInterval< T, Img< T >> extended = Views.extendValue( source, maxVal ); + + erode( extended, offsetTarget, strel, maxVal, numThreads ); + return target; + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void erodeInPlace( final RandomAccessible< T > source, final Interval interval, final List< Shape > strels, final int numThreads ) + { + for ( final Shape strel : strels ) + { + erodeInPlace( source, interval, strel, numThreads ); + } + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} * + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. * + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. This is required to + * perform a proper mathematical erosion. Because we operate on a + * generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void erodeInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final T maxVal, final int numThreads ) + { + for ( final Shape strel : strels ) + { + erodeInPlace( source, interval, strel, maxVal, numThreads ); + } + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void erodeInPlace( final RandomAccessible< T > source, final Interval interval, final Shape strel, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, interval ); + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, maxVal ); + final Img< T > img = factory.create( interval, maxVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + erode( source, translated, strel, numThreads ); + MorphologyUtils.copy( translated, source, numThreads ); + } + + /** + * Performs the erosion morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Erosion_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} * + *

      + * This method relies on a specified maximal value to start comparing to + * other pixels in the neighborhood. For this code to properly perform + * erosion, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image. This + * normally unseen parameter is required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}. This is required to + * perform a proper mathematical erosion. Because we operate on a + * generic {@link Type}, it has to be provided manually. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable & Type}. + */ + public static < T extends Type< T > & Comparable< T > > void erodeInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final T maxVal, final int numThreads ) + { + + final ExtendedRandomAccessibleInterval< T, RandomAccessibleInterval< T >> extended = Views.extendValue( source, maxVal ); + + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, maxVal ); + final Img< T > img = factory.create( interval, maxVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + erode( extended, translated, strel, maxVal, numThreads ); + MorphologyUtils.copy( translated, extended, numThreads ); + } + + /** + * Private constructor. Unused. + */ + private Erosion() + {} +} diff --git a/src/main/java/varun_algorithm_morphology/MorphologyUtils.java b/src/main/java/varun_algorithm_morphology/MorphologyUtils.java new file mode 100644 index 0000000..03ba9ca --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/MorphologyUtils.java @@ -0,0 +1,723 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.Vector; + +import net.imglib2.Cursor; +import net.imglib2.Dimensions; +import net.imglib2.EuclideanSpace; +import net.imglib2.FinalDimensions; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.array.ArrayImgs; +import net.imglib2.img.array.ArrayRandomAccess; +import net.imglib2.img.basictypeaccess.array.LongArray; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.img.list.ListImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.Type; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.operators.Sub; +import net.imglib2.util.Intervals; +import net.imglib2.util.Util; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Neighborhood; +import varun_algorithm_neighborhood.Shape; +import varun_multithreading.Chunk; +import varun_multithreading.SimpleMultiThreading; + +public class MorphologyUtils +{ + + /** + * Static util to compute the final image dimensions and required offset + * when performing a full dilation with the specified strel. + * + * @param source + * the source image. + * @param strel + * the strel to use for dilation. + * @return a 2-elements {@code long[][]}: + *

        + *
      1. a {@code long[]} array with the final image target + * dimensions. + *
      2. a {@code long[]} array with the offset to apply to the + * source image. + *
      + */ + public static final < T > long[][] computeTargetImageDimensionsAndOffset( final Interval source, final Shape strel ) + { + /* + * Compute target image size + */ + + final long[] targetDims; + + /* + * Get a neighborhood to play with. Note: if we would have a dedicated + * interface for structuring elements, that would extend Shape and + * Dimensions, we would need to do what we are going to do now. On top + * of that, this is the part that causes the full dilation not to be a + * real full dilation: if the structuring element has more dimensions + * than the source, they are ignored. This is because we use the source + * as the Dimension to create the sample neighborhood we play with. + */ + final Neighborhood< BitType > sampleNeighborhood = MorphologyUtils.getNeighborhood( strel, source ); + int ndims = sampleNeighborhood.numDimensions(); + ndims = Math.max( ndims, source.numDimensions() ); + targetDims = new long[ ndims ]; + for ( int d = 0; d < ndims; d++ ) + { + long d1; + if ( d < source.numDimensions() ) + { + d1 = source.dimension( d ); + } + else + { + d1 = 1; + } + + long d2; + if ( d < sampleNeighborhood.numDimensions() ) + { + d2 = sampleNeighborhood.dimension( d ); + } + else + { + d2 = 1; + } + + targetDims[ d ] = d1 + d2 - 1; + } + + // Offset coordinates so that they match the source coordinates, which + // will not be extended. + final long[] offset = new long[ source.numDimensions() ]; + for ( int d = 0; d < offset.length; d++ ) + { + if ( d < sampleNeighborhood.numDimensions() ) + { + offset[ d ] = -sampleNeighborhood.min( d ); + } + else + { + offset[ d ] = 0; + } + } + + return new long[][] { targetDims, offset }; + } + + static final void appendLine( final RandomAccess< BitType > ra, final long maxX, final StringBuilder str ) + { + // Top line + str.append( '┌' ); + for ( long x = 0; x < maxX; x++ ) + { + str.append( '─' ); + } + str.append( "┐\n" ); + // Center + str.append( '│' ); + for ( long x = 0; x < maxX; x++ ) + { + ra.setPosition( x, 0 ); + if ( ra.get().get() ) + { + str.append( '█' ); + } + else + { + str.append( ' ' ); + } + } + str.append( "│\n" ); + // Bottom line + str.append( '└' ); + for ( long x = 0; x < maxX; x++ ) + { + str.append( '─' ); + } + str.append( "┘\n" ); + } + + private static final void appendManySlice( final RandomAccess< BitType > ra, final long maxX, final long maxY, final long maxZ, final StringBuilder str ) + { + // Z names + final long width = Math.max( maxX + 3, 9l ); + for ( int z = 0; z < maxZ; z++ ) + { + final String sample = "Z = " + z + ":"; + str.append( sample ); + for ( int i = 0; i < width - sample.length(); i++ ) + { + str.append( ' ' ); + } + } + str.append( '\n' ); + + // Top line + for ( int z = 0; z < maxZ; z++ ) + { + str.append( '┌' ); + for ( long x = 0; x < maxX; x++ ) + { + str.append( '─' ); + } + str.append( "┐ " ); + for ( int i = 0; i < width - maxX - 3; i++ ) + { + str.append( ' ' ); + } + } + str.append( '\n' ); + + // Neighborhood + for ( long y = 0; y < maxY; y++ ) + { + ra.setPosition( y, 1 ); + + for ( int z = 0; z < maxZ; z++ ) + { + ra.setPosition( z, 2 ); + str.append( '│' ); + for ( long x = 0; x < maxX; x++ ) + { + ra.setPosition( x, 0 ); + if ( ra.get().get() ) + { + str.append( '█' ); + } + else + { + str.append( ' ' ); + } + } + str.append( '│' ); + for ( int i = 0; i < width - maxX - 2; i++ ) + { + str.append( ' ' ); + } + } + str.append( '\n' ); + } + + // Bottom line + for ( int z = 0; z < maxZ; z++ ) + { + str.append( '└' ); + for ( long x = 0; x < maxX; x++ ) + { + str.append( '─' ); + } + str.append( "┘ " ); + for ( int i = 0; i < width - maxX - 3; i++ ) + { + str.append( ' ' ); + } + } + str.append( '\n' ); + } + + private static final void appendSingleSlice( final RandomAccess< BitType > ra, final long maxX, final long maxY, final StringBuilder str ) + { + // Top line + str.append( '┌' ); + for ( long x = 0; x < maxX; x++ ) + { + str.append( '─' ); + } + str.append( "┐\n" ); + for ( long y = 0; y < maxY; y++ ) + { + str.append( '│' ); + ra.setPosition( y, 1 ); + for ( long x = 0; x < maxX; x++ ) + { + ra.setPosition( x, 0 ); + if ( ra.get().get() ) + { + str.append( '█' ); + } + else + { + str.append( ' ' ); + } + } + str.append( "│\n" ); + } + // Bottom line + str.append( '└' ); + for ( long x = 0; x < maxX; x++ ) + { + str.append( '─' ); + } + str.append( "┘\n" ); + } + + static < T extends Type< T > > void copy( final IterableInterval< T > source, final RandomAccessible< T > target, final int numThreads ) + { + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( source.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology copy thread " + i ) + { + @Override + public void run() + { + final Cursor< T > sourceCursor = source.localizingCursor(); + sourceCursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > targetRandomAccess = target.randomAccess(); + + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + sourceCursor.fwd(); + targetRandomAccess.setPosition( sourceCursor ); + targetRandomAccess.get().set( sourceCursor.get() ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + + static < T extends Type< T > > void copy2( final RandomAccessible< T > source, final IterableInterval< T > target, final int numThreads ) + { + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology copy2 thread " + i ) + { + @Override + public void run() + { + final Cursor< T > targetCursor = target.localizingCursor(); + targetCursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > sourceRandomAccess = source.randomAccess(); + + // iterate over the input cursor + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + targetCursor.fwd(); + sourceRandomAccess.setPosition( targetCursor ); + targetCursor.get().set( sourceRandomAccess.get() ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + + static < T extends Type< T > > Img< T > copyCropped( final Img< T > largeSource, final Interval interval, final int numThreads ) + { + final long[] offset = new long[ largeSource.numDimensions() ]; + for ( int d = 0; d < offset.length; d++ ) + { + offset[ d ] = ( largeSource.dimension( d ) - interval.dimension( d ) ) / 2; + } + final Img< T > create = largeSource.factory().create( interval, largeSource.firstElement().copy() ); + + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( create.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology copyCropped thread " + i ) + { + @Override + public void run() + { + final IntervalView< T > intervalView = Views.offset( largeSource, offset ); + final Cursor< T > cursor = create.cursor(); + cursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > randomAccess = intervalView.randomAccess(); + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + cursor.fwd(); + randomAccess.setPosition( cursor ); + cursor.get().set( randomAccess.get() ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + return create; + } + + /** + * Get an instance of type T from a {@link RandomAccess} on accessible that + * is positioned at the min of interval. + * + * @param accessible + * @param interval + * @return type instance + */ + static < T extends Type< T >> T createVariable( final RandomAccessible< T > accessible, final Interval interval ) + { + final RandomAccess< T > a = accessible.randomAccess(); + interval.min( a ); + return a.get().createVariable(); + } + + public static final Neighborhood< BitType > getNeighborhood( final Shape shape, final EuclideanSpace space ) + { + final int numDims = space.numDimensions(); + final long[] dimensions = Util.getArrayFromValue( 1l, numDims ); + final ArrayImg< BitType, LongArray > img = ArrayImgs.bits( dimensions ); + final IterableInterval< Neighborhood< BitType >> neighborhoods = shape.neighborhoods( img ); + final Neighborhood< BitType > neighborhood = neighborhoods.cursor().next(); + return neighborhood; + } + + static < T > ImgFactory< T > getSuitableFactory( final long[] targetSize, final T type ) + { + final FinalDimensions dim = FinalDimensions.wrap( targetSize ); + return getSuitableFactory( dim, type ); + } + + @SuppressWarnings( { "rawtypes", "unchecked" } ) + static < T > ImgFactory< T > getSuitableFactory( final Dimensions targetSize, final T type ) + { + if ( type instanceof NativeType ) + { + final NativeType nt = ( NativeType ) type; + if ( Intervals.numElements( targetSize ) <= Integer.MAX_VALUE ) { + return new ArrayImgFactory(); + } + final int cellSize = ( int ) Math.pow( Integer.MAX_VALUE / nt.getEntitiesPerPixel().getRatio(), 1.0 / targetSize.numDimensions() ); + return new CellImgFactory( cellSize ); + } + else + { + return new ListImgFactory< T >(); + } + } + + /** + * Returns a string representation of the specified flat structuring element + * (given as a {@link Shape}), cast over the dimensionality specified by an + * {@link EuclideanSpace}. + *

      + * This method only prints the first 3 dimensions of the structuring + * element. Dimensions above 3 are skipped. + * + * @param shape + * the structuring element to print. + * @param dimensionality + * the dimensionality to cast it over. This is required as + * {@link Shape} does not carry a dimensionality, and we need one + * to generate a neighborhood to iterate. + * @return a string representation of the structuring element. + */ + public static final String printNeighborhood( final Shape shape, final int dimensionality ) + { + final Img< BitType > neighborhood; + { + final long[] dimensions = Util.getArrayFromValue( 1l, dimensionality ); + + final ArrayImg< BitType, LongArray > img = ArrayImgs.bits( dimensions ); + final ArrayRandomAccess< BitType > randomAccess = img.randomAccess(); + randomAccess.setPosition( Util.getArrayFromValue( 0, dimensions.length ) ); + randomAccess.get().set( true ); + neighborhood = Dilation.dilateFull( img, shape, 1 ); + } + + final StringBuilder str = new StringBuilder(); + for ( int d = 3; d < neighborhood.numDimensions(); d++ ) + { + if ( neighborhood.dimension( d ) > 1 ) + { + str.append( "Cannot print structuring elements with n dimensions > 3.\n" + "Skipping dimensions beyond 3.\n\n" ); + break; + } + } + + final RandomAccess< BitType > randomAccess = neighborhood.randomAccess(); + if ( neighborhood.numDimensions() > 2 ) + { + appendManySlice( randomAccess, neighborhood.dimension( 0 ), neighborhood.dimension( 1 ), neighborhood.dimension( 2 ), str ); + } + else if ( neighborhood.numDimensions() > 1 ) + { + appendSingleSlice( randomAccess, neighborhood.dimension( 0 ), neighborhood.dimension( 1 ), str ); + } + else if ( neighborhood.numDimensions() > 0 ) + { + appendLine( randomAccess, neighborhood.dimension( 0 ), str ); + } + else + { + str.append( "Void structuring element.\n" ); + } + + return str.toString(); + } + + /** + * Does A = A - B. Writes the results in A. + * + * @param A + * A + * @param B + * B + * @param numThreads + */ + static < T extends Sub< T > > void subAAB( final RandomAccessible< T > A, final IterableInterval< T > B, final int numThreads ) + { + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( B.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology subAAB thread " + i ) + { + @Override + public void run() + { + final Cursor< T > Bcursor = B.localizingCursor(); + Bcursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > Ara = A.randomAccess(); + + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + Bcursor.fwd(); + Ara.setPosition( Bcursor ); + Ara.get().sub( Bcursor.get() ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + + + /** + * Does A = A - B. Writes the results in A. + * + * @param A + * A + * @param B + * B + * @param numThreads + */ + static < T extends Sub< T > > void subAAB2( final IterableInterval< T > A, final RandomAccessible< T > B, final int numThreads ) + { + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( A.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology subAAB2 thread " + i ) + { + @Override + public void run() + { + final Cursor< T > Acursor = A.localizingCursor(); + Acursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > Bra = B.randomAccess(); // LOL + + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + Acursor.fwd(); + Bra.setPosition( Acursor ); + Acursor.get().sub( Bra.get() ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + + + /** + * Does A = B - A. Writes the results in A. + * + * @param source + * A + * @param target + * B + * @param numThreads + */ + static < T extends Sub< T > & Type< T >> void subABA( final RandomAccessible< T > source, final IterableInterval< T > target, final int numThreads ) + { + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( target.size(), numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology subABA thread " + i ) + { + @Override + public void run() + { + final T tmp = createVariable( source, target ); + final Cursor< T > targetCursor = target.localizingCursor(); + targetCursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > sourceRandomAccess = source.randomAccess(); + + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + targetCursor.fwd(); + sourceRandomAccess.setPosition( targetCursor ); + + tmp.set( targetCursor.get() ); + tmp.sub( sourceRandomAccess.get() ); + + sourceRandomAccess.get().set( tmp ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + + /** + * Does A = B - A. Writes the results in A. + * + * @param source + * A + * @param target + * B + * @param numThreads + */ + static < T extends Sub< T > & Type< T >> void subABA2( final RandomAccessibleInterval< T > source, final RandomAccessible< T > target, final int numThreads ) + { + final long size = Intervals.numElements( source ); + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( size, numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology subABA2 thread " + i ) + { + @Override + public void run() + { + final T tmp = createVariable( target, source ); + final Cursor< T > sourceCursor = Views.iterable( source ).localizingCursor(); + sourceCursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > targetRandomAccess = target.randomAccess( source ); + + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + + } + while ( sourceCursor.hasNext() ) + { + sourceCursor.fwd(); + targetRandomAccess.setPosition( sourceCursor ); + + tmp.set( targetRandomAccess.get() ); + tmp.sub( sourceCursor.get() ); + + targetRandomAccess.get().set( tmp ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + + /** + * Does B = A - B. Writes the results in B. + * + * @param A + * A + * @param B + * B + * @param numThreads + */ + static < T extends Type< T > & Sub< T > > void subBAB( final RandomAccessible< T > A, final IterableInterval< T > B, final int numThreads ) + { + final long size = Intervals.numElements( B ); + final Vector< Chunk > chunks = SimpleMultiThreading.divideIntoChunks( size, numThreads ); + final Thread[] threads = SimpleMultiThreading.newThreads( numThreads ); + + for ( int i = 0; i < threads.length; i++ ) + { + final Chunk chunk = chunks.get( i ); + threads[ i ] = new Thread( "Morphology subBAB thread " + i ) + { + @Override + public void run() + { + final T tmp = createVariable( A, B ); + final Cursor< T > BCursor = B.localizingCursor(); + BCursor.jumpFwd( chunk.getStartPosition() ); + final RandomAccess< T > Ara = A.randomAccess(); + + for ( long step = 0; step < chunk.getLoopSize(); step++ ) + { + BCursor.fwd(); + Ara.setPosition( BCursor ); + + tmp.set( Ara.get() ); + tmp.sub( BCursor.get() ); + + BCursor.get().set( tmp ); + } + } + }; + } + + SimpleMultiThreading.startAndJoin( threads ); + } + +} diff --git a/src/main/java/varun_algorithm_morphology/Opening.java b/src/main/java/varun_algorithm_morphology/Opening.java new file mode 100644 index 0000000..b6f0e17 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/Opening.java @@ -0,0 +1,632 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.List; + +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.view.ExtendedRandomAccessibleInterval; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Shape; + +/** + * Morphological opening for ImgLib2. See Opening_( + * morphology). + * + * @author Jean-Yves Tinevez - 2014 + */ +public class Opening +{ + /** + * Performs the morphological opening operation on a {@link RealType} + * {@link Img}, using a list of {@link Shape}s as a structuring element. See + * Opening_( + * morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + * + * @param source + * the {@link Img} to operate on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result image. Must + * extends {@link RealType}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends RealType< T >> Img< T > open( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + final Img< T > eroded = Erosion.erode( source, strels, numThreads ); + final Img< T > dilated = Dilation.dilate( eroded, strels, numThreads ); + return dilated; + } + + /** + * Performs the morphological opening operation on an {@link Img} of + * {@link Comparable} , using a list of {@link Shape}s as a structuring + * element. See Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link Img} to operate on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends Type< T > & Comparable< T > > Img< T > open( final Img< T > source, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > eroded = Erosion.erode( source, strels, maxVal, numThreads ); + final Img< T > dilated = Dilation.dilate( eroded, strels, minVal, numThreads ); + return dilated; + } + + /** + * Performs the morphological opening operation on a {@link RealType} + * {@link Img}, using a {@link Shape} as a structuring element. See Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + * + * @param source + * the {@link Img} to operate on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result image. Must + * extends {@link RealType}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends RealType< T >> Img< T > open( final Img< T > source, final Shape strel, final int numThreads ) + { + final Img< T > eroded = Erosion.erode( source, strel, numThreads ); + final Img< T > dilated = Dilation.dilate( eroded, strel, numThreads ); + return dilated; + } + + + /** + * Performs the morphological opening operation on an {@link Img} of + * {@link Comparable} , using a {@link Shape} as a structuring element. See + * Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link Img} to operate on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the result. Must be a + * sub-type of {@code T extends Comparable & Type}. + * @return an {@link Img} of the same type and same dimensions that of the + * source. + */ + public static final < T extends Type< T > & Comparable< T > > Img< T > open( final Img< T > source, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > eroded = Erosion.erode( source, strel, maxVal, numThreads ); + final Img< T > dilated = Dilation.dilate( eroded, strel, minVal, numThreads ); + return dilated; + } + + /** + * Performs the morphological opening operation on a {@link RealType} source + * {@link RandomAccessible}, using a list of {@link Shape}s as a structuring + * element, and writes the result on a specified target which must be an + * {@link IterableInterval}. See Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T > > void open( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, target ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, target ); + minVal.setReal( minVal.getMinValue() ); + open( source, target, strels, minVal, maxVal, numThreads ); + } + + /** + * Performs the morphological opening operation on a source + * {@link RandomAccessible}, using a list of {@link Shape}s as a structuring + * element, and writes the result on a specified target which must be an + * {@link IterableInterval}. See Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target is left untouched. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform opening, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the structuring element, as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@code Compparable}. + */ + public static < T extends Type< T > & Comparable< T > > void open( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + // Create temp image + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( target, maxVal ); + final Img< T > img = factory.create( target, maxVal ); + final long[] min = new long[ target.numDimensions() ]; + target.min( min ); + + final IntervalView< T > translated = Views.translate( img, min ); + Erosion.erode( source, translated, strels, maxVal, numThreads ); + + final ExtendedRandomAccessibleInterval< T, IntervalView< T >> extended = Views.extendValue( translated, minVal ); + Dilation.dilate( extended, target, strels, minVal, numThreads ); + } + + /** + * Performs the morphological opening operation on a {@link RealType} source + * {@link RandomAccessible}, using a {@link Shape} as a structuring element, + * and writes the result on a specified target which must be an + * {@link IterableInterval}. See Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T > > void open( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, target ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, target ); + minVal.setReal( minVal.getMinValue() ); + open( source, target, strel, minVal, maxVal, numThreads ); + } + + /** + * Performs the morphological opening operation on a source + * {@link RandomAccessible}, using a {@link Shape} as a structuring element, + * and writes the result on a specified target which must be an + * {@link IterableInterval}. See Opening_(morphology). + *

      + * The opening operation is simply an erosion followed by a dilation. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform opening, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@code Comparable}. + */ + public static < T extends Type< T > & Comparable< T > > void open( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + // Create temp image + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( target, maxVal ); + final Img< T > img = factory.create( target, maxVal ); + final long[] min = new long[ target.numDimensions() ]; + target.min( min ); + + final IntervalView< T > translated = Views.translate( img, min ); + Erosion.erode( source, translated, strel, maxVal, numThreads ); + + final ExtendedRandomAccessibleInterval< T, IntervalView< T >> extended = Views.extendValue( translated, minVal ); + Dilation.dilate( extended, target, strel, minVal, numThreads ); + } + + /** + * Performs the opening morphological operation, on a source + * {@link RandomAccessibleInterval} using a list of {@link Shape}s as a flat + * structuring element. + * + * See + * Opening_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} * + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is left untouched. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void openInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, interval ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, interval ); + minVal.setReal( minVal.getMinValue() ); + + openInPlace( source, interval, strels, minVal, maxVal, numThreads ); + } + + /** + * Performs the opening morphological operation, on a + * {@link RandomAccessibleInterval} using a list of {@link Shape}s as a flat + * structuring element. + * + * See + * Opening_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is left untouched. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform opening, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable}. + */ + public static < T extends Type< T > & Comparable< T >> void openInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + for ( final Shape strel : strels ) + { + openInPlace( source, interval, strel, minVal, maxVal, numThreads ); + } + } + + /** + * Performs the opening morphological operation, on a {@link RealType} + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Opening_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T > > void openInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final int numThreads ) + { + final T maxVal = MorphologyUtils.createVariable( source, interval ); + maxVal.setReal( maxVal.getMaxValue() ); + final T minVal = MorphologyUtils.createVariable( source, interval ); + minVal.setReal( minVal.getMinValue() ); + + openInPlace( source, interval, strel, minVal, maxVal, numThreads ); + } + + /** + * Performs the opening morphological operation, on a + * {@link RandomAccessibleInterval} using a {@link Shape} as a flat + * structuring element. + * + * See + * Opening_(morphology). + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform opening, it is sufficient that the specified max value is larger + * (against {@link Comparable}) than any of the value found in the source + * image, and conversely for the min value. These normally unseen parameters + * are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends Comparable}. + */ + public static < T extends Type< T > & Comparable< T >> void openInPlace( final RandomAccessibleInterval< T > source, final Interval interval, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + final ExtendedRandomAccessibleInterval< T, RandomAccessibleInterval< T >> extended = Views.extendValue( source, maxVal ); + + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, maxVal ); + final Img< T > img = factory.create( interval, maxVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + open( extended, translated, strel, minVal, maxVal, numThreads ); + MorphologyUtils.copy( translated, extended, numThreads ); + } + + /** + * Private constructor. Unused. + */ + private Opening() + {} +} diff --git a/src/main/java/varun_algorithm_morphology/StructuringElements.java b/src/main/java/varun_algorithm_morphology/StructuringElements.java new file mode 100644 index 0000000..05f75fc --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/StructuringElements.java @@ -0,0 +1,500 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + +import java.util.ArrayList; +import java.util.List; + +import varun_algorithm_neighborhood.CenteredRectangleShape; +import varun_algorithm_neighborhood.DiamondTipsShape; +import varun_algorithm_neighborhood.HorizontalLineShape; +import varun_algorithm_neighborhood.HyperSphereShape; +import varun_algorithm_neighborhood.PeriodicLineShape; +import varun_algorithm_neighborhood.RectangleShape; +import varun_algorithm_neighborhood.Shape; + +/** + * A collection of static utilities to facilitate the creation of morphological + * structuring elements. + * + * @author Jean-Yves Tinevez Sep - Nov 2013 + */ +public class StructuringElements +{ + + /** + * Radius above which it is advantageous in 2D for the diamond + * structuring element to be decomposed in a sequence of small + * {@link DiamondTipsShape}s rather than in a single, large + * {@link DiamondShape}. + */ + private static final int HEURISTICS_DIAMOND_RADIUS_2D = 4; + + /** + * Radius above which it is advantageous for the diamond structuring element + * to be decomposed in a sequence of small {@link DiamondTipsShape}s rather + * than in a single, large {@link DiamondShape}. + */ + private static final int HEURISTICS_DIAMOND_RADIUS_OTHERSD = 2; + + /* + * METHODS + */ + + /** + * Generates a centered disk flat structuring element for morphological + * operations. + *

      + * The structuring element (strel) is returned as a {@link List} of + * {@link Shape}s, for structuring elements can be decomposed to yield a + * better performance. In 2D, the disk strel can be + * approximated by several periodic lines. The resulting strel is + * only an approximation of a disk, and this method offers a parameter to + * select the level of approximation. For other dimensionalities, no + * optimization are available yet and the parameter is ignored. + *

      + * This methods relies on heuristics to determine automatically what + * decomposition level to use. + * + * @param radius + * the radius of the disk, so that it extends over + * {@code 2 × radius + 1} in all dimensions + * @param dimensionality + * the dimensionality of the target problem. + * @return a disk structuring element as a new list of {@link Shape}s. + */ + public static final List< Shape > disk( final long radius, final int dimensionality ) + { + final int decomposition; + /* + * My great heuristics, "determined experimentally". I choose the non-0 + * (expect for small radius) decomposition that was giving the most + * resembling disk shape. + */ + if ( dimensionality == 2 ) + { + if ( radius < 4 ) + { + decomposition = 0; + } + else if ( radius < 9 ) + { + decomposition = 4; + } + else if ( radius < 12 ) + { + decomposition = 6; + } + else if ( radius < 17 ) + { + decomposition = 8; + } + else + { + decomposition = 6; + } + } + else + { + decomposition = 0; + } + return disk( radius, dimensionality, decomposition ); + } + + /** + * Generates a centered disk flat structuring element for morphological + * operations. + *

      + * The structuring element (strel) is returned as a {@link List} of + * {@link Shape}s, for structuring elements can be decomposed to yield a + * better performance. In 2D, the disk strel can be + * approximated by several periodic lines. The resulting strel is + * only an approximation of a disk, and this method offers a parameter to + * select the level of approximation. For other dimensionalities, no + * optimization are available yet and the parameter is ignored. + * + * @param radius + * the radius of the disk, so that it extends over + * {@code 2 × radius + 1} in all dimensions + * @param dimensionality + * the dimensionality of the target problem. + * @param decomposition + * the decomposition to use. Only values 0, 4, 6 and 8 are + * accepted: + *

        + *
      1. No approximation is made and a full dimension-generic disk + * is returned.
      2. + *
      3. The disk is decomposed in 4 periodic lines, plus + * in some cases 2 horizontal lines.
      4. + *
      5. The disk is decomposed in 6 periodic lines, plus + * in some cases 2 horizontal lines.
      6. + *
      7. The disk is decomposed in 8 periodic lines, plus + * in some cases 2 horizontal lines.
      8. + *
      + * This parameter is ignored for dimensionality other than 2. + * @return a disk structuring element as a new list of {@link Shape}s. + */ + public static final List< Shape > disk( final long radius, final int dimensionality, final int decomposition ) + { + if ( dimensionality == 2 ) + { + + if ( decomposition == 0 ) + { + /* + * No approximation + */ + final List< Shape > strel = new ArrayList< Shape >( 1 ); + strel.add( new HyperSphereShape( radius ) ); + return strel; + } + else if ( decomposition == 8 || decomposition == 4 || decomposition == 6 ) + { + /* + * Rolf Adams, "Radial Decomposition of Discs and Spheres," + * CVGIP: Graphical Models and Image Processing, vol. 55, no. 5, + * September 1993, pp. 325-332. + */ + + final List< int[] > vectors = new ArrayList< int[] >( decomposition ); + switch ( decomposition ) + { + case 4: + { + vectors.add( new int[] { 1, 0 } ); // 0º + vectors.add( new int[] { 1, 1 } ); // 45º + vectors.add( new int[] { 0, 1 } ); // 90º + vectors.add( new int[] { -1, 1 } ); // 135º + break; + } + case 6: + { + vectors.add( new int[] { 1, 0 } ); // 0º + vectors.add( new int[] { 2, 1 } ); // 60º + vectors.add( new int[] { 1, 2 } ); // 30º + vectors.add( new int[] { 0, 1 } ); // 90º + vectors.add( new int[] { -1, 2 } ); // 120º + vectors.add( new int[] { -2, 1 } ); // 150º + break; + } + case 8: + { + vectors.add( new int[] { 1, 0 } ); // 0º + vectors.add( new int[] { 2, 1 } ); // 60º + vectors.add( new int[] { 1, 1 } ); // 45º + vectors.add( new int[] { 1, 2 } ); // 30º + vectors.add( new int[] { 0, 1 } ); // 90º + vectors.add( new int[] { -1, 2 } ); // 120º + vectors.add( new int[] { -1, 1 } ); // 135º + vectors.add( new int[] { -2, 1 } ); // 150º + break; + } + default: + throw new IllegalArgumentException( "The decomposition number must be 0, 4, 6 or 8. Got " + decomposition + "." ); + } + + final double theta = Math.PI / ( 2 * decomposition ); + final double radialExtent = 2 * radius / ( 1 / Math.tan( theta ) + 1 / Math.sin( theta ) ); + final List< Shape > lines = new ArrayList< Shape >( decomposition + 2 ); + + long actualRadius = 0; + for ( final int[] vector : vectors ) + { + final double norm = Math.sqrt( vector[ 0 ] * vector[ 0 ] + vector[ 1 ] * vector[ 1 ] ); + final long span = ( long ) Math.floor( radialExtent / norm ); + lines.add( new PeriodicLineShape( span, vector ) ); + /* + * This estimates the actual radius of the final strel. + * Because of the digitization on a grid (we used floor() + * above), it will be smaller than the desired radius. + */ + actualRadius += span * Math.abs( vector[ 0 ] ); + } + + /* + * Compensate for the actual strel being too small + */ + if ( actualRadius < radius ) + { + final long dif = radius - actualRadius; + lines.add( new HorizontalLineShape( dif, 0, false ) ); + lines.add( new HorizontalLineShape( dif, 1, false ) ); + } + + return lines; + } + else + { + throw new IllegalArgumentException( "The decomposition number must be 0, 4, 6 or 8. Got " + decomposition + "." ); + } + + } + else + { + /* + * All other dims + */ + final List< Shape > strel = new ArrayList< Shape >( 1 ); + strel.add( new HyperSphereShape( radius ) ); + return strel; + } + + } + + /** + * Generates a centered square flat structuring element for morphological + * operations. + *

      + * This method specify the square size using its radius to comply to + * sibling methods. The extend of the generated square is + * {@code 2 × radius + 1} in all dimensions. + *

      + * The structuring element (strel) is returned as a {@link List} of + * {@link Shape}s, for Structuring elements can be decomposed to yield a + * better performance. The square strel can be decomposed in a succession of + * orthogonal lines and yield the exact same results on any of the + * morphological operations. Because the decomposition becomes + * dimension-specific, the dimensionality of the target problem must be + * specified. Warning: Undesired effects will occur if the specified + * dimensionality and target dimensionality do not match. Non-decomposed + * version are dimension-generic. + * + * @param radius + * the radius of the square. + * @param dimensionality + * the dimensionality of the target problem. + * @param decompose + * if {@code true}, the structuring element will be + * optimized through decomposition. + * @return a new structuring element, as a list of {@link Shape}s. + */ + public static final List< Shape > square( final int radius, final int dimensionality, final boolean decompose ) + { + if ( decompose ) + { + final List< Shape > strels = new ArrayList< Shape >( dimensionality ); + for ( int d = 0; d < dimensionality; d++ ) + { + strels.add( new HorizontalLineShape( radius, d, false ) ); + } + return strels; + } + else + { + final List< Shape > strel = new ArrayList< Shape >( 1 ); + strel.add( new RectangleShape( radius, false ) ); + return strel; + } + } + + /** + * Generates a centered square flat structuring element for morphological + * operations. + *

      + * This method specify the square size using its radius to comply to + * sibling methods. The extend of the generated square is + * {@code 2 × radius + 1} in all dimensions. + *

      + * The structuring element (strel) is returned as a {@link List} of + * {@link Shape}s, for Structuring elements can be decomposed to yield a + * better performance. The square strel can be decomposed in a succession of + * orthogonal lines and yield the exact same results on any of the + * morphological operations. Because the decomposition becomes + * dimension-specific, the dimensionality of the target problem must be + * specified. Warning: Undesired effects will occur if the specified + * dimensionality and target dimensionality do not match. Non-decomposed + * version are dimension-generic. + *

      + * This method determines whether it is worth returning a decomposed strel + * based on simple heuristics. + * + * @param radius + * the radius of the square. + * @param dimensionality + * the dimensionality of the target problem. + * @return a new structuring element, as a list of {@link Shape}s. + */ + public static final List< Shape > square( final int radius, final int dimensionality ) + { + /* + * I borrow this "heuristic" to decide whether or not we should + * decompose to MATLAB: If the number of neighborhood we get by + * decomposing is more than half of what we get without decomposition, + * then it is not worth doing decomposition. + */ + final long decomposedNNeighbohoods = dimensionality * ( 2 * radius + 1 ); + final long fullNNeighbohoods = ( long ) Math.pow( 2 * radius + 1, dimensionality ); + final boolean decompose = ( decomposedNNeighbohoods < fullNNeighbohoods / 2 ); + return square( radius, dimensionality, decompose ); + } + + /** + * Generates a symmetric, centered, rectangular flat structuring element for + * morphological operations. + *

      + * The structuring element (strel) is returned as a {@link List} of + * {@link Shape}s, for Structuring elements can be decomposed to yield a + * better performance. The rectangle strel can be decomposed in a succession + * of orthogonal lines and yield the exact same results on any of the + * morphological operations. + * + * @param halfSpans + * an {@code int[]} array containing the half-span of the + * symmetric rectangle in each dimension. The total extent of the + * rectangle will therefore be {@code 2 × halfSpan[d] + 1} + * in each dimension. + * @param decompose + * if {@code true}, the strel will be returned as a + * {@link List} of {@link HorizontalLineShape}, indeed performing + * the rectangle decomposition. If {@code false}, the list + * will be made of a single {@link CenteredRectangleShape}. + * @return the desired structuring element, as a {@link List} of + * {@link Shape}s. + */ + public static final List< Shape > rectangle( final int[] halfSpans, final boolean decompose ) + { + final List< Shape > strels; + if ( decompose ) + { + strels = new ArrayList< Shape >( halfSpans.length ); + for ( int d = 0; d < halfSpans.length; d++ ) + { + int r = halfSpans[ d ]; + r = Math.max( 0, r ); + if ( r == 0 ) + { // No need for empty lines + continue; + } + final HorizontalLineShape line = new HorizontalLineShape( r, d, false ); + strels.add( line ); + } + } + else + { + + strels = new ArrayList< Shape >( 1 ); + final CenteredRectangleShape square = new CenteredRectangleShape( halfSpans, false ); + strels.add( square ); + + } + return strels; + } + + /** + * Generates a symmetric, centered, rectangular flat structuring element for + * morphological operations. + *

      + * The structuring element (strel) is returned as a {@link List} of + * {@link Shape}s, for Structuring elements can be decomposed to yield a + * better performance. The rectangle strel can be decomposed in a succession + * of orthogonal lines and yield the exact same results on any of the + * morphological operations. This method uses a simple heuristic to decide + * whether to decompose the rectangle or not. + * + * @param halfSpans + * an {@code int[]} array containing the half-span of the + * symmetric rectangle in each dimension. The total extent of the + * rectangle will therefore be {@code 2 × halfSpan[d] + 1} + * in each dimension. + * @return the desired structuring element, as a {@link List} of + * {@link Shape}s. + */ + public static final List< Shape > rectangle( final int halfSpans[] ) + { + /* + * I borrow this "heuristic" to decide whether or not we should + * decompose to MATLAB: If the number of neighborhood we get by + * decomposing is more than half of what we get without decomposition, + * then it is not worth doing decomposition. + */ + long decomposedNNeighbohoods = 0; + long fullNNeighbohoods = 1; + for ( int i = 0; i < halfSpans.length; i++ ) + { + final int l = 2 * halfSpans[ i ] + 1; + decomposedNNeighbohoods += l; + fullNNeighbohoods *= l; + } + + if ( decomposedNNeighbohoods > fullNNeighbohoods / 2 ) + { + // Do not optimize + return rectangle( halfSpans, false ); + } + else + { + // Optimize + return rectangle( halfSpans, true ); + } + } + + + + + + /** + * Creates a new periodic line structuring element, that will iterate over + * {@code 2 × span + 1} pixels as follow: + * + *

      +	 * position - span x increments,
      +	 * ...
      +	 * position - 2 × increments,
      +	 * position - increments,
      +	 * position,
      +	 * position + increments,
      +	 * position + 2 × increments,
      +	 * ...
      +	 * position + span x increments
      +	 * 
      + * + * The importance of periodic lines is explained in [1]. + * + * @param span + * the span of the neighborhood, so that it will iterate over + * {@code 2 × span + 1} pixels. + * @param increments + * the values by which each element of the position vector is to + * be incremented when iterating. + * @see [1] + * Jones and Soilles.Periodic lines: Definition, cascades, and + * application to granulometries. Pattern Recognition Letters (1996) + * vol. 17 (10) pp. 1057-1063. + */ + public static final Shape periodicLine( final long span, final int[] increments ) + { + return new PeriodicLineShape( span, increments ); + } +} diff --git a/src/main/java/varun_algorithm_morphology/TopHat.java b/src/main/java/varun_algorithm_morphology/TopHat.java new file mode 100644 index 0000000..6fdb639 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/TopHat.java @@ -0,0 +1,646 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology; + + +import java.util.List; + +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.operators.Sub; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Shape; + +/** + * White top-hat transform for ImgLib2. + * + * See Top-hat + * transform. + * + * @author Jean-Yves Tinevez - 2014 + */ +public class TopHat +{ + /** + * Performs the top-hat (white top-hat) morphological operation, on a + * {@link RealType} {@link Img} using a list of {@link Shape}s as a flat + * structuring element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > topHat( final Img< T > source, final List< Shape > strels, final int numThreads ) + { + if ( strels.isEmpty() ) { return source; } + final Img< T > opened = Opening.open( source, strels, numThreads ); + MorphologyUtils.subABA( opened, source, numThreads ); + return opened; + } + + /** + * Performs the top-hat (white top-hat) morphological operation, using a + * list of {@link Shape}s as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is returned. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Type}. + * + * @param source + * the source image. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, + * because we want to be able to compare pixels between + * themselves and to subtract them. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T > > Img< T > topHat( final Img< T > source, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > opened = Opening.open( source, strels, minVal, maxVal, numThreads ); + MorphologyUtils.subABA( opened, source, numThreads ); + return opened; + } + + /** + * Performs the top-hat (white top-hat) morphological operation, on a + * {@link RealType} {@link Img} using a {@link Shape} as a flat structuring + * element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends RealType}. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends RealType< T >> Img< T > topHat( final Img< T > source, final Shape strel, final int numThreads ) + { + final Img< T > opened = Opening.open( source, strel, numThreads ); + MorphologyUtils.subABA( opened, source, numThreads ); + return opened; + } + + /** + * Performs the top-hat (white top-hat) morphological operation, using a + * {@link Shape} as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result image has the same dimensions that of the source image. It is + * limited to flat structuring elements, only having {@code on/off} + * pixels, contrary to grayscale structuring elements. This allows to simply + * use a {@link Shape} as a type for these structuring elements. + *

      + * This method relies on a specified minimal and maximal value to start + * comparing to other pixels in the neighborhood. For this code to perform + * properly, it is sufficient that the specified min value is smaller + * (against {@link Comparable}) than any of the value found in the source + * image, and the converse for the max value. These normally unseen + * parameters are required to operate on + * {@code T extends Comparable & Sub}. + * + * @param source + * the source image. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source {@link Img} (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source {@link Img} (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, + * because we want to be able to compare pixels between + * themselves and to subtract them. + * @return a new {@link Img}, of same dimensions than the source. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T > > Img< T > topHat( final Img< T > source, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + final Img< T > opened = Opening.open( source, strel, minVal, maxVal, numThreads ); + MorphologyUtils.subABA( opened, source, numThreads ); + return opened; + } + + /** + * Performs the top-hat (white top-hat) morphological operation on a + * {@link RealType} source {@link RandomAccessible}, using a list of + * {@link Shape}s as a structuring element, and writes the result on a + * specified target which must be an {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target receives a copy of the source. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T >> void topHat( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final int numThreads ) + { + Opening.open( source, target, strels, numThreads ); + MorphologyUtils.subBAB( source, target, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation on a source + * {@link RandomAccessible}, using a list of {@link Shape}s as a structuring + * element, and writes the result on a specified target which must be an + * {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the target receives a copy of the source. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on + * {@code T extends Comparable & Sub}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strels + * the list of {@link Shape}s that serves as a structuring + * element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, + * because we want to be able to compare pixels between + * themselves and to subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void topHat( final RandomAccessible< T > source, final IterableInterval< T > target, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + Opening.open( source, target, strels, minVal, maxVal, numThreads ); + MorphologyUtils.subBAB( source, target, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation on a + * {@link RealType} source {@link RandomAccessible}, using a {@link Shape} + * as a structuring element, and writes the result on a specified target + * which must be an {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source and the result. Must extends + * {@link RealType}. + */ + public static < T extends RealType< T >> void topHat( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final int numThreads ) + { + Opening.open( source, target, strel, numThreads ); + MorphologyUtils.subBAB( source, target, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation on a source + * {@link RandomAccessible}, using a {@link Shape} as a structuring element, + * and writes the result on a specified target which must be an + * {@link IterableInterval}. + * + * See Top-hat + * transform. + *

      + * Careful: Target must point to a different structure than source. + * In place operation will not work but will not generate an error. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * It is limited to flat structuring elements, only having + * {@code on/off} pixels, contrary to grayscale structuring elements. + * This allows to simply use a {@link Shape} as a type for these structuring + * elements. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on + * {@code T extends Comparable & Sub}. + * + * @param source + * the {@link RandomAccessible} to operate on. + * @param target + * the {@link IterableInterval} to write the results on. + * @param strel + * the {@link Shape} that serves as a structuring element. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, + * because we want to be able to compare pixels between + * themselves and to subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void topHat( final RandomAccessible< T > source, final IterableInterval< T > target, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + Opening.open( source, target, strel, minVal, maxVal, numThreads ); + MorphologyUtils.subBAB( source, target, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation, on a + * {@link RealType} {@link Img} using a list of {@link Shape}s as a flat + * structuring element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)}. + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is left untouched. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T >> void topHatInPlace( final RandomAccessible< T > source, final Interval interval, final List< Shape > strels, final int numThreads ) + { + // Prepare tmp holder + final T minVal = MorphologyUtils.createVariable( source, interval ); + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Opening.open( source, translated, strels, numThreads ); + MorphologyUtils.subABA2( translated, source, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation, using a + * list of {@link Shape}s as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * The structuring element is specified through a list of {@link Shape}s, to + * allow for performance optimization through structuring element + * decomposition. Each shape is processed in order as given in the list. If + * the list is empty, the source image is left untouched. + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on + * {@code T extends Comparable & Sub}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strels + * the structuring element as a list of {@link Shape}s. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, + * because we want to be able to compare pixels between + * themselves and to subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void topHatInPlace( final RandomAccessible< T > source, final Interval interval, final List< Shape > strels, final T minVal, final T maxVal, final int numThreads ) + { + // Prepare tmp holder + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Opening.open( source, translated, strels, minVal, maxVal, numThreads ); + MorphologyUtils.subABA2( translated, source, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation, on a + * {@link RealType} {@link Img} using a {@link Shape} as a flat structuring + * element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image. Must be a sub-type of + * {@code T extends RealType}. + */ + public static < T extends RealType< T >> void topHatInPlace( final RandomAccessible< T > source, final Interval interval, final Shape strel, final int numThreads ) + { + // Prepare tmp holder + final T minVal = MorphologyUtils.createVariable( source, interval ); + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Opening.open( source, translated, strel, numThreads ); + MorphologyUtils.subABA2( translated, source, numThreads ); + } + + /** + * Performs the top-hat (white top-hat) morphological operation, using a + * {@link Shape} as a flat structuring element. + * + * See Top-hat + * transform. + *

      + * The result is written in the source image. This method is limited to flat + * structuring elements, only having {@code on/off} pixels, contrary to + * grayscale structuring elements. This allows to simply use a {@link Shape} + * as a type for these structuring elements. + *

      + * It is the caller responsibility to ensure that the source is sufficiently + * padded to properly cover the target range plus the shape size. See + * e.g. {@link Views#extendValue(RandomAccessibleInterval, Type)} + *

      + * This method relies on specified minimal and maximal values to start + * comparing to other pixels in the neighborhood. For this code to properly + * perform, it is sufficient that the specified max value is larger (against + * {@link Comparable}) than any of the value found in the source image, and + * conversely for the min value. These normally unseen parameters are + * required to operate on + * {@code T extends Comparable & Sub}. + * + * @param source + * the source image. + * @param interval + * an interval in the source image to process. + * @param strel + * the structuring element as a {@link Shape}. + * @param minVal + * a T containing set to a value smaller than any of the values + * in the source (against {@link Comparable}). + * @param maxVal + * a T containing set to a value larger than any of the values in + * the source (against {@link Comparable}). + * @param numThreads + * the number of threads to use for the calculation. + * @param + * the type of the source image and the top-hat result. Must be a + * sub-type of {@code T extends Comparable & Sub}, + * because we want to be able to compare pixels between + * themselves and to subtract them. + */ + public static < T extends Type< T > & Comparable< T > & Sub< T >> void topHatInPlace( final RandomAccessible< T > source, final Interval interval, final Shape strel, final T minVal, final T maxVal, final int numThreads ) + { + // Prepare tmp holder + final ImgFactory< T > factory = MorphologyUtils.getSuitableFactory( interval, minVal ); + final Img< T > img = factory.create( interval, minVal ); + final long[] min = new long[ interval.numDimensions() ]; + interval.min( min ); + final IntervalView< T > translated = Views.translate( img, min ); + + Opening.open( source, translated, strel, minVal, maxVal, numThreads ); + MorphologyUtils.subABA2( translated, source, numThreads ); + } + + + /** + * Private default constructor. + */ + private TopHat() + {} + +} diff --git a/src/main/java/varun_algorithm_morphology/distance/Distance.java b/src/main/java/varun_algorithm_morphology/distance/Distance.java new file mode 100644 index 0000000..e9b2a26 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/distance/Distance.java @@ -0,0 +1,80 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_morphology.distance; + + +/** + * Family of strictly convex, real valued functions that are separable in all + * dimension. The interface thus specifies just a one-dimensional function that + * is parameterized by an offset along both the x- and the y-axis. These + * parameters are passed at evaluation along with the dimension in which the + * function is to be evaluated. + *

      + * Two distinct members of the same family, {@code d = f(x)} and + * {@code d' = f(x - x0) + y0}, must have exactly one intersection point (for + * each dimension): + *

      + * + *
      + * |{ x : f(x) = f(x -x0) + y0 }| = 1
      + * 
      + *

      + * This interface is used in {@link DistanceTransform}: + *

      + *

      + * {@code D( p ) = min_q f(q) + d(p,q)} where p,q are points on a grid/image. + *

      + * + * @author Philipp Hanslovsky + * + */ +public interface Distance +{ + + /** + * Evaluate function with family parameters xShift and yShift at position x + * in dimension dim. + */ + double evaluate( double x, double xShift, double yShift, int dim ); + + /** + * + * Determine the intersection point in dimension dim of two members of the + * function family. The members are parameterized by xShift1, yShift1, + * xShift2, yShift2, with xShift1 < xShift2. + */ + double intersect( double xShift1, double yShift1, double xShift2, double yShift2, int dim ); + +} diff --git a/src/main/java/varun_algorithm_morphology/distance/DistanceTransform.java b/src/main/java/varun_algorithm_morphology/distance/DistanceTransform.java new file mode 100644 index 0000000..4997a71 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/distance/DistanceTransform.java @@ -0,0 +1,802 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_morphology.distance; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.stream.DoubleStream; +import java.util.stream.IntStream; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.img.Img; +import net.imglib2.img.array.ArrayImg; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.cell.CellImg; +import net.imglib2.img.cell.CellImgFactory; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.integer.LongType; +import net.imglib2.type.numeric.real.DoubleType; +import net.imglib2.util.Intervals; +import net.imglib2.util.ValuePair; +import net.imglib2.view.Views; +import net.imglib2.view.composite.RealComposite; + +/** + * ImgLib2 implementation of n-dimensional distance transform D of sampled + * functions f with distance measure d: + * Distance + * Transforms of Sampled Functions (DOI:ch 10.4086/toc.2012.v008a019). + *

      + * {@code D( p ) = min_q f(q) + d(p,q)} where p,q are points on a grid/image. + *

      + *

      + * The implemented algorithm has complexity O(dn) where d is the number of + * dimensions of the image, and n is the total number of pixels/voxels. + *

      + * + * @author Philipp Hanslovsky + */ +public class DistanceTransform +{ + + /** + * + * Switch for calling convenience method with pre-defined distances. + * + */ + public static enum DISTANCE_TYPE + { + EUCLIDIAN, + L1 + } + + /** + * Create distance transform of source using Euclidian (L2) or L1 distance. + * Intermediate and final results will be stored in source (@{link + * DoubleType} recommended). The distance can be weighted (individually for + * each dimension, if desired) against the image values via the weights + * parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param distanceType + * Defines distance to be used: Euclidian or L1 + * @param weights + * Individual weights for each dimension, balancing image values + * and Euclidian distance. + */ + public static < T extends RealType< T > > void transform( + final RandomAccessibleInterval< T > source, + final DISTANCE_TYPE distanceType, + final double... weights ) + { + transform( source, source, distanceType, weights ); + } + + /** + * Create distance transform of source using Euclidian (L2) or L1 distance. + * Intermediate and final results will be stored in source (@{link + * DoubleType} recommended). The distance can be weighted (individually for + * each dimension, if desired) against the image values via the weights + * parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param distanceType + * Defines distance to be used: Euclidian or L1 + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + * @param weights + * Individual weights for each dimension, balancing image values + * and Euclidian distance. + * @throws InterruptedException + * @throws ExecutionException + */ + public static < T extends RealType< T > > void transform( + final RandomAccessibleInterval< T > source, + final DISTANCE_TYPE distanceType, + final ExecutorService es, + final int nTasks, + final double... weights ) throws InterruptedException, ExecutionException + { + transform( source, source, distanceType, es, nTasks, weights ); + } + + /** + * Create distance transform of source using Euclidian (L2) or L1 distance. + * Intermediate results will be stored in target (@{link DoubleType} + * recommended). The distance can be weighted (individually for each + * dimension, if desired) against the image values via the weights + * parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param target + * Intermediate and final results of distance transform. + * @param distanceType + * Defines distance to be used: Euclidian or L1 + * @param weights + * Individual weights for each dimension, balancing image values + * and Euclidian distance. + */ + public static < T extends RealType< T >, U extends RealType< U > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final DISTANCE_TYPE distanceType, + final double... weights ) + { + transform( source, target, target, distanceType, weights ); + } + + /** + * Create distance transform of source using Euclidian (L2) or L1 distance. + * Intermediate results will be stored in target (@{link DoubleType} + * recommended). The distance can be weighted (individually for each + * dimension, if desired) against the image values via the weights + * parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param target + * Intermediate and final results of distance transform. + * @param distanceType + * Defines distance to be used: Euclidian or L1 + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + * @param weights + * Individual weights for each dimension, balancing image values + * and Euclidian distance. + * @throws InterruptedException + * @throws ExecutionException + */ + public static < T extends RealType< T >, U extends RealType< U > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final DISTANCE_TYPE distanceType, + final ExecutorService es, + final int nTasks, + final double... weights ) throws InterruptedException, ExecutionException + { + transform( source, target, target, distanceType, es, nTasks, weights ); + } + + /** + * Create distance transform of source using Euclidian (L2) or L1 distance. + * Intermediate results will be stored in tmp (@{link DoubleType} + * recommended). The output will be written into target. The distance can be + * weighted (individually for each dimension, if desired) against the image + * values via the weights parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param tmp + * Storage for intermediate results. + * @param target + * Final result of distance transform. + * @param distanceType + * Defines distance to be used: Euclidian or L1 + * @param weights + * Individual weights for each dimension, balancing image values + * and Euclidian distance. + */ + public static < T extends RealType< T >, U extends RealType< U >, V extends RealType< V > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > tmp, + final RandomAccessibleInterval< V > target, + final DISTANCE_TYPE distanceType, + final double... weights ) + { + + final boolean isIsotropic = weights.length <= 1; + final double[] w = weights.length == source.numDimensions() ? weights : DoubleStream.generate( () -> weights.length == 0 ? 1.0 : weights[ 0 ] ).limit( source.numDimensions() ).toArray(); + + switch ( distanceType ) + { + case EUCLIDIAN: + transform( source, tmp, target, isIsotropic ? new EuclidianDistanceIsotropic( w[ 0 ] ) : new EuclidianDistanceAnisotropic( w ) ); + break; + case L1: + transformL1( source, tmp, target, w ); + break; + default: + break; + } + } + + /** + * Create distance transform of source using Euclidian (L2) or L1 distance. + * Intermediate results will be stored in tmp (@{link DoubleType} + * recommended). The output will be written into target. The distance can be + * weighted (individually for each dimension, if desired) against the image + * values via the weights parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param tmp + * Storage for intermediate results. + * @param target + * Final result of distance transform. + * @param distanceType + * Defines distance to be used: Euclidian or L1 + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + * @param weights + * Individual weights for each dimension, balancing image values + * and Euclidian distance. + * @throws InterruptedException + * @throws ExecutionException + */ + public static < T extends RealType< T >, U extends RealType< U >, V extends RealType< V > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > tmp, + final RandomAccessibleInterval< V > target, + final DISTANCE_TYPE distanceType, + final ExecutorService es, + final int nTasks, + final double... weights ) throws InterruptedException, ExecutionException + { + + final boolean isIsotropic = weights.length <= 1; + final double[] w = weights.length == source.numDimensions() ? weights : DoubleStream.generate( () -> weights.length == 0 ? 1.0 : weights[ 0 ] ).limit( source.numDimensions() ).toArray(); + + switch ( distanceType ) + { + case EUCLIDIAN: + transform( source, tmp, target, isIsotropic ? new EuclidianDistanceIsotropic( w[ 0 ] ) : new EuclidianDistanceAnisotropic( w ), es, nTasks ); + break; + case L1: + transformL1( source, tmp, target, es, nTasks, w ); + break; + default: + break; + } + } + + /** + * Create distance transform of source using arbitrary {@link Distance} d. + * Intermediate and final results will be stored in source (@{link + * DoubleType} recommended). + * + * @param source + * Input function on which distance transform should be computed. + * @param d + * {@link Distance} between two points. + */ + public static < T extends RealType< T > > void transform( + final RandomAccessibleInterval< T > source, + final Distance d ) + { + transform( source, source, d ); + } + + /** + * Create distance transform of source using arbitrary {@link Distance} d. + * Intermediate and final results will be stored in source (@{link + * DoubleType} recommended). + * + * @param source + * Input function on which distance transform should be computed. + * @param d + * {@link Distance} between two points. + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + * @throws InterruptedException + * @throws ExecutionException + */ + public static < T extends RealType< T > > void transform( + final RandomAccessibleInterval< T > source, + final Distance d, + final ExecutorService es, + final int nTasks ) throws InterruptedException, ExecutionException + { + transform( source, source, d, es, nTasks ); + } + + /** + * Create distance transform of source using arbitrary {@link Distance} d. + * Intermediate and final results will be stored in target (@{link + * DoubleType} recommended). + * + * @param source + * Input function on which distance transform should be computed. + * @param target + * Final result of distance transform. + * @param d + * {@link Distance} between two points. + */ + public static < T extends RealType< T >, U extends RealType< U > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final Distance d ) + { + transform( source, target, target, d ); + } + + /** + * Create distance transform of source using arbitrary {@link Distance} d. + * Intermediate and final results will be stored in target (@{link + * DoubleType} recommended). + * + * @param source + * Input function on which distance transform should be computed. + * @param target + * Final result of distance transform. + * @param d + * {@link Distance} between two points. + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + */ + public static < T extends RealType< T >, U extends RealType< U > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final Distance d, + final ExecutorService es, + final int nTasks ) throws InterruptedException, ExecutionException + { + transform( source, target, target, d, es, nTasks ); + } + + /** + * Create distance transform of source using arbitrary {@link Distance} d. + * Intermediate results will be stored in tmp (@{link DoubleType} + * recommended). The output will be written into target. + * + * @param source + * Input function on which distance transform should be computed. + * @param tmp + * Storage for intermediate results. + * @param target + * Final result of distance transform. + * @param d + * {@link Distance} between two points. + */ + public static < T extends RealType< T >, U extends RealType< U >, V extends RealType< V > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > tmp, + final RandomAccessibleInterval< V > target, + final Distance d ) + { + + assert source.numDimensions() == target.numDimensions(): "Dimension mismatch"; + final int nDim = source.numDimensions(); + final int lastDim = nDim - 1; + + if ( nDim == 1 ) + transformAlongDimension( + ( RandomAccessible< T > ) Views.addDimension( source ), + Views.interval( Views.addDimension( target ), new FinalInterval( target.dimension( 0 ), 1 ) ), + d, + 0 ); + else + transformAlongDimension( source, tmp, d, 0 ); + + for ( int dim = 1; dim < nDim; ++dim ) + if ( dim == lastDim ) + transformAlongDimension( tmp, target, d, dim ); + else + transformAlongDimension( tmp, tmp, d, dim ); + } + + /** + * Create distance transform of source using arbitrary {@link Distance} d. + * Intermediate results will be stored in tmp (@{link DoubleType} + * recommended). The output will be written into target. + * + * @param source + * Input function on which distance transform should be computed. + * @param tmp + * Storage for intermediate results. + * @param target + * Final result of distance transform. + * @param d + * {@link Distance} between two points. + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + * @throws InterruptedException + * @throws ExecutionException + */ + public static < T extends RealType< T >, U extends RealType< U >, V extends RealType< V > > void transform( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > tmp, + final RandomAccessibleInterval< V > target, + final Distance d, + final ExecutorService es, + final int nTasks ) throws InterruptedException, ExecutionException + { + + assert source.numDimensions() == target.numDimensions(): "Dimension mismatch"; + final int nDim = source.numDimensions(); + final int lastDim = nDim - 1; + + if ( nDim == 1 ) + transformAlongDimensionParallel( + ( RandomAccessible< T > ) Views.addDimension( source ), + Views.interval( Views.addDimension( target ), new FinalInterval( target.dimension( 0 ), 1 ) ), + d, + 0, + es, + nTasks ); + else + transformAlongDimensionParallel( source, tmp, d, 0, es, nTasks ); + + for ( int dim = 1; dim < nDim; ++dim ) + if ( dim == lastDim ) + transformAlongDimensionParallel( tmp, target, d, dim, es, nTasks ); + else + transformAlongDimensionParallel( tmp, tmp, d, dim, es, nTasks ); + } + + /** + * Create distance transform of source using L1 distance. Intermediate + * results will be stored in tmp (@{link DoubleType} recommended). The + * output will be written into target. The distance can be weighted + * (individually for each dimension, if desired) against the image values + * via the weights parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param tmp + * Storage for intermediate results. + * @param target + * Final result of distance transform. + * @param weights + * Individual weights for each dimension, balancing image values + * and L1 distance. + */ + private static < T extends RealType< T >, U extends RealType< U >, V extends RealType< V > > void transformL1( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > tmp, + final RandomAccessibleInterval< V > target, + final double... weights ) + { + assert source.numDimensions() == target.numDimensions(): "Dimension mismatch"; + final int nDim = source.numDimensions(); + final int lastDim = nDim - 1; + + if ( nDim == 1 ) + transformL1AlongDimension( + ( RandomAccessible< T > ) Views.addDimension( source ), + Views.interval( Views.addDimension( target ), new FinalInterval( target.dimension( 0 ), 1 ) ), + 0, + weights[ 0 ] ); + else + transformL1AlongDimension( source, tmp, 0, weights[ 0 ] ); + + for ( int dim = 1; dim < nDim; ++dim ) + if ( dim == lastDim ) + transformL1AlongDimension( tmp, target, dim, weights[ dim ] ); + else + transformL1AlongDimension( tmp, tmp, dim, weights[ dim ] ); + } + + /** + * Create distance transform of source using L1 distance. Intermediate + * results will be stored in tmp (@{link DoubleType} recommended). The + * output will be written into target. The distance can be weighted + * (individually for each dimension, if desired) against the image values + * via the weights parameter. + * + * @param source + * Input function on which distance transform should be computed. + * @param tmp + * Storage for intermediate results. + * @param target + * Final result of distance transform. + * @param es + * {@link ExecutorService} for parallel execution. + * @param nTasks + * Number of tasks/parallelism + * @param weights + * Individual weights for each dimension, balancing image values + * and L1 distance. + * @throws InterruptedException + * @throws ExecutionException + */ + private static < T extends RealType< T >, U extends RealType< U >, V extends RealType< V > > void transformL1( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > tmp, + final RandomAccessibleInterval< V > target, + final ExecutorService es, + final int nTasks, + final double... weights ) throws InterruptedException, ExecutionException + { + assert source.numDimensions() == target.numDimensions(): "Dimension mismatch"; + final int nDim = source.numDimensions(); + final int lastDim = nDim - 1; + + if ( nDim == 1 ) + transformL1AlongDimensionParallel( + ( RandomAccessible< T > ) Views.addDimension( source ), + Views.interval( Views.addDimension( target ), new FinalInterval( target.dimension( 0 ), 1 ) ), + 0, + weights[ 0 ], + es, + nTasks ); + else + transformL1AlongDimensionParallel( source, tmp, 0, weights[ 0 ], es, nTasks ); + + for ( int dim = 1; dim < nDim; ++dim ) + if ( dim == lastDim ) + transformL1AlongDimensionParallel( tmp, target, dim, weights[ dim ], es, nTasks ); + else + transformL1AlongDimensionParallel( tmp, tmp, dim, weights[ dim ], es, nTasks ); + } + + private static < T extends RealType< T >, U extends RealType< U > > void transformAlongDimension( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final Distance d, + final int dim ) + { + final int lastDim = target.numDimensions() - 1; + final long size = target.dimension( dim ); + final RealComposite< DoubleType > tmp = Views.collapseReal( createAppropriateOneDimensionalImage( size, new DoubleType() ) ).randomAccess().get(); + // do not permute if we already work on last dimension + final Cursor< RealComposite< T > > s = Views.flatIterable( Views.collapseReal( dim == lastDim ? Views.interval( source, target ) : Views.permute( Views.interval( source, target ), dim, lastDim ) ) ).cursor(); + final Cursor< RealComposite< U > > t = Views.flatIterable( Views.collapseReal( dim == lastDim ? target : Views.permute( target, dim, lastDim ) ) ).cursor(); + final RealComposite< LongType > lowerBoundDistanceIndex = Views.collapseReal( createAppropriateOneDimensionalImage( size, new LongType() ) ).randomAccess().get(); + final RealComposite< DoubleType > envelopeIntersectLocation = Views.collapseReal( createAppropriateOneDimensionalImage( size + 1, new DoubleType() ) ).randomAccess().get(); + + while ( s.hasNext() ) + { + final RealComposite< T > sourceComp = s.next(); + final RealComposite< U > targetComp = t.next(); + for ( long i = 0; i < size; ++i ) + tmp.get( i ).set( sourceComp.get( i ).getRealDouble() ); + transformSingleColumn( tmp, targetComp, lowerBoundDistanceIndex, envelopeIntersectLocation, d, dim, size ); + } + } + + private static < T extends RealType< T >, U extends RealType< U > > void transformAlongDimensionParallel( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final Distance d, + final int dim, + final ExecutorService es, + final int nTasks ) throws InterruptedException, ExecutionException + { + int largestDim = getLargestDimension( Views.hyperSlice( target, dim, target.min( dim ) ) ); + // ignore dimension along which we calculate transform + if ( largestDim >= dim ) + largestDim += 1; + final long size = target.dimension( dim ); + final long stepPerChunk = Math.max( size / nTasks, 1 ); + + final long[] min = Intervals.minAsLongArray( target ); + final long[] max = Intervals.maxAsLongArray( target ); + + final long largestDimMin = target.min( largestDim ); + final long largestDimMax = target.max( largestDim ); + + final ArrayList< Callable< Void > > tasks = new ArrayList<>(); + for ( long m = largestDimMin, M = largestDimMin + stepPerChunk - 1; m <= largestDimMax; m += stepPerChunk, M += stepPerChunk ) + { + min[ largestDim ] = m; + max[ largestDim ] = Math.min( M, largestDimMax ); + final Interval fi = new FinalInterval( min, max ); + tasks.add( () -> { + transformAlongDimension( source, Views.interval( target, fi ), d, dim ); + return null; + } ); + } + + invokeAllAndWait( es, tasks ); + } + + private static < T extends RealType< T >, U extends RealType< U > > void transformSingleColumn( + final RealComposite< T > source, + final RealComposite< U > target, + final RealComposite< LongType > lowerBoundDistanceIndex, + final RealComposite< DoubleType > envelopeIntersectLocation, + final Distance d, + final int dim, + final long size ) + { + long k = 0; + + lowerBoundDistanceIndex.get( 0 ).set( 0 ); + envelopeIntersectLocation.get( 0 ).set( Double.NEGATIVE_INFINITY ); + envelopeIntersectLocation.get( 1 ).set( Double.POSITIVE_INFINITY ); + for ( long position = 1; position < size; ++position ) + { + long envelopeIndexAtK = lowerBoundDistanceIndex.get( k ).get(); + final double sourceAtPosition = source.get( position ).getRealDouble(); + double s = d.intersect( envelopeIndexAtK, source.get( envelopeIndexAtK ).getRealDouble(), position, sourceAtPosition, dim ); + + for ( double envelopeValueAtK = envelopeIntersectLocation.get( k ).get(); s <= envelopeValueAtK; envelopeValueAtK = envelopeIntersectLocation.get( k ).get() ) + { + --k; + envelopeIndexAtK = lowerBoundDistanceIndex.get( k ).get(); + s = d.intersect( envelopeIndexAtK, source.get( envelopeIndexAtK ).getRealDouble(), position, sourceAtPosition, dim ); + } + ++k; + lowerBoundDistanceIndex.get( k ).set( position ); + envelopeIntersectLocation.get( k ).set( s ); + envelopeIntersectLocation.get( k + 1 ).set( Double.POSITIVE_INFINITY ); + } + + k = 0; + + for ( long position = 0; position < size; ++position ) + { + while ( envelopeIntersectLocation.get( k + 1 ).get() < position ) + ++k; + final long envelopeIndexAtK = lowerBoundDistanceIndex.get( k ).get(); + // copy necessary because of the following line, access to source + // after write to source -> source and target cannot be the same + target.get( position ).setReal( d.evaluate( position, envelopeIndexAtK, source.get( envelopeIndexAtK ).getRealDouble(), dim ) ); + } + + } + + private static < T extends RealType< T >, U extends RealType< U > > void transformL1AlongDimension( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final int dim, + final double weight ) + { + final int lastDim = target.numDimensions() - 1; + final long size = target.dimension( dim ); + final RealComposite< DoubleType > tmp = Views.collapseReal( createAppropriateOneDimensionalImage( size, new DoubleType() ) ).randomAccess().get(); + // do not permute if we already work on last dimension + final Cursor< RealComposite< T > > s = Views.flatIterable( Views.collapseReal( dim == lastDim ? Views.interval( source, target ) : Views.permute( Views.interval( source, target ), dim, lastDim ) ) ).cursor(); + final Cursor< RealComposite< U > > t = Views.flatIterable( Views.collapseReal( dim == lastDim ? target : Views.permute( target, dim, lastDim ) ) ).cursor(); + + while ( s.hasNext() ) + { + final RealComposite< T > sourceComp = s.next(); + final RealComposite< U > targetComp = t.next(); + for ( long i = 0; i < size; ++i ) + tmp.get( i ).set( sourceComp.get( i ).getRealDouble() ); + transformL1SingleColumn( tmp, targetComp, weight, size ); + } + } + + private static < T extends RealType< T >, U extends RealType< U > > void transformL1AlongDimensionParallel( + final RandomAccessible< T > source, + final RandomAccessibleInterval< U > target, + final int dim, + final double weight, + final ExecutorService es, + final int nTasks ) throws InterruptedException, ExecutionException + { + int largestDim = getLargestDimension( Views.hyperSlice( target, dim, target.min( dim ) ) ); + // ignore dimension along which we calculate transform + if ( largestDim >= dim ) + largestDim += 1; + final long size = target.dimension( dim ); + final long stepPerChunk = Math.max( size / nTasks, 1 ); + + final long[] min = Intervals.minAsLongArray( target ); + final long[] max = Intervals.maxAsLongArray( target ); + + final long largestDimMin = target.min( largestDim ); + final long largestDimMax = target.max( largestDim ); + + final ArrayList< Callable< Void > > tasks = new ArrayList<>(); + for ( long m = largestDimMin, M = largestDimMin + stepPerChunk - 1; m <= largestDimMax; m += stepPerChunk, M += stepPerChunk ) + { + min[ largestDim ] = m; + max[ largestDim ] = Math.min( M, largestDimMax ); + final Interval fi = new FinalInterval( min, max ); + tasks.add( () -> { + transformL1AlongDimension( source, Views.interval( target, fi ), dim, weight ); + return null; + } ); + } + + invokeAllAndWait( es, tasks ); + + } + + private static < T extends RealType< T >, U extends RealType< U > > void transformL1SingleColumn( + final RealComposite< T > source, + final RealComposite< U > target, + final double weight, + final long size ) + { + + target.get( 0 ).setReal( source.get( 0 ).getRealDouble() ); + + for ( long i = 1; i < size; ++i ) + { + final double other = target.get( i - 1 ).getRealDouble(); + target.get( i ).setReal( Math.min( source.get( i ).getRealDouble(), other + weight ) ); + } + + for ( long i = size - 2; i > -1; --i ) + { + final double other = target.get( i + 1 ).getRealDouble(); + final U t = target.get( i ); + t.setReal( Math.min( t.getRealDouble(), other + weight ) ); + } + + } + + /** + * Convenience method to invoke all tasks with a given + * {@link ExecutorService}. + */ + private static < T > void invokeAllAndWait( final ExecutorService es, final Collection< Callable< T > > tasks ) throws InterruptedException, ExecutionException + { + final List< Future< T > > futures = es.invokeAll( tasks ); + for ( final Future< T > f : futures ) + f.get(); + } + + /** + * Convenience method for creating an appropriate storage img: + * {@link ArrayImg} if size is less than {@link Integer#MAX_VALUE}, + * {@link CellImg} otherwise. + */ + private static < T extends NativeType< T > & RealType< T > > Img< T > createAppropriateOneDimensionalImage( final long size, final T t ) + { + final long[] dim = new long[] { 1, size }; + return size > Integer.MAX_VALUE ? new CellImgFactory< T >( Integer.MAX_VALUE ).create( dim, t ) : new ArrayImgFactory< T >().create( dim, t ); + } + + /** + * Convenience method to find largest dimension of {@link Interval} + * interval. + */ + public static int getLargestDimension( final Interval interval ) + { + return IntStream.range( 0, interval.numDimensions() ).mapToObj( i -> new ValuePair<>( i, interval.dimension( i ) ) ).max( ( p1, p2 ) -> Long.compare( p1.getB(), p2.getB() ) ).get().getA(); + } + +} diff --git a/src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceAnisotropic.java b/src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceAnisotropic.java new file mode 100644 index 0000000..0d30668 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceAnisotropic.java @@ -0,0 +1,90 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_morphology.distance; + +import java.util.Arrays; +import java.util.stream.DoubleStream; + + +/** + * + * Implementation of weighted anisotropic Euclidian distance: + * + * D( p ) = min_q f(q) + sum_i w_i*(p_i - q_i)*(p_i - q_i). + * + * @author Philipp Hanslovsky + * + */ +public class EuclidianDistanceAnisotropic implements Distance +{ + + private final double[] weights; + + private final double[] oneOverTwoTimesWeights; + + /** + * When accounting for anisotropic image data, the ratios of the weights + * should be equal to the squared ratios of the voxel sizes, e.g. if voxel + * is twice as long along the y-axis, the weight for the y-axis should be + * four times as big as the weight for the x-axis. + * + */ + public EuclidianDistanceAnisotropic( final double... weights ) + { + super(); + this.weights = weights; + this.oneOverTwoTimesWeights = Arrays.stream( weights ).map( w -> 0.5 / w ).toArray(); + } + + public EuclidianDistanceAnisotropic( final int nDim, final double weight ) + { + this( DoubleStream.generate( () -> weight ).limit( nDim ).toArray() ); + } + + @Override + public double evaluate( final double x, final double xShift, final double yShift, final int dim ) + { + final double diff = x - xShift; + return weights[ dim ] * diff * diff + yShift; + } + + @Override + public double intersect( final double xShift1, final double yShift1, final double xShift2, final double yShift2, final int dim ) + { + final double a = weights[ dim ]; + return oneOverTwoTimesWeights[ dim ] * ( a * xShift2 * xShift2 + yShift2 - ( a * xShift1 * xShift1 + yShift1 ) ) / ( xShift2 - xShift1 ); + } + +} diff --git a/src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceIsotropic.java b/src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceIsotropic.java new file mode 100644 index 0000000..a0ccfda --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/distance/EuclidianDistanceIsotropic.java @@ -0,0 +1,74 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_morphology.distance; + + +/** + * + * Implementation of weighted isotropic Euclidian distance: + * + * D( p ) = min_q f(q) + w * sum_i (p_i - q_i)*(p_i - q_i). + * + * @author Philipp Hanslovsky + * + */ +public class EuclidianDistanceIsotropic implements Distance +{ + + private final double weight; + + private final double oneOverTwoTimesWeight; + + public EuclidianDistanceIsotropic( final double weight ) + { + super(); + this.weight = weight; + this.oneOverTwoTimesWeight = 0.5 / weight; + } + + @Override + public double evaluate( final double x, final double xShift, final double yShift, final int dim ) + { + final double diff = x - xShift; + return weight * diff * diff + yShift; + } + + @Override + public double intersect( final double xShift1, final double yShift1, final double xShift2, final double yShift2, final int dim ) + { + return oneOverTwoTimesWeight * ( weight * xShift2 * xShift2 + yShift2 - ( weight * xShift1 * xShift1 + yShift1 ) ) / ( xShift2 - xShift1 ); + } + +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Abstract3x3TableOperation.java b/src/main/java/varun_algorithm_morphology/table2d/Abstract3x3TableOperation.java new file mode 100644 index 0000000..6e981ae --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Abstract3x3TableOperation.java @@ -0,0 +1,118 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; +import net.imglib2.view.ExtendedRandomAccessibleInterval; +import net.imglib2.view.Views; +import varun_algorithm_neighborhood.Neighborhood; +import varun_algorithm_neighborhood.RectangleShape; + +/** + * Performs a binary operation on a 3x3 2-D neighborhood using a table of truth + * values to drive the operation. + * + * @author Lee Kamentsky + * @author Leon Yang + */ +public abstract class Abstract3x3TableOperation +{ + private static final RectangleShape shape = new RectangleShape( 1, false ); + + /** + * The index to the table that's returned is built by examining each pixel + * and accumulating 2^pixel number. The pixels are numbered like this: + * + *
      +	 * 0 1 2
      +	 * 3 4 5
      +	 * 6 7 8
      +	 * 
      + * + * @return a 512-element table that holds the truth values for each 3x3 + * combination + */ + protected abstract boolean[] getTable(); + + /** + * Gets default value for neighborhood pixels outside of the image. For + * example, an erosion operator should return true because otherwise pixels + * at the border would be eroded without evidence that they were not in the + * interior, and vice-versa for dilate. + * + * @return the extended value for this operation + */ + protected abstract boolean getExtendedValue(); + + protected < T extends BooleanType< T > > Img< T > calculate( final Img< T > source ) + { + final Img< T > target = source.factory().create( source, source.firstElement().copy() ); + final T extendedVal = source.firstElement().createVariable(); + extendedVal.set( getExtendedValue() ); + final ExtendedRandomAccessibleInterval< T, Img< T > > extended = Views.extendValue( source, extendedVal ); + calculate( extended, target ); + return target; + } + + protected < T extends BooleanType< T > > void calculate( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + final RandomAccessible< Neighborhood< T > > accessible = shape.neighborhoodsRandomAccessible( source ); + final RandomAccess< Neighborhood< T > > randomAccess = accessible.randomAccess( target ); + final Cursor< T > cursorTarget = target.cursor(); + final boolean[] table = getTable(); + while ( cursorTarget.hasNext() ) + { + final T targetVal = cursorTarget.next(); + randomAccess.setPosition( cursorTarget ); + final Neighborhood< T > neighborhood = randomAccess.get(); + final Cursor< T > nc = neighborhood.cursor(); + int idx = 0; + // Assume that the neighborhood obtained is of FlatIterationOrder, + // and assemble the index using bitwise operations. + while ( nc.hasNext() ) + { + idx <<= 1; + idx |= nc.next().get() ? 1 : 0; + } + + targetVal.set( table[ idx ] ); + } + } + +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Branchpoints.java b/src/main/java/varun_algorithm_morphology/table2d/Branchpoints.java new file mode 100644 index 0000000..9726508 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Branchpoints.java @@ -0,0 +1,149 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Removes all pixels except those that are the branchpoints of a skeleton. This + * operation should be applied to an image after skeletonizing. It leaves only + * those pixels that are at the intersection of branches. + *

      + * For example: + *

      + *
      + * 1 0 0 0 0    ? 0 0 0 0
      + * 0 1 0 0 0    0 0 0 0 0
      + * 0 0 1 0 0 -> 0 0 1 0 0
      + * 0 1 0 1 0    0 0 0 0 0
      + * 1 0 0 0 1    ? 0 0 0 ?
      + * 
      + * + * @author Lee Kamentsky + */ +public class Branchpoints extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > branchpoints( final Img< T > source ) + { + return new Branchpoints().calculate( source ); + } + + public static < T extends BooleanType< T > > void branchpoints( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Branchpoints().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, false, true, false, false, + false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, + false, false, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, false, true, false, false, + true, true, true, true, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, false, true, false, false, + false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, + false, false, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + false, false, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, false, true, false, false, + false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, + false, false, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, true, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Bridge.java b/src/main/java/varun_algorithm_morphology/table2d/Bridge.java new file mode 100644 index 0000000..9764ab8 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Bridge.java @@ -0,0 +1,146 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Sets a pixel to 1 if it has two non-zero neighbors that are on opposite sides + * of this pixel. + *

      + * For example: + * + *

      + * 1 0 0    1 0 0
      + * 0 0 0 -> 0 1 0
      + * 0 0 1    0 0 1
      + * 
      + * + * @author Lee Kamentsky + */ +public class Bridge extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > bridge( final Img< T > source ) + { + return new Bridge().calculate( source ); + } + + public static < T extends BooleanType< T > > void bridge( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Bridge().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, true, false, false, + false, false, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, false, false, false, true, false, false, + true, true, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, false, false, false, true, false, false, + true, true, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, false, false, false, true, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Clean.java b/src/main/java/varun_algorithm_morphology/table2d/Clean.java new file mode 100644 index 0000000..cc15829 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Clean.java @@ -0,0 +1,145 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Removes isolated pixels. + *

      + * For example: + * + *

      + * 0 0 0    0 0 0
      + * 0 1 0 -> 0 0 0
      + * 0 0 0    0 0 0
      + * 
      + * + * @author Lee Kamentsky + */ +public class Clean extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > clean( final Img< T > source ) + { + return new Clean().calculate( source ); + } + + public static < T extends BooleanType< T > > void clean( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Clean().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Endpoints.java b/src/main/java/varun_algorithm_morphology/table2d/Endpoints.java new file mode 100644 index 0000000..33b7e6f --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Endpoints.java @@ -0,0 +1,147 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Removes all pixels except the ones that are at the end of a skeleton. + *

      + * For example: + * + *

      + * 0 0 0 0 0    0 0 0 0 0
      + * 0 1 0 1 0    0 1 0 1 0
      + * 0 0 1 0 0 -> 0 0 0 0 0
      + * 0 1 0 1 0    0 0 0 0 0
      + * 0 1 0 0 1    0 ? 0 0 ?
      + * 
      + * + * @author Lee Kamentsky + */ +public class Endpoints extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > endpoints( final Img< T > source ) + { + return new Endpoints().calculate( source ); + } + + public static < T extends BooleanType< T > > void endpoints( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Endpoints().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Fill.java b/src/main/java/varun_algorithm_morphology/table2d/Fill.java new file mode 100644 index 0000000..76dad3e --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Fill.java @@ -0,0 +1,145 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Sets a pixel to 1 if all of its neighbors are 1. + *

      + * For example: + * + *

      + * 1 1 1    1 1 1
      + * 1 0 1 -> 1 1 1
      + * 1 1 1    1 1 1
      + * 
      + * + * @author Lee Kamentsky + */ +public class Fill extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return true; + } + + public static < T extends BooleanType< T > > Img< T > fill( final Img< T > source ) + { + return new Fill().calculate( source ); + } + + public static < T extends BooleanType< T > > void fill( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Fill().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Hbreak.java b/src/main/java/varun_algorithm_morphology/table2d/Hbreak.java new file mode 100644 index 0000000..6a1f0c7 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Hbreak.java @@ -0,0 +1,145 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Removes pixels that form vertical bridges between horizontal lines. + *

      + * For example: + * + *

      + * 1 1 1    1 1 1
      + * 0 1 0 -> 0 0 0
      + * 1 1 1    1 1 1
      + * 
      + * + * @author Lee Kamentsky + */ +public class Hbreak extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > hbreak( final Img< T > source ) + { + return new Hbreak().calculate( source ); + } + + public static < T extends BooleanType< T > > void hbreak( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Hbreak().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, false, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Life.java b/src/main/java/varun_algorithm_morphology/table2d/Life.java new file mode 100644 index 0000000..8480eef --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Life.java @@ -0,0 +1,145 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Applies the interaction rules from the Game of Life, an example of a cellular + * automaton. + *

      + * More specifically: + * + *

      + * 1. If a pixel has less than 2 neighbors are true, set to false.
      + * 2. If a pixel has 2 or 3 neighbors are true, do not change.
      + * 3. If a pixel has more than 3 neighbors are true, set to false.
      + * 
      + * + * @author Lee Kamentsky + */ +public class Life extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > life( final Img< T > source ) + { + return new Life().calculate( source ); + } + + public static < T extends BooleanType< T > > void life( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Life().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, false, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, false, + false, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, false, + false, true, true, true, true, true, true, false, + true, true, true, false, true, false, false, false, + false, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, false, + false, true, true, true, true, true, true, false, + true, true, true, false, true, false, false, false, + false, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, false, + false, true, true, true, true, true, true, false, + true, true, true, false, true, false, false, false, + false, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + false, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, true, false, true, true, false, + false, true, true, false, true, false, false, false, + false, true, true, true, true, true, true, false, + true, true, true, false, true, false, false, false, + false, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + false, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, true, true, false, true, false, false, false, + true, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Majority.java b/src/main/java/varun_algorithm_morphology/table2d/Majority.java new file mode 100644 index 0000000..38da132 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Majority.java @@ -0,0 +1,146 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Each pixel takes on the value of the majority that surround it (keep pixel + * value to break ties). + *

      + * For example: + * + *

      + * 1 1 1    1 1 1
      + * 1 0 1 -> 1 1 1
      + * 0 0 0    0 0 0
      + * 
      + * + * @author Lee Kamentsky + */ +public class Majority extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > majority( final Img< T > source ) + { + return new Majority().calculate( source ); + } + + public static < T extends BooleanType< T > > void majority( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Majority().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, true, + false, false, false, true, false, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Remove.java b/src/main/java/varun_algorithm_morphology/table2d/Remove.java new file mode 100644 index 0000000..653c2aa --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Remove.java @@ -0,0 +1,146 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Removes pixels that are otherwise surrounded by others (4 connected). The + * effect is to leave the perimeter of a solid object. + *

      + * For example: + * + *

      + * 0 1 0    0 1 0
      + * 1 1 1 -> 1 0 1
      + * 0 1 0    0 1 0
      + * 
      + * + * @author Lee Kamentsky + */ +public class Remove extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > remove( final Img< T > source ) + { + return new Remove().calculate( source ); + } + + public static < T extends BooleanType< T > > void remove( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Remove().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, false, false, true, true, false, false + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Spur.java b/src/main/java/varun_algorithm_morphology/table2d/Spur.java new file mode 100644 index 0000000..66b73c3 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Spur.java @@ -0,0 +1,86 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.list.ListImgFactory; +import net.imglib2.type.BooleanType; +import net.imglib2.type.NativeType; +import net.imglib2.util.Util; + +/** + * Removes spur pixels, i.e., pixels that have exactly one 8-connected neighbor. + * This operation essentially removes the endpoints of lines. + *

      + * For example: + * + *

      + * 0 0 0 0    0 0 0 0
      + * 0 1 0 0    0 0 0 0
      + * 0 0 1 0 -> 0 0 1 0
      + * 1 1 1 1    1 1 1 1
      + * 
      + * + * @author Lee Kamentsky + */ +public class Spur +{ + public static < T extends BooleanType< T > > Img< T > spur( final Img< T > source ) + { + return new Spur2().calculate( new Spur1().calculate( source ) ); + } + + @SuppressWarnings( { "unchecked", "rawtypes" } ) + public static < T extends BooleanType< T > > void spur( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + long[] targetDims = new long[] { target.dimension( 0 ), target.dimension( 1 ) }; + final T extendedVal = target.firstElement().createVariable(); + extendedVal.set( false ); + + final ImgFactory< T > factory; + if ( extendedVal instanceof NativeType ) + factory = ( ImgFactory< T > ) Util.getArrayOrCellImgFactory( target, ( NativeType ) extendedVal ); + else + factory = new ListImgFactory< T >(); + Img< T > temp = factory.create( targetDims, extendedVal ); + + new Spur1().calculate( source, temp ); + new Spur2().calculate( temp, target ); + } + +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Spur1.java b/src/main/java/varun_algorithm_morphology/table2d/Spur1.java new file mode 100644 index 0000000..64a3203 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Spur1.java @@ -0,0 +1,137 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Helper class for {@link Spur}. + * + * @author Lee Kamentsky + */ +class Spur1 extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return true; + } + + public static < T extends BooleanType< T > > Img< T > spur1( final Img< T > source ) + { + return new Spur1().calculate( source ); + } + + public static < T extends BooleanType< T > > void spur1( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Spur1().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, false, false, true, false, true, true, true, + false, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Spur2.java b/src/main/java/varun_algorithm_morphology/table2d/Spur2.java new file mode 100644 index 0000000..507bf73 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Spur2.java @@ -0,0 +1,137 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Helper class for {@link Spur}. + * + * @author Lee Kamentsky + */ +class Spur2 extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return true; + } + + public static < T extends BooleanType< T > > Img< T > spur2( final Img< T > source ) + { + return new Spur2().calculate( source ); + } + + public static < T extends BooleanType< T > > void spur2( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Spur2().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Thicken.java b/src/main/java/varun_algorithm_morphology/table2d/Thicken.java new file mode 100644 index 0000000..b901c71 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Thicken.java @@ -0,0 +1,152 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Dilates the exteriors of objects where that dilation does not 8-connect the + * object with another. The image is labeled and the labeled objects are filled. + * Unlabeled points adjacent to uniquely labeled points change from background + * to foreground. + *

      + * For example: + * + *

      + * 0 0 0    ? ? ?
      + * 0 0 0 -> ? 1 ?
      + * 0 0 1    ? ? ?
      + * 
      + * 1 0 0    ? ? ?
      + * 0 0 0 -> ? 0 ?
      + * 0 0 1    ? ? ?
      + * 
      + * + * @author Lee Kamentsky + */ +public class Thicken extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > thicken( final Img< T > source ) + { + return new Thicken().calculate( source ); + } + + public static < T extends BooleanType< T > > void thicken( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Thicken().calculate( source, target ); + } + + private static final boolean[] table = { + false, true, true, true, true, false, true, true, + true, true, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, true, true, true, false, true, true, + false, false, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, false, false, false, false, false, false, + true, true, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, false, false, false, false, false, false, + true, true, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, true, true, true, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, false, false, false, false, false, false, + true, true, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, true, true, true, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, true, true, true, false, true, true, + false, false, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, false, false, false, false, false, false, + true, true, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, true, true, true, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, false, false, false, false, false, false, + true, true, true, true, false, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, false, true, true, true, false, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Thin.java b/src/main/java/varun_algorithm_morphology/table2d/Thin.java new file mode 100644 index 0000000..687ea53 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Thin.java @@ -0,0 +1,100 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.img.ImgFactory; +import net.imglib2.img.list.ListImgFactory; +import net.imglib2.type.BooleanType; +import net.imglib2.type.NativeType; +import net.imglib2.util.Util; + +/** + * Thin lines preserving the Euler number using the thinning algorithm # 1 + * described in Guo, "Parallel Thinning with Two Subiteration Algorithms", + * Communications of the ACM, Vol 32 #3, page 359. The result generally + * preserves the lines in an image while eroding their thickness. + *

      + * For example: + * + *

      + * 1 1 1 1 1    1 1 1 1 1
      + * 1 1 0 1 0    1 1 0 1 0
      + * 1 1 1 1 0 -> 1 1 1 0 0
      + * 1 1 0 1 0    1 0 0 1 0
      + * 1 0 0 0 0    1 0 0 0 0
      + * 
      + * + * Notice that this implementation decides to returns the above result, instead + * of this following one, which is equivalently valid: + * + *
      + * 1 1 1 1 1
      + * 1 1 0 1 0
      + * 1 1 1 1 0
      + * 1 0 0 0 0
      + * 1 0 0 0 0
      + * 
      + * + * @author Lee Kamentsky + */ +public class Thin +{ + public static < T extends BooleanType< T > > Img< T > thin( final Img< T > source ) + { + return new Thin2().calculate( new Thin1().calculate( source ) ); + } + + @SuppressWarnings( { "unchecked", "rawtypes" } ) + public static < T extends BooleanType< T > > void thin( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + long[] targetDims = new long[] { target.dimension( 0 ), target.dimension( 1 ) }; + final T extendedVal = target.firstElement().createVariable(); + extendedVal.set( false ); + + final ImgFactory< T > factory; + if ( extendedVal instanceof NativeType ) + factory = ( ImgFactory< T > ) Util.getArrayOrCellImgFactory( target, ( NativeType ) extendedVal ); + else + factory = new ListImgFactory< T >(); + Img< T > temp = factory.create( targetDims, extendedVal ); + + new Thin1().calculate( source, temp ); + new Thin2().calculate( temp, target ); + } + +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Thin1.java b/src/main/java/varun_algorithm_morphology/table2d/Thin1.java new file mode 100644 index 0000000..4efbb37 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Thin1.java @@ -0,0 +1,137 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Helper class for {@link Thin}. + * + * @author Lee Kamentsky + */ +public class Thin1 extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return true; + } + + public static < T extends BooleanType< T > > Img< T > thin1( final Img< T > source ) + { + return new Thin1().calculate( source ); + } + + public static < T extends BooleanType< T > > void thin1( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Thin1().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, false, + true, true, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, false, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + false, false, false, false, true, true, false, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + false, false, false, false, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + false, false, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + false, false, false, false, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + false, false, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Thin2.java b/src/main/java/varun_algorithm_morphology/table2d/Thin2.java new file mode 100644 index 0000000..21f2944 --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Thin2.java @@ -0,0 +1,137 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Helper class for {@link Thin}. + * + * @author Lee Kamentsky + */ +public class Thin2 extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return true; + } + + public static < T extends BooleanType< T > > Img< T > thin2( final Img< T > source ) + { + return new Thin2().calculate( source ); + } + + public static < T extends BooleanType< T > > void thin2( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Thin2().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, false, + true, true, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, false, false, true, true, false, false, + true, true, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, false, true, false, false, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, true, false, true, false, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, false, false, false, true, false, false, + true, true, true, false, true, true, true, false, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, false, false, true, false, false, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + false, true, false, true, false, true, false, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_morphology/table2d/Vbreak.java b/src/main/java/varun_algorithm_morphology/table2d/Vbreak.java new file mode 100644 index 0000000..0cbbefd --- /dev/null +++ b/src/main/java/varun_algorithm_morphology/table2d/Vbreak.java @@ -0,0 +1,145 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_morphology.table2d; + +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessible; + +import net.imglib2.img.Img; +import net.imglib2.type.BooleanType; + +/** + * Removes pixels that form horizontal bridges between vertical lines. + *

      + * For example: + * + *

      + * 1 0 1    1 0 1
      + * 1 1 1 -> 1 0 1
      + * 1 0 1    1 0 1
      + * 
      + * + * @author Lee Kamentsky + */ +public class Vbreak extends Abstract3x3TableOperation +{ + @Override + protected boolean[] getTable() + { + return table; + } + + @Override + protected boolean getExtendedValue() + { + return false; + } + + public static < T extends BooleanType< T > > Img< T > vbreak( final Img< T > source ) + { + return new Vbreak().calculate( source ); + } + + public static < T extends BooleanType< T > > void vbreak( final RandomAccessible< T > source, final IterableInterval< T > target ) + { + new Vbreak().calculate( source, target ); + } + + private static final boolean[] table = { + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, false, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true, + false, false, false, false, false, false, false, false, + false, false, false, false, false, false, false, false, + true, true, true, true, true, true, true, true, + true, true, true, true, true, true, true, true + }; +} diff --git a/src/main/java/varun_algorithm_neighborhood/CenteredRectangleShape.java b/src/main/java/varun_algorithm_neighborhood/CenteredRectangleShape.java new file mode 100644 index 0000000..ac2142d --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/CenteredRectangleShape.java @@ -0,0 +1,156 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.Util; +import varun_algorithm_neighborhood.RectangleShape.NeighborhoodsAccessible; +import varun_algorithm_neighborhood.RectangleShape.NeighborhoodsIterableInterval; + +/** + * A factory for Accessibles on rectangular neighborhoods. + *

      + * This specific factory differs to {@link RectangleShape} in that it allows + * non-isotropic rectangular shapes. However, it constrains the the neighborhood + * to be symmetric by its origin. + *

      + * The size of the neighborhood is specified by an {@code int[]} span + * array, so that in every dimension {@code d}, the extent of the + * neighborhood is given by {@code 2 × span[d] + 1}. + *

      + * This factory exists because {@link RectangleShape} which is based on the same + * components, only allows for square neighborhoods. + * + * @author Tobias Pietzsch + * @author Jean-Yves Tinevez + * @author Jonathan Hale (University of Konstanz) + */ +public class CenteredRectangleShape implements Shape +{ + final int[] span; + + final boolean skipCenter; + + /** + * Constructs a factory for symmetric, non-isotropic rectangle + * neighborhoods. + *

      + * The size of the neighborhood is specified by an {@code int[]} span + * array, so that in every dimension {@code d}, the extent of the + * neighborhood is given by {@code 2 × span[d] + 1}. + * + * @param span + * the span of the neighborhood. + * @param skipCenter + * whether we should skip the central pixel or not. + */ + public CenteredRectangleShape( final int[] span, final boolean skipCenter ) + { + this.span = span; + this.skipCenter = skipCenter; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenterUnsafe.< T >factory() : RectangleNeighborhoodUnsafe.< T >factory(); + final Interval spanInterval = createSpan(); + return new NeighborhoodsIterableInterval< T >( source, spanInterval, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenterUnsafe.< T >factory() : RectangleNeighborhoodUnsafe.< T >factory(); + final Interval spanInterval = createSpan(); + return new NeighborhoodsAccessible< T >( source, spanInterval, f ); + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenter.< T >factory() : RectangleNeighborhood.< T >factory(); + final Interval spanInterval = createSpan(); + return new NeighborhoodsIterableInterval< T >( source, spanInterval, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenter.< T >factory() : RectangleNeighborhood.< T >factory(); + final Interval spanInterval = createSpan(); + return new NeighborhoodsAccessible< T >( source, spanInterval, f ); + } + + /** + * @return {@code true} if {@code skipCenter} was set to true + * during construction, {@code false} otherwise. + * @see CenteredRectangleShape#CenteredRectangleShape(int[], boolean) + */ + public boolean isSkippingCenter() + { + return skipCenter; + } + + /** + * @return Copy of the span of this shape. + */ + public int[] getSpan() + { + return span.clone(); + } + + @Override + public String toString() + { + return "CenteredRectangleShape, span = " + Util.printCoordinates( span ); + } + + private Interval createSpan() + { + final long[] min = new long[ span.length ]; + final long[] max = new long[ span.length ]; + for ( int d = 0; d < span.length; ++d ) + { + min[ d ] = -span[ d ]; + max[ d ] = span[ d ]; + } + return new FinalInterval( min, max ); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhood.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhood.java new file mode 100644 index 0000000..940931a --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhood.java @@ -0,0 +1,410 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; + +import net.imglib2.util.Intervals; + +/** + * A {@link Neighborhood} that iterates through the tips of a + * multi-dimensional diamond. + *

      + * Though it has very few direct applications, it is used in structuring element + * decomposition for mathematical morphology. + * + * @author Jean-Yves Tinevez Nov 6, 2013 + * @param + */ +public class DiamondTipsNeighborhood< T > extends AbstractLocalizable implements Neighborhood< T > +{ + + public static < T > DiamondTipsNeighborhoodFactory< T > factory() + { + return new DiamondTipsNeighborhoodFactory< T >() + { + @Override + public DiamondTipsNeighborhood< T > create( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + return new DiamondTipsNeighborhood< T >( position, radius, sourceRandomAccess ); + } + }; + } + + private final long radius; + + private final RandomAccess< T > sourceRandomAccess; + + private final Interval structuringElementBoundingBox; + + /** + * Creates a new diamond tip neighborhood. + * + * @param position + * the central position of the diamond. + * @param radius + * the diamond radius in all dimensions. + * @param sourceRandomAccess + * a {@link RandomAccess} on the target source. + */ + public DiamondTipsNeighborhood( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + this.radius = radius; + this.sourceRandomAccess = sourceRandomAccess; + this.structuringElementBoundingBox = createInterval(); + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( sourceRandomAccess.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + + @Override + public long size() + { + return sourceRandomAccess.numDimensions() * 2; + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; + } + + @Override + public double realMin( final int d ) + { + return position[ d ] - radius; + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < min.length; d++ ) + { + min[ d ] = position[ d ] - radius; + } + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < position.length; d++ ) + { + min.setPosition( position[ d ] - radius, d ); + } + } + + @Override + public double realMax( final int d ) + { + return position[ d ] + radius; + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < max.length; d++ ) + { + max[ d ] = position[ d ] + radius; + } + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < position.length; d++ ) + { + max.setPosition( position[ d ] + radius, d ); + } + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return position[ d ] - radius; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < min.length; d++ ) + { + min[ d ] = position[ d ] - radius; + } + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < position.length; d++ ) + { + min.setPosition( position[ d ] - radius, d ); + } + } + + @Override + public long max( final int d ) + { + return position[ d ] + radius; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < max.length; d++ ) + { + max[ d ] = position[ d ] + radius; + } + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < position.length; d++ ) + { + max.setPosition( position[ d ] + radius, d ); + } + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < dimensions.length; d++ ) + { + dimensions[ d ] = 2 * radius + 1; + } + } + + @Override + public long dimension( final int d ) + { + return 2 * radius + 1; + } + + @Override + public Interval getStructuringElementBoundingBox() + { + return structuringElementBoundingBox; + } + + public final class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private final RandomAccess< T > source; + + private int currentDim; + + private boolean parity; + + public LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + reset(); + } + + protected LocalCursor( final LocalCursor c ) + { + super( c.numDimensions() ); + this.source = c.source.copyRandomAccess(); + this.currentDim = c.currentDim; + this.parity = c.parity; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + if ( !parity ) + { + if ( currentDim >= 0 ) + { + source.setPosition( position[ currentDim ], currentDim ); + } + currentDim++; + source.move( -radius, currentDim ); + parity = true; + } + else + { + source.move( 2 * radius, currentDim ); + parity = false; + + } + } + + @Override + public void jumpFwd( final long steps ) + { + for ( int i = 0; i < steps; i++ ) + { + fwd(); + } + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + source.setPosition( position ); + currentDim = -1; + parity = false; + } + + @Override + public boolean hasNext() + { + return currentDim < source.numDimensions() - 1 || parity; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } + + private Interval createInterval() + { + final long[] minmax = new long[ 2 * position.length ]; + for ( int i = 0; i < position.length; i++ ) + { + minmax[ i ] = position[ i ] - radius; + } + for ( int i = position.length; i < minmax.length; i++ ) + { + minmax[ i ] = position[ i - position.length ] + radius; + } + return Intervals.createMinMax( minmax ); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodCursor.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodCursor.java new file mode 100644 index 0000000..fb184e3 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodCursor.java @@ -0,0 +1,168 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.IntervalIndexer; + +public class DiamondTipsNeighborhoodCursor< T > extends DiamondTipsNeighborhoodLocalizableSampler< T > implements Cursor< Neighborhood< T > > +{ + + private final long[] dimensions; + + private final long maxIndex; + + private long maxIndexOnLine; + + private long index; + + private long[] min; + + private long[] max; + + public DiamondTipsNeighborhoodCursor( final RandomAccessibleInterval< T > source, final long radius, final DiamondTipsNeighborhoodFactory< T > factory ) + { + super( source, radius, factory, source ); + dimensions = new long[ n ]; + + min = new long[ n ]; + max = new long[ n ]; + source.dimensions( dimensions ); + source.min( min ); + source.max( max ); + + long size = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + size *= dimensions[ d ]; + } + maxIndex = size - 1; + reset(); + } + + public DiamondTipsNeighborhoodCursor( final DiamondTipsNeighborhoodCursor< T > c ) + { + super( c ); + dimensions = c.dimensions.clone(); + maxIndex = c.maxIndex; + index = c.index; + maxIndexOnLine = c.maxIndexOnLine; + } + + @Override + public void fwd() + { + ++currentPos[ 0 ]; + if ( ++index > maxIndexOnLine ) + { + nextLine(); + } + } + + private void nextLine() + { + currentPos[ 0 ] = min[ 0 ]; + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + ++currentPos[ d ]; + if ( currentPos[ d ] > max[ d ] ) + { + currentPos[ d ] = min[ d ]; + } + else + { + break; + } + } + } + + @Override + public void reset() + { + index = -1; + maxIndexOnLine = -1; + System.arraycopy( max, 0, currentPos, 0, n ); + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public void jumpFwd( final long steps ) + { + index += steps; + if ( index < 0 ) + { + maxIndexOnLine = ( ( 1 + index ) / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + final long size = maxIndex + 1; + IntervalIndexer.indexToPositionWithOffset( size - ( -index % size ), dimensions, min, currentPos ); + } + else + { + maxIndexOnLine = ( 1 + index / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + IntervalIndexer.indexToPositionWithOffset( index, dimensions, min, currentPos ); + } + } + + @Override + public Neighborhood< T > next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public DiamondTipsNeighborhoodCursor< T > copy() + { + return new DiamondTipsNeighborhoodCursor< T >( this ); + } + + @Override + public DiamondTipsNeighborhoodCursor< T > copyCursor() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodFactory.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodFactory.java new file mode 100644 index 0000000..6f9aae8 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodFactory.java @@ -0,0 +1,41 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + +public interface DiamondTipsNeighborhoodFactory< T > +{ + public Neighborhood< T > create( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ); +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodLocalizableSampler.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodLocalizableSampler.java new file mode 100644 index 0000000..b283142 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodLocalizableSampler.java @@ -0,0 +1,154 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + +public abstract class DiamondTipsNeighborhoodLocalizableSampler< T > extends AbstractEuclideanSpace implements Localizable, Sampler< Neighborhood< T >> +{ + protected final RandomAccessible< T > source; + + protected final Interval sourceInterval; + + protected final long radius; + + protected final DiamondTipsNeighborhoodFactory< T > factory; + + protected final long[] currentPos; + + protected final Neighborhood< T > currentNeighborhood; + + public DiamondTipsNeighborhoodLocalizableSampler( final RandomAccessible< T > source, final long radius, final DiamondTipsNeighborhoodFactory< T > factory, Interval accessInterval ) + { + super( source.numDimensions() ); + this.source = source; + this.radius = radius; + this.factory = factory; + this.currentPos = new long[ n ]; + + if ( accessInterval == null && source instanceof Interval ) + accessInterval = ( Interval ) source; + + if ( accessInterval == null ) + { + sourceInterval = null; + } + else + { + final long[] accessMin = new long[ n ]; + final long[] accessMax = new long[ n ]; + accessInterval.min( accessMin ); + accessInterval.max( accessMax ); + for ( int d = 0; d < n; ++d ) + { + accessMin[ d ] = currentPos[ d ] - radius; + accessMax[ d ] = currentPos[ d ] + radius; + } + sourceInterval = new FinalInterval( accessMin, accessMax ); + } + + this.currentNeighborhood = factory.create( currentPos, radius, sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + protected DiamondTipsNeighborhoodLocalizableSampler( final DiamondTipsNeighborhoodLocalizableSampler< T > c ) + { + super( c.n ); + this.source = c.source; + this.sourceInterval = c.sourceInterval; + this.radius = c.radius; + this.factory = c.factory; + this.currentPos = c.currentPos.clone(); + this.currentNeighborhood = factory.create( currentPos, radius, source.randomAccess() ); + } + + @Override + public void localize( final float[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return currentNeighborhood.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return currentNeighborhood.getDoublePosition( d ); + } + + @Override + public Neighborhood< T > get() + { + return currentNeighborhood; + } + + @Override + public void localize( final int[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return currentNeighborhood.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return currentNeighborhood.getLongPosition( d ); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodRandomAccess.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodRandomAccess.java new file mode 100644 index 0000000..1440bf9 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodRandomAccess.java @@ -0,0 +1,162 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + +public class DiamondTipsNeighborhoodRandomAccess< T > extends DiamondTipsNeighborhoodLocalizableSampler< T > implements RandomAccess< Neighborhood< T > > +{ + + protected DiamondTipsNeighborhoodRandomAccess( final RandomAccessible< T > source, final long radius, final DiamondTipsNeighborhoodFactory< T > factory ) + { + super( source, radius, factory, null ); + } + + protected DiamondTipsNeighborhoodRandomAccess( final RandomAccessible< T > source, final long radius, final DiamondTipsNeighborhoodFactory< T > factory, final Interval interval ) + { + super( source, radius, factory, interval ); + } + + private DiamondTipsNeighborhoodRandomAccess( final DiamondTipsNeighborhoodRandomAccess< T > c ) + { + super( c ); + } + + @Override + public void fwd( final int d ) + { + ++currentPos[ d ]; + } + + @Override + public void bck( final int d ) + { + --currentPos[ d ]; + } + + @Override + public void move( final int distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final long distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long distance = localizable.getLongPosition( d ); + currentPos[ d ] += distance; + } + } + + @Override + public void move( final int[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void move( final long[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void setPosition( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long position = localizable.getLongPosition( d ); + currentPos[ d ] = position; + } + } + + @Override + public void setPosition( final int[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final long[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final int position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public void setPosition( final long position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public DiamondTipsNeighborhoodRandomAccess< T > copy() + { + return new DiamondTipsNeighborhoodRandomAccess< T >( this ); + } + + @Override + public DiamondTipsNeighborhoodRandomAccess< T > copyRandomAccess() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodUnsafe.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodUnsafe.java new file mode 100644 index 0000000..dbbf0a5 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsNeighborhoodUnsafe.java @@ -0,0 +1,77 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + +public class DiamondTipsNeighborhoodUnsafe< T > extends DiamondTipsNeighborhood< T > +{ + public static < T > DiamondTipsNeighborhoodFactory< T > factory() + { + return new DiamondTipsNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + return new DiamondTipsNeighborhoodUnsafe< T >( position, radius, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + DiamondTipsNeighborhoodUnsafe( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + super( position, radius, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/DiamondTipsShape.java b/src/main/java/varun_algorithm_neighborhood/DiamondTipsShape.java new file mode 100644 index 0000000..65d894d --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/DiamondTipsShape.java @@ -0,0 +1,209 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractInterval; +import net.imglib2.Cursor; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + + + +/** + * TODO + * + * @author Jean-Yves Tinevez + * @author Jonathan Hale (University of Konstanz) + */ +public class DiamondTipsShape implements Shape +{ + + private final long radius; + + /** + * Constructor + * + * @param radius + * Radius for the diamond shape. + */ + public DiamondTipsShape( final long radius ) + { + this.radius = radius; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + return new NeighborhoodsIterableInterval< T >( source, radius, DiamondTipsNeighborhoodUnsafe.< T >factory() ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + final DiamondTipsNeighborhoodFactory< T > f = DiamondTipsNeighborhoodUnsafe.< T >factory(); + return new NeighborhoodsAccessible< T >( source, radius, f ); + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + return new NeighborhoodsIterableInterval< T >( source, radius, DiamondTipsNeighborhood.< T >factory() ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + final DiamondTipsNeighborhoodFactory< T > f = DiamondTipsNeighborhood.< T >factory(); + return new NeighborhoodsAccessible< T >( source, radius, f ); + } + + /** + * @return Radius of this shape. + * @see DiamondTipsShape#DiamondTipsShape(long) + */ + public long getRadius() + { + return radius; + } + + @Override + public String toString() + { + return "DiamondTipsShape, radius = " + radius; + } + + public static final class NeighborhoodsIterableInterval< T > extends AbstractInterval implements IterableInterval< Neighborhood< T > > + { + final RandomAccessibleInterval< T > source; + + final DiamondTipsNeighborhoodFactory< T > factory; + + final long size; + + private final long radius; + + public NeighborhoodsIterableInterval( final RandomAccessibleInterval< T > source, final long radius, final DiamondTipsNeighborhoodFactory< T > factory ) + { + super( source ); + this.source = source; + this.radius = radius; + this.factory = factory; + long s = source.dimension( 0 ); + for ( int d = 1; d < n; ++d ) + { + s *= source.dimension( d ); + } + size = s; + } + + @Override + public Cursor< Neighborhood< T >> cursor() + { + return new DiamondTipsNeighborhoodCursor< T >( source, radius, factory ); + } + + @Override + public long size() + { + return size; + } + + @Override + public Neighborhood< T > firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Neighborhood< T >> iterator() + { + return cursor(); + } + + @Override + public Cursor< Neighborhood< T >> localizingCursor() + { + return cursor(); + } + } + + public static final class NeighborhoodsAccessible< T > extends AbstractEuclideanSpace implements RandomAccessible< Neighborhood< T > > + { + final RandomAccessible< T > source; + + final DiamondTipsNeighborhoodFactory< T > factory; + + private final long radius; + + public NeighborhoodsAccessible( final RandomAccessible< T > source, final long radius, final DiamondTipsNeighborhoodFactory< T > factory ) + { + super( source.numDimensions() ); + this.source = source; + this.radius = radius; + this.factory = factory; + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess() + { + return new DiamondTipsNeighborhoodRandomAccess< T >( source, radius, factory ); + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess( final Interval interval ) + { + return randomAccess(); + } + + @Override + public int numDimensions() + { + return source.numDimensions(); + } + + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhood.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhood.java new file mode 100644 index 0000000..4dad6c3 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhood.java @@ -0,0 +1,404 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; + +import net.imglib2.util.Intervals; + +public class HorizontalLineNeighborhood< T > extends AbstractLocalizable implements Neighborhood< T > +{ + public static < T > HorizontalLineNeighborhoodFactory< T > factory() + { + return new HorizontalLineNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long span, final int dim, final boolean skipCenter, final RandomAccess< T > sourceRandomAccess ) + { + return new HorizontalLineNeighborhood< T >( position, span, dim, skipCenter, sourceRandomAccess ); + } + }; + } + + /** + * The span of the line. The iterated interval will be of size + * {@code 2 x span + 1 }. + */ + private final long span; + + /** + * The dimension along which the line should extend. + */ + private final int dim; + + private final RandomAccess< T > sourceRandomAccess; + + private final Interval structuringElementBoundingBox; + + private final long maxIndex; + + /** If {@code true}, the central pixel will be skipped. */ + private final boolean skipCenter; + + HorizontalLineNeighborhood( final long[] position, final long span, final int dim, final boolean skipCenter, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + this.skipCenter = skipCenter; + if ( skipCenter ) + { + this.maxIndex = 2 * span; + } + else + { + this.maxIndex = 2 * span + 1; + } + this.span = span; + this.dim = dim; + this.sourceRandomAccess = sourceRandomAccess; + this.structuringElementBoundingBox = createInterval(); + } + + @Override + public Interval getStructuringElementBoundingBox() + { + return structuringElementBoundingBox; + } + + @Override + public long size() + { + return maxIndex; + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return structuringElementBoundingBox.realMin( d ); + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = structuringElementBoundingBox.realMin( d ); + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( structuringElementBoundingBox.realMin( d ), d ); + } + + @Override + public double realMax( final int d ) + { + return structuringElementBoundingBox.realMax( d ); + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = structuringElementBoundingBox.realMax( d ); + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( structuringElementBoundingBox.realMax( d ), d ); + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return structuringElementBoundingBox.min( d ); + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = structuringElementBoundingBox.min( d ); + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( structuringElementBoundingBox.min( d ), d ); + } + + @Override + public long max( final int d ) + { + return structuringElementBoundingBox.max( d ); + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = structuringElementBoundingBox.max( d ); + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( structuringElementBoundingBox.max( d ), d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < n; ++d ) + dimensions[ d ] = dimension( d ); + } + + @Override + public long dimension( final int d ) + { + if ( d == dim ) + { + return maxIndex; + } + else + { + return 1; + } + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( sourceRandomAccess.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + + public final class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private final RandomAccess< T > source; + + private long index; + + public LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + reset(); + } + + protected LocalCursor( final LocalCursor c ) + { + super( c.numDimensions() ); + source = c.source.copyRandomAccess(); + index = c.index; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + source.fwd( dim ); + if ( skipCenter && index == span ) + { + source.fwd( dim ); + } + index++; + } + + @Override + public void jumpFwd( final long steps ) + { + for ( int i = 0; i < steps; i++ ) + { + fwd(); + } + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + source.setPosition( position ); + source.move( -span - 1, dim ); + index = 0; + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } + + private Interval createInterval() + { + final long[] minsize = new long[ 2 * position.length ]; + for ( int i = 0; i < position.length; i++ ) + { + if ( i == dim ) + { + minsize[ i ] = position[ i ] - span; + } + else + { + minsize[ i ] = position[ i ]; + } + } + for ( int i = position.length; i < minsize.length; i++ ) + { + if ( i - position.length == dim ) + { + minsize[ i ] = position[ i - position.length ] + span; + } + else + { + minsize[ i ] = position[ i - position.length ]; + } + } + return Intervals.createMinMax( minsize ); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodCursor.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodCursor.java new file mode 100644 index 0000000..c7d532a --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodCursor.java @@ -0,0 +1,163 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.IntervalIndexer; + +public class HorizontalLineNeighborhoodCursor< T > extends HorizontalLineNeighborhoodLocalizableSampler< T > implements Cursor< Neighborhood< T > > +{ + private final long[] dimensions; + + private long index; + + private final long maxIndex; + + private long maxIndexOnLine; + + private long[] min; + + private long[] max; + + public HorizontalLineNeighborhoodCursor( final RandomAccessibleInterval< T > source, final long span, final int dim, final boolean skipCenter, final HorizontalLineNeighborhoodFactory< T > factory ) + { + super( source, span, dim, skipCenter, factory, source ); + + dimensions = new long[ n ]; + + min = new long[ n ]; + max = new long[ n ]; + source.dimensions( dimensions ); + source.min( min ); + source.max( max ); + + long size = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + size *= dimensions[ d ]; + maxIndex = size - 1; + reset(); + } + + private HorizontalLineNeighborhoodCursor( final HorizontalLineNeighborhoodCursor< T > c ) + { + super( c ); + dimensions = c.dimensions.clone(); + maxIndex = c.maxIndex; + index = c.index; + maxIndexOnLine = c.maxIndexOnLine; + } + + @Override + public void fwd() + { + ++currentPos[ 0 ]; + if ( ++index > maxIndexOnLine ) + nextLine(); + } + + private void nextLine() + { + currentPos[ 0 ] = min[ 0 ]; + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + ++currentPos[ d ]; + if ( currentPos[ d ] > max[ d ] ) + { + currentPos[ d ] = min[ d ]; + } + else + break; + } + } + + @Override + public void reset() + { + index = -1; + maxIndexOnLine = -1; + System.arraycopy( max, 0, currentPos, 0, n ); + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public void jumpFwd( final long steps ) + { + index += steps; + if ( index < 0 ) + { + maxIndexOnLine = ( ( 1 + index ) / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + final long size = maxIndex + 1; + IntervalIndexer.indexToPositionWithOffset( size - ( -index % size ), dimensions, min, currentPos ); + } + else + { + maxIndexOnLine = ( 1 + index / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + IntervalIndexer.indexToPositionWithOffset( index, dimensions, min, currentPos ); + } + } + + @Override + public Neighborhood< T > next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public HorizontalLineNeighborhoodCursor< T > copy() + { + return new HorizontalLineNeighborhoodCursor< T >( this ); + } + + @Override + public HorizontalLineNeighborhoodCursor< T > copyCursor() + { + return copy(); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodFactory.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodFactory.java new file mode 100644 index 0000000..065db7e --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodFactory.java @@ -0,0 +1,41 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + +public interface HorizontalLineNeighborhoodFactory< T > +{ + public Neighborhood< T > create( final long[] position, final long span, final int dim, final boolean skipCenter, final RandomAccess< T > sourceRandomAccess ); +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodLocalizableSampler.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodLocalizableSampler.java new file mode 100644 index 0000000..2bdcd1a --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodLocalizableSampler.java @@ -0,0 +1,163 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + + +public abstract class HorizontalLineNeighborhoodLocalizableSampler< T > extends AbstractEuclideanSpace implements Localizable, Sampler< Neighborhood< T > > +{ + protected final RandomAccessible< T > source; + + protected final Interval sourceInterval; + + protected final long span; + + protected final int dim; + + protected final long[] currentPos; + + protected final HorizontalLineNeighborhoodFactory< T > neighborhoodFactory; + + protected final Neighborhood< T > currentNeighborhood; + + private final boolean skipCenter; + + public HorizontalLineNeighborhoodLocalizableSampler( final RandomAccessible< T > source, final long span, final int dim, final boolean skipCenter, final HorizontalLineNeighborhoodFactory< T > factory, Interval accessInterval ) + { + super( source.numDimensions() ); + this.source = source; + this.span = span; + this.dim = dim; + this.skipCenter = skipCenter; + this.currentPos = new long[ n ]; + this.neighborhoodFactory = factory; + + if ( accessInterval == null && source instanceof Interval ) + accessInterval = ( Interval ) source; + + if ( accessInterval == null ) + { + sourceInterval = null; + } + else + { + final long[] accessMin = new long[ n ]; + final long[] accessMax = new long[ n ]; + accessInterval.min( accessMin ); + accessInterval.max( accessMax ); + for ( int d = 0; d < n; ++d ) + { + accessMin[ d ] = currentPos[ d ] - span; + accessMax[ d ] = currentPos[ d ] + span; + } + sourceInterval = new FinalInterval( accessMin, accessMax ); + } + + currentNeighborhood = neighborhoodFactory.create( currentPos, span, dim, false, sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + protected HorizontalLineNeighborhoodLocalizableSampler( final HorizontalLineNeighborhoodLocalizableSampler< T > c ) + { + super( c.n ); + source = c.source; + sourceInterval = c.sourceInterval; + span = c.span; + skipCenter = c.skipCenter; + dim = c.dim; + neighborhoodFactory = c.neighborhoodFactory; + currentPos = c.currentPos.clone(); + currentNeighborhood = neighborhoodFactory.create( currentPos, span, dim, skipCenter, source.randomAccess() ); + } + + @Override + public Neighborhood< T > get() + { + return currentNeighborhood; + } + + @Override + public void localize( final int[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return currentNeighborhood.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return currentNeighborhood.getLongPosition( d ); + } + + @Override + public void localize( final float[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return currentNeighborhood.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return currentNeighborhood.getDoublePosition( d ); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodRandomAccess.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodRandomAccess.java new file mode 100644 index 0000000..7095570 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodRandomAccess.java @@ -0,0 +1,161 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + +public class HorizontalLineNeighborhoodRandomAccess< T > extends HorizontalLineNeighborhoodLocalizableSampler< T > implements RandomAccess< Neighborhood< T > > +{ + public HorizontalLineNeighborhoodRandomAccess( final RandomAccessible< T > source, final long span, final int dim, final boolean skipCenter, final HorizontalLineNeighborhoodFactory< T > factory, final Interval interval ) + { + super( source, span, dim, skipCenter, factory, interval ); + } + + public HorizontalLineNeighborhoodRandomAccess( final RandomAccessible< T > source, final long span, final int dim, final boolean skipCenter, final HorizontalLineNeighborhoodFactory< T > factory ) + { + super( source, span, dim, skipCenter, factory, null ); + } + + private HorizontalLineNeighborhoodRandomAccess( final HorizontalLineNeighborhoodRandomAccess< T > c ) + { + super( c ); + } + + @Override + public void fwd( final int d ) + { + ++currentPos[ d ]; + } + + @Override + public void bck( final int d ) + { + --currentPos[ d ]; + } + + @Override + public void move( final int distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final long distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long distance = localizable.getLongPosition( d ); + currentPos[ d ] += distance; + } + } + + @Override + public void move( final int[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void move( final long[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void setPosition( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long position = localizable.getLongPosition( d ); + currentPos[ d ] = position; + } + } + + @Override + public void setPosition( final int[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final long[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final int position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public void setPosition( final long position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public HorizontalLineNeighborhoodRandomAccess< T > copy() + { + return new HorizontalLineNeighborhoodRandomAccess< T >( this ); + } + + @Override + public HorizontalLineNeighborhoodRandomAccess< T > copyRandomAccess() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodUnsafe.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodUnsafe.java new file mode 100644 index 0000000..ad261c6 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineNeighborhoodUnsafe.java @@ -0,0 +1,79 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + + +public class HorizontalLineNeighborhoodUnsafe< T > extends HorizontalLineNeighborhood< T > +{ + + public static < T > HorizontalLineNeighborhoodFactory< T > factory() + { + return new HorizontalLineNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long span, final int dim, final boolean skipCenter, final RandomAccess< T > sourceRandomAccess ) + { + return new HorizontalLineNeighborhoodUnsafe< T >( position, span, dim, skipCenter, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + HorizontalLineNeighborhoodUnsafe( final long[] position, final long span, final int dim, final boolean skipCenter, final RandomAccess< T > sourceRandomAccess ) + { + super( position, span, dim, skipCenter, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/HorizontalLineShape.java b/src/main/java/varun_algorithm_neighborhood/HorizontalLineShape.java new file mode 100644 index 0000000..a59c261 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HorizontalLineShape.java @@ -0,0 +1,246 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractInterval; +import net.imglib2.Cursor; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +/** + * A {@link Shape} representing finite, centered, symmetric lines, that are + * parallel to the image axes. + * + * @author Jean-Yves Tinevez# + * @author Jonathan Hale (University of Konstanz) + */ +public class HorizontalLineShape implements Shape +{ + private final long span; + + private final boolean skipCenter; + + private final int dim; + + /** + * Create a new line shape. + * + * @param span + * the span of the line in both directions, so that its total + * extend is given by {@code 2 x span + 1}. + * @param dim + * the dimension along which to lay this line. + * @param skipCenter + * if {@code true}, the shape will skip the center point of + * the line. + */ + public HorizontalLineShape( final long span, final int dim, final boolean skipCenter ) + { + this.span = span; + this.dim = dim; + this.skipCenter = skipCenter; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + return new NeighborhoodsIterableInterval< T >( source, span, dim, skipCenter, HorizontalLineNeighborhoodUnsafe.< T >factory() ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + final HorizontalLineNeighborhoodFactory< T > f = HorizontalLineNeighborhoodUnsafe.< T >factory(); + return new NeighborhoodsAccessible< T >( source, span, dim, skipCenter, f ); + } + + @Override + public < T > IterableInterval< Neighborhood< T >> neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + return new NeighborhoodsIterableInterval< T >( source, span, dim, skipCenter, HorizontalLineNeighborhood.< T >factory() ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + final HorizontalLineNeighborhoodFactory< T > f = HorizontalLineNeighborhood.< T >factory(); + return new NeighborhoodsAccessible< T >( source, span, dim, skipCenter, f ); + } + + /** + * @return {@code true} if {@code skipCenter} was set to true + * during construction, {@code false} otherwise. + * @see CenteredRectangleShape#CenteredRectangleShape(int[], boolean) + */ + public boolean isSkippingCenter() + { + return skipCenter; + } + + /** + * @return The span of this shape. + */ + public long getSpan() + { + return span; + } + + /** + * @return The dimension along which the line is layed. + */ + public int getLineDimension() + { + return dim; + } + + @Override + public String toString() + { + return "HorizontalLineShape, span = " + span + " in dim " + dim + ", skipCenter = " + skipCenter; + } + + public static final class NeighborhoodsIterableInterval< T > extends AbstractInterval implements IterableInterval< Neighborhood< T > > + { + final RandomAccessibleInterval< T > source; + + final long span; + + final HorizontalLineNeighborhoodFactory< T > factory; + + final long size; + + final int dim; + + final boolean skipCenter; + + public NeighborhoodsIterableInterval( final RandomAccessibleInterval< T > source, final long span, final int dim, final boolean skipCenter, final HorizontalLineNeighborhoodFactory< T > factory ) + { + super( source ); + this.source = source; + this.span = span; + this.dim = dim; + this.skipCenter = skipCenter; + this.factory = factory; + long s = source.dimension( 0 ); + for ( int d = 1; d < n; ++d ) + s *= source.dimension( d ); + size = s; + } + + @Override + public Cursor< Neighborhood< T >> cursor() + { + return new HorizontalLineNeighborhoodCursor< T >( source, span, dim, skipCenter, factory ); + } + + @Override + public long size() + { + return size; + } + + @Override + public Neighborhood< T > firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Neighborhood< T >> iterator() + { + return cursor(); + } + + @Override + public Cursor< Neighborhood< T >> localizingCursor() + { + return cursor(); + } + } + + public static final class NeighborhoodsAccessible< T > extends AbstractEuclideanSpace implements RandomAccessible< Neighborhood< T > > + { + final RandomAccessible< T > source; + + final HorizontalLineNeighborhoodFactory< T > factory; + + private final long span; + + private final int dim; + + private final boolean skipCenter; + + public NeighborhoodsAccessible( final RandomAccessible< T > source, final long span, final int dim, final boolean skipCenter, final HorizontalLineNeighborhoodFactory< T > factory ) + { + super( source.numDimensions() ); + this.source = source; + this.span = span; + this.dim = dim; + this.skipCenter = skipCenter; + this.factory = factory; + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess() + { + return new HorizontalLineNeighborhoodRandomAccess< T >( source, span, dim, skipCenter, factory ); + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess( final Interval interval ) + { + return randomAccess(); + } + + @Override + public int numDimensions() + { + return source.numDimensions(); + } + + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhood.java b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhood.java new file mode 100644 index 0000000..57a8bdd --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhood.java @@ -0,0 +1,447 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; + + +/** + * TODO + * + * @author Tobias Pietzsch + * @author Stephan Preibisch + * @author Stephan Saalfeld + */ +public class HyperSphereNeighborhood< T > extends AbstractLocalizable implements Neighborhood< T > +{ + public static < T > HyperSphereNeighborhoodFactory< T > factory() + { + return new HyperSphereNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + return new HyperSphereNeighborhood< T >( position, radius, sourceRandomAccess ); + } + }; + } + + private final RandomAccess< T > sourceRandomAccess; + + private final long radius; + + private final int maxDim; + + private final long size; + + private final Interval structuringElementBoundingBox; + + HyperSphereNeighborhood( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + this.sourceRandomAccess = sourceRandomAccess; + this.radius = radius; + maxDim = n - 1; + size = computeSize(); + + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + + for ( int d = 0; d < n; d++ ) + { + min[ d ] = -radius; + max[ d ] = radius; + } + + structuringElementBoundingBox = new FinalInterval( min, max ); + } + + /** + * Compute the number of elements for iteration + */ + protected long computeSize() + { + final LocalCursor cursor = new LocalCursor( sourceRandomAccess ); + + // "compute number of pixels" + long size = 0; + while ( cursor.hasNext() ) + { + cursor.fwd(); + ++size; + } + + return size; + } + + public final class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private final RandomAccess< T > source; + + // the current radius in each dimension we are at + private final double[] r; + + // the current radius in each dimension truncated to long + private final long[] ri; + + // the remaining number of steps in each dimension we still have to go + private final long[] s; + + public LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + r = new double[ n ]; + ri = new long[ n ]; + s = new long[ n ]; + reset(); + } + + protected LocalCursor( final LocalCursor c ) + { + super( c.numDimensions() ); + source = c.source.copyRandomAccess(); + r = c.r.clone(); + ri = c.ri.clone(); + s = c.s.clone(); + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + + if ( --s[ 0 ] >= 0 ) + source.fwd( 0 ); + else + { + int d = 1; + for ( ; d < n; ++d ) + { + if ( --s[ d ] >= 0 ) + { + source.fwd( d ); + break; + } + } + + for ( ; d > 0; --d ) + { + final int e = d - 1; + final double rd = r[ d ]; + final long pd = s[ d ] - ri[ d ]; + + final double rad = Math.sqrt( rd * rd - pd * pd ); + final long radi = ( long ) rad; + r[ e ] = rad; + ri[ e ] = radi; + s[ e ] = 2 * radi; + + source.setPosition( position[ e ] - radi, e ); + } + } + } + + @Override + public void jumpFwd( final long steps ) + { + for ( long i = 0; i < steps; ++i ) + fwd(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + for ( int d = 0; d < maxDim; ++d ) + { + r[ d ] = ri[ d ] = s[ d ] = 0; + source.setPosition( position[ d ], d ); + } + + source.setPosition( position[ maxDim ] - radius - 1, maxDim ); + + r[ maxDim ] = radius; + ri[ maxDim ] = radius; + s[ maxDim ] = 1 + 2 * radius; + } + + @Override + public boolean hasNext() + { + return s[ maxDim ] > 0; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } + + @Override + public Interval getStructuringElementBoundingBox() + { + return structuringElementBoundingBox; + } + + @Override + public long size() + { + return size; + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return position[ d ] - radius; + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < min.length; d++ ) + { + min[ d ] = position[ d ] - radius; + } + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < min.numDimensions(); d++ ) + { + min.setPosition( position[ d ] - radius, d ); + } + } + + @Override + public double realMax( final int d ) + { + return position[ d ] + radius; + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < max.length; d++ ) + { + max[ d ] = position[ d ] + radius; + } + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < max.numDimensions(); d++ ) + { + max.setPosition( position[ d ] + radius, d ); + } + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return position[ d ] - radius; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < min.length; d++ ) + { + min[ d ] = position[ d ] - radius; + } + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < min.numDimensions(); d++ ) + { + min.setPosition( position[ d ] - radius, d ); + } + } + + @Override + public long max( final int d ) + { + return position[ d ] + radius; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < max.length; d++ ) + { + max[ d ] = position[ d ] + radius; + } + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < max.numDimensions(); d++ ) + { + max.setPosition( position[ d ] + radius, d ); + } + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < dimensions.length; d++ ) + { + dimensions[ d ] = ( 2 * radius ) + 1; + } + } + + @Override + public long dimension( final int d ) + { + return ( 2 * radius ) + 1; + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( sourceRandomAccess.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodCursor.java b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodCursor.java new file mode 100644 index 0000000..ba2b84b --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodCursor.java @@ -0,0 +1,164 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.IntervalIndexer; + +public final class HyperSphereNeighborhoodCursor< T > extends HypersphereNeighborhoodLocalizableSampler< T > implements Cursor< Neighborhood< T > > +{ + private final long[] dimensions; + + private final long[] min; + + private final long[] max; + + private long index; + + private final long maxIndex; + + private long maxIndexOnLine; + + public HyperSphereNeighborhoodCursor( final RandomAccessibleInterval< T > source, final long radius, final HyperSphereNeighborhoodFactory< T > factory ) + { + super( source, radius, factory, source ); + + dimensions = new long[ n ]; + min = new long[ n ]; + max = new long[ n ]; + source.dimensions( dimensions ); + source.min( min ); + source.max( max ); + long size = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + size *= dimensions[ d ]; + maxIndex = size - 1; + reset(); + } + + private HyperSphereNeighborhoodCursor( final HyperSphereNeighborhoodCursor< T > c ) + { + super( c ); + dimensions = c.dimensions.clone(); + min = c.min.clone(); + max = c.max.clone(); + maxIndex = c.maxIndex; + index = c.index; + maxIndexOnLine = c.maxIndexOnLine; + } + + @Override + public void fwd() + { + ++currentPos[ 0 ]; + if ( ++index > maxIndexOnLine ) + nextLine(); + } + + private void nextLine() + { + currentPos[ 0 ] = min[ 0 ]; + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + ++currentPos[ d ]; + if ( currentPos[ d ] > max[ d ] ) + { + currentPos[ d ] = min[ d ]; + } + else + break; + } + } + + @Override + public void reset() + { + index = -1; + maxIndexOnLine = -1; + System.arraycopy( max, 0, currentPos, 0, n ); + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public void jumpFwd( final long steps ) + { + index += steps; + if ( index < 0 ) + { + maxIndexOnLine = ( ( 1 + index ) / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + final long size = maxIndex + 1; + IntervalIndexer.indexToPositionWithOffset( size - ( -index % size ), dimensions, min, currentPos ); + } + else + { + maxIndexOnLine = ( 1 + index / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + IntervalIndexer.indexToPositionWithOffset( index, dimensions, min, currentPos ); + } + } + + @Override + public Neighborhood< T > next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public HyperSphereNeighborhoodCursor< T > copy() + { + return new HyperSphereNeighborhoodCursor< T >( this ); + } + + @Override + public HyperSphereNeighborhoodCursor< T > copyCursor() + { + return copy(); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodFactory.java b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodFactory.java new file mode 100644 index 0000000..d8f41e7 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodFactory.java @@ -0,0 +1,49 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + +/** + * INCOMPLETE !!! + * + * TODO + * + * @author Tobias Pietzsch + */ +public interface HyperSphereNeighborhoodFactory< T > +{ + public Neighborhood< T > create( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ); +} diff --git a/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodRandomAccess.java b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodRandomAccess.java new file mode 100644 index 0000000..5d4b119 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodRandomAccess.java @@ -0,0 +1,149 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + + +public class HyperSphereNeighborhoodRandomAccess< T > extends HypersphereNeighborhoodLocalizableSampler< T > implements RandomAccess< Neighborhood< T > > +{ + public HyperSphereNeighborhoodRandomAccess( final RandomAccessible< T > source, final long radius, final HyperSphereNeighborhoodFactory< T > factory ) + { + super( source, radius, factory, null ); + } + + public HyperSphereNeighborhoodRandomAccess( final RandomAccessible< T > source, final long radius, final HyperSphereNeighborhoodFactory< T > factory, final Interval interval ) + { + super( source, radius, factory, interval ); + } + + protected HyperSphereNeighborhoodRandomAccess( final HyperSphereNeighborhoodRandomAccess< T > c ) + { + super( c ); + } + + @Override + public void fwd( final int d ) + { + ++currentPos[ d ]; + } + + @Override + public void bck( final int d ) + { + --currentPos[ d ]; + } + + @Override + public void move( final int distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final long distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + currentPos[ d ] += localizable.getLongPosition( d ); + } + + @Override + public void move( final int[] distance ) + { + for ( int d = 0; d < n; ++d ) + currentPos[ d ] += distance[ d ]; + } + + @Override + public void move( final long[] distance ) + { + for ( int d = 0; d < n; ++d ) + currentPos[ d ] += distance[ d ]; + } + + @Override + public void setPosition( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + currentPos[ d ] = localizable.getLongPosition( d ); + } + + @Override + public void setPosition( final int[] position ) + { + for ( int d = 0; d < n; ++d ) + currentPos[ d ] = position[ d ]; + } + + @Override + public void setPosition( final long[] position ) + { + for ( int d = 0; d < n; ++d ) + currentPos[ d ] = position[ d ]; + } + + @Override + public void setPosition( final int position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public void setPosition( final long position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public HyperSphereNeighborhoodRandomAccess< T > copy() + { + return new HyperSphereNeighborhoodRandomAccess< T >( this ); + } + + @Override + public HyperSphereNeighborhoodRandomAccess< T > copyRandomAccess() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodUnsafe.java b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodUnsafe.java new file mode 100644 index 0000000..c17251c --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HyperSphereNeighborhoodUnsafe.java @@ -0,0 +1,78 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + + +public final class HyperSphereNeighborhoodUnsafe< T > extends HyperSphereNeighborhood< T > +{ + public static < T > HyperSphereNeighborhoodFactory< T > factory() + { + return new HyperSphereNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + return new HyperSphereNeighborhoodUnsafe< T >( position, radius, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + HyperSphereNeighborhoodUnsafe( final long[] position, final long radius, final RandomAccess< T > sourceRandomAccess ) + { + super( position, radius, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HyperSphereShape.java b/src/main/java/varun_algorithm_neighborhood/HyperSphereShape.java new file mode 100644 index 0000000..58a00ca --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HyperSphereShape.java @@ -0,0 +1,191 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractInterval; +import net.imglib2.Cursor; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + + +/** + * A factory for Accessibles on hyper-sphere neighboorhoods. + * + * @author Tobias Pietzsch + * @author Jonathan Hale (University of Konstanz) + */ +public class HyperSphereShape implements Shape +{ + final long radius; + + public HyperSphereShape( final long radius ) + { + this.radius = radius; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + return new NeighborhoodsIterableInterval< T >( source, radius, HyperSphereNeighborhoodUnsafe.< T >factory() ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + return new NeighborhoodsAccessible< T >( source, radius, HyperSphereNeighborhoodUnsafe.< T >factory() ); + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + return new NeighborhoodsIterableInterval< T >( source, radius, HyperSphereNeighborhood.< T >factory() ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + return new NeighborhoodsAccessible< T >( source, radius, HyperSphereNeighborhood.< T >factory() ); + } + + /** + * @return The radius of this shape. + */ + public long getRadius() + { + return radius; + } + + @Override + public String toString() + { + return "HyperSphereShape, radius = " + radius; + } + + public static final class NeighborhoodsIterableInterval< T > extends AbstractInterval implements IterableInterval< Neighborhood< T > > + { + final RandomAccessibleInterval< T > source; + + final long radius; + + final long size; + + final HyperSphereNeighborhoodFactory< T > factory; + + public NeighborhoodsIterableInterval( final RandomAccessibleInterval< T > source, final long radius, final HyperSphereNeighborhoodFactory< T > factory ) + { + super( source ); + this.source = source; + this.radius = radius; + this.factory = factory; + + long s = source.dimension( 0 ); + for ( int d = 1; d < n; ++d ) + s *= source.dimension( d ); + size = s; + } + + @Override + public long size() + { + return size; + } + + @Override + public Neighborhood< T > firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Neighborhood< T >> iterator() + { + return cursor(); + } + + @Override + public Cursor< Neighborhood< T >> cursor() + { + return new HyperSphereNeighborhoodCursor< T >( source, radius, factory ); + } + + @Override + public Cursor< Neighborhood< T >> localizingCursor() + { + return cursor(); + } + } + + public static final class NeighborhoodsAccessible< T > extends AbstractEuclideanSpace implements RandomAccessible< Neighborhood< T > > + { + final RandomAccessible< T > source; + + final long radius; + + final HyperSphereNeighborhoodFactory< T > factory; + + public NeighborhoodsAccessible( final RandomAccessible< T > source, final long radius, final HyperSphereNeighborhoodFactory< T > factory ) + { + super( source.numDimensions() ); + this.source = source; + this.radius = radius; + this.factory = factory; + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess() + { + return new HyperSphereNeighborhoodRandomAccess< T >( source, radius, factory ); + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess( final Interval interval ) + { + return new HyperSphereNeighborhoodRandomAccess< T >( source, radius, factory, interval ); + } + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/HypersphereNeighborhoodLocalizableSampler.java b/src/main/java/varun_algorithm_neighborhood/HypersphereNeighborhoodLocalizableSampler.java new file mode 100644 index 0000000..104f33e --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/HypersphereNeighborhoodLocalizableSampler.java @@ -0,0 +1,156 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + + +public abstract class HypersphereNeighborhoodLocalizableSampler< T > extends AbstractEuclideanSpace implements Localizable, Sampler< Neighborhood< T > > +{ + protected final RandomAccessible< T > source; + + protected final long radius; + + protected final Interval sourceInterval; + + protected final HyperSphereNeighborhoodFactory< T > neighborhoodFactory; + + protected final Neighborhood< T > currentNeighborhood; + + protected final long[] currentPos; + + public HypersphereNeighborhoodLocalizableSampler( final RandomAccessible< T > source, final long radius, final HyperSphereNeighborhoodFactory< T > factory, Interval accessInterval ) + { + super( source.numDimensions() ); + this.source = source; + this.radius = radius; + neighborhoodFactory = factory; + currentPos = new long[ n ]; + + if ( accessInterval == null && source instanceof Interval ) + accessInterval = ( Interval ) source; + + if ( accessInterval == null ) + { + sourceInterval = null; + } + else + { + final long[] accessMin = new long[ n ]; + final long[] accessMax = new long[ n ]; + accessInterval.min( accessMin ); + accessInterval.max( accessMax ); + for ( int d = 0; d < n; ++d ) + { + accessMin[ d ] -= radius; + accessMax[ d ] += radius; + } + sourceInterval = new FinalInterval( accessMin, accessMax ); + } + currentNeighborhood = neighborhoodFactory.create( currentPos, radius, + sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + protected HypersphereNeighborhoodLocalizableSampler( final HypersphereNeighborhoodLocalizableSampler< T > c ) + { + super( c.n ); + source = c.source; + radius = c.radius; + sourceInterval = c.sourceInterval; + neighborhoodFactory = c.neighborhoodFactory; + currentPos = c.currentPos.clone(); + currentNeighborhood = neighborhoodFactory.create( currentPos, radius, + sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + @Override + public Neighborhood< T > get() + { + return currentNeighborhood; + } + + @Override + public void localize( final int[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return currentNeighborhood.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return currentNeighborhood.getLongPosition( d ); + } + + @Override + public void localize( final float[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return currentNeighborhood.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return currentNeighborhood.getDoublePosition( d ); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/Neighborhood.java b/src/main/java/varun_algorithm_neighborhood/Neighborhood.java new file mode 100644 index 0000000..98d0ad7 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/Neighborhood.java @@ -0,0 +1,44 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.Localizable; + +public interface Neighborhood< T > extends IterableInterval< T >, Localizable +{ + public Interval getStructuringElementBoundingBox(); +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhood.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhood.java new file mode 100644 index 0000000..acaf408 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhood.java @@ -0,0 +1,387 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; +import net.imglib2.util.Intervals; + + +public class PairOfPointsNeighborhood< T > extends AbstractLocalizable implements Neighborhood< T > +{ + public static < T > PairOfPointsNeighborhoodFactory< T > factory() + { + return new PairOfPointsNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long[] offset, final RandomAccess< T > sourceRandomAccess ) + { + return new PairOfPointsNeighborhood< T >( position, offset, sourceRandomAccess ); + } + }; + } + + private final long[] offset; + + private final int ndims; + + private final RandomAccess< T > ra; + + PairOfPointsNeighborhood( final long[] position, final long[] offset, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + ra = sourceRandomAccess.copyRandomAccess(); + this.offset = offset; + this.ndims = sourceRandomAccess.numDimensions(); + + } + + public class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private int index; + + private RandomAccess< T > source; + + private LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + reset(); + } + + private LocalCursor( final LocalCursor c ) + { + this( c.source.copyRandomAccess() ); + this.index = c.index; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + index++; + if ( index == 1 ) + { + for ( int d = 0; d < offset.length; d++ ) + { + source.move( offset[ d ], d ); + } + } + } + + @Override + public void jumpFwd( final long steps ) + { + for ( long i = 0; i < steps; ++i ) + { + fwd(); + } + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + index = -1; + source.setPosition( position ); + } + + @Override + public boolean hasNext() + { + return index < 1; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + for ( int d = 0; d < position.length; d++ ) + { + position[ d ] = source.getLongPosition( d ); + } + } + + @Override + public void localize( final float[] position ) + { + for ( int d = 0; d < position.length; d++ ) + { + position[ d ] = source.getFloatPosition( d ); + } + } + + @Override + public void localize( final double[] position ) + { + for ( int d = 0; d < position.length; d++ ) + { + position[ d ] = source.getDoublePosition( d ); + } + } + + @Override + public void localize( final int[] position ) + { + for ( int d = 0; d < position.length; d++ ) + { + position[ d ] = source.getIntPosition( d ); + } + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } + + @Override + public Interval getStructuringElementBoundingBox() + { + final long[] minmax = new long[ ndims * 2 ]; + for ( int d = 0; d < ndims; d++ ) + { + minmax[ d ] = Math.min( position[ d ], position[ d ] + offset[ d ] ); + } + for ( int d = ndims; d < 2 * ndims; d++ ) + { + final int sd = d - ndims; + minmax[ d ] = Math.max( position[ sd ], position[ sd ] + offset[ sd ] ); + } + return Intervals.createMinMax( minmax ); + } + + @Override + public long size() + { + return 2l; + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return Math.min( position[ d ], position[ d ] + offset[ d ] ); + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < min.length; d++ ) + { + min[ d ] = Math.min( position[ d ], position[ d ] + offset[ d ] ); + } + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < min.numDimensions(); d++ ) + { + min.setPosition( Math.min( position[ d ], position[ d ] + offset[ d ] ), d ); + } + } + + @Override + public double realMax( final int d ) + { + return Math.max( position[ d ], position[ d ] + offset[ d ] ); + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < max.length; d++ ) + { + max[ d ] = Math.max( position[ d ], position[ d ] + offset[ d ] ); + } + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < max.numDimensions(); d++ ) + { + max.setPosition( Math.max( position[ d ], position[ d ] + offset[ d ] ), d ); + } + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return Math.min( position[ d ], position[ d ] + offset[ d ] ); + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < min.length; d++ ) + { + min[ d ] = Math.min( position[ d ], position[ d ] + offset[ d ] ); + } + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < min.numDimensions(); d++ ) + { + min.setPosition( Math.min( position[ d ], position[ d ] + offset[ d ] ), d ); + } + } + + @Override + public long max( final int d ) + { + return Math.max( position[ d ], position[ d ] + offset[ d ] ); + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < max.length; d++ ) + { + max[ d ] = Math.max( position[ d ], position[ d ] + offset[ d ] ); + } + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < max.numDimensions(); d++ ) + { + max.setPosition( Math.max( position[ d ], position[ d ] + offset[ d ] ), d ); + } + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < dimensions.length; d++ ) + { + dimensions[ d ] = Math.abs( offset[ d ] ) + 1; + } + } + + @Override + public long dimension( final int d ) + { + return Math.abs( offset[ d ] ) + 1; + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( ra.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodCursor.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodCursor.java new file mode 100644 index 0000000..9fb007d --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodCursor.java @@ -0,0 +1,162 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.IntervalIndexer; + +public class PairOfPointsNeighborhoodCursor< T > extends PairOfPointsNeighborhoodLocalizableSampler< T > implements Cursor< Neighborhood< T > > +{ + private final long[] dimensions; + + private long index; + + private final long maxIndex; + + private long maxIndexOnLine; + + private long[] min; + + private long[] max; + + public PairOfPointsNeighborhoodCursor( final RandomAccessibleInterval< T > source, final long[] offset, final PairOfPointsNeighborhoodFactory< T > factory ) + { + super( source, offset, factory, source ); + + dimensions = new long[ n ]; + min = new long[ n ]; + max = new long[ n ]; + source.dimensions( dimensions ); + source.min( min ); + source.max( max ); + + long size = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + size *= dimensions[ d ]; + maxIndex = size - 1; + reset(); + } + + private PairOfPointsNeighborhoodCursor( final PairOfPointsNeighborhoodCursor< T > c ) + { + super( c ); + dimensions = c.dimensions.clone(); + maxIndex = c.maxIndex; + index = c.index; + maxIndexOnLine = c.maxIndexOnLine; + } + + @Override + public void fwd() + { + ++currentPos[ 0 ]; + if ( ++index > maxIndexOnLine ) + nextLine(); + } + + private void nextLine() + { + currentPos[ 0 ] = min[ 0 ]; + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + ++currentPos[ d ]; + if ( currentPos[ d ] > max[ d ] ) + { + currentPos[ d ] = min[ d ]; + } + else + break; + } + } + + @Override + public void reset() + { + index = -1; + maxIndexOnLine = -1; + System.arraycopy( max, 0, currentPos, 0, n ); + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public void jumpFwd( final long steps ) + { + index += steps; + if ( index < 0 ) + { + maxIndexOnLine = ( ( 1 + index ) / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + final long size = maxIndex + 1; + IntervalIndexer.indexToPositionWithOffset( size - ( -index % size ), dimensions, min, currentPos ); + } + else + { + maxIndexOnLine = ( 1 + index / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + IntervalIndexer.indexToPositionWithOffset( index, dimensions, min, currentPos ); + } + } + + @Override + public Neighborhood< T > next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public PairOfPointsNeighborhoodCursor< T > copy() + { + return new PairOfPointsNeighborhoodCursor< T >( this ); + } + + @Override + public PairOfPointsNeighborhoodCursor< T > copyCursor() + { + return copy(); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodFactory.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodFactory.java new file mode 100644 index 0000000..6ca98e1 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodFactory.java @@ -0,0 +1,42 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + + +public interface PairOfPointsNeighborhoodFactory< T > +{ + Neighborhood< T > create( long[] position, long[] offset, RandomAccess< T > sourceRandomAccess ); +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodLocalizableSampler.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodLocalizableSampler.java new file mode 100644 index 0000000..d55dd83 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodLocalizableSampler.java @@ -0,0 +1,155 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + + +public abstract class PairOfPointsNeighborhoodLocalizableSampler< T > extends AbstractEuclideanSpace implements Localizable, Sampler< Neighborhood< T > > +{ + protected final RandomAccessible< T > source; + + protected final Interval sourceInterval; + + protected final long[] offset; + + protected final long[] currentPos; + + protected final PairOfPointsNeighborhoodFactory< T > neighborhoodFactory; + + protected final Neighborhood< T > currentNeighborhood; + + public PairOfPointsNeighborhoodLocalizableSampler( final RandomAccessible< T > source, final long[] offset, final PairOfPointsNeighborhoodFactory< T > factory, Interval accessInterval ) + { + super( source.numDimensions() ); + this.source = source; + this.offset = offset; + this.currentPos = new long[ n ]; + neighborhoodFactory = factory; + + if ( accessInterval == null && source instanceof Interval ) + accessInterval = ( Interval ) source; + + if ( accessInterval == null ) + { + sourceInterval = null; + } + else + { + final long[] accessMin = new long[ n ]; + final long[] accessMax = new long[ n ]; + accessInterval.min( accessMin ); + accessInterval.max( accessMax ); + for ( int d = 0; d < n; ++d ) + { + accessMin[ d ] = Math.min( currentPos[ d ], currentPos[ d ] + offset[ d ] ); + accessMax[ d ] = Math.max( currentPos[ d ], currentPos[ d ] + offset[ d ] ); + } + sourceInterval = new FinalInterval( accessMin, accessMax ); + } + + currentNeighborhood = neighborhoodFactory.create( currentPos, offset, sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + protected PairOfPointsNeighborhoodLocalizableSampler( final PairOfPointsNeighborhoodLocalizableSampler< T > c ) + { + super( c.n ); + source = c.source; + sourceInterval = c.sourceInterval; + offset = c.offset.clone(); + neighborhoodFactory = c.neighborhoodFactory; + currentPos = c.currentPos.clone(); + currentNeighborhood = neighborhoodFactory.create( currentPos, offset, source.randomAccess() ); + } + + @Override + public Neighborhood< T > get() + { + return currentNeighborhood; + } + + @Override + public void localize( final int[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return currentNeighborhood.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return currentNeighborhood.getLongPosition( d ); + } + + @Override + public void localize( final float[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return currentNeighborhood.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return currentNeighborhood.getDoublePosition( d ); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodRandomAccess.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodRandomAccess.java new file mode 100644 index 0000000..d644057 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodRandomAccess.java @@ -0,0 +1,162 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + + +public class PairOfPointsNeighborhoodRandomAccess< T > extends PairOfPointsNeighborhoodLocalizableSampler< T > implements RandomAccess< Neighborhood< T > > +{ + public PairOfPointsNeighborhoodRandomAccess( final RandomAccessible< T > source, final long[] offset, final PairOfPointsNeighborhoodFactory< T > factory, final Interval interval ) + { + super( source, offset, factory, interval ); + } + + public PairOfPointsNeighborhoodRandomAccess( final RandomAccessible< T > source, final long[] offset, final PairOfPointsNeighborhoodFactory< T > factory ) + { + super( source, offset, factory, null ); + } + + private PairOfPointsNeighborhoodRandomAccess( final PairOfPointsNeighborhoodRandomAccess< T > c ) + { + super( c ); + } + + @Override + public void fwd( final int d ) + { + ++currentPos[ d ]; + } + + @Override + public void bck( final int d ) + { + --currentPos[ d ]; + } + + @Override + public void move( final int distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final long distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long distance = localizable.getLongPosition( d ); + currentPos[ d ] += distance; + } + } + + @Override + public void move( final int[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void move( final long[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void setPosition( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long position = localizable.getLongPosition( d ); + currentPos[ d ] = position; + } + } + + @Override + public void setPosition( final int[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final long[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final int position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public void setPosition( final long position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public PairOfPointsNeighborhoodRandomAccess< T > copy() + { + return new PairOfPointsNeighborhoodRandomAccess< T >( this ); + } + + @Override + public PairOfPointsNeighborhoodRandomAccess< T > copyRandomAccess() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodUnsafe.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodUnsafe.java new file mode 100644 index 0000000..9fbc2ac --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsNeighborhoodUnsafe.java @@ -0,0 +1,79 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + + +public class PairOfPointsNeighborhoodUnsafe< T > extends PairOfPointsNeighborhood< T > +{ + + public static < T > PairOfPointsNeighborhoodFactory< T > factory() + { + return new PairOfPointsNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long[] offset, final RandomAccess< T > sourceRandomAccess ) + { + return new PairOfPointsNeighborhoodUnsafe< T >( position, offset, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + PairOfPointsNeighborhoodUnsafe( final long[] position, final long[] offset, final RandomAccess< T > sourceRandomAccess ) + { + super( position, offset, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PairOfPointsShape.java b/src/main/java/varun_algorithm_neighborhood/PairOfPointsShape.java new file mode 100644 index 0000000..5f4d156 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PairOfPointsShape.java @@ -0,0 +1,205 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractInterval; +import net.imglib2.Cursor; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.Util; + +/** + * A {@link Shape} representing a pair of points. + *

      + * The Shape as its origin at the first point, and the second one is simply + * found by adding the value of the {@code offset} array to its position. + * + * @author Jean-Yves Tinevez, 2013 + */ +public class PairOfPointsShape implements Shape +{ + private final long[] offset; + + /** + * Create a new pair of points shape. + *

      + * + * @param offset + * the offset of the second point with respect to the origin, as + * a {@code long[]} array. + */ + public PairOfPointsShape( final long[] offset ) + { + this.offset = offset; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + final PairOfPointsNeighborhoodFactory< T > f = PairOfPointsNeighborhoodUnsafe.< T >factory(); + return new NeighborhoodsIterableInterval< T >( source, offset, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + final PairOfPointsNeighborhoodFactory< T > f = PairOfPointsNeighborhoodUnsafe.< T >factory(); + return new NeighborhoodsAccessible< T >( source, offset, f ); + } + + @Override + public < T > IterableInterval< Neighborhood< T >> neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + final PairOfPointsNeighborhoodFactory< T > f = PairOfPointsNeighborhood.< T >factory(); + return new NeighborhoodsIterableInterval< T >( source, offset, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + final PairOfPointsNeighborhoodFactory< T > f = PairOfPointsNeighborhood.< T >factory(); + return new NeighborhoodsAccessible< T >( source, offset, f ); + } + + /** + * @return Copy of the offset of this shape. + */ + public long[] getOffset() + { + return offset.clone(); + } + + @Override + public String toString() + { + return "PairOfPointsShape, offset = " + Util.printCoordinates( offset ); + } + + public static final class NeighborhoodsIterableInterval< T > extends AbstractInterval implements IterableInterval< Neighborhood< T > > + { + final RandomAccessibleInterval< T > source; + + final PairOfPointsNeighborhoodFactory< T > factory; + + final long[] offset; + + public NeighborhoodsIterableInterval( final RandomAccessibleInterval< T > source, final long[] offset, final PairOfPointsNeighborhoodFactory< T > factory ) + { + super( source ); + this.source = source; + this.offset = offset; + this.factory = factory; + } + + @Override + public Cursor< Neighborhood< T >> cursor() + { + return new PairOfPointsNeighborhoodCursor< T >( source, offset, factory ); + } + + @Override + public long size() + { + return 2; + } + + @Override + public Neighborhood< T > firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Neighborhood< T >> iterator() + { + return cursor(); + } + + @Override + public Cursor< Neighborhood< T >> localizingCursor() + { + return cursor(); + } + } + + public static final class NeighborhoodsAccessible< T > extends AbstractEuclideanSpace implements RandomAccessible< Neighborhood< T > > + { + final RandomAccessible< T > source; + + final PairOfPointsNeighborhoodFactory< T > factory; + + private final long[] offset; + + public NeighborhoodsAccessible( final RandomAccessible< T > source, final long[] offset, final PairOfPointsNeighborhoodFactory< T > factory ) + { + super( source.numDimensions() ); + this.source = source; + this.offset = offset; + this.factory = factory; + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess() + { + return new PairOfPointsNeighborhoodRandomAccess< T >( source, offset, factory ); + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess( final Interval interval ) + { + return randomAccess(); + } + + @Override + public int numDimensions() + { + return source.numDimensions(); + } + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhood.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhood.java new file mode 100644 index 0000000..0180ac5 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhood.java @@ -0,0 +1,429 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; + +import net.imglib2.util.Intervals; + +/** + * A neighborhood that iterates over what is termed "Periodic lines", and best + * explained in Ronald Jones and Pierre Soilles publication: + *

      + * Jones and Soilles. Periodic lines: Definition, cascades, and application + * to granulometries. Pattern Recognition Letters (1996) vol. 17 (10) pp. 1057-1063 + * + * @author Jean-Yves Tinevez Sep 3, 2013 + * + * @param + */ +public class PeriodicLineNeighborhood< T > extends AbstractLocalizable implements Neighborhood< T > +{ + public static < T > PeriodicLineNeighborhoodFactory< T > factory() + { + return new PeriodicLineNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long span, final int[] increments, final RandomAccess< T > sourceRandomAccess ) + { + return new PeriodicLineNeighborhood< T >( position, span, increments, sourceRandomAccess ); + } + }; + } + + /** + * The span of the line. The neighborhood will have + * {@code 2 x span + 1 } elements iterated. Careful: because of the + * increments, the extent of the neighborhood might be much larger. + */ + private final long span; + + /** + * The increments for the periodic line. The elements iterated have an index + * calculated from: + * + *

      +	 * position - span x increments,
      +	 * ...
      +	 * position - 2 × increments,
      +	 * position - increments,
      +	 * position,
      +	 * position + increments,
      +	 * position + 2 × increments,
      +	 * ...
      +	 * position + span x increments
      +	 * 
      + */ + private final int[] increments; + + private final RandomAccess< T > sourceRandomAccess; + + private final Interval structuringElementBoundingBox; + + private final long maxIndex; + + /** + * Creates a new periodic line, centered on {@code position}, and that + * will iterate over {@code 2 × span + 1} pixels as follow: + * + *
      +	 * position - span x increments,
      +	 * ...
      +	 * position - 2 × increments,
      +	 * position - increments,
      +	 * position,
      +	 * position + increments,
      +	 * position + 2 × increments,
      +	 * ...
      +	 * position + span x increments
      +	 * 
      + * + * @param position + * the center of the neighborhood. + * @param span + * the span of the neighborhood, so that it will iterate over + * {@code 2 × span + 1} pixels. + * @param increments + * the values by which each element of the position vector is to + * be incremented when iterating. + * @param sourceRandomAccess + * a {@link RandomAccess} over the source. + */ + PeriodicLineNeighborhood( final long[] position, final long span, final int[] increments, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + this.increments = increments; + this.span = span; + this.maxIndex = 2 * span + 1; + this.sourceRandomAccess = sourceRandomAccess; + this.structuringElementBoundingBox = createInterval(); + } + + @Override + public Interval getStructuringElementBoundingBox() + { + return structuringElementBoundingBox; + } + + @Override + public long size() + { + return maxIndex; + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return structuringElementBoundingBox.realMin( d ); + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = structuringElementBoundingBox.realMin( d ); + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( structuringElementBoundingBox.realMin( d ), d ); + } + + @Override + public double realMax( final int d ) + { + return structuringElementBoundingBox.realMax( d ); + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = structuringElementBoundingBox.realMax( d ); + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( structuringElementBoundingBox.realMax( d ), d ); + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return structuringElementBoundingBox.min( d ); + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = structuringElementBoundingBox.min( d ); + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( structuringElementBoundingBox.min( d ), d ); + } + + @Override + public long max( final int d ) + { + return structuringElementBoundingBox.max( d ); + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = structuringElementBoundingBox.max( d ); + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( structuringElementBoundingBox.max( d ), d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < n; ++d ) + dimensions[ d ] = dimension( d ); + } + + @Override + public long dimension( final int d ) + { + return 1 + 2 * span * Math.abs( increments[ d ] ); + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( sourceRandomAccess.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + + public final class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private final RandomAccess< T > source; + + private long index; + + private LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + reset(); + } + + private LocalCursor( final LocalCursor c ) + { + super( c.numDimensions() ); + source = c.source.copyRandomAccess(); + index = c.index; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + source.move( increments ); + index++; + } + + @Override + public void jumpFwd( final long steps ) + { + for ( int i = 0; i < steps; i++ ) + { + fwd(); + } + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + source.setPosition( position ); + final int[] minusIncrements = new int[ increments.length ]; + for ( int i = 0; i < minusIncrements.length; i++ ) + { + minusIncrements[ i ] = -increments[ i ]; + + } + for ( int i = 0; i <= span; i++ ) + { + source.move( minusIncrements ); + } + index = 0; + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } + + private Interval createInterval() + { + final long[] minmax = new long[ 2 * position.length ]; + for ( int i = 0; i < position.length; i++ ) + { + minmax[ i ] = position[ i ] - span * Math.abs( increments[ i ] ); + } + for ( int i = position.length; i < minmax.length; i++ ) + { + minmax[ i ] = position[ i - position.length ] + span * Math.abs( increments[ i - position.length ] ); + } + return Intervals.createMinMax( minmax ); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodCursor.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodCursor.java new file mode 100644 index 0000000..ead6bff --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodCursor.java @@ -0,0 +1,162 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.IntervalIndexer; + +public class PeriodicLineNeighborhoodCursor< T > extends PeriodicLineNeighborhoodLocalizableSampler< T > implements Cursor< Neighborhood< T > > +{ + private final long[] dimensions; + + private long index; + + private final long maxIndex; + + private long maxIndexOnLine; + + private long[] min; + + private long[] max; + + public PeriodicLineNeighborhoodCursor( final RandomAccessibleInterval< T > source, final long span, final int[] increments, final PeriodicLineNeighborhoodFactory< T > factory ) + { + super( source, span, increments, factory, source ); + + dimensions = new long[ n ]; + min = new long[ n ]; + max = new long[ n ]; + source.dimensions( dimensions ); + source.min( min ); + source.max( max ); + + long size = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + size *= dimensions[ d ]; + maxIndex = size - 1; + reset(); + } + + private PeriodicLineNeighborhoodCursor( final PeriodicLineNeighborhoodCursor< T > c ) + { + super( c ); + dimensions = c.dimensions.clone(); + maxIndex = c.maxIndex; + index = c.index; + maxIndexOnLine = c.maxIndexOnLine; + } + + @Override + public void fwd() + { + ++currentPos[ 0 ]; + if ( ++index > maxIndexOnLine ) + nextLine(); + } + + private void nextLine() + { + currentPos[ 0 ] = min[ 0 ]; + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + ++currentPos[ d ]; + if ( currentPos[ d ] > max[ d ] ) + { + currentPos[ d ] = min[ d ]; + } + else + break; + } + } + + @Override + public void reset() + { + index = -1; + maxIndexOnLine = -1; + System.arraycopy( max, 0, currentPos, 0, n ); + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public void jumpFwd( final long steps ) + { + index += steps; + if ( index < 0 ) + { + maxIndexOnLine = ( ( 1 + index ) / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + final long size = maxIndex + 1; + IntervalIndexer.indexToPositionWithOffset( size - ( -index % size ), dimensions, min, currentPos ); + } + else + { + maxIndexOnLine = ( 1 + index / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + IntervalIndexer.indexToPositionWithOffset( index, dimensions, min, currentPos ); + } + } + + @Override + public Neighborhood< T > next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public PeriodicLineNeighborhoodCursor< T > copy() + { + return new PeriodicLineNeighborhoodCursor< T >( this ); + } + + @Override + public PeriodicLineNeighborhoodCursor< T > copyCursor() + { + return copy(); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodFactory.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodFactory.java new file mode 100644 index 0000000..50e5972 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodFactory.java @@ -0,0 +1,41 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + +public interface PeriodicLineNeighborhoodFactory< T > +{ + public Neighborhood< T > create( final long[] position, final long span, final int[] increments, final RandomAccess< T > sourceRandomAccess ); +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodLocalizableSampler.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodLocalizableSampler.java new file mode 100644 index 0000000..ae14d33 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodLocalizableSampler.java @@ -0,0 +1,158 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + + +public abstract class PeriodicLineNeighborhoodLocalizableSampler< T > extends AbstractEuclideanSpace implements Localizable, Sampler< Neighborhood< T > > +{ + protected final RandomAccessible< T > source; + + protected final Interval sourceInterval; + + protected final long span; + + protected final long[] currentPos; + + protected final PeriodicLineNeighborhoodFactory< T > neighborhoodFactory; + + protected final Neighborhood< T > currentNeighborhood; + + private final int[] increments; + + public PeriodicLineNeighborhoodLocalizableSampler( final RandomAccessible< T > source, final long span, final int[] increments, final PeriodicLineNeighborhoodFactory< T > factory, Interval accessInterval ) + { + super( source.numDimensions() ); + this.source = source; + this.span = span; + this.increments = increments; + this.currentPos = new long[ n ]; + neighborhoodFactory = factory; + + if ( accessInterval == null && source instanceof Interval ) + accessInterval = ( Interval ) source; + + if ( accessInterval == null ) + { + sourceInterval = null; + } + else + { + final long[] accessMin = new long[ n ]; + final long[] accessMax = new long[ n ]; + accessInterval.min( accessMin ); + accessInterval.max( accessMax ); + for ( int d = 0; d < n; ++d ) + { + accessMin[ d ] = currentPos[ d ] - increments[ d ] * span; + accessMax[ d ] = currentPos[ d ] + increments[ d ] * span; + } + sourceInterval = new FinalInterval( accessMin, accessMax ); + } + currentNeighborhood = neighborhoodFactory.create( currentPos, span, increments, sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + protected PeriodicLineNeighborhoodLocalizableSampler( final PeriodicLineNeighborhoodLocalizableSampler< T > c ) + { + super( c.n ); + source = c.source; + sourceInterval = c.sourceInterval; + span = c.span; + increments = c.increments.clone(); + neighborhoodFactory = c.neighborhoodFactory; + currentPos = c.currentPos.clone(); + currentNeighborhood = neighborhoodFactory.create( currentPos, span, increments, source.randomAccess() ); + } + + @Override + public Neighborhood< T > get() + { + return currentNeighborhood; + } + + @Override + public void localize( final int[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return currentNeighborhood.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return currentNeighborhood.getLongPosition( d ); + } + + @Override + public void localize( final float[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return currentNeighborhood.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return currentNeighborhood.getDoublePosition( d ); + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodRandomAccess.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodRandomAccess.java new file mode 100644 index 0000000..0aa8b1f --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodRandomAccess.java @@ -0,0 +1,162 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + + +public class PeriodicLineNeighborhoodRandomAccess< T > extends PeriodicLineNeighborhoodLocalizableSampler< T > implements RandomAccess< Neighborhood< T > > +{ + public PeriodicLineNeighborhoodRandomAccess( final RandomAccessible< T > source, final long span, final int[] increments, final PeriodicLineNeighborhoodFactory< T > factory, final Interval interval ) + { + super( source, span, increments, factory, interval ); + } + + public PeriodicLineNeighborhoodRandomAccess( final RandomAccessible< T > source, final long span, final int[] increments, final PeriodicLineNeighborhoodFactory< T > factory ) + { + super( source, span, increments, factory, null ); + } + + private PeriodicLineNeighborhoodRandomAccess( final PeriodicLineNeighborhoodRandomAccess< T > c ) + { + super( c ); + } + + @Override + public void fwd( final int d ) + { + ++currentPos[ d ]; + } + + @Override + public void bck( final int d ) + { + --currentPos[ d ]; + } + + @Override + public void move( final int distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final long distance, final int d ) + { + currentPos[ d ] += distance; + } + + @Override + public void move( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long distance = localizable.getLongPosition( d ); + currentPos[ d ] += distance; + } + } + + @Override + public void move( final int[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void move( final long[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + } + } + + @Override + public void setPosition( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long position = localizable.getLongPosition( d ); + currentPos[ d ] = position; + } + } + + @Override + public void setPosition( final int[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final long[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + } + } + + @Override + public void setPosition( final int position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public void setPosition( final long position, final int d ) + { + currentPos[ d ] = position; + } + + @Override + public PeriodicLineNeighborhoodRandomAccess< T > copy() + { + return new PeriodicLineNeighborhoodRandomAccess< T >( this ); + } + + @Override + public PeriodicLineNeighborhoodRandomAccess< T > copyRandomAccess() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodUnsafe.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodUnsafe.java new file mode 100644 index 0000000..c8a31e7 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineNeighborhoodUnsafe.java @@ -0,0 +1,79 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import net.imglib2.RandomAccess; + + +public class PeriodicLineNeighborhoodUnsafe< T > extends PeriodicLineNeighborhood< T > +{ + + public static < T > PeriodicLineNeighborhoodFactory< T > factory() + { + return new PeriodicLineNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long span, final int[] increments, final RandomAccess< T > sourceRandomAccess ) + { + return new PeriodicLineNeighborhoodUnsafe< T >( position, span, increments, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + PeriodicLineNeighborhoodUnsafe( final long[] position, final long span, final int[] increments, final RandomAccess< T > sourceRandomAccess ) + { + super( position, span, increments, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/PeriodicLineShape.java b/src/main/java/varun_algorithm_neighborhood/PeriodicLineShape.java new file mode 100644 index 0000000..ccfdbea --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/PeriodicLineShape.java @@ -0,0 +1,247 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractInterval; +import net.imglib2.Cursor; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.Util; + +/** + * A factory for Accessibles on {@link PeriodicLineNeighborhood}s, that iterate + * over what is termed "Periodic lines", and is best explained in Ronald Jones + * and Pierre Soilles publication: + *

      + * Jones and Soilles. Periodic lines: Definition, cascades, and application + * to granulometries. Pattern Recognition Letters (1996) vol. 17 (10) pp. 1057-1063 + * + * @author Jean-Yves Tinevez, 2013 + * @author Jonathan Hale (University of Konstanz) + */ +public class PeriodicLineShape implements Shape +{ + private final long span; + + private final int[] increments; + + /** + * Creates a new periodic line shape, that will iterate over + * {@code 2 × span + 1} pixels as follow: + * + *

      +	 * position - span x increments,
      +	 * ...
      +	 * position - 2 × increments,
      +	 * position - increments,
      +	 * position,
      +	 * position + increments,
      +	 * position + 2 × increments,
      +	 * ...
      +	 * position + span x increments
      +	 * 
      + * + * @param span + * the span of the neighborhood, so that it will iterate over + * {@code 2 × span + 1} pixels. Must be positive. + * @param increments + * the values by which each element of the position vector is to + * be incremented when iterating. + */ + public PeriodicLineShape( final long span, final int[] increments ) + { + if ( span < 0 ) { throw new IllegalArgumentException( "Span cannot be negative." ); } + this.span = span; + this.increments = increments; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + final PeriodicLineNeighborhoodFactory< T > f = PeriodicLineNeighborhoodUnsafe.< T >factory(); + return new NeighborhoodsIterableInterval< T >( source, span, increments, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + final PeriodicLineNeighborhoodFactory< T > f = PeriodicLineNeighborhoodUnsafe.< T >factory(); + return new NeighborhoodsAccessible< T >( source, span, increments, f ); + } + + @Override + public < T > IterableInterval< Neighborhood< T >> neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + final PeriodicLineNeighborhoodFactory< T > f = PeriodicLineNeighborhood.< T >factory(); + return new NeighborhoodsIterableInterval< T >( source, span, increments, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + final PeriodicLineNeighborhoodFactory< T > f = PeriodicLineNeighborhood.< T >factory(); + return new NeighborhoodsAccessible< T >( source, span, increments, f ); + } + + /** + * @return The span of this shape. + */ + public long getSpan() + { + return span; + } + + /** + * @return Copy of the increments of this shape. + */ + public int[] getIncrements() + { + return increments.clone(); + } + + @Override + public String toString() + { + return "PeriodicLineShape, span = " + span + ", increments = " + Util.printCoordinates( increments ); + } + + public static final class NeighborhoodsIterableInterval< T > extends AbstractInterval implements IterableInterval< Neighborhood< T > > + { + final RandomAccessibleInterval< T > source; + + final long span; + + final PeriodicLineNeighborhoodFactory< T > factory; + + final long size; + + final int[] increments; + + public NeighborhoodsIterableInterval( final RandomAccessibleInterval< T > source, final long span, final int[] increments, final PeriodicLineNeighborhoodFactory< T > factory ) + { + super( source ); + this.source = source; + this.span = span; + this.increments = increments; + this.factory = factory; + long s = source.dimension( 0 ); + for ( int d = 1; d < n; ++d ) + s *= source.dimension( d ); + size = s; + } + + @Override + public Cursor< Neighborhood< T >> cursor() + { + return new PeriodicLineNeighborhoodCursor< T >( source, span, increments, factory ); + } + + @Override + public long size() + { + return size; + } + + @Override + public Neighborhood< T > firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Neighborhood< T >> iterator() + { + return cursor(); + } + + @Override + public Cursor< Neighborhood< T >> localizingCursor() + { + return cursor(); + } + } + + public static final class NeighborhoodsAccessible< T > extends AbstractEuclideanSpace implements RandomAccessible< Neighborhood< T > > + { + final RandomAccessible< T > source; + + final PeriodicLineNeighborhoodFactory< T > factory; + + private final long span; + + private final int[] increments; + + public NeighborhoodsAccessible( final RandomAccessible< T > source, final long span, final int[] increments, final PeriodicLineNeighborhoodFactory< T > factory ) + { + super( source.numDimensions() ); + this.source = source; + this.span = span; + this.increments = increments; + this.factory = factory; + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess() + { + return new PeriodicLineNeighborhoodRandomAccess< T >( source, span, increments, factory ); + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess( final Interval interval ) + { + return randomAccess(); + } + + @Override + public int numDimensions() + { + return source.numDimensions(); + } + } + +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhood.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhood.java new file mode 100644 index 0000000..defc00d --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhood.java @@ -0,0 +1,379 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; + + +public class RectangleNeighborhood< T > extends AbstractLocalizable implements Neighborhood< T > +{ + public static < T > RectangleNeighborhoodFactory< T > factory() + { + return new RectangleNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + return new RectangleNeighborhood< T >( position, currentMin, currentMax, span, sourceRandomAccess ); + } + }; + } + + private final long[] currentMin; + + private final long[] currentMax; + + private final long[] dimensions; + + private final RandomAccess< T > sourceRandomAccess; + + private final Interval structuringElementBoundingBox; + + private final long maxIndex; + + RectangleNeighborhood( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + this.currentMin = currentMin; + this.currentMax = currentMax; + dimensions = new long[ n ]; + span.dimensions( dimensions ); + + long mi = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + mi *= dimensions[ d ]; + maxIndex = mi; + + this.sourceRandomAccess = sourceRandomAccess; + this.structuringElementBoundingBox = span; + } + + @Override + public Interval getStructuringElementBoundingBox() + { + return structuringElementBoundingBox; + } + + /** + * @return the sourceRandomAccess + */ + public RandomAccess< T > getSourceRandomAccess() + { + return sourceRandomAccess; + } + + @Override + public long size() + { + return maxIndex; // -1 because we skip the center pixel + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return currentMin[ d ]; + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = currentMin[ d ]; + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( currentMin[ d ], d ); + } + + @Override + public double realMax( final int d ) + { + return currentMax[ d ]; + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = currentMax[ d ]; + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( currentMax[ d ], d ); + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return currentMin[ d ]; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = currentMin[ d ]; + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( currentMin[ d ], d ); + } + + @Override + public long max( final int d ) + { + return currentMax[ d ]; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = currentMax[ d ]; + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( currentMax[ d ], d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < n; ++d ) + dimensions[ d ] = this.dimensions[ d ]; + } + + @Override + public long dimension( final int d ) + { + return dimensions[ d ]; + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( sourceRandomAccess.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + + public final class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private final RandomAccess< T > source; + + private long index; + + private long maxIndexOnLine; + + public LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + reset(); + } + + protected LocalCursor( final LocalCursor c ) + { + super( c.numDimensions() ); + source = c.source.copyRandomAccess(); + index = c.index; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + source.fwd( 0 ); + if ( ++index > maxIndexOnLine ) + nextLine(); + } + + private void nextLine() + { + source.setPosition( currentMin[ 0 ], 0 ); + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + source.fwd( d ); + if ( source.getLongPosition( d ) > currentMax[ d ] ) + source.setPosition( currentMin[ d ], d ); + else + break; + } + } + + @Override + public void jumpFwd( final long steps ) + { + for ( long i = 0; i < steps; ++i ) + fwd(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + source.setPosition( currentMin ); + source.bck( 0 ); + index = 0; + maxIndexOnLine = dimensions[ 0 ]; + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodCursor.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodCursor.java new file mode 100644 index 0000000..1a4493e --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodCursor.java @@ -0,0 +1,182 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.RandomAccessibleInterval; + +import net.imglib2.util.IntervalIndexer; + +public final class RectangleNeighborhoodCursor< T > extends RectangleNeighborhoodLocalizableSampler< T > implements Cursor< Neighborhood< T > > +{ + private final long[] dimensions; + + private final long[] min; + + private final long[] max; + + private long index; + + private final long maxIndex; + + private long maxIndexOnLine; + + public RectangleNeighborhoodCursor( final RandomAccessibleInterval< T > source, final Interval span, final RectangleNeighborhoodFactory< T > factory ) + { + super( source, span, factory, source ); + + dimensions = new long[ n ]; + min = new long[ n ]; + max = new long[ n ]; + source.dimensions( dimensions ); + source.min( min ); + source.max( max ); + long size = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + size *= dimensions[ d ]; + maxIndex = size - 1; + reset(); + } + + private RectangleNeighborhoodCursor( final RectangleNeighborhoodCursor< T > c ) + { + super( c ); + dimensions = c.dimensions.clone(); + min = c.min.clone(); + max = c.max.clone(); + maxIndex = c.maxIndex; + index = c.index; + maxIndexOnLine = c.maxIndexOnLine; + } + + @Override + public void fwd() + { + ++currentPos[ 0 ]; + ++currentMin[ 0 ]; + ++currentMax[ 0 ]; + if ( ++index > maxIndexOnLine ) + nextLine(); + } + + private void nextLine() + { + currentPos[ 0 ] = min[ 0 ]; + currentMin[ 0 ] -= dimensions[ 0 ]; + currentMax[ 0 ] -= dimensions[ 0 ]; + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + ++currentPos[ d ]; + ++currentMin[ d ]; + ++currentMax[ d ]; + if ( currentPos[ d ] > max[ d ] ) + { + currentPos[ d ] = min[ d ]; + currentMin[ d ] -= dimensions[ d ]; + currentMax[ d ] -= dimensions[ d ]; + } + else + break; + } + } + + @Override + public void reset() + { + index = -1; + maxIndexOnLine = -1; + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = max[ d ]; + currentMin[ d ] = currentPos[ d ] + span.min( d ); + currentMax[ d ] = currentPos[ d ] + span.max( d ); + } + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public void jumpFwd( final long steps ) + { + index += steps; + if ( index < 0 ) + { + maxIndexOnLine = ( ( 1 + index ) / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + final long size = maxIndex + 1; + IntervalIndexer.indexToPositionWithOffset( size - ( -index % size ), dimensions, min, currentPos ); + } + else + { + maxIndexOnLine = ( 1 + index / dimensions[ 0 ] ) * dimensions[ 0 ] - 1; + IntervalIndexer.indexToPositionWithOffset( index, dimensions, min, currentPos ); + } + for ( int d = 0; d < n; ++d ) + { + currentMin[ d ] = currentPos[ d ] + span.min( d ); + currentMax[ d ] = currentPos[ d ] + span.max( d ); + } + } + + @Override + public Neighborhood< T > next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public RectangleNeighborhoodCursor< T > copy() + { + return new RectangleNeighborhoodCursor< T >( this ); + } + + @Override + public RectangleNeighborhoodCursor< T > copyCursor() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodFactory.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodFactory.java new file mode 100644 index 0000000..0125142 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodFactory.java @@ -0,0 +1,43 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.RandomAccess; + +public interface RectangleNeighborhoodFactory< T > +{ + public Neighborhood< T > create( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ); +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodLocalizableSampler.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodLocalizableSampler.java new file mode 100644 index 0000000..9fab482 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodLocalizableSampler.java @@ -0,0 +1,164 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + + +public abstract class RectangleNeighborhoodLocalizableSampler< T > extends AbstractEuclideanSpace implements Localizable, Sampler< Neighborhood< T > > +{ + protected final RandomAccessible< T > source; + + protected final Interval span; + + protected final Interval sourceInterval; + + protected final RectangleNeighborhoodFactory< T > neighborhoodFactory; + + protected final Neighborhood< T > currentNeighborhood; + + protected final long[] currentPos; + + protected final long[] currentMin; + + protected final long[] currentMax; + + public RectangleNeighborhoodLocalizableSampler( final RandomAccessible< T > source, final Interval span, final RectangleNeighborhoodFactory< T > factory, Interval accessInterval ) + { + super( source.numDimensions() ); + this.source = source; + this.span = span; + neighborhoodFactory = factory; + currentPos = new long[ n ]; + currentMin = new long[ n ]; + currentMax = new long[ n ]; + + if ( accessInterval == null && source instanceof Interval ) + accessInterval = ( Interval ) source; + + if ( accessInterval == null ) + { + sourceInterval = null; + } + else + { + final long[] accessMin = new long[ n ]; + final long[] accessMax = new long[ n ]; + accessInterval.min( accessMin ); + accessInterval.max( accessMax ); + for ( int d = 0; d < n; ++d ) + { + accessMin[ d ] += span.min( d ); + accessMax[ d ] += span.max( d ); + } + sourceInterval = new FinalInterval( accessMin, accessMax ); + } + currentNeighborhood = neighborhoodFactory.create( currentPos, currentMin, currentMax, span, + sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + protected RectangleNeighborhoodLocalizableSampler( final RectangleNeighborhoodLocalizableSampler< T > c ) + { + super( c.n ); + source = c.source; + span = c.span; + sourceInterval = c.sourceInterval; + neighborhoodFactory = c.neighborhoodFactory; + currentPos = c.currentPos.clone(); + currentMin = c.currentMin.clone(); + currentMax = c.currentMax.clone(); + currentNeighborhood = neighborhoodFactory.create( currentPos, currentMin, currentMax, span, + sourceInterval == null ? source.randomAccess() : source.randomAccess( sourceInterval ) ); + } + + @Override + public Neighborhood< T > get() + { + return currentNeighborhood; + } + + @Override + public void localize( final int[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return currentNeighborhood.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return currentNeighborhood.getLongPosition( d ); + } + + @Override + public void localize( final float[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + currentNeighborhood.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return currentNeighborhood.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return currentNeighborhood.getDoublePosition( d ); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodRandomAccess.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodRandomAccess.java new file mode 100644 index 0000000..3bc68be --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodRandomAccess.java @@ -0,0 +1,187 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + + +public final class RectangleNeighborhoodRandomAccess< T > extends RectangleNeighborhoodLocalizableSampler< T > implements RandomAccess< Neighborhood< T > > +{ + public RectangleNeighborhoodRandomAccess( final RandomAccessible< T > source, final Interval span, final RectangleNeighborhoodFactory< T > factory ) + { + super( source, span, factory, null ); + } + + public RectangleNeighborhoodRandomAccess( final RandomAccessible< T > source, final Interval span, final RectangleNeighborhoodFactory< T > factory, final Interval interval ) + { + super( source, span, factory, interval ); + } + + private RectangleNeighborhoodRandomAccess( final RectangleNeighborhoodRandomAccess< T > c ) + { + super( c ); + } + + @Override + public void fwd( final int d ) + { + ++currentPos[ d ]; + ++currentMin[ d ]; + ++currentMax[ d ]; + } + + @Override + public void bck( final int d ) + { + --currentPos[ d ]; + --currentMin[ d ]; + --currentMax[ d ]; + } + + @Override + public void move( final int distance, final int d ) + { + currentPos[ d ] += distance; + currentMin[ d ] += distance; + currentMax[ d ] += distance; + } + + @Override + public void move( final long distance, final int d ) + { + currentPos[ d ] += distance; + currentMin[ d ] += distance; + currentMax[ d ] += distance; + } + + @Override + public void move( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long distance = localizable.getLongPosition( d ); + currentPos[ d ] += distance; + currentMin[ d ] += distance; + currentMax[ d ] += distance; + } + } + + @Override + public void move( final int[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + currentMin[ d ] += distance[ d ]; + currentMax[ d ] += distance[ d ]; + } + } + + @Override + public void move( final long[] distance ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] += distance[ d ]; + currentMin[ d ] += distance[ d ]; + currentMax[ d ] += distance[ d ]; + } + } + + @Override + public void setPosition( final Localizable localizable ) + { + for ( int d = 0; d < n; ++d ) + { + final long position = localizable.getLongPosition( d ); + currentPos[ d ] = position; + currentMin[ d ] = position + span.min( d ); + currentMax[ d ] = position + span.max( d ); + } + } + + @Override + public void setPosition( final int[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + currentMin[ d ] = position[ d ] + span.min( d ); + currentMax[ d ] = position[ d ] + span.max( d ); + } + } + + @Override + public void setPosition( final long[] position ) + { + for ( int d = 0; d < n; ++d ) + { + currentPos[ d ] = position[ d ]; + currentMin[ d ] = position[ d ] + span.min( d ); + currentMax[ d ] = position[ d ] + span.max( d ); + } + } + + @Override + public void setPosition( final int position, final int d ) + { + currentPos[ d ] = position; + currentMin[ d ] = position + span.min( d ); + currentMax[ d ] = position + span.max( d ); + } + + @Override + public void setPosition( final long position, final int d ) + { + currentPos[ d ] = position; + currentMin[ d ] = position + span.min( d ); + currentMax[ d ] = position + span.max( d ); + } + + @Override + public RectangleNeighborhoodRandomAccess< T > copy() + { + return new RectangleNeighborhoodRandomAccess< T >( this ); + } + + @Override + public RectangleNeighborhoodRandomAccess< T > copyRandomAccess() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenter.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenter.java new file mode 100644 index 0000000..52768ae --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenter.java @@ -0,0 +1,381 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractLocalizable; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.Positionable; +import net.imglib2.RandomAccess; +import net.imglib2.RealPositionable; + +import net.imglib2.util.IntervalIndexer; + +public class RectangleNeighborhoodSkipCenter< T > extends AbstractLocalizable implements Neighborhood< T > +{ + public static < T > RectangleNeighborhoodFactory< T > factory() + { + return new RectangleNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + return new RectangleNeighborhoodSkipCenter< T >( position, currentMin, currentMax, span, sourceRandomAccess ); + } + }; + } + + private final long[] currentMin; + + private final long[] currentMax; + + private final long[] dimensions; + + private final RandomAccess< T > sourceRandomAccess; + + private final Interval structuringElementBoundingBox; + + private final long maxIndex; + + private final long midIndex; + + RectangleNeighborhoodSkipCenter( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + super( position ); + this.currentMin = currentMin; + this.currentMax = currentMax; + dimensions = new long[ n ]; + span.dimensions( dimensions ); + + long mi = dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + mi *= dimensions[ d ]; + maxIndex = mi; + + final long[] centerOffset = new long[ n ]; + for ( int d = 0; d < n; ++d ) + centerOffset[ d ] = -span.min( d ); + midIndex = IntervalIndexer.positionToIndex( centerOffset, dimensions ) + 1; + + this.sourceRandomAccess = sourceRandomAccess; + this.structuringElementBoundingBox = span; + } + + @Override + public Interval getStructuringElementBoundingBox() + { + return structuringElementBoundingBox; + } + + @Override + public long size() + { + return maxIndex - 1; // -1 because we skip the center pixel + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return currentMin[ d ]; + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = currentMin[ d ]; + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( currentMin[ d ], d ); + } + + @Override + public double realMax( final int d ) + { + return currentMax[ d ]; + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = currentMax[ d ]; + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( currentMax[ d ], d ); + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return currentMin[ d ]; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < n; ++d ) + min[ d ] = currentMin[ d ]; + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < n; ++d ) + min.setPosition( currentMin[ d ], d ); + } + + @Override + public long max( final int d ) + { + return currentMax[ d ]; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < n; ++d ) + max[ d ] = currentMax[ d ]; + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < n; ++d ) + max.setPosition( currentMax[ d ], d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < n; ++d ) + dimensions[ d ] = this.dimensions[ d ]; + } + + @Override + public long dimension( final int d ) + { + return dimensions[ d ]; + } + + @Override + public LocalCursor cursor() + { + return new LocalCursor( sourceRandomAccess.copyRandomAccess() ); + } + + @Override + public LocalCursor localizingCursor() + { + return cursor(); + } + + public final class LocalCursor extends AbstractEuclideanSpace implements Cursor< T > + { + private final RandomAccess< T > source; + + private long index; + + private long maxIndexOnLine; + + public LocalCursor( final RandomAccess< T > source ) + { + super( source.numDimensions() ); + this.source = source; + reset(); + } + + protected LocalCursor( final LocalCursor c ) + { + super( c.numDimensions() ); + source = c.source.copyRandomAccess(); + index = c.index; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + source.fwd( 0 ); + if ( ++index > maxIndexOnLine ) + nextLine(); + if ( index == midIndex ) + fwd(); + } + + private void nextLine() + { + source.setPosition( currentMin[ 0 ], 0 ); + maxIndexOnLine += dimensions[ 0 ]; + for ( int d = 1; d < n; ++d ) + { + source.fwd( d ); + if ( source.getLongPosition( d ) > currentMax[ d ] ) + source.setPosition( currentMin[ d ], d ); + else + break; + } + } + + @Override + public void jumpFwd( final long steps ) + { + for ( long i = 0; i < steps; ++i ) + fwd(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + { + // NB: no action. + } + + @Override + public void reset() + { + source.setPosition( currentMin ); + source.bck( 0 ); + index = 0; + maxIndexOnLine = dimensions[ 0 ]; + } + + @Override + public boolean hasNext() + { + return index < maxIndex; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalCursor copy() + { + return new LocalCursor( this ); + } + + @Override + public LocalCursor copyCursor() + { + return copy(); + } + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenterUnsafe.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenterUnsafe.java new file mode 100644 index 0000000..e1f5510 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodSkipCenterUnsafe.java @@ -0,0 +1,79 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.RandomAccess; + + +public final class RectangleNeighborhoodSkipCenterUnsafe< T > extends RectangleNeighborhoodSkipCenter< T > +{ + public static < T > RectangleNeighborhoodFactory< T > factory() + { + return new RectangleNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + return new RectangleNeighborhoodSkipCenterUnsafe< T >( position, currentMin, currentMax, span, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + RectangleNeighborhoodSkipCenterUnsafe( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + super( position, currentMin, currentMax, span, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodUnsafe.java b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodUnsafe.java new file mode 100644 index 0000000..9c756b0 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleNeighborhoodUnsafe.java @@ -0,0 +1,79 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Interval; +import net.imglib2.RandomAccess; + + +public final class RectangleNeighborhoodUnsafe< T > extends RectangleNeighborhood< T > +{ + public static < T > RectangleNeighborhoodFactory< T > factory() + { + return new RectangleNeighborhoodFactory< T >() + { + @Override + public Neighborhood< T > create( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + return new RectangleNeighborhoodUnsafe< T >( position, currentMin, currentMax, span, sourceRandomAccess ); + } + }; + } + + private final LocalCursor theCursor; + + private final LocalCursor firstElementCursor; + + RectangleNeighborhoodUnsafe( final long[] position, final long[] currentMin, final long[] currentMax, final Interval span, final RandomAccess< T > sourceRandomAccess ) + { + super( position, currentMin, currentMax, span, sourceRandomAccess ); + theCursor = super.cursor(); + firstElementCursor = super.cursor(); + } + + @Override + public T firstElement() + { + firstElementCursor.reset(); + return firstElementCursor.next(); + } + + @Override + public LocalCursor cursor() + { + theCursor.reset(); + return theCursor; + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/RectangleShape.java b/src/main/java/varun_algorithm_neighborhood/RectangleShape.java new file mode 100644 index 0000000..51d3b5a --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/RectangleShape.java @@ -0,0 +1,229 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import java.util.Iterator; + +import net.imglib2.AbstractEuclideanSpace; +import net.imglib2.AbstractInterval; +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.FlatIterationOrder; +import net.imglib2.Interval; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; + + + +/** + * A factory for Accessibles on rectangular neighboorhoods. + * + * @author Tobias Pietzsch + * @author Jonathan Hale (University of Konstanz) + */ +public class RectangleShape implements Shape +{ + final int span; + + final boolean skipCenter; + + /** + * @param span + * @param skipCenter + */ + public RectangleShape( final int span, final boolean skipCenter ) + { + this.span = span; + this.skipCenter = skipCenter; + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoods( final RandomAccessibleInterval< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenterUnsafe.< T >factory() : RectangleNeighborhoodUnsafe.< T >factory(); + final Interval spanInterval = createSpan( source.numDimensions() ); + return new NeighborhoodsIterableInterval< T >( source, spanInterval, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessible( final RandomAccessible< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenterUnsafe.< T >factory() : RectangleNeighborhoodUnsafe.< T >factory(); + final Interval spanInterval = createSpan( source.numDimensions() ); + return new NeighborhoodsAccessible< T >( source, spanInterval, f ); + } + + @Override + public < T > NeighborhoodsIterableInterval< T > neighborhoodsSafe( final RandomAccessibleInterval< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenter.< T >factory() : RectangleNeighborhood.< T >factory(); + final Interval spanInterval = createSpan( source.numDimensions() ); + return new NeighborhoodsIterableInterval< T >( source, spanInterval, f ); + } + + @Override + public < T > NeighborhoodsAccessible< T > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ) + { + final RectangleNeighborhoodFactory< T > f = skipCenter ? RectangleNeighborhoodSkipCenter.< T >factory() : RectangleNeighborhood.< T >factory(); + final Interval spanInterval = createSpan( source.numDimensions() ); + return new NeighborhoodsAccessible< T >( source, spanInterval, f ); + } + + private Interval createSpan( final int n ) + { + final long[] min = new long[ n ]; + final long[] max = new long[ n ]; + for ( int d = 0; d < n; ++d ) + { + min[ d ] = -span; + max[ d ] = span; + } + return new FinalInterval( min, max ); + } + + /** + * @return {@code true} if {@code skipCenter} was set to true + * during construction, {@code false} otherwise. + * @see CenteredRectangleShape#CenteredRectangleShape(int[], boolean) + */ + public boolean isSkippingCenter() + { + return skipCenter; + } + + /** + * @return The span of this shape. + */ + public int getSpan() + { + return span; + } + + @Override + public String toString() + { + return "RectangleShape, span = " + span; + } + + public static final class NeighborhoodsIterableInterval< T > extends AbstractInterval implements IterableInterval< Neighborhood< T > > + { + final RandomAccessibleInterval< T > source; + + final Interval span; + + final RectangleNeighborhoodFactory< T > factory; + + final long size; + + public NeighborhoodsIterableInterval( final RandomAccessibleInterval< T > source, final Interval span, final RectangleNeighborhoodFactory< T > factory ) + { + super( source ); + this.source = source; + this.span = span; + this.factory = factory; + long s = source.dimension( 0 ); + for ( int d = 1; d < n; ++d ) + s *= source.dimension( d ); + size = s; + } + + @Override + public Cursor< Neighborhood< T >> cursor() + { + return new RectangleNeighborhoodCursor< T >( source, span, factory ); + } + + @Override + public long size() + { + return size; + } + + @Override + public Neighborhood< T > firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return new FlatIterationOrder( this ); + } + + @Override + public Iterator< Neighborhood< T >> iterator() + { + return cursor(); + } + + @Override + public Cursor< Neighborhood< T >> localizingCursor() + { + return cursor(); + } + } + + public static final class NeighborhoodsAccessible< T > extends AbstractEuclideanSpace implements RandomAccessible< Neighborhood< T > > + { + final RandomAccessible< T > source; + + final Interval span; + + final RectangleNeighborhoodFactory< T > factory; + + public NeighborhoodsAccessible( final RandomAccessible< T > source, final Interval span, final RectangleNeighborhoodFactory< T > factory ) + { + super( source.numDimensions() ); + this.source = source; + this.span = span; + this.factory = factory; + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess() + { + return new RectangleNeighborhoodRandomAccess< T >( source, span, factory ); + } + + @Override + public RandomAccess< Neighborhood< T >> randomAccess( final Interval interval ) + { + return new RectangleNeighborhoodRandomAccess< T >( source, span, factory, interval ); + } + } +} diff --git a/src/main/java/varun_algorithm_neighborhood/Shape.java b/src/main/java/varun_algorithm_neighborhood/Shape.java new file mode 100644 index 0000000..867d0b0 --- /dev/null +++ b/src/main/java/varun_algorithm_neighborhood/Shape.java @@ -0,0 +1,177 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_neighborhood; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.Sampler; + +/** + * A factory for Accessibles on {@link Neighborhood Neighborhoods}. + * + * @author Tobias Pietzsch + */ +public interface Shape +{ + /** + * Get an {@link IterableInterval} that contains all {@link Neighborhood + * Neighborhoods} of the source image. + * + *

      + * A {@link Cursor} on the resulting accessible can be used to access the + * {@link Neighborhood neighborhoods}. As usual, when the cursor is moved, a + * neighborhood {@link Sampler#get() obtained} previously from the cursor + * should be considered invalid. + *

      + * + *

      + * The {@link Neighborhood neighborhoods} that are obtained from the + * resulting accessible are unsafe in the following sense: Every time, + * a {@link Cursor} is requested (using {@link Neighborhood#cursor()} etc) + * the same {@link Cursor} instance is re-used. If you require to have more + * than one {@link Cursor} into the current neighborhood at a given time you + * have several options: + *

      + *
        + *
      1. {@link Cursor#copy()} the cursor you obtained using + * {@link Neighborhood#cursor()}. + *
      2. use multiple parent cursors, i.e., cursors on the + * {@link IterableInterval}<{@link Neighborhood}>. + *
      3. use {@link #neighborhoodsSafe(RandomAccessibleInterval)} which + * returns a new {@link Neighborhood#cursor()} every time (but therefore is + * not recommended if you want to use enhanced for loops). + *
      + * + * @param source + * source image. + * @return an {@link IterableInterval} that contains all + * {@link Neighborhood Neighborhoods} of the source image. + */ + public < T > IterableInterval< Neighborhood< T > > neighborhoods( final RandomAccessibleInterval< T > source ); + + /** + * Get an {@link RandomAccessibleInterval} that contains all + * {@link Neighborhood Neighborhoods} of the source image. + *

      + * A {@link RandomAccess} on the resulting accessible can be used to access + * the {@link Neighborhood neighborhoods}. As usual, when the access is + * moved, a neighborhood {@link Sampler#get() obtained} previously from the + * access should be considered invalid. + *

      + *

      + * The {@link Neighborhood neighborhoods} that are obtained from the + * resulting accessible are unsafe in the following sense: Every time, + * a {@link Cursor} is requested (using {@link Neighborhood#cursor()} etc) + * the same {@link Cursor} instance is re-used. If you require to have more + * than one {@link Cursor} into the current neighborhood at a given time you + * have several options: + *

      + *
        + *
      1. {@link Cursor#copy()} the cursor you obtained using + * {@link Neighborhood#cursor()}. + *
      2. use multiple parent {@link RandomAccess RandomAccesses}, i.e., + * accesses on the + * {@link RandomAccessibleInterval}<{@link Neighborhood}>. + *
      3. use {@link #neighborhoodsRandomAccessibleSafe} which returns a new + * {@link Neighborhood#cursor()} every time (but therefore is not + * recommended if you want to use enhanced for loops). + *
      + * + * @param source + * source image. + * @return an {@link RandomAccessibleInterval} that contains all + * {@link Neighborhood Neighborhoods} of the source image. + */ + public < T > RandomAccessible< Neighborhood< T > > neighborhoodsRandomAccessible( final RandomAccessible< T > source ); + + /** + * Get an {@link IterableInterval} that contains all {@link Neighborhood + * Neighborhoods} of the source image. + * + *

      + * A {@link Cursor} on the resulting accessible can be used to access the + * {@link Neighborhood neighborhoods}. As usual, when the cursor is moved, a + * neighborhood {@link Sampler#get() obtained} previously from the cursor + * should be considered invalid. + *

      + * + *

      + * Every time, a {@link Cursor} is requested from a {@link Neighborhood} + * (where the neighborhood in turn is obtained from a cursor on the + * IterableInterval returned by this method) a new {@link Cursor} instance + * is created. If you want to use enhanced for loops on the + * {@link Neighborhood neighborhoods}, consider using + * {@link #neighborhoods(RandomAccessibleInterval)} which re-uses the same + * instance every time (but therefore has to be used carefully). + *

      + * + * @param source + * source image. + * @return an {@link IterableInterval} that contains all + * {@link Neighborhood Neighborhoods} of the source image. + */ + public < T > IterableInterval< Neighborhood< T > > neighborhoodsSafe( final RandomAccessibleInterval< T > source ); + + /** + * Get an {@link RandomAccessibleInterval} that contains all + * {@link Neighborhood Neighborhoods} of the source image. + * + *

      + * A {@link RandomAccess} on the resulting accessible can be used to access + * the {@link Neighborhood neighborhoods}. As usual, when the access is + * moved, a neighborhood {@link Sampler#get() obtained} previously from the + * access should be considered invalid. + *

      + * + *

      + * Every time, a {@link Cursor} is requested from a {@link Neighborhood} + * (where the neighborhood in turn is obtained from a cursor on the + * IterableInterval returned by this method) a new {@link Cursor} instance + * is created. If you want to use enhanced for loops on the + * {@link Neighborhood neighborhoods}, consider using + * {@link #neighborhoods(RandomAccessibleInterval)} which re-uses the same + * instance every time (but therefore has to be used carefully). + *

      + * + * @param source + * source image. + * @return an {@link RandomAccessibleInterval} that contains all + * {@link Neighborhood Neighborhoods} of the source image. + */ + public < T > RandomAccessible< Neighborhood< T > > neighborhoodsRandomAccessibleSafe( final RandomAccessible< T > source ); +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/Angleobject.java b/src/main/java/varun_algorithm_ransac_Ransac/Angleobject.java new file mode 100644 index 0000000..4e369af --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/Angleobject.java @@ -0,0 +1,22 @@ +package varun_algorithm_ransac_Ransac; + +import ij.gui.Line; + +public class Angleobject { + + public final Line lineA; + public final Line lineB; + public final double angle; + + + public Angleobject(final Line lineA, final Line lineB, final double angle ) { + + this.lineA = lineA; + this.lineB = lineB; + this.angle = angle; + + + } + + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/BisectorEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/BisectorEllipsoid.java new file mode 100644 index 0000000..c790b9d --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/BisectorEllipsoid.java @@ -0,0 +1,110 @@ +package varun_algorithm_ransac_Ransac; + +import net.imglib2.util.LinAlgHelpers; + +public class BisectorEllipsoid implements NumericalSolvers { + + + + public double run( + final int numComponents, + final double[] e, + final double[] y, + final double[] x ) + { + final int n = e.length; + final double[] z = new double[ n ]; + double sumZSqr = 0; + for ( int i = 0; i < numComponents; ++i ) + { + z[ i ] = y[ i ] / e[ i ]; + sumZSqr += z[ i ] * z[ i ]; + } + + if ( sumZSqr == 1 ) + { + // The point is on the hyperellipsoid. + for ( int i = 0; i < numComponents; ++i ) + { + x[ i ] = y[ i ]; + } + return 0; + } + + final double emin = e[ numComponents - 1 ]; + final double[] pSqr = new double[ n ]; + final double[] numerator = new double[ n ]; + for ( int i = 0; i < numComponents; ++i ) + { + final double p = e[ i ] / emin; + pSqr[ i ] = p * p; + numerator[ i ] = pSqr[ i ] * z[ i ]; + } + + double s = 0, smin = z[ numComponents - 1 ] - 1, smax; + if ( sumZSqr < 1 ) + { + // The point is strictly inside the hyperellipsoid. + smax = 0; + } + else + { + // The point is strictly outside the hyperellipsoid. + smax = LengthRobust( numerator ) - 1; + } + + final double jmax = GetMaxBisections(); + for ( int j = 0; j < jmax; ++j ) + { + s = ( smin + smax ) * 0.5; + if ( s == smin || s == smax ) + { + break; + } + + double g = -1; + for ( int i = 0; i < numComponents; ++i ) + { + final double ratio = numerator[ i ] / ( s + pSqr[ i ] ); + g += ratio * ratio; + } + + if ( g > 0 ) + { + smin = s; + } + else if ( g < 0 ) + { + smax = s; + } + else + { + break; + } + } +// if(s> ArrayList> GetCoordinates( + RandomAccessibleInterval source, final T threshold) { + + ArrayList> coordinatelist = new ArrayList>(); + + Interval interval = Intervals.expand(source, -1); + int ndims = source.numDimensions(); + if (ndims > 3) + IJ.error("Only three dimensional Ellipsoids are supported"); + + source = Views.interval(source, interval); + + final Cursor center = Views.iterable(source).localizingCursor(); + + while(center.hasNext()) { + + final T centerValue = center.next(); + double[] posf = new double[ndims]; + center.localize(posf); + final RealPoint rpos = new RealPoint(posf); + if (centerValue.compareTo(threshold) > 0) + coordinatelist.add(new ValuePair(rpos, centerValue)); + + } + + + return coordinatelist; + } + + public static ArrayList GetCoordinatesBit( + RandomAccessibleInterval actualRoiimg) { + + ArrayList coordinatelist = new ArrayList(); + int ndims = actualRoiimg.numDimensions(); + if (ndims > 3) + IJ.error("Only three dimensional Ellipsoids are supported"); + + + final Cursor center = Views.iterable(actualRoiimg).localizingCursor(); + + + + while(center.hasNext()) { + + center.fwd(); + + double[] posf = new double[ndims]; + center.localize(posf); + final RealPoint rpos = new RealPoint(posf); + if(center.get().get() > 0) { + coordinatelist.add(rpos); + } + + } + + + + + return coordinatelist; + } + + + /** + * Sor a list of reallocalizable + * + * + */ + + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/DisplayasROI.java b/src/main/java/varun_algorithm_ransac_Ransac/DisplayasROI.java new file mode 100644 index 0000000..86e9a9a --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/DisplayasROI.java @@ -0,0 +1,87 @@ +package varun_algorithm_ransac_Ransac; + +import ij.gui.EllipseRoi; +import ij.gui.Line; +import ij.gui.OvalRoi; + +public class DisplayasROI { + + + + /** + * 2D correlated Gaussian + * + * @param mean + * (x,y) components of mean vector + * @param cov + * (xx, xy, yy) components of covariance matrix + * @return ImageJ roi + */ + public static EllipseRoi create2DEllipse(final double[] mean, final double[] cov) { + final double a = cov[0]; + final double b = cov[1]; + final double c = cov[2]; + final double d = Math.sqrt(a * a + 4 * b * b - 2 * a * c + c * c); + final double scale1 = Math.sqrt(0.5 * (a + c + d)) ; + final double scale2 = Math.sqrt(0.5 * (a + c - d)) ; + final double theta; + + theta = 0.5 * Math.atan2((2 * b), (a - c)); + + final double x = mean[0]; + final double y = mean[1]; + final double dx = scale1 * Math.cos(theta); + final double dy = scale1 * Math.sin(theta); + final EllipseRoi ellipse = new EllipseRoi(x - dx, y - dy, x + dx, y + dy, scale2 / scale1); + + return ellipse; + } + + + public static OvalRoi createOval(final double[] mean, final double radius) { + + OvalRoi currentRoi = new OvalRoi(mean[0], mean[1], radius, radius); + + return currentRoi; + } + + public static EllipseRoi create2DCircle(final double[] mean, final double radius) { + + final double a = radius; + final double scale1 = Math.sqrt(a) ; + final double scale2 = Math.sqrt(a) ; + final double theta = 3.14 * 0.5; + final double x = mean[0]; + final double y = mean[1]; + final double dx = scale1 * Math.cos(theta); + final double dy = scale1 * Math.sin(theta); + final EllipseRoi ellipse = new EllipseRoi(x - dx, y - dy, x + dx, y + dy, scale2 / scale1); + + return ellipse; + } + + + public static Line create2DLine(final double[] lineparam, final double[] sourcepoint) { + + final double slope = lineparam[0]; + final double intercept = lineparam[1]; + + final double midx = sourcepoint[0]; + + + final double length = 10; + double startx = midx - length / (Math.sqrt(1 + slope * slope)); + double endx = midx + length / (Math.sqrt(1 + slope * slope)); + + + + + double starty = slope * startx + intercept; + double endy = slope * endx + intercept; + Line line = new Line(startx, starty, endx, endy); + + return line; + + } + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/DistPointHyperEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/DistPointHyperEllipsoid.java new file mode 100644 index 0000000..926f9eb --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/DistPointHyperEllipsoid.java @@ -0,0 +1,210 @@ +package varun_algorithm_ransac_Ransac; + +import net.imglib2.RealLocalizable; +import net.imglib2.RealPoint; +import net.imglib2.util.LinAlgHelpers; + +/** + * This is an adaption of the algorithm described in + * "Distance from a Point to an Ellipse, an Ellipsoid, or a Hyperellipsoid" by + * David Eberly and available as a C++ implementation from + * www.geometrictools.com under the + * Boost License + * + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> + */ +public class DistPointHyperEllipsoid { + + // Initialize Numerical Solvers + + + + public static class Result { + public double distance; + public double[] closestPointCoords; + public RealPoint closestPoint; + } + + public static Result distPointHyperEllipsoid(final RealLocalizable point, final HyperEllipsoid hyperellipsoid, final NumericalSolvers numsol) { + final double[] p = new double[point.numDimensions()]; + point.localize(p); + if (hyperellipsoid != null) + return distPointHyperEllipsoid(p, hyperellipsoid, numsol); + else + return null; + } + + public static Result distPointHyperEllipsoid(final double[] point, final HyperEllipsoid hyperellipsoid, final NumericalSolvers numsol) { + final int n = point.length; + + final Result result = new Result(); + + // Compute the coordinates of Y in the hyperellipsoid coordinate system. + final double[] diff = new double[n]; +// System.out.println(point.length + " " + hyperellipsoid.getCenter().length + " " + diff.length); + LinAlgHelpers.subtract(point, hyperellipsoid.getCenter(), diff); + final double[] y = new double[n]; + LinAlgHelpers.mult(hyperellipsoid.getAxes(), diff, y); + + // Compute the closest hyperellipsoid point in the axis-aligned + // coordinate system. + final double[] x = new double[n]; + final double sqrDistance = SqrDistance(hyperellipsoid.getRadii(), y, x, numsol); + result.distance = Math.sqrt(sqrDistance); + + // Convert back to the original coordinate system. + LinAlgHelpers.multT(hyperellipsoid.getAxes(), x, y); + LinAlgHelpers.add(y, hyperellipsoid.getCenter(), y); + result.closestPointCoords = y; + result.closestPoint = RealPoint.wrap(y); + + return result; + } + + private static double SqrDistance(final double[] e, final double[] y, final double[] x, final NumericalSolvers numsol) { + final int n = e.length; + // Determine negations for y to the first octant. + final boolean[] negate = new boolean[n]; + for (int i = 0; i < n; ++i) { + negate[i] = (y[i] < 0); + } + + // Determine the axis order for decreasing extents. + final double[] minuse = new double[n]; + final int[] permute = new int[n]; + for (int i = 0; i < n; ++i) { + minuse[i] = -e[i]; + permute[i] = i; + } + net.imglib2.util.Util.quicksort(minuse, permute, 0, n - 1); + + final int[] invPermute = new int[n]; + for (int i = 0; i < n; ++i) { + invPermute[permute[i]] = i; + } + + final double[] locE = new double[n]; + final double[] locY = new double[n]; + for (int i = 0; i < n; ++i) { + final int j = permute[i]; + locE[i] = e[j]; + locY[i] = Math.abs(y[j]); + } + + final double[] locX = new double[n]; + final double sqrDistance = SqrDistanceSpecial(locE, locY, locX, numsol); + + // Restore the axis order and reflections. + for (int i = 0; i < n; ++i) { + final int j = invPermute[i]; + x[i] = negate[i] ? -locX[j] : locX[j]; + } + + return sqrDistance; + } + + /** + * The hyperellipsoid is sum_{d=0}^{N-1} (x[d]/e[d])^2 = 1 with the e[d] + * positive and nonincreasing: e[d] >= e[d+1] for all d. The query point is + * (y[0],...,y[N-1]) with y[d] >= 0 for all d. The function returns the squared + * distance from the query point to the hyperellipsoid. It also computes the + * hyperellipsoid point (x[0],...,x[N-1]) that is closest to (y[0],...,y[N-1]), + * where x[d] >= 0 for all d. + * + * @param e + * axis lengths. e[d] >= e[d+1] for all d. + * @param y + * query point. y[d] >= 0 for all d. + * @param x + * hyperellipsoid point closest to y is stored here. (x[d] >= 0 for + * all d.) + * @return squared distance from the query point to the hyperellipsoid. + */ + private static double SqrDistanceSpecial(final double[] e, final double[] y, final double[] x, final NumericalSolvers numsol) { + final int n = e.length; + double sqrDistance = 0; + + final double[] ePos = new double[n]; + final double[] yPos = new double[n]; + final double[] xPos = new double[n]; + int numPos = 0; + for (int i = 0; i < n; ++i) { + if (y[i] > 0) { + ePos[numPos] = e[i]; + yPos[numPos] = y[i]; + ++numPos; + } else { + x[i] = 0; + } + } + + if (y[n - 1] > 0) { + sqrDistance = numsol.run(numPos, ePos, yPos, xPos); + } else + // y[n-1] = 0 + { + final double[] numer = new double[n - 1]; + final double[] denom = new double[n - 1]; + final double eNm1Sqr = e[n - 1] * e[n - 1]; + for (int i = 0; i < numPos; ++i) { + numer[i] = ePos[i] * yPos[i]; + denom[i] = ePos[i] * ePos[i] - eNm1Sqr; + } + + boolean inSubHyperbox = true; + for (int i = 0; i < numPos; ++i) { + if (numer[i] >= denom[i]) { + inSubHyperbox = false; + break; + } + } + + boolean inSubHyperellipsoid = false; + if (inSubHyperbox) { + // yPos[] is inside the axis-aligned bounding box of the + // subhyperellipsoid. This intermediate test is designed to + // guard against the division by zero when ePos[i] == e[n-1] for + // some i. + final double[] xde = new double[n - 1]; + double discr = 1; + for (int i = 0; i < numPos; ++i) { + xde[i] = numer[i] / denom[i]; + discr -= xde[i] * xde[i]; + } + if (discr > 0) { + // yPos[] is inside the subhyperellipsoid. The closest + // hyperellipsoid point has x[n-1] > 0. + sqrDistance = 0; + for (int i = 0; i < numPos; ++i) { + xPos[i] = ePos[i] * xde[i]; + final double diff = xPos[i] - yPos[i]; + sqrDistance += diff * diff; + } + x[n - 1] = e[n - 1] * Math.sqrt(discr); + sqrDistance += x[n - 1] * x[n - 1]; + inSubHyperellipsoid = true; + } + } + + if (!inSubHyperellipsoid) { + // yPos[] is outside the subhyperellipsoid. The closest + // hyperellipsoid point has x[n-1] == 0 and is on the + // domain-boundary hyperellipsoid. + x[n - 1] = 0; + sqrDistance = numsol.run(numPos, ePos, yPos, xPos); + } + } + + // Fill in those x[] values that were not zeroed out initially. + numPos = 0; + for (int i = 0; i < n; ++i) { + if (y[i] > 0) { + x[i] = xPos[numPos]; + ++numPos; + } + } + return sqrDistance; + } + +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_ransac_Ransac/EberlyCode.java b/src/main/java/varun_algorithm_ransac_Ransac/EberlyCode.java new file mode 100644 index 0000000..207e96e --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/EberlyCode.java @@ -0,0 +1,1375 @@ +package varun_algorithm_ransac_Ransac; +/* +public class EberlyCode { + // David Eberly, Geometric Tools, Redmond WA 98052 + // Copyright (c) 1998-2017 + // Distributed under the Boost Software License, Version 1.0. + // http://www.boost.org/LICENSE_1_0.txt + // http://www.geometrictools.com/License/Boost/LICENSE_1_0.txt + // File Version: 3.0.1 (2017/04/16) + + #pragma once + + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // The test-intersection and find-intersection queries implemented here are + // discussed in the document + // http://www.geometrictools.com/Documentation/IntersectionOfEllipses.pdf + // The Real type should support exact rational arithmetic in order for the + // polynomial root construction to be robust. The classification of the + // intersections depends on various sign tests of computed values. If these + // values are computed with floating-point arithmetic, the sign tests can + // lead to misclassification. + // + // The area-of-intersection query is discussed in the document + // http://www.geometrictools.com/Documentation/AreaIntersectingEllipses.pdf + + namespace gte + { + + template + class TIQuery, Ellipse2> + { + public: + // The query tests the relationship between the ellipses as solid + // objects. + enum + { + ELLIPSES_SEPARATED, + ELLIPSES_OVERLAP, + ELLIPSE0_OUTSIDE_ELLIPSE1_BUT_TANGENT, + ELLIPSE0_STRICTLY_CONTAINS_ELLIPSE1, + ELLIPSE0_CONTAINS_ELLIPSE1_BUT_TANGENT, + ELLIPSE1_STRICTLY_CONTAINS_ELLIPSE0, + ELLIPSE1_CONTAINS_ELLIPSE0_BUT_TANGENT, + ELLIPSES_EQUAL + }; + + // The ellipse axes are already normalized, which most likely introduced + // rounding errors. + int operator()(Ellipse2 const& ellipse0, + Ellipse2 const& ellipse1); + + private: + void GetRoots(Real d0, Real c0, int& numRoots, Real* roots); + void GetRoots(Real d0, Real d1, Real c0, Real c1, int& numRoots, + Real* roots); + + int Classify(Real minSqrDistance, Real maxSqrDistance, Real d0c0pd1c1); + }; + + template + class FIQuery, Ellipse2> + { + public: + // The queries find the intersections (if any) of the ellipses treated as + // hollow objects. The implementations use the same concepts. + FIQuery(); + + struct Result + { + // This value is true when the ellipses intersect in at least one + // point. + bool intersect; + + // If the ellipses are not the same, numPoints is 0 through 4 and + // that number of elements of 'points' are valid. If the ellipses + // are the same, numPoints is set to maxInt and 'points' is invalid. + int numPoints; + std::array, 4> points; + std::array isTransverse; + }; + + // The ellipse axes are already normalized, which most likely introduced + // rounding errors. + Result operator()(Ellipse2 const& ellipse0, + Ellipse2 const& ellipse1); + + // The axis directions do not have to be unit length. The quadratic + // equations are constructed according to the details of the PDF document + // about the intersection of ellipses. + Result operator()(Vector2 const& center0, + Vector2 const axis0[2], Vector2 const& sqrExtent0, + Vector2 const& center1, Vector2 const axis1[2], + Vector2 const& sqrExtent1); + + + // Compute the area of intersection of ellipses. + struct AreaResult + { + // The configuration of the two ellipses. + enum + { + ELLIPSES_ARE_EQUAL, + ELLIPSES_ARE_SEPARATED, + E0_CONTAINS_E1, + E1_CONTAINS_E0, + ONE_CHORD_REGION, + FOUR_CHORD_REGION + }; + + // One of the enumerates, determined in the call to AreaDispatch. + int configuration; + + // Information about the ellipse-ellipse intersection points. + Result findResult; + + // The area of intersection of the ellipses. + Real area; + }; + + // The ellipse axes are already normalized, which most likely introduced + // rounding errors. + AreaResult AreaOfIntersection(Ellipse2 const& ellipse0, + Ellipse2 const& ellipse1); + + // The axis directions do not have to be unit length. The positive + // definite matrices are constructed according to the details of the PDF + // documentabout the intersection of ellipses. + AreaResult AreaOfIntersection(Vector2 const& center0, + Vector2 const axis0[2], Vector2 const& sqrExtent0, + Vector2 const& center1, Vector2 const axis1[2], + Vector2 const& sqrExtent1); + + private: + // Compute the coefficients of the quadratic equation but with the + // y^2-coefficient of 1. + void ToCoefficients(Vector2 const& center, + Vector2 const axis[2], Vector2 const& SqrExtent, + std::array& coeff); + + void DoRootFinding(Result& result); + void D4NotZeroEBarNotZero(Result& result); + void D4NotZeroEBarZero(Real const& xbar, Result& result); + void D4ZeroD2NotZeroE2NotZero(Result& result); + void D4ZeroD2NotZeroE2Zero(Result& result); + void D4ZeroD2Zero(Result& result); + + // Helper function for D4ZeroD2Zero. + void SpecialIntersection(Real const& x, bool transverse, Result& result); + + + // Helper functions for AreaOfIntersection. + struct EllipseInfo + { + Vector2 center; + std::array, 2> axis; + Vector2 extent, sqrExtent; + Matrix2x2 M; + Real AB; // extent[0] * extent[1] + Real halfAB; // extent[0] * extent[1] / 2 + Real BpA; // extent[1] + extent[0] + Real BmA; // extent[1] - extent[0] + }; + + // The axis, extent, and sqrExtent members of E must be set before this + // function is called. + void FinishEllipseInfo(EllipseInfo& E); + + void AreaDispatch(EllipseInfo const& E0, EllipseInfo const& E1, + AreaResult& ar); + + void AreaCS(EllipseInfo const& E0, EllipseInfo const& E1, + AreaResult& ar); + + void Area2(EllipseInfo const& E0, EllipseInfo const& E1, int i0, int i1, + AreaResult& ar); + + void Area4(EllipseInfo const& E0, EllipseInfo const& E1, + AreaResult& ar); + + Real ComputeAreaChordRegion(EllipseInfo const& E, + Vector2 const& P0mC, Vector2 const& P1mC); + + Real ComputeIntegral(EllipseInfo const& E, Real const& theta); + + + // Constants that are set up once (optimization for rational arithmetic). + Real mZero, mOne, mTwo, mPi, mTwoPi; + + // Various polynomial coefficients. The short names are meant to match + // the variable names used in the PDF documentation. + std::array mA, mB, mD, mF; + std::array mC, mE; + Real mA2Div2, mA4Div2; + }; + + // Template aliases for convenience. + template + using TIEllipses2 = TIQuery, Ellipse2>; + + template + using FIEllipses2 = FIQuery, Ellipse2>; + + + // TIQuery for intersections + + template + int TIQuery, Ellipse2>::operator()( + Ellipse2 const& ellipse0, Ellipse2 const& ellipse1) + { + Real const zero = 0, one = 1; + + // Get the parameters of ellipe0. + Vector2 K0 = ellipse0.center; + Matrix2x2 R0; + R0.SetCol(0, ellipse0.axis[0]); + R0.SetCol(1, ellipse0.axis[1]); + Matrix2x2 D0{ + one / (ellipse0.extent[0] * ellipse0.extent[0]), zero, + zero, one / (ellipse0.extent[1] * ellipse0.extent[1]) }; + + // Get the parameters of ellipse1. + Vector2 K1 = ellipse1.center; + Matrix2x2 R1; + R1.SetCol(0, ellipse1.axis[0]); + R1.SetCol(1, ellipse1.axis[1]); + Matrix2x2 D1{ + one / (ellipse1.extent[0] * ellipse1.extent[0]), zero, + zero, one / (ellipse1.extent[1] * ellipse1.extent[1]) }; + + // Compute K2. + Matrix2x2 D0NegHalf{ + ellipse0.extent[0], zero, + zero, ellipse0.extent[1] }; + Matrix2x2 D0Half{ + one / ellipse0.extent[0], zero, + zero, one / ellipse0.extent[1] }; + Vector2 K2 = D0Half*((K1 - K0)*R0); + + // Compute M2. + Matrix2x2 R1TR0D0NegHalf = MultiplyATB(R1, R0 * D0NegHalf); + Matrix2x2 M2 = MultiplyATB(R1TR0D0NegHalf, D1) * R1TR0D0NegHalf; + + // Factor M2 = R*D*R^T. + SymmetricEigensolver2x2 es; + std::array D; + std::array, 2> evec; + es(M2(0, 0), M2(0, 1), M2(1, 1), +1, D, evec); + Matrix2x2 R; + R.SetCol(0, evec[0]); + R.SetCol(1, evec[1]); + + // Compute K = R^T*K2. + Vector2 K = K2 * R; + + // Transformed ellipse0 is Z^T*Z = 1 and transformed ellipse1 is + // (Z-K)^T*D*(Z-K) = 0. + + // The minimum and maximum squared distances from the origin of points on + // transformed ellipse1 are used to determine whether the ellipses + // intersect, are separated, or one contains the other. + Real minSqrDistance = std::numeric_limits::max(); + Real maxSqrDistance = zero; + + if (K == Vector2::Zero()) + { + // The special case of common centers must be handled separately. It + // is not possible for the ellipses to be separated. + for (int i = 0; i < 2; ++i) + { + Real invD = one / D[i]; + if (invD < minSqrDistance) + { + minSqrDistance = invD; + } + if (invD > maxSqrDistance) + { + maxSqrDistance = invD; + } + } + return Classify(minSqrDistance, maxSqrDistance, zero); + } + + // The closest point P0 and farthest point P1 are solutions to + // s0*D*(P0 - K) = P0 and s1*D1*(P1 - K) = P1 for some scalars s0 and s1 + // that are roots to the function + // f(s) = d0*k0^2/(d0*s-1)^2 + d1*k1^2/(d1*s-1)^2 - 1 + // where D = diagonal(d0,d1) and K = (k0,k1). + Real d0 = D[0], d1 = D[1]; + Real c0 = K[0] * K[0], c1 = K[1] * K[1]; + + // Sort the values so that d0 >= d1. This allows us to bound the roots of + // f(s), of which there are at most 4. + std::vector> param(2); + if (d0 >= d1) + { + param[0] = std::make_pair(d0, c0); + param[1] = std::make_pair(d1, c1); + } + else + { + param[0] = std::make_pair(d1, c1); + param[1] = std::make_pair(d0, c0); + } + + std::vector> valid; + valid.reserve(2); + if (param[0].first > param[1].first) + { + // d0 > d1 + for (int i = 0; i < 2; ++i) + { + if (param[i].second > zero) + { + valid.push_back(param[i]); + } + } + } + else + { + // d0 = d1 + param[0].second += param[1].second; + if (param[0].second > zero) + { + valid.push_back(param[0]); + } + } + + size_t numValid = valid.size(); + int numRoots = 0; + Real roots[4]; + if (numValid == 2) + { + GetRoots(valid[0].first, valid[1].first, valid[0].second, + valid[1].second, numRoots, roots); + } + else if (numValid == 1) + { + GetRoots(valid[0].first, valid[0].second, numRoots, roots); + } + // else: numValid cannot be zero because we already handled case K = 0 + + for (int i = 0; i < numRoots; ++i) + { + Real s = roots[i]; + Real p0 = d0 * K[0] * s / (d0 * s - (Real)1); + Real p1 = d1 * K[1] * s / (d1 * s - (Real)1); + Real sqrDistance = p0 * p0 + p1 * p1; + if (sqrDistance < minSqrDistance) + { + minSqrDistance = sqrDistance; + } + if (sqrDistance > maxSqrDistance) + { + maxSqrDistance = sqrDistance; + } + } + + return Classify(minSqrDistance, maxSqrDistance, d0 * c0 + d1 * c1); + } + + template + void TIQuery, Ellipse2>::GetRoots(Real d0, Real c0, + int& numRoots, Real* roots) + { + // f(s) = d0*c0/(d0*s-1)^2 - 1 + Real const one = (Real)1; + Real temp = sqrt(d0*c0); + Real inv = one / d0; + numRoots = 2; + roots[0] = (one - temp) * inv; + roots[1] = (one + temp) * inv; + } + + template + void TIQuery, Ellipse2>::GetRoots(Real d0, Real d1, + Real c0, Real c1, int& numRoots, Real* roots) + { + // f(s) = d0*c0/(d0*s-1)^2 + d1*c1/(d1*s-1)^2 - 1 + // with d0 > d1 + + Real const zero = 0, one = (Real)1; + Real d0c0 = d0 * c0; + Real d1c1 = d1 * c1; + Real sum = d0c0 + d1c1; + Real sqrtsum = sqrt(sum); + + std::function F = [&one, d0, d1, d0c0, d1c1](Real s) + { + Real invN0 = one / (d0*s - one); + Real invN1 = one / (d1*s - one); + Real term0 = d0c0 * invN0 * invN0; + Real term1 = d1c1 * invN1 * invN1; + Real f = term0 + term1 - one; + return f; + }; + + std::function DF = [&one, d0, d1, d0c0, d1c1](Real s) + { + Real const two = 2; + Real invN0 = one / (d0*s - one); + Real invN1 = one / (d1*s - one); + Real term0 = d0 * d0c0 * invN0 * invN0 * invN0; + Real term1 = d1 * d1c1 * invN1 * invN1 * invN1; + Real df = -two * (term0 + term1); + return df; + }; + + unsigned int const maxIterations = (unsigned int)( + 3 + std::numeric_limits::digits - + std::numeric_limits::min_exponent); + unsigned int iterations; + numRoots = 0; + + Real invD0 = one / d0; + Real invD1 = one / d1; + Real smin, smax, s, fval; + + // Compute root in (-infinity,1/d0). Obtain a lower bound for the root + // better than -std::numeric_limits::max(). + smax = invD0; + fval = sum - one; + if (fval > zero) + { + smin = (one - sqrtsum) * invD1; // < 0 + fval = F(smin); + LogAssert(fval <= zero, "Unexpected condition."); + } + else + { + smin = zero; + } + iterations = RootsBisection::Find(F, smin, smax, -one, one, + maxIterations, s); + fval = F(s); + LogAssert(iterations > 0, "Unexpected condition."); + roots[numRoots++] = s; + + // Compute roots (if any) in (1/d0,1/d1). It is the case that + // F(1/d0) = +infinity, F'(1/d0) = -infinity + // F(1/d1) = +infinity, F'(1/d1) = +infinity + // F"(s) > 0 for all s in the domain of F + // Compute the unique root r of F'(s) on (1/d0,1/d1). The bisector needs + // only the signs at the endpoints, so we pass -1 and +1 instead of the + // infinite values. If F(r) < 0, F(s) has two roots in the interval. + // If F(r) = 0, F(s) has only one root in the interval. + Real const oneThird = (Real)(1.0 / 3.0); + Real rho = pow(d0 * d0c0 / (d1 * d1c1), oneThird); + Real smid = (one + rho) / (d0 + rho * d1); + Real fmid = F(smid); + if (fmid <= zero) + { + // Pass in signs rather than infinities, because the bisector cares + // only about the signs. + iterations = RootsBisection::Find(F, invD0, smid, one, -one, + maxIterations, s); + fval = F(s); + LogAssert(iterations > 0, "Unexpected condition."); + roots[numRoots++] = s; + iterations = RootsBisection::Find(F, smid, invD1, -one, one, + maxIterations, s); + fval = F(s); + LogAssert(iterations > 0, "Unexpected condition."); + roots[numRoots++] = s; + } + else if (fmid == zero) + { + roots[numRoots++] = smid; + } + + // Compute root in (1/d1,+infinity). Obtain an upper bound for the root + // better than std::numeric_limits::max(). + smin = invD1; + smax = (one + sqrtsum) * invD1; // > 1/d1 + fval = F(smax); + LogAssert(fval <= zero, "Unexpected condition."); + iterations = RootsBisection::Find(F, smin, smax, one, -one, + maxIterations, s); + fval = F(s); + LogAssert(iterations > 0, "Unexpected condition."); + roots[numRoots++] = s; + } + + template + int TIQuery, Ellipse2>::Classify( + Real minSqrDistance, Real maxSqrDistance, Real d0c0pd1c1) + { + Real const one = (Real)1; + + if (maxSqrDistance < one) + { + return ELLIPSE0_STRICTLY_CONTAINS_ELLIPSE1; + } + else if (maxSqrDistance > one) + { + if (minSqrDistance < one) + { + return ELLIPSES_OVERLAP; + } + else if (minSqrDistance > one) + { + if (d0c0pd1c1 > one) + { + return ELLIPSES_SEPARATED; + } + else + { + return ELLIPSE1_STRICTLY_CONTAINS_ELLIPSE0; + } + } + else // minSqrDistance = 1 + { + if (d0c0pd1c1 > one) + { + return ELLIPSE0_OUTSIDE_ELLIPSE1_BUT_TANGENT; + } + else + { + return ELLIPSE1_CONTAINS_ELLIPSE0_BUT_TANGENT; + } + } + } + else // maxSqrDistance = 1 + { + if (minSqrDistance < one) + { + return ELLIPSE0_CONTAINS_ELLIPSE1_BUT_TANGENT; + } + else // minSqrDistance = 1 + { + return ELLIPSES_EQUAL; + } + } + } + + + + // FIQuery constructor for both intersection and area queries. + + template + FIQuery, Ellipse2>::FIQuery() + : + mZero((Real)0), + mOne((Real)1), + mTwo((Real)2), + mPi((Real)GTE_C_PI), + mTwoPi((Real)GTE_C_TWO_PI) + { + } + + + + // FIQuery functions for ellipse-ellipse intersection points. + + template + typename FIQuery, Ellipse2>::Result + FIQuery, Ellipse2>::operator()( + Ellipse2 const& ellipse0, Ellipse2 const& ellipse1) + { + Vector2 rCenter, rAxis[2], rSqrExtent; + + rCenter = { ellipse0.center[0], ellipse0.center[1] }; + rAxis[0] = { ellipse0.axis[0][0], ellipse0.axis[0][1] }; + rAxis[1] = { ellipse0.axis[1][0], ellipse0.axis[1][1] }; + rSqrExtent = + { + ellipse0.extent[0] * ellipse0.extent[0], + ellipse0.extent[1] * ellipse0.extent[1] + }; + ToCoefficients(rCenter, rAxis, rSqrExtent, mA); + + rCenter = { ellipse1.center[0], ellipse1.center[1] }; + rAxis[0] = { ellipse1.axis[0][0], ellipse1.axis[0][1] }; + rAxis[1] = { ellipse1.axis[1][0], ellipse1.axis[1][1] }; + rSqrExtent = + { + ellipse1.extent[0] * ellipse1.extent[0], + ellipse1.extent[1] * ellipse1.extent[1] + }; + ToCoefficients(rCenter, rAxis, rSqrExtent, mB); + + Result result; + DoRootFinding(result); + return result; + } + + template + typename FIQuery, Ellipse2>::Result + FIQuery, Ellipse2>::operator()( + Vector2 const& center0, Vector2 const axis0[2], + Vector2 const& sqrExtent0, Vector2 const& center1, + Vector2 const axis1[2], Vector2 const& sqrExtent1) + { + Vector2 rCenter, rAxis[2], rSqrExtent; + + rCenter = { center0[0], center0[1] }; + rAxis[0] = { axis0[0][0], axis0[0][1] }; + rAxis[1] = { axis0[1][0], axis0[1][1] }; + rSqrExtent = { sqrExtent0[0], sqrExtent0[1] }; + ToCoefficients(rCenter, rAxis, rSqrExtent, mA); + + rCenter = { center1[0], center1[1] }; + rAxis[0] = { axis1[0][0], axis1[0][1] }; + rAxis[1] = { axis1[1][0], axis1[1][1] }; + rSqrExtent = { sqrExtent1[0], sqrExtent1[1] }; + ToCoefficients(rCenter, rAxis, rSqrExtent, mB); + + Result result; + DoRootFinding(result); + return result; + } + + template + void FIQuery, Ellipse2>::ToCoefficients( + Vector2 const& center, Vector2 const axis[2], + Vector2 const& sqrExtent, std::array& coeff) + { + Real denom0 = Dot(axis[0], axis[0]) * sqrExtent[0]; + Real denom1 = Dot(axis[1], axis[1]) * sqrExtent[1]; + Matrix2x2 outer0 = OuterProduct(axis[0], axis[0]); + Matrix2x2 outer1 = OuterProduct(axis[1], axis[1]); + Matrix2x2 A = outer0 / denom0 + outer1 / denom1; + Vector2 product = A * center; + Vector2 B = -mTwo * product; + Real const& denom = A(1, 1); + coeff[0] = (Dot(center, product) - mOne) / denom; + coeff[1] = B[0] / denom; + coeff[2] = B[1] / denom; + coeff[3] = A(0, 0) / denom; + coeff[4] = mTwo * A(0, 1) / denom; + // coeff[5] = A(1, 1) / denom = 1; + } + + template + void FIQuery, Ellipse2>::DoRootFinding( + Result& result) + { + bool allZero = true; + for (int i = 0; i < 5; ++i) + { + mD[i] = mA[i] - mB[i]; + if (mD[i] != mZero) + { + allZero = false; + } + } + if (allZero) + { + result.intersect = false; + result.numPoints = std::numeric_limits::max(); + return; + } + + result.numPoints = 0; + + mA2Div2 = mA[2] / mTwo; + mA4Div2 = mA[4] / mTwo; + mC[0] = mA[0] - mA2Div2 * mA2Div2; + mC[1] = mA[1] - mA2Div2 * mA[4]; + mC[2] = mA[3] - mA4Div2 * mA4Div2; // c[2] > 0 + mE[0] = mD[0] - mA2Div2 * mD[2]; + mE[1] = mD[1] - mA2Div2 * mD[4] - mA4Div2 * mD[2]; + mE[2] = mD[3] - mA4Div2 * mD[4]; + + if (mD[4] != mZero) + { + Real xbar = -mD[2] / mD[4]; + Real ebar = mE[0] + xbar * (mE[1] + xbar * mE[2]); + if (ebar != mZero) + { + D4NotZeroEBarNotZero(result); + } + else + { + D4NotZeroEBarZero(xbar, result); + } + } + else if (mD[2] != mZero) // d[4] = 0 + { + if (mE[2] != mZero) + { + D4ZeroD2NotZeroE2NotZero(result); + } + else + { + D4ZeroD2NotZeroE2Zero(result); + } + } + else // d[2] = d[4] = 0 + { + D4ZeroD2Zero(result); + } + + result.intersect = (result.numPoints > 0); + } + + template + void FIQuery, Ellipse2>::D4NotZeroEBarNotZero( + Result& result) + { + // The graph of w = -e(x)/d(x) is a hyperbola. + Real d2d2 = mD[2] * mD[2], d2d4 = mD[2] * mD[4], d4d4 = mD[4] * mD[4]; + Real e0e0 = mE[0] * mE[0], e0e1 = mE[0] * mE[1], e0e2 = mE[0] * mE[2]; + Real e1e1 = mE[1] * mE[1], e1e2 = mE[1] * mE[2], e2e2 = mE[2] * mE[2]; + std::array f = + { + mC[0] * d2d2 + e0e0, + mC[1] * d2d2 + mTwo * (mC[0] * d2d4 + e0e1), + mC[2] * d2d2 + mC[0] * d4d4 + e1e1 + mTwo * (mC[1] * d2d4 + e0e2), + mC[1] * d4d4 + mTwo * (mC[2] * d2d4 + e1e2), + mC[2] * d4d4 + e2e2 // > 0 + }; + + std::map rmMap; + RootsPolynomial::template SolveQuartic(f[0], f[1], f[2], + f[3], f[4], rmMap); + + // xbar cannot be a root of f(x), so d(x) != 0 and we can solve + // directly for w = -e(x)/d(x). + for (auto const& rm : rmMap) + { + Real const& x = rm.first; + Real w = -(mE[0] + x * (mE[1] + x * mE[2])) / (mD[2] + mD[4] * x); + Real y = w - (mA2Div2 + x * mA4Div2); + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = (rm.second == 1); + } + } + + template + void FIQuery, Ellipse2>::D4NotZeroEBarZero( + Real const& xbar, Result& result) + { + // Factor e(x) = (d2 + d4*x)*(h0 + h1*x). The w-equation has + // two solution components, x = xbar and w = -(h0 + h1*x). + Real translate, w, y; + + // Compute intersection of x = xbar with ellipse. + Real ncbar = -(mC[0] + xbar * (mC[1] + xbar * mC[2])); + if (ncbar >= mZero) + { + translate = mA2Div2 + xbar * mA4Div2; + w = Function::Sqrt(ncbar); + y = w - translate; + result.points[result.numPoints] = { xbar, y }; + if (w > mZero) + { + result.isTransverse[result.numPoints++] = true; + w = -w; + y = w - translate; + result.points[result.numPoints] = { xbar, y }; + result.isTransverse[result.numPoints++] = true; + } + else + { + result.isTransverse[result.numPoints++] = false; + } + } + + // Compute intersections of w = -(h0 + h1*x) with ellipse. + std::array h; + h[1] = mE[2] / mD[4]; + h[0] = (mE[1] - mD[2] * h[1]) / mD[4]; + std::array f = + { + mC[0] + h[0] * h[0], + mC[1] + mTwo * h[0] * h[1], + mC[2] + h[1] * h[1] // > 0 + }; + + std::map rmMap; + RootsPolynomial::template SolveQuadratic(f[0], f[1], f[2], + rmMap); + for (auto const& rm : rmMap) + { + Real const& x = rm.first; + translate = mA2Div2 + x * mA4Div2; + w = -(h[0] + x * h[1]); + y = w - translate; + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = (rm.second == 1); + } + } + + template + void FIQuery, Ellipse2>::D4ZeroD2NotZeroE2NotZero( + Result& result) + { + Real d2d2 = mD[2] * mD[2]; + std::array f = + { + mC[0] * d2d2 + mE[0] * mE[0], + mC[1] * d2d2 + mTwo * mE[0] * mE[1], + mC[2] * d2d2 + mE[1] * mE[1] + mTwo * mE[0] * mE[2], + mTwo * mE[1] * mE[2], + mE[2] * mE[2] // > 0 + }; + + std::map rmMap; + RootsPolynomial::template SolveQuartic(f[0], f[1], f[2], f[3], + f[4], rmMap); + for (auto const& rm : rmMap) + { + Real const& x = rm.first; + Real translate = mA2Div2 + x * mA4Div2; + Real w = -(mE[0] + x * (mE[1] + x * mE[2])) / mD[2]; + Real y = w - translate; + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = (rm.second == 1); + } + } + + template + void FIQuery, Ellipse2>::D4ZeroD2NotZeroE2Zero( + Result& result) + { + Real d2d2 = mD[2] * mD[2]; + std::array f = + { + mC[0] * d2d2 + mE[0] * mE[0], + mC[1] * d2d2 + mTwo * mE[0] * mE[1], + mC[2] * d2d2 + mE[1] * mE[1] + }; + + std::map rmMap; + RootsPolynomial::template SolveQuadratic(f[0], f[1], f[2], + rmMap); + for (auto const& rm : rmMap) + { + Real const& x = rm.first; + Real translate = mA2Div2 + x * mA4Div2; + Real w = -(mE[0] + x * mE[1]) / mD[2]; + Real y = w - translate; + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = (rm.second == 1); + } + } + + template + void FIQuery, Ellipse2>::D4ZeroD2Zero( + Result& result) + { + // e(x) cannot be identically zero, because if it were, then all + // d[i] = 0. But we tested that case previously and exited. + + if (mE[2] != mZero) + { + // Make e(x) monic, f(x) = e(x)/e2 = x^2 + (e1/e2)*x + (e0/e2) + // = x^2 + f1*x + f0. + std::array f = { mE[0] / mE[2], mE[1] / mE[2] }; + + Real mid = -f[1] / mTwo; + Real discr = mid * mid - f[0]; + if (discr > mZero) + { + // The theoretical roots of e(x) are x = -f1/2 + s*sqrt(discr) + // where s in {-1,+1}. For each root, determine exactly the + // sign of c(x). We need c(x) <= 0 in order to solve for + // w^2 = -c(x). At the root, + // c(x) = c0 + c1*x + c2*x^2 = c0 + c1*x - c2*(f0 + f1*x) + // = (c0 - c2*f0) + (c1 - c2*f1)*x + // = g0 + g1*x + // We need g0 + g1*x <= 0. + Real sqrtDiscr = Function::Sqrt(discr); + std::array g = + { + mC[0] - mC[2] * f[0], + mC[1] - mC[2] * f[1] + }; + + if (g[1] > mZero) + { + // We need s*sqrt(discr) <= -g[0]/g[1] + f1/2. + Real r = -g[0] / g[1] - mid; + + // s = +1: + if (r >= mZero) + { + Real rsqr = r * r; + if (discr < rsqr) + { + SpecialIntersection(mid + sqrtDiscr, true, result); + } + else if (discr == rsqr) + { + SpecialIntersection(mid + sqrtDiscr, false, result); + } + } + + // s = -1: + if (r > mZero) + { + SpecialIntersection(mid - sqrtDiscr, true, result); + } + else + { + Real rsqr = r * r; + if (discr > rsqr) + { + SpecialIntersection(mid - sqrtDiscr, true, result); + } + else if (discr == rsqr) + { + SpecialIntersection(mid - sqrtDiscr, false, result); + } + } + } + else if (g[1] < mZero) + { + // We need s*sqrt(discr) >= -g[0]/g[1] + f1/2. + Real r = -g[0] / g[1] - mid; + + // s = -1: + if (r <= mZero) + { + Real rsqr = r * r; + if (discr < rsqr) + { + SpecialIntersection(mid - sqrtDiscr, true, result); + } + else + { + SpecialIntersection(mid - sqrtDiscr, false, result); + } + } + + // s = +1: + if (r < mZero) + { + SpecialIntersection(mid + sqrtDiscr, true, result); + } + else + { + Real rsqr = r * r; + if (discr > rsqr) + { + SpecialIntersection(mid + sqrtDiscr, true, result); + } + else if (discr == rsqr) + { + SpecialIntersection(mid + sqrtDiscr, false, result); + } + } + } + else // g[1] = 0 + { + // The graphs of c(x) and f(x) are parabolas of the same + // shape. One is a vertical translation of the other. + if (g[0] < mZero) + { + // The graph of f(x) is above that of c(x). + SpecialIntersection(mid - sqrtDiscr, true, result); + SpecialIntersection(mid + sqrtDiscr, true, result); + } + else if (g[0] == mZero) + { + // The graphs of c(x) and f(x) are the same parabola. + SpecialIntersection(mid - sqrtDiscr, false, result); + SpecialIntersection(mid + sqrtDiscr, false, result); + } + } + } + else if (discr == mZero) + { + // The theoretical root of f(x) is x = -f1/2. + Real nchat = -(mC[0] + mid * (mC[1] + mid * mC[2])); + if (nchat > mZero) + { + SpecialIntersection(mid, true, result); + } + else if (nchat == mZero) + { + SpecialIntersection(mid, false, result); + } + } + } + else if (mE[1] != mZero) + { + Real xhat = -mE[0] / mE[1]; + Real nchat = -(mC[0] + xhat * (mC[1] + xhat * mC[2])); + if (nchat > mZero) + { + SpecialIntersection(xhat, true, result); + } + else if (nchat == mZero) + { + SpecialIntersection(xhat, false, result); + } + } + } + + template + void FIQuery, Ellipse2>::SpecialIntersection( + Real const& x, bool transverse, Result& result) + { + if (transverse) + { + Real translate = mA2Div2 + x * mA4Div2; + Real nc = -(mC[0] + x * (mC[1] + x * mC[2])); + if (nc < mZero) + { + // Clamp to eliminate the rounding error, but duplicate the point + // because we know that it is a transverse intersection. + nc = mZero; + } + + Real w = Function::Sqrt(nc); + Real y = w - translate; + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = true; + w = -w; + y = w - translate; + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = true; + } + else + { + // The vertical line at the root is tangent to the ellipse. + Real y = -(mA2Div2 + x * mA4Div2); // w = 0 + result.points[result.numPoints] = { x, y }; + result.isTransverse[result.numPoints++] = false; + } + } + + + + // FIQuery functions for area of intersection of ellipses. + + template + typename FIQuery, Ellipse2>::AreaResult + FIQuery, Ellipse2>::AreaOfIntersection( + Ellipse2 const& ellipse0, Ellipse2 const& ellipse1) + { + EllipseInfo E0; + E0.center = ellipse0.center; + E0.axis = ellipse0.axis; + E0.extent = ellipse0.extent; + E0.sqrExtent = ellipse0.extent * ellipse0.extent; + FinishEllipseInfo(E0); + + EllipseInfo E1; + E1.center = ellipse1.center; + E1.axis = ellipse1.axis; + E1.extent = ellipse1.extent; + E1.sqrExtent = ellipse1.extent * ellipse0.extent; + FinishEllipseInfo(E1); + + AreaResult ar; + ar.configuration = 0; + ar.findResult = operator()(ellipse0, ellipse1); + ar.area = mZero; + AreaDispatch(E0, E1, ar); + return ar; + } + + template + typename FIQuery, Ellipse2>::AreaResult + FIQuery, Ellipse2>::AreaOfIntersection( + Vector2 const& center0, Vector2 const axis0[2], + Vector2 const& sqrExtent0, Vector2 const& center1, + Vector2 const axis1[2], Vector2 const& sqrExtent1) + { + EllipseInfo E0; + E0.center = center0; + E0.axis = { axis0[0], axis0[1] }; + E0.extent = + { + Function::Sqrt(sqrExtent0[0]), + Function::Sqrt(sqrExtent0[1]) + }; + E0.sqrExtent = sqrExtent0; + FinishEllipseInfo(E0); + + EllipseInfo E1; + E1.center = center1; + E1.axis = { axis1[0], axis1[1] }; + E1.extent = + { + Function::Sqrt(sqrExtent1[0]), + Function::Sqrt(sqrExtent1[1]) + }; + E1.sqrExtent = sqrExtent1; + FinishEllipseInfo(E1); + + AreaResult ar; + ar.configuration = 0; + ar.findResult = operator()(center0, axis0, sqrExtent0, center1, axis1, + sqrExtent1); + ar.area = mZero; + AreaDispatch(E0, E1, ar); + return ar; + } + + template + void FIQuery, Ellipse2>::FinishEllipseInfo( + EllipseInfo& E) + { + E.M = OuterProduct(E.axis[0], E.axis[0]) / + (E.sqrExtent[0] * Dot(E.axis[0], E.axis[0])); + E.M += OuterProduct(E.axis[1], E.axis[1]) / + (E.sqrExtent[1] * Dot(E.axis[1], E.axis[1])); + E.AB = E.extent[0] * E.extent[1]; + E.halfAB = E.AB / mTwo; + E.BpA = E.extent[1] + E.extent[0]; + E.BmA = E.extent[1] - E.extent[0]; + } + + template + void FIQuery, Ellipse2>::AreaDispatch( + EllipseInfo const& E0, EllipseInfo const& E1, AreaResult& ar) + { + if (ar.findResult.intersect) + { + if (ar.findResult.numPoints == 1) + { + // Containment or separation. + AreaCS(E0, E1, ar); + } + else if (ar.findResult.numPoints == 2) + { + if (ar.findResult.isTransverse[0]) + { + // Both intersection points are transverse. + Area2(E0, E1, 0, 1, ar); + } + else + { + // Both intersection points are tangential, so one ellipse + // is contained in the other. + AreaCS(E0, E1, ar); + } + } + else if (ar.findResult.numPoints == 3) + { + // The tangential intersection is irrelevant in the area + // computation. + if (!ar.findResult.isTransverse[0]) + { + Area2(E0, E1, 1, 2, ar); + } + else if (!ar.findResult.isTransverse[1]) + { + Area2(E0, E1, 2, 0, ar); + } + else // ar.findResult.isTransverse[2] == false + { + Area2(E0, E1, 0, 1, ar); + } + } + else // ar.findResult.numPoints == 4 + { + Area4(E0, E1, ar); + } + } + else + { + // Containment, separation, or same ellipse. + AreaCS(E0, E1, ar); + } + } + + template + void FIQuery, Ellipse2>::AreaCS( + EllipseInfo const& E0, EllipseInfo const& E1, AreaResult& ar) + { + if (ar.findResult.numPoints <= 1) + { + Vector2 diff = E0.center - E1.center; + Real qform0 = Dot(diff, E0.M * diff); + Real qform1 = Dot(diff, E1.M * diff); + if (qform0 > mOne && qform1 > mOne) + { + // Each ellipse center is outside the other ellipse, so the + // ellipses are separated (numPoints == 0) or outside each + // other and just touching (numPoints == 1). + ar.configuration = AreaResult::ELLIPSES_ARE_SEPARATED; + ar.area = mZero; + } + else + { + // One ellipse is inside the other. Determine this indirectly by + // comparing areas. + if (E0.AB < E1.AB) + { + ar.configuration = AreaResult::E1_CONTAINS_E0; + ar.area = mPi * E0.AB; + } + else + { + ar.configuration = AreaResult::E0_CONTAINS_E1; + ar.area = mPi * E1.AB; + } + } + } + else + { + ar.configuration = AreaResult::ELLIPSES_ARE_EQUAL; + ar.area = mPi * E0.AB; + } + } + + template + void FIQuery, Ellipse2>::Area2( + EllipseInfo const& E0, EllipseInfo const& E1, int i0, int i1, + AreaResult& ar) + { + ar.configuration = AreaResult::ONE_CHORD_REGION; + + // The endpoints of the chord. + Vector2 const& P0 = ar.findResult.points[i0]; + Vector2 const& P1 = ar.findResult.points[i1]; + + // Compute locations relative to the ellipses. + Vector2 P0mC0 = P0 - E0.center, P0mC1 = P0 - E1.center; + Vector2 P1mC0 = P1 - E0.center, P1mC1 = P1 - E1.center; + + // Compute the ellipse normal vectors at endpoint P0. This is sufficient + // information to determine chord endpoint order. + Vector2 N0 = E0.M * P0mC0, N1 = E1.M * P0mC1; + Real dotperp = DotPerp(N1, N0); + + // Choose the endpoint order for the chord region associated with E0. + if (dotperp > mZero) + { + // The chord order for E0 is and for E1 is . + ar.area = + ComputeAreaChordRegion(E0, P0mC0, P1mC0) + + ComputeAreaChordRegion(E1, P1mC1, P0mC1); + } + else + { + // The chord order for E0 is and for E1 is . + ar.area = + ComputeAreaChordRegion(E0, P1mC0, P0mC0) + + ComputeAreaChordRegion(E1, P0mC1, P1mC1); + } + } + + template + void FIQuery, Ellipse2>::Area4( + EllipseInfo const& E0, EllipseInfo const& E1, AreaResult& ar) + { + ar.configuration = AreaResult::FOUR_CHORD_REGION; + + // Select a counterclockwise ordering of the points of intersection. Use + // the polar coordinates for E0 to do this. The multimap is used in the + // event that computing the intersections involved numerical rounding + // errors that lead to a duplicate intersection, even though the + // intersections are all labeled as transverse. [See the comment in the + // function SpecialIntersection.] + std::multimap ordering; + int i; + for (i = 0; i < 4; ++i) + { + Vector2 PmC = ar.findResult.points[i] - E0.center; + Real x = Dot(E0.axis[0], PmC); + Real y = Dot(E0.axis[1], PmC); + Real theta = Function::ATan2(y, x); + ordering.insert(std::make_pair(theta, i)); + } + + std::array permute; + i = 0; + for (auto const& element : ordering) + { + permute[i++] = element.second; + } + + // Start with the area of the convex quadrilateral. + Vector2 diag20 = + ar.findResult.points[permute[2]] - ar.findResult.points[permute[0]]; + Vector2 diag31 = + ar.findResult.points[permute[3]] - ar.findResult.points[permute[1]]; + ar.area = Function::FAbs(DotPerp(diag20, diag31)) / mTwo; + + // Visit each pair of consecutive points. The selection of ellipse for + // the chord-region area calculation uses the "most counterclockwise" + // tangent vector. + for (int i0 = 3, i1 = 0; i1 < 4; i0 = i1++) + { + // Get a pair of consecutive points. + Vector2 const& P0 = ar.findResult.points[permute[i0]]; + Vector2 const& P1 = ar.findResult.points[permute[i1]]; + + // Compute locations relative to the ellipses. + Vector2 P0mC0 = P0 - E0.center, P0mC1 = P0 - E1.center; + Vector2 P1mC0 = P1 - E0.center, P1mC1 = P1 - E1.center; + + // Compute the ellipse normal vectors at endpoint P0. + Vector2 N0 = E0.M * P0mC0, N1 = E1.M * P0mC1; + Real dotperp = DotPerp(N1, N0); + if (dotperp > mZero) + { + // The chord goes with ellipse E0. + ar.area += ComputeAreaChordRegion(E0, P0mC0, P1mC0); + } + else + { + // The chord goes with ellipse E1. + ar.area += ComputeAreaChordRegion(E1, P0mC1, P1mC1); + } + } + } + + template + Real FIQuery, Ellipse2>::ComputeAreaChordRegion( + EllipseInfo const& E, Vector2 const& P0mC, + Vector2 const& P1mC) + { + // Compute polar coordinates for P0 and P1 on the ellipse. + Real x0 = Dot(E.axis[0], P0mC); + Real y0 = Dot(E.axis[1], P0mC); + Real theta0 = Function::ATan2(y0, x0); + Real x1 = Dot(E.axis[0], P1mC); + Real y1 = Dot(E.axis[1], P1mC); + Real theta1 = Function::ATan2(y1, x1); + + // The arc straddles the atan2 discontinuity on the negative x-axis. Wrap + // the second angle to be larger than the first angle. + if (theta1 < theta0) + { + theta1 += mTwoPi; + } + + // Compute the area portion of the sector due to the triangle. + Real triArea = Function::FAbs(DotPerp(P0mC, P1mC)) / mTwo; + + // Compute the chord region area. + Real dtheta = theta1 - theta0; + Real F0, F1, sectorArea; + if (dtheta <= mPi) + { + // Use the area formula directly. + // area(theta0,theta1) = F(theta1) - F(theta0) - area(triangle) + F0 = ComputeIntegral(E, theta0); + F1 = ComputeIntegral(E, theta1); + sectorArea = F1 - F0; + return sectorArea - triArea; + } + else + { + // The angle of the elliptical sector is larger than pi radians. + // Use the area formula + // area(theta0,theta1) = pi*a*b - area(theta1,theta0) + theta0 += mTwoPi; // ensure theta0 > theta1 + F0 = ComputeIntegral(E, theta0); + F1 = ComputeIntegral(E, theta1); + sectorArea = F0 - F1; + return mPi * E.AB - (sectorArea - triArea); + } + } + + template + Real FIQuery, Ellipse2>::ComputeIntegral( + EllipseInfo const& E, Real const& theta) + { + Real twoTheta = mTwo * theta; + Real sn = Function::Sin(twoTheta); + Real cs = Function::Cos(twoTheta); + Real arg = E.BmA * sn / (E.BpA + E.BmA * cs); + return E.halfAB * (theta - Function::ATan(arg)); + } + + + } +} +*/ \ No newline at end of file diff --git a/src/main/java/varun_algorithm_ransac_Ransac/Ellipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/Ellipsoid.java new file mode 100644 index 0000000..a4faf1c --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/Ellipsoid.java @@ -0,0 +1,42 @@ +package varun_algorithm_ransac_Ransac; + + +import net.imglib2.util.LinAlgHelpers; + +public class Ellipsoid extends HyperEllipsoid +{ + /** + * Construct 3D ellipsoid. Some of the parameters may be null. The center + * parameter is always required. Moreover, either + *
        + *
      • covariance or
      • + *
      • precision or
      • + *
      • axes and radii
      • + *
      + * must be provided. + * + * @param center + * coordinates of center. must not be {@code null}. + * @param covariance + * @param precision + * @param axes + * @param radii + */ + public Ellipsoid( final double[] center, final double[][] covariance, final double[][] precision, final double[][] axes, final double[] radii ) + { + super( center, covariance, precision, axes, radii ); + } + + @Override + public String toString() + { + return "center = " + + LinAlgHelpers.toString( getCenter() ) + + "\nradii = " + + LinAlgHelpers.toString( getRadii() ) + + "\naxes = " + + LinAlgHelpers.toString( getAxes() ) + + "\nprecision = " + + LinAlgHelpers.toString( getPrecision() ); + } +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/FitCircle.java b/src/main/java/varun_algorithm_ransac_Ransac/FitCircle.java new file mode 100644 index 0000000..bf64ff3 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/FitCircle.java @@ -0,0 +1,248 @@ +package varun_algorithm_ransac_Ransac; + + +import net.imglib2.util.LinAlgHelpers; +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.ArrayRealVector; +import org.apache.commons.math3.linear.DecompositionSolver; +import org.apache.commons.math3.linear.RealMatrix; +import org.apache.commons.math3.linear.RealVector; +import org.apache.commons.math3.linear.SingularValueDecomposition; + +import Jama.EigenvalueDecomposition; +import Jama.Matrix; + +/** + * Adapted from BoneJ's FitEllipsoid. + * + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> Modified by Varun Kapoor; + */ +public class FitCircle +{ + /** + *

      + * Ellipsoid fitting method by Yury Petrov.
      + * Fits an ellipsoid in the form Ax2 + + * By2 + Cz2 + 2Dxy + 2Exz + * + 2Fyz + 2Gx + 2Hy + 2Iz = 1
      + * To an n * 3 array of coordinates. + *

      + * + * @see

      + * MATLAB script + *

      + */ + + public static Ellipsoid yuryPetrov( final double[][] points, final int ndims ) + { + final int nPoints = points.length; + + + + + + + if (ndims > 2) { + + if ( nPoints < 9 ) + throw new IllegalArgumentException( "Too few points; need at least 9 to calculate a unique ellipsoid" ); + + + RealMatrix MatrixD = new Array2DRowRealMatrix(nPoints, 9); + for (int i = 0; i < nPoints; i++) { + + + final double x = points[i][0]; + final double y = points[i][1]; + final double z = points[i][2]; + + double xx = x*x; + double yy =y*y; + double zz = z*z; + double xy = 2 * x *y; + double xz = 2 * x * z; + double yz = 2 * y * z; + + + + + MatrixD.setEntry(i, 0, xx); + MatrixD.setEntry(i, 1, yy); + MatrixD.setEntry(i, 2, zz); + MatrixD.setEntry(i, 3, xy); + MatrixD.setEntry(i, 4, xz); + MatrixD.setEntry(i, 5, yz); + MatrixD.setEntry(i, 6, 2 * x); + MatrixD.setEntry(i, 7, 2 * y); + MatrixD.setEntry(i, 8, 2 * z); + + + + } + + RealMatrix dtd = MatrixD.transpose().multiply(MatrixD); + + + // Create a vector of ones. + RealVector ones = new ArrayRealVector(nPoints); + ones.mapAddToSelf(1); + + // Multiply: d' * ones.mapAddToSelf(1) + RealVector dtOnes = MatrixD.transpose().operate(ones); + + // Find ( d' * d )^-1 + DecompositionSolver solver = new SingularValueDecomposition(dtd) + .getSolver(); + RealMatrix dtdi = solver.getInverse(); + + // v = (( d' * d )^-1) * ( d' * ones.mapAddToSelf(1)); + RealVector v = dtdi.operate(dtOnes); + + + + return ellipsoidFromEquation( v ); + + } + + else + { + + + + RealMatrix MatrixD = new Array2DRowRealMatrix(nPoints, 5); + for (int i = 0; i < nPoints; i++) { + final double x = points[i][0]; + final double y = points[i][1]; + + double xx = x*x; + double yy =y*y; + double xy = 2 * x *y; + + + + + MatrixD.setEntry(i, 0, xx); + MatrixD.setEntry(i, 1, yy); + MatrixD.setEntry(i, 2, xy); + MatrixD.setEntry(i, 3, 2 * x); + MatrixD.setEntry(i, 4, 2 * y); + + + + } + + RealMatrix dtd = MatrixD.transpose().multiply(MatrixD); + + + // Create a vector of ones. + RealVector ones = new ArrayRealVector(nPoints); + ones.mapAddToSelf(1); + + // Multiply: d' * ones.mapAddToSelf(1) + RealVector dtOnes = MatrixD.transpose().operate(ones); + + // Find ( d' * d )^-1 + DecompositionSolver solver = new SingularValueDecomposition(dtd) + .getSolver(); + RealMatrix dtdi = solver.getInverse(); + + // v = (( d' * d )^-1) * ( d' * ones.mapAddToSelf(1)); + RealVector v = dtdi.operate(dtOnes); + + + + return circleFromEquation2D( v ); + + + + } + + } + + + + /** + * Calculate the matrix representation of the ellipsoid from the equation variables + * ax2 + by2 + cz2 + + * 2dxy + 2exz + 2fyz + 2gx + 2hy + + * 2iz = 1
      + * + * @param V vector (a,b,c,d,e,f,g,h,i) + * @return the ellipsoid. + */ + private static Ellipsoid ellipsoidFromEquation( final RealVector V ) + { + final double a = V.getEntry(0); + final double b = V.getEntry( 1); + final double c = V.getEntry( 2); + final double d = V.getEntry( 3); + final double e = V.getEntry( 4); + final double f = V.getEntry( 5); + final double g = V.getEntry( 6); + final double h = V.getEntry( 7); + final double i = V.getEntry( 8); + + double[] Coefficents = V.toArray(); + + + final double[][] aa = new double[][] { + { a, d, e }, + { d, b, f }, + { e, f, c } }; + final double[] bb = new double[] { g, h, i }; + final double[] cc = new Matrix( aa ).solve( new Matrix( bb, 3 ) ).getRowPackedCopy(); + LinAlgHelpers.scale( cc, -1, cc ); + + final double[] At = new double[ 3 ]; + LinAlgHelpers.mult( aa, cc, At ); + final double r33 = LinAlgHelpers.dot( cc, At ) + 2 * LinAlgHelpers.dot( bb, cc ) - 1; + LinAlgHelpers.scale( aa, -1 / r33, aa ); + int n = cc.length; + double[][] covariance = new Matrix(aa).inverse().getArray(); + return (new Ellipsoid( cc, null, aa, null, null )); + } + + + + private static Ellipsoid circleFromEquation2D( final RealVector V ) + { + final double a = V.getEntry(0); + + final double d = V.getEntry( 3); + final double e = V.getEntry( 4); + double[] Coefficents = V.toArray(); + + + final double[][] aa = new double[][] { + { a, 0 }, + { 0, a } }; + final double[] bb = new double[] { d, e }; + final double[] cc = new Matrix( aa ).solve( new Matrix( bb, 2 ) ).getRowPackedCopy(); + LinAlgHelpers.scale( cc, -1, cc ); + final double[] At = new double[ 2 ]; + LinAlgHelpers.mult( aa, cc, At ); + final double r33 = LinAlgHelpers.dot( cc, At ) + 2 * LinAlgHelpers.dot( bb, cc ) - 1; + LinAlgHelpers.scale( aa, -1 / r33, aa ); + int n = cc.length; + double[][] covariance = new Matrix(aa).inverse().getArray(); + + return (new Ellipsoid( cc, null, aa, null, null )); + } + + + private static double[] computeAxisAndRadiiFromCovariance(double[][] covariance, int n) + { + final EigenvalueDecomposition eig = new Matrix( covariance ).eig(); + final Matrix ev = eig.getD(); + double[] radii = new double[ n ]; + for ( int d = 0; d < n; ++d ) + radii[ d ] = Math.sqrt( ev.get( d, d ) ); + return radii; + } + + + +} + diff --git a/src/main/java/varun_algorithm_ransac_Ransac/FitEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/FitEllipsoid.java new file mode 100644 index 0000000..d609888 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/FitEllipsoid.java @@ -0,0 +1,108 @@ +package varun_algorithm_ransac_Ransac; + + +import net.imglib2.util.LinAlgHelpers; +import org.apache.commons.math3.linear.Array2DRowRealMatrix; +import org.apache.commons.math3.linear.ArrayRealVector; +import org.apache.commons.math3.linear.DecompositionSolver; +import org.apache.commons.math3.linear.RealMatrix; +import org.apache.commons.math3.linear.RealVector; +import org.apache.commons.math3.linear.SingularValueDecomposition; + +import Jama.EigenvalueDecomposition; +import Jama.Matrix; + +import net.imglib2.util.LinAlgHelpers; +import Jama.CholeskyDecomposition; +import Jama.Matrix; + +/** + * Adapted from BoneJ's FitEllipsoid. + * + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> + */ +public class FitEllipsoid +{ + /** + *

      + * Ellipsoid fitting method by Yury Petrov.
      + * Fits an ellipsoid in the form Ax2 + + * By2 + Cz2 + 2Dxy + 2Exz + * + 2Fyz + 2Gx + 2Hy + 2Iz = 1
      + * To an n * 3 array of coordinates. + *

      + * + * @see

      + * MATLAB script + *

      + */ + public static Ellipsoid yuryPetrov( final double[][] points ) + { + final int nPoints = points.length; + if ( nPoints < 9 ) + throw new IllegalArgumentException( "Too few points; need at least 9 to calculate a unique ellipsoid" ); + + final double[][] d = new double[ nPoints ][ 9 ]; + final double[][] b = new double[ 9 ][ 1 ]; + for (int i = 0; i < nPoints; i++) { + final double x = points[i][0]; + final double y = points[i][1]; + final double z = points[i][2]; + d[i][0] = x * x; + d[i][1] = y * y; + d[i][2] = z * z; + d[i][3] = 2 * x * y; + d[i][4] = 2 * x * z; + d[i][5] = 2 * y * z; + d[i][6] = 2 * x; + d[i][7] = 2 * y; + d[i][8] = 2 * z; + for ( int j = 0; j < 9; ++j ) + b[j][0] += d[i][j]; + } + + final double[][] DTD = new double[ 9 ][ 9 ]; + LinAlgHelpers.multATB( d, d, DTD ); + final Matrix V = new CholeskyDecomposition( new Matrix( DTD ) ).solve( new Matrix( b ) ); + return ellipsoidFromEquation( V ); + } + + /** + * Calculate the matrix representation of the ellipsoid from the equation variables + * ax2 + by2 + cz2 + + * 2dxy + 2exz + 2fyz + 2gx + 2hy + + * 2iz = 1
      + * + * @param V vector (a,b,c,d,e,f,g,h,i) + * @return the ellipsoid. + */ + private static Ellipsoid ellipsoidFromEquation( final Matrix V ) + { + final double a = V.get( 0, 0 ); + final double b = V.get( 1, 0 ); + final double c = V.get( 2, 0 ); + final double d = V.get( 3, 0 ); + final double e = V.get( 4, 0 ); + final double f = V.get( 5, 0 ); + final double g = V.get( 6, 0 ); + final double h = V.get( 7, 0 ); + final double i = V.get( 8, 0 ); + + final double[][] aa = new double[][] { + { a, d, e }, + { d, b, f }, + { e, f, c } }; + final double[] bb = new double[] { g, h, i }; + final double[] cc = new Matrix( aa ).solve( new Matrix( bb, 3 ) ).getRowPackedCopy(); + LinAlgHelpers.scale( cc, -1, cc ); + + final double[] At = new double[ 3 ]; + LinAlgHelpers.mult( aa, cc, At ); + final double r33 = LinAlgHelpers.dot( cc, At ) + 2 * LinAlgHelpers.dot( bb, cc ) - 1; + LinAlgHelpers.scale( aa, -1 / r33, aa ); + + return new Ellipsoid( cc, null, aa, null, null ); + } +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/FitLocalEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/FitLocalEllipsoid.java new file mode 100644 index 0000000..4935c58 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/FitLocalEllipsoid.java @@ -0,0 +1,56 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.ArrayList; +import net.imglib2.RealLocalizable; + + +public class FitLocalEllipsoid { + + + + public static RansacFunctionEllipsoid findLocalEllipsoid( + final ArrayList points, int ndims) + { + + + final ArrayList remainingPoints = new ArrayList(); + if (points != null) + remainingPoints.addAll(points); + int size = points.size(); + final double[] pointA = new double[ndims]; + final double[] pointB = new double[ndims]; + final double[] pointC = new double[ndims]; + + int splitindex; + if (size % 2 == 0) + splitindex = size / 2; + else + splitindex = (size - 1) / 2; + + for (int i = 0; i < ndims; ++i) { + pointA[i] = points.get(0).getDoublePosition(i); + pointB[i] = points.get(splitindex).getDoublePosition(i); + pointC[i] = points.get(size - 1).getDoublePosition(i); + + } + + + + + final Circle ellipsoid = Circle.FitCircleMb(pointA, pointB, pointC); + + RansacFunctionEllipsoid localellipse = new RansacFunctionEllipsoid(ellipsoid, points, points); + + + return localellipse; + + + + + + + + } + + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/HyperEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/HyperEllipsoid.java new file mode 100644 index 0000000..4ebfd4d --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/HyperEllipsoid.java @@ -0,0 +1,221 @@ +package varun_algorithm_ransac_Ransac; + +import Jama.EigenvalueDecomposition; +import Jama.Matrix; +import net.imglib2.AbstractRealLocalizable; +import net.imglib2.RealLocalizable; +import net.imglib2.util.LinAlgHelpers; + +/** + * Hyperellipsoid in n dimensions. + * + *

      + * Points x on the ellipsoid are (x - c)^T * M * (x - c) = 1, where + * c = {@link #getCenter()} and M = {@link #getPrecision()}. + * + *

      + * M = R * D * R^T, where D is diagonal with entries 1/e_i^2 and e_i are radii {@link #getRadii()}. + * R is orthogonal matrix whose columns are the axis directions, that is, R^T = {@link #getAxes()}. + * + *

      + * To rotate a point x into ellipsoid coordinates (axis-aligned ellipsoid) compute R^T * x. + * + * @author Tobias Pietzsch <tobias.pietzsch@gmail.com> + */ +public class HyperEllipsoid extends AbstractRealLocalizable +{ + private double[][] axes; + + private double[] radii; + + private double[][] covariance; + + private double[][] precision; + + /** + * Construct hyperellipsoid. Some of the parameters may be null. The center + * parameter is always required. Moreover, either + *

        + *
      • covariance or
      • + *
      • precision or
      • + *
      • axes and radii
      • + *
      + * must be provided. + * + * @param center + * coordinates of center. must not be {@code null}. + * @param covariance + * @param precision + * @param axes + * @param radii + */ + protected HyperEllipsoid( final double[] center, final double[][] covariance, final double[][] precision, final double[][] axes, final double[] radii ) + { + super( center ); + this.axes = axes; + this.radii = radii; + this.covariance = covariance; + this.precision = precision; + } + + /** + * Get coordinates of center. + * + * @return center coordinates. + */ + public double[] getCenter() + { + return position; + } + + /** + * Get axes as unit vectors. + * Indices are {@code axes[axisIndex][dimensionIndex]}. + * + * @return axes as array of unit vectors. + */ + public double[][] getAxes() + { + if ( axes == null ) + { + if ( covariance != null ) + computeAxisAndRadiiFromCovariance(); + else + computeAxisAndRadiiFromPrecision(); + } + return axes; + } + + /** + * Get array of radius along each {@link #getAxes() axis}. + * + * @return radii. + */ + public double[] getRadii() + { + if ( radii == null ) + { + if ( covariance != null ) + computeAxisAndRadiiFromCovariance(); + else + computeAxisAndRadiiFromPrecision(); + } + return radii; + } + + /** + * Get the covariance matrix. + * + * @return covariance matrix. + */ + public double[][] getCovariance() + { + if ( covariance == null ) + { + if ( precision != null ) + computeCovarianceFromPrecision(); + else + computeCovarianceFromAxesAndRadii(); + } + return covariance; + } + + /** + * Get the covariance matrix. + * + * @param m is set to covariance matrix. + */ + public void getCovariance( final double[][] m ) + { + LinAlgHelpers.copy( getCovariance(), m ); + } + + /** + * Get the precision (inverse covariance) matrix. + * + * @return precision matrix. + */ + public double[][] getPrecision() + { + if ( precision == null ) + { + if ( covariance != null ) + computePrecisionFromCovariance(); + else + computePrecisionFromAxesAndRadii(); + } + return precision; + } + + public boolean contains( final double[] point ) + { + final double[] x = new double[ n ]; + final double[] y = new double[ n ]; + LinAlgHelpers.subtract( point, getCenter(), x ); + LinAlgHelpers.mult( getPrecision(), x, y ); + return LinAlgHelpers.dot( x, y ) <= 1; + } + + public boolean contains( final RealLocalizable point ) + { + final double[] p = new double[ n ]; + point.localize( p ); + return contains( p ); + } + + private void computeCovarianceFromAxesAndRadii() + { + final double[][] tmp = new double[ n ][]; + covariance = new double[ n ][]; + for ( int d = 0; d < n; ++d ) + { + tmp[ d ] = new double[ n ]; + covariance[ d ] = new double[ n ]; + LinAlgHelpers.scale( axes[ d ], radii[ d ] * radii[ d ], tmp[ d ] ); + } + LinAlgHelpers.multATB( axes, tmp, covariance ); + } + + private void computeCovarianceFromPrecision() + { + covariance = new Matrix( precision ).inverse().getArray(); + } + + private void computePrecisionFromAxesAndRadii() + { + final double[][] tmp = new double[ n ][]; + precision = new double[ n ][]; + for ( int d = 0; d < n; ++d ) + { + tmp[ d ] = new double[ n ]; + precision[ d ] = new double[ n ]; + LinAlgHelpers.scale( axes[ d ], 1.0 / ( radii[ d ] * radii[ d ] ), tmp[ d ] ); + } + LinAlgHelpers.multATB( axes, tmp, precision ); + } + + private void computePrecisionFromCovariance() + { + precision = new Matrix( covariance ).inverse().getArray(); + } + + private void computeAxisAndRadiiFromPrecision() + { + final EigenvalueDecomposition eig = new Matrix( precision ).eig(); + axes = eig.getV().transpose().getArray(); + final Matrix ev = eig.getD(); + radii = new double[ n ]; + for ( int d = 0; d < n; ++d ) + radii[ d ] = Math.sqrt( 1 / ev.get( d, d ) ); + } + + private void computeAxisAndRadiiFromCovariance() + { + final EigenvalueDecomposition eig = new Matrix( covariance ).eig(); + axes = eig.getV().transpose().getArray(); + final Matrix ev = eig.getD(); + radii = new double[ n ]; + for ( int d = 0; d < n; ++d ) + radii[ d ] = Math.sqrt( ev.get( d, d ) ); + } +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonEllipsoid.java new file mode 100644 index 0000000..12413d9 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonEllipsoid.java @@ -0,0 +1,175 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.Random; + +import net.imglib2.util.LinAlgHelpers; + +/** + * Newton Raphson routine to get the shortest distance of a point from an + * ellipsoid. + * + * @author Varun Kapoor + * + */ + +public class NewtonRaphsonEllipsoid implements NumericalSolvers { + + public static int MAX_ITER = 10000; + public static double MIN_CHANGE = 1.0E-1; + public double xc = 0, xcNew = 0, func = 0, funcdiff = 0, funcsecdiff = 0, newratio = 0, newsecratio = 0; + double damp = 0.01; + + public NewtonRaphsonEllipsoid() { + + + } + + public double run(final int numComponents, final double[] ellipseCoeff, final double[] sourcePoint, + final double[] targetPoint) { + + final int n = ellipseCoeff.length; + final double[] z = new double[ n ]; + double sumZSqr = 0; + for ( int i = 0; i < numComponents; ++i ) + { + z[ i ] = sourcePoint[ i ] / ellipseCoeff[ i ]; + sumZSqr += z[ i ] * z[ i ]; + } + + if ( sumZSqr == 1 ) + { + // The point is on the hyperellipsoid. + for ( int i = 0; i < numComponents; ++i ) + { + targetPoint[ i ] = sourcePoint[ i ]; + } + return 0; + } + + final double emin = ellipseCoeff[ numComponents - 1 ]; + final double[] pSqr = new double[ n ]; + final double[] numerator = new double[ n ]; + for ( int i = 0; i < numComponents; ++i ) + { + final double p = ellipseCoeff[ i ] / emin; + pSqr[ i ] = p * p; + numerator[ i ] = pSqr[ i ] * z[ i ]; + } + + + + xc = -ellipseCoeff[1] * ellipseCoeff[1] + ellipseCoeff[1] * sourcePoint[1]; + xcNew = xc; + // Main NR method + updateFunctions(xc, sourcePoint, ellipseCoeff, numComponents); + int count = 0; + int iteration = 0; + for (int iter = 0; iter < MAX_ITER; ++iter) { + + + xc = xcNew; + ++iteration; + + if ( iteration % 1000 == 0 ||count > 1 ) + { + damp = new Random().nextDouble(); + iterate(); + damp = 1; + } + else + { + iterate(); + } + + if(Double.isNaN(xcNew) || Math.abs(xcNew) > 1.0E10) { + xcNew = xc; + count++; + + } + + else count = 0; + + if (count > 10) + break; + + + if (iteration > MAX_ITER) + break; + + updateFunctions(xcNew, sourcePoint, ellipseCoeff, numComponents); + + + System.out.println((xc - xcNew) + " " + iteration); + + + if (Math.abs(xc - xcNew) < MIN_CHANGE) + break; + + } + + double sqrDistance = 0; + for ( int i = 0; i < numComponents; ++i ) + { + targetPoint[ i ] = pSqr[ i ] * sourcePoint[ i ] / ( xc + pSqr[ i ] ); + final double diff = targetPoint[ i ] - sourcePoint[ i ]; + sqrDistance += diff * diff; + } + return sqrDistance; + } + + protected void iterate() { + + this.xcNew = iterate(xc, func, funcdiff, funcsecdiff); + + + } + + public double iterate(final double oldpoint, final double function, final double functionderiv, + final double functionsecderiv) { + + return oldpoint - (function / functionderiv)* (1 + damp * 0.5 * function * functionsecderiv / (functionderiv * functionderiv) ); + + } + + protected void updateFunctions(final double xc, final double[] sourcePoint, final double[] ellipseCoeff, + final int numComponents) { + + func = -1; + funcdiff = 0; + funcsecdiff = 0; + + final int n = ellipseCoeff.length; + final double[] z = new double[n]; + + final double emin = ellipseCoeff[n - 1]; + final double[] pSqr = new double[n]; + final double[] numerator = new double[n]; + final double[] denominator = new double[n]; + + for (int i = 0; i < numComponents; ++i) { + z[i] = sourcePoint[i] / ellipseCoeff[i]; + + final double p = ellipseCoeff[i] / emin; + pSqr[i] = p * p; + numerator[i] = pSqr[i] * z[i]; + + denominator[i] = xc + pSqr[i]; + final double ratio = numerator[ i ] / denominator[i]; + final double ratiosq = - 2* ( numerator[ i ] * numerator[ i ] )/ (denominator[i] * denominator[i] * denominator[i] ); + final double ratiocube = 6* ( numerator[ i ] * numerator[ i ] )/ (denominator[i] * denominator[i] * denominator[i] * denominator[i] ); + func += ratio * ratio; + funcdiff += ratiosq; + funcsecdiff += ratiocube; + + } + + + + } + private static double LengthRobust( final double[] v ) + { + return LinAlgHelpers.length( v ); + } + + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonPolynomial.java b/src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonPolynomial.java new file mode 100644 index 0000000..3ad69ce --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/NewtonRaphsonPolynomial.java @@ -0,0 +1,180 @@ +/*- + * #%L + * Microtubule tracker. + * %% + * Copyright (C) 2017 MTrack developers. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ +package varun_algorithm_ransac_Ransac; + +import java.util.Random; + +/** + * Newton Raphson routine to get the shortest distance of a point from a + * curve. + * + * @author Varun Kapoor, Stephan Preibisch + * + */ +public class NewtonRaphsonPolynomial +{ + public static int MAX_ITER = 1000000; + public static double MIN_CHANGE = 1.0E-3; + + public double xc, xcNew, polyfunc, polyfuncdiff, delpolyfuncdiff, dmin, dMinDiff, secdelpolyfuncdiff, dminsecdiff; + final int degree; + final double[] powCache; + double damp = 1; + final Random rndx; + + public NewtonRaphsonPolynomial( final Random rndx, final int degree ) + { + // Initial guesses for Newton Raphson + this.rndx = rndx; + this.xc = rndx.nextFloat(); + this.xcNew = rndx.nextFloat() * rndx.nextFloat(); + this.degree = degree; + this.powCache = new double[ degree + 4 ]; + } + + public double run( final double x, final double y, final double[] coeff ) + { + updatePowCache( xc ); + computeFunctions( coeff ); + + int iteration = 0; + + do + { + xc = xcNew; + dmin = (polyfunc - y) * polyfuncdiff + (xc - x); + dMinDiff = polyfuncdiff * polyfuncdiff + (polyfunc - y)* delpolyfuncdiff + 1; + dminsecdiff = (polyfunc - y)*secdelpolyfuncdiff + delpolyfuncdiff * polyfuncdiff + 2 * polyfuncdiff * delpolyfuncdiff ; + + // Compute the first iteration of the new point + + ++iteration; + + if ( iteration % 1000 == 0 ) + { + damp = rndx.nextDouble(); + iterate(); + damp = 1; + } + else + { + iterate(); + } + + if ( Double.isNaN( xcNew ) ) + xcNew = xc; + + // Compute the functions and the required derivates at the new point + delpolyfuncdiff = 0; + polyfunc = 0; + polyfuncdiff = 0; + secdelpolyfuncdiff = 0; + + // precompute the powers + updatePowCache( xcNew ); + computeFunctions( coeff ); + + if ( iteration >= MAX_ITER ) + break; + } + while ( Math.abs( ( xcNew - xc ) ) > MIN_CHANGE ); + + // After the solution is found compute the y co-oordinate of the point + // on the curve + polyfunc = 0; + for (int j = degree; j >= 0; j--) + polyfunc += coeff[j] * Math.pow(xc, j); + + // Get the distance of (x1, y1) point from the curve and return the + // value + return distance( x, y, xc, polyfunc ); + } + + protected void updatePowCache( final double xc ) + { + for ( int j = degree; j >= -3; j-- ) + if ( j >= 0 ) + powCache[ j + 3 ] = pow( xc, j ); + else + powCache[ j + 3 ] = Math.pow( xc, j ); + } + + protected void computeFunctions( final double[] coeff ) + { + for ( int j = degree; j >= 0; j-- ) + { + double c = coeff[ j ]; + polyfunc += c * powCache[ j + 3 ]; + + c *= j; + polyfuncdiff += c * powCache[ j + 2 ]; + + c *= ( j - 1 ); + delpolyfuncdiff += c * powCache[ j + 1 ]; + + c *= ( j - 2 ); + secdelpolyfuncdiff += c * powCache[ j ]; + } + } + + protected void iterate() + { + this.xcNew = iterate( xc, dmin, dMinDiff, dminsecdiff ); + } + + public double iterate( final double oldpoint, final double function, final double functionderiv, final double functionsecderiv ) + { + return oldpoint - (function / functionderiv) * (1 + damp * 0.5 * function * functionsecderiv / (functionderiv * functionderiv) ); + } + + public static double distance( final double minX, final double minY, final double maxX, final double maxY ) + { + double tmp; + double distance = 0; + + tmp = maxX - minX; + distance += tmp*tmp; + + tmp = maxY - minY; + distance += tmp*tmp; + + return Math.sqrt( distance ); + } + + public static double pow( final double a, final int b ) + { + if ( b == 0 ) + return 1; + else if ( b == 1 ) + return a; + else + { + double result = a; + + for ( int i = 1; i < b; i++ ) + result *= a; + + return result; + } + } + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/NumericalSolvers.java b/src/main/java/varun_algorithm_ransac_Ransac/NumericalSolvers.java new file mode 100644 index 0000000..1a40e10 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/NumericalSolvers.java @@ -0,0 +1,8 @@ +package varun_algorithm_ransac_Ransac; + +public interface NumericalSolvers { + + public double run(final int numComponents, final double[] ellipseCoeff, final double[] sourcePoint, + final double[] targetPoint); + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/RansacFunctionEllipsoid.java b/src/main/java/varun_algorithm_ransac_Ransac/RansacFunctionEllipsoid.java new file mode 100644 index 0000000..904baec --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/RansacFunctionEllipsoid.java @@ -0,0 +1,34 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.ArrayList; + +import net.imglib2.RealLocalizable; + +public class RansacFunctionEllipsoid{ + +public final Circle function; + + + +public final ArrayList inliers; + +public final ArrayList candidates; + +/** + * + * A ransac function output containing an ellipsoid function + * @param function + * @param linearfunction + * @param inliers + */ +public RansacFunctionEllipsoid(final Circle function, ArrayList inliers, ArrayList candidates ) { + + + this.function = function; + + this.inliers = inliers; + + this.candidates = candidates; + +} +} \ No newline at end of file diff --git a/src/main/java/varun_algorithm_ransac_Ransac/ResultRoot.java b/src/main/java/varun_algorithm_ransac_Ransac/ResultRoot.java new file mode 100644 index 0000000..b1b1678 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/ResultRoot.java @@ -0,0 +1,18 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.Vector; + +public class ResultRoot { + + + final Vector intersection; + + public ResultRoot(final Vector intersection) { + + this.intersection = intersection; + + } + + + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/Solvers.java b/src/main/java/varun_algorithm_ransac_Ransac/Solvers.java new file mode 100644 index 0000000..b3c1764 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/Solvers.java @@ -0,0 +1,575 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.ArrayList; + +import net.imglib2.util.Pair; +import net.imglib2.util.ValuePair; + +public class Solvers { + + + /** + * + * Quartic solvers needed to find interesection of ellipses, adopted from David Eberlz SolveQuartics.h code + * @V Kapoor + */ + + + public static ArrayList> SolveQuartic(double[] p){ + + ArrayList> Rootmap = new ArrayList>(); + + int rat2 = 2, rat3 = 3, rat4 = 4, rat6 = 6; + + + double q0 = (p[0] / p[4]); + double q1 = (p[1] / p[4]); + double q2 = (p[2] / p[4]); + double q3 = (p[3] / p[4]); + + double q3fourth = q3 / rat4; + double q3fourthSqr = q3fourth * q3fourth; + double c0 = q0 - q3fourth * (q1 - q3fourth * ( q2 - q3fourthSqr * rat3)); + double c1 = q1 - rat2 * q3fourth * (q2 - rat4 * q3fourthSqr); + double c2 = q2 -rat6 * q3fourthSqr; + + ArrayList> RootmapLocal = SolveDepressedQuartic(c0, c1, c2); + for (Pair rm : RootmapLocal) { + + double root = rm.getB() - q3fourth; + Rootmap.add(new ValuePair(rm.getA(), root)); + + + } + + + + return Rootmap; + } + + + public static ArrayList> SolveDepressedQuadratic(double c0){ + ArrayList> Rootmap = new ArrayList>(); + + double zero = 0; + if (c0 < zero ) + { + //Two simple roots + + double root1 = Math.sqrt(-c0); + double root0 = - root1; + Rootmap.add(new ValuePair(1, root0)); + Rootmap.add(new ValuePair(1, root1)); + + } + + else if (c0 == zero) { + + // One double root + Rootmap.add(new ValuePair(2, zero)); + + + } + + else + { + + + //Roots are complex + } + + return Rootmap; + + } + + + public static ArrayList> SolveDepressedCubic(double c0, double c1){ + ArrayList> Rootmap = new ArrayList>(); + + double zero = 0; + if (c0 == zero) + { + ArrayList> RootmapLocal = SolveDepressedQuadratic(c1); + + for (Pair rm: RootmapLocal) { + + if(rm.getB()!=0) { + + //The quadratic does not have a root of zero, Insert one for cubic + + Rootmap.add(new ValuePair(1, zero)); + } + } + + return Rootmap; + } + + double oneThird = 1.0 / 3.0; + + if (c1 ==zero) + { + + double root0; + if (c0 > zero) + { + + root0 = -Math.pow(c0, oneThird); + + } + + else + { + + root0 = Math.pow(-c0, oneThird); + + } + + Rootmap.add(new ValuePair(1, root0)); + + return Rootmap; + + + + } + + double rat2 = 2, rat3 = 3, rat4 = 4, rat27 = 27, rat108 = 108; + double delta = -(rat4 * c1 * c1 * c1 + rat27 * c0 * c0); + if (delta > zero) + { + System.out.println("Delta > zero"); + //Three simple roots + double deltaDiv108 = delta / rat108; + double betaRe = -c0 / rat2; + double betaIM = Math.sqrt(deltaDiv108); + double theta = Math.atan2(betaIM, betaRe); + double thetaDiv3 = theta / rat3; + double angle = thetaDiv3; + double cs = Math.cos(angle); + double sn = Math.sin(angle); + double rhoSqr = betaRe * betaRe + betaIM * betaIM; + double rhoPowThird = Math.pow(rhoSqr, 1.0 / 6.0); + double temp0 = rhoPowThird * cs; + double temp1 = rhoPowThird * sn * Math.sqrt(3); + double root0 = rat2 * temp0; + double root1 = -temp0 - temp1; + double root2 = -temp0 + temp1; + Rootmap.add(new ValuePair(1, root0)); + Rootmap.add(new ValuePair(1, root1)); + Rootmap.add(new ValuePair(1, root2)); + } + + else if (delta < zero) + { + System.out.println("Delta < zero"); + // One Simple root + double deltaDiv108 = delta / rat108; + double temp0 = -c0 / rat2; + double temp1 = Math.sqrt(-deltaDiv108); + double temp2 = temp0 - temp1; + double temp3 = temp0 + temp1; + + double temp22,temp33; + + if (temp2 >=zero) + { + temp22 = Math.pow(temp2, oneThird); + + } + else + { + + temp22 = -Math.pow(-temp2, oneThird); + + } + if (temp3 >= zero) + { + + temp33 = Math.pow(temp3, oneThird); + + } + + else + { + + temp33 = -Math.pow(-temp3, oneThird); + } + + double root0 = temp22 + temp33; + Rootmap.add(new ValuePair(1, root0)); + + + } + + else + { + System.out.println("Delta Nothing zero"); + //One simple root and one double root. + double root0 = -rat3 * c0 / (rat2 * c1); + double root1 = -rat2 * root0; + Rootmap.add(new ValuePair(2, root0)); + + Rootmap.add(new ValuePair(1, root1)); + + + + } + + + return Rootmap; + + + } + + + public static ArrayList> SolveBiquadratic(double c0, double c2) + { + + ArrayList> Rootmap = new ArrayList>(); + + double zero = 0, rat2 = 2, rat256 = 256; + double c2Half = c2 / rat2; + double a1 = c0 - c2Half * c2Half; + double delta = rat256 * c0 * a1 * a1; + if (delta > zero) + { + System.out.println("Delta > zero Biquad"); + if (c2 < zero) + { + + if (a1 < zero) + { + + // Four simple roots + + double temp0 = Math.sqrt(-a1); + double temp1 = -c2Half - temp0; + double temp2 = -c2Half + temp0; + double root1 = Math.sqrt(temp1); + double root0 = -root1; + double root2 = Math.sqrt(temp2); + double root3 = -root2; + + Rootmap.add(new ValuePair(1, root0)); + Rootmap.add(new ValuePair(1, root1)); + Rootmap.add(new ValuePair(1, root2)); + Rootmap.add(new ValuePair(1, root3)); + + } + + else + { + + + //Roots are complex + + + } + + } + + else + { + + //c2 > 0 + + //Roots are complex + + + + } + + return Rootmap; + + } + + else if (delta < zero) + { + System.out.println("Delta < zero Biquad"); + // Two simple real roots + + double root1 = Math.sqrt(-c2Half); + double root0 = -root1; + Rootmap.add(new ValuePair(1, root0)); + Rootmap.add(new ValuePair(1, root1)); + + return Rootmap; + + + + } + + else // delta = 0 + { + + if (c2 < zero) + { + + // Two double real roots + double root1 = Math.sqrt(-c2Half); + double root0 = -root1; + + Rootmap.add(new ValuePair(2, root0)); + Rootmap.add(new ValuePair(2, root1)); + + + } + else // c2 > 0 + { + + // Roots are complex + + } + + return Rootmap; + } + + + + + + + } + + public static ArrayList> SolveCubic(double[] p) + { + ArrayList> Rootmap = new ArrayList>(); + + int rat2 = 2, rat3 = 3; + double q0 = p[0] / p[3]; + double q1 = p[1] / p[3]; + double q2 = p[2] / p[3]; + double q2third = q2 / rat3; + double c0 = q0 - q2third * (q1 - rat2 * q2third * q2third); + double c1 = q1 - q2 * q2third; + ArrayList> RootmapLocal = SolveDepressedCubic(c0, c1); + + for (Pair rm: RootmapLocal) { + + double root = rm.getB() - q2third; + Rootmap.add(new ValuePair(rm.getA(), root)); + + } + + return Rootmap; + + } + + public static ArrayList> SolveDepressedQuartic(double c0, double c1, double c2){ + ArrayList> Rootmap = new ArrayList>(); + + + double zero = 0; + if (c0 == zero) + { + ArrayList> RootmapLocal = SolveDepressedCubic(c1, c2); + + for (Pair rm: RootmapLocal) { + + if(rm.getB()!=0) { + + //The quadratic does not have a root of zero, Insert one for cubic + + Rootmap.add(new ValuePair(1, zero)); + } + } + + return Rootmap; + + + + } + + if (c1 == zero) + { + Rootmap = SolveBiquadratic(c0, c2); + + return Rootmap; + + } + + double rat2 =2 , rat4 = 4, rat8 = 8, rat12 = 12, rat16 = 16, rat27 = 27, rat36 = 36; + double c0sqr = c0 * c0, c1sqr = c1 * c1, c2sqr = c2 * c2; + double delta = c1sqr * (-rat27 * c1sqr + rat4 * c2 * (rat36 * c0 -c2sqr)) + rat16 * c0 * (c2sqr * (c2sqr -rat8 * c0) + rat16 * c0sqr); + double a0 = rat12 * c0 + c2sqr; + double a1 = rat4 * c0 - c2sqr; + + if (delta > zero) + { + + System.out.println("Delta > zero QuarticDep" + delta); + if (c2 < zero && a1 < zero) + { + + // Four simple roots + ArrayList> RootmapLocal = SolveCubic(new double[] {c1sqr - rat4 * c0 *c2, rat8 * c0, rat4 * c2, -rat8}); + + double t = RootmapLocal.get(RootmapLocal.size() - 1).getB(); + double alphaSqr = rat2 * t - c2; + double alpha = Math.sqrt(alphaSqr); + double sgnC1; + + if (c1 > zero) + { + + sgnC1 = 1.0; + + } + + else + { + + sgnC1 = -1.0; + + } + + + double arg = t * t - c0; + double beta = (sgnC1 * Math.sqrt(Math.max(arg, 0))); + double D0 = alphaSqr - rat4 * (t + beta); + double sqrtD0 = Math.sqrt(Math.max(D0, 0)); + double D1 = alphaSqr - rat4 * (t - beta); + double sqrtD1 = Math.sqrt(Math.max(D1, 0)); + double root0 = (alpha - sqrtD0) / rat2; + double root1 = (alpha + sqrtD0) / rat2; + double root2 = (- alpha - sqrtD1) / rat2; + double root3 = (- alpha + sqrtD1) / rat2; + + Rootmap.add(new ValuePair( 1, root0)); + Rootmap.add(new ValuePair( 1, root1)); + Rootmap.add(new ValuePair( 1, root2)); + Rootmap.add(new ValuePair( 1, root3)); + + + + + } + + else { + + // c2>=0 or a1 >=0 + // Roots are complex + + } + return Rootmap; + + } + + else if (delta < zero) + { + + System.out.println("Delta < zero QuarticDep"); + + // Two simple real roots, one complex conjugate pair + ArrayList> RootmapLocal = SolveCubic(new double[] {c1sqr - rat4 * c0 * c2, rat8 * c0, rat4 * c2, -rat8}); + double t = RootmapLocal.get(RootmapLocal.size() - 1).getB(); + double alphaSqr = rat2* t - c2; + double alpha = Math.sqrt(Math.max(alphaSqr, 0)); + double sgnC1; + if (c1 > zero) + { + sgnC1 = 1.0; + + } + + else + { + + sgnC1 = -1.0; + + } + + double arg = t * t - c0; + double beta = (sgnC1 * Math.sqrt(Math.max(arg, 0))); + double root0, root1; + if (sgnC1 > 0) + { + System.out.println("sgnC1 > 0 QuarticDep"); + double D1 = alphaSqr - rat4 * (t - beta); + double sqrtD1 = Math.sqrt(Math.max(D1, 0)); + root0 = (-alpha -sqrtD1) / rat2; + root1 = (-alpha + sqrtD1) / rat2; + + } + else + { + System.out.println("sgnC1 < 0 QuarticDep"); + double D0 = alphaSqr - rat4 * (t + beta); + double sqrtD0 = Math.sqrt(Math.max(D0, 0)); + root0 = (alpha - sqrtD0) / rat2; + root1 = (alpha + sqrtD0) / rat2; + + + } + Rootmap.add(new ValuePair (1, root0)); + Rootmap.add(new ValuePair (1, root1)); + + return Rootmap; + + } + else { // delta = 0 + + + if (a1 > zero || (c2 > zero && (a1!=zero || c1!=zero))) + { + + // One double real root + int rat9 = 9; + double root0 = -c1 * a0 / (rat9 * c1sqr -rat2 * c2 * a1); + Rootmap.add(new ValuePair (2, root0)); + + + } + else + { + + int rat3 = 3; + if (a0!= zero) + { + + // One double real root, two simple real roots + int rat9 = 9; + double root0 = -c1 * a0 / (rat9 * c1sqr - rat2 * c2 * a1); + double alpha = rat2 * root0; + double beta = c2 + rat3 * root0 * root0; + double discr = alpha * alpha - rat4 * beta; + double temp1 = Math.sqrt(discr); + double root1 = (-alpha - temp1) / rat2; + double root2 = (-alpha + temp1) / rat2; + Rootmap.add(new ValuePair (2, root0)); + + Rootmap.add(new ValuePair (1, root1)); + + Rootmap.add(new ValuePair (1, root2)); + + + } + else + { + + // One triple real root, one simple real root + + double root0 = -rat3 * c1 / (rat4 * c2); + double root1 = -rat3 * root0; + + Rootmap.add(new ValuePair (3, root0)); + + Rootmap.add(new ValuePair (1, root1)); + + + } + + + } + + + return Rootmap; + + } + + + + + } + + +} diff --git a/src/main/java/varun_algorithm_ransac_Ransac/SortRealLocalizable.java b/src/main/java/varun_algorithm_ransac_Ransac/SortRealLocalizable.java new file mode 100644 index 0000000..dff1a2b --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/SortRealLocalizable.java @@ -0,0 +1,115 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import net.imglib2.RealLocalizable; + +public class SortRealLocalizable { + + + public static void SortPoints(List points) { + + Collections.sort(points, new Comparator() { + + + + @Override + public int compare(final RealLocalizable o1, final RealLocalizable o2) { + + int i= 0; + + int numDim = o1.numDimensions(); + while(i < numDim) { + if(o1.getDoublePosition(i) != o2.getDoublePosition(i)) { + + + return (int) Math.signum(o1.getDoublePosition(i) - o2.getDoublePosition(i)); + } + i++; + + } + + return o1.hashCode() - o2.hashCode(); + + + } + + + }); + } + + + + + public static void SortPointsX(List points) { + + Collections.sort(points, new Comparator() { + + + + @Override + public int compare(final RealLocalizable o1, final RealLocalizable o2) { + + + final double X1 = o1.getDoublePosition(0); + final double X2 = o2.getDoublePosition(0); + + if (X1 < X2) + return -1; + else if (X1 == X2) + return 0; + else + return 1; + + + + } + + + }); + } + + + + public static void SortPointsY(List points) { + + Collections.sort(points, new Comparator() { + + + + @Override + public int compare(final RealLocalizable o1, final RealLocalizable o2) { + + + final double Y1 = o1.getDoublePosition(1); + final double Y2 = o2.getDoublePosition(1); + + if (Y1 < Y2) + return -1; + else if (Y1 == Y2) + return 0; + else + return 1; + + + + } + + + }); + + + + } + + + + } + + + + + + diff --git a/src/main/java/varun_algorithm_ransac_Ransac/SortSegments.java b/src/main/java/varun_algorithm_ransac_Ransac/SortSegments.java new file mode 100644 index 0000000..56b16f5 --- /dev/null +++ b/src/main/java/varun_algorithm_ransac_Ransac/SortSegments.java @@ -0,0 +1,43 @@ +package varun_algorithm_ransac_Ransac; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import net.imglib2.RealLocalizable; +import net.imglib2.util.Pair; + +public class SortSegments { + + public static > void Sort( + ArrayList>>> segments) { + + Collections.sort(segments, new Comparator>>>() { + + @Override + public int compare(Pair>> o1, + Pair>> o2) { + + int i = 0; + int numDim = o1.getA().numDimensions(); + + while (i < numDim) { + + if (o1 != o2) { + + return (int) Math.signum(o1.getA().getCenter()[i] - o2.getA().getCenter()[i]); + + } + i++; + + } + + return o1.hashCode() - o2.hashCode(); + } + + }); + + } + +} diff --git a/src/main/java/varun_algorithm_region/BresenhamLine.java b/src/main/java/varun_algorithm_region/BresenhamLine.java new file mode 100644 index 0000000..94bd276 --- /dev/null +++ b/src/main/java/varun_algorithm_region/BresenhamLine.java @@ -0,0 +1,304 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program. If not, see + * . + * #L% + */ + +package varun_algorithm_region; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; + +import net.imglib2.Cursor; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + +/** + * n-Dimension Bresenham line (http://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm") + * : an iterator that + * iterates all the pixels between two points. + *

      + * This implementation uses eager initialization: all the coordinates along the + * line are calculated at once when the {@link #reset(Localizable, Localizable)} + * method is called. + *

      + * Adapted from the Bresenham3D class in VIB-lib by Johannes + * Schindelin. + * + * @author Jean-Yves Tinevez Apr, 2012 + * @param + */ +public class BresenhamLine< T > implements Cursor< T > +{ + + private Localizable P1; + + private Localizable P2; + + private Iterator< long[] > iterator; + + private long[] current; + + private final RandomAccess< T > ra; + + /* + * CONSTRUCTOR + */ + + public BresenhamLine( final RandomAccessible< T > source ) + { + this.ra = source.randomAccess(); + } + + public BresenhamLine( final RandomAccessible< T > source, final Localizable P1, final Localizable P2 ) + { + this( source.randomAccess(), P1, P2 ); + } + + public BresenhamLine( RandomAccess< T > ra, Localizable P1, Localizable P2 ) + { + this.ra = ra; + this.P1 = P1; + this.P2 = P2; + reset( P1, P2 ); + } + + /* + * METHODS + */ + + public void reset( final Localizable P1, Localizable P2 ) + { + this.iterator = generateCoords( P1, P2 ).iterator(); + } + + @Override + public T get() + { + return ra.get(); + } + + @Override + public void fwd() + { + current = iterator.next(); + ra.setPosition( current ); + } + + @Override + public void reset() + { + reset( P1, P2 ); + } + + @Override + public boolean hasNext() + { + return iterator.hasNext(); + } + + @Override + public void localize( long[] position ) + { + for ( int d = 0; d < current.length; d++ ) + { + position[ d ] = current[ d ]; + } + } + + @Override + public long getLongPosition( int d ) + { + return current[ d ]; + } + + @Override + public BresenhamLine< T > copy() + { + return new BresenhamLine< T >( ra, P1, P2 ); + } + + @Override + public BresenhamLine< T > copyCursor() + { + return copy(); + } + + @Override + public void localize( float[] position ) + { + for ( int d = 0; d < current.length; d++ ) + { + position[ d ] = current[ d ]; + } + } + + @Override + public void localize( double[] position ) + { + for ( int d = 0; d < current.length; d++ ) + { + position[ d ] = current[ d ]; + } + } + + @Override + public float getFloatPosition( int d ) + { + return current[ d ]; + } + + @Override + public double getDoublePosition( int d ) + { + return current[ d ]; + } + + @Override + public int numDimensions() + { + return current.length; + } + + @Override + public void jumpFwd( long steps ) + { + for ( int i = 0; i < steps; i++ ) + { + current = iterator.next(); + } + ra.setPosition( current ); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + {} + + @Override + public void localize( int[] position ) + { + for ( int d = 0; d < current.length; d++ ) + { + position[ d ] = ( int ) current[ d ]; + } + } + + @Override + public int getIntPosition( int d ) + { + return ( int ) current[ d ]; + } + + public static final ArrayList< long[] > generateCoords( final Localizable start, final Localizable end ) + { + + final int nd = start.numDimensions(); + long[] dxs = new long[ nd ]; + long[] as = new long[ nd ]; + float[] signs = new float[ nd ]; + + for ( int d = 0; d < nd; d++ ) + { + dxs[ d ] = end.getLongPosition( d ) - start.getLongPosition( d ); + as[ d ] = Math.abs( dxs[ d ] ) << 1; + signs[ d ] = Math.signum( dxs[ d ] ); + } + + // Find dominant direction & max ∆x + int dm = -1; + long am = -1; + long maxDelta = -1; + for ( int d = 0; d < nd; d++ ) + { + if ( as[ d ] >= am ) + { + dm = d; + am = as[ d ]; + } + + if ( Math.abs( dxs[ d ] ) > maxDelta ) + { + maxDelta = Math.abs( dxs[ d ] ); + } + } + + long[] xd = new long[ nd ]; + for ( int d = 0; d < nd; d++ ) + { + xd[ d ] = as[ d ] - ( as[ dm ] >> 1 ); + } + + long[] x = new long[ nd ]; + start.localize( x ); + + final ArrayList< long[] > coords = new ArrayList< long[] >( ( int ) maxDelta ); + while ( true ) + { + + coords.add( x ); + x = Arrays.copyOf( x, x.length ); + + if ( x[ dm ] == end.getLongPosition( dm ) ) { return coords; } + + for ( int d = 0; d < nd; d++ ) + { + if ( d == dm ) + { + continue; + } + + if ( xd[ d ] >= 0 ) + { + x[ d ] += signs[ d ]; + xd[ d ] -= as[ dm ]; + } + + } + + x[ dm ] += signs[ dm ]; + + for ( int d = 0; d < nd; d++ ) + { + if ( d == dm ) + { + continue; + } + + xd[ d ] += as[ d ]; + } + + } + + } + +} diff --git a/src/main/java/varun_algorithm_region/CircleCursor.java b/src/main/java/varun_algorithm_region/CircleCursor.java new file mode 100644 index 0000000..dd19bac --- /dev/null +++ b/src/main/java/varun_algorithm_region/CircleCursor.java @@ -0,0 +1,335 @@ +package varun_algorithm_region; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; + +/** + * Iterates over a Bresenham 2D circle using the mid-point algorithm. + *

      + * Each point of the circle is iterated exactly once, and there is no "hole" in + * the circle. Contrary to the algorithm linked below, the circles generated by + * this cursor are "slim": each pixel of the circle is connected with + * 8-connectivity. For instance, a bitmap excerpt from such a circle looks like + * this: + * + *

      + * ..........
      + * OOO.......
      + * ...OO.....
      + * .....O....
      + * .....O....
      + * 
      + * + * @author Jean-Yves Tinevez + * @see Midpoint + * circle algorithm on Wikipedia. + * + */ +public class CircleCursor< T > implements Cursor< T > +{ + private final RandomAccessible< T > rai; + + private final RandomAccess< T > ra; + + private final Localizable center; + + private final long radius; + + private final int dimX; + + private final int dimY; + + private long x; + + private long y; + + private long dx; + + private long dy; + + private long f; + + private Octant octant; + + private boolean hasNext; + + private static enum Octant + { + INIT, EAST, NORTH, WEST, SOUTH, O1, O2, O3, O4, O5, O6, O7, O8; + } + + /** + * Iterates over a Bresenham circle in the target {@link RandomAccessible}. + * Each point of the circle is iterated exactly once, and there is no "hole" + * in the circle. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must be at least of dimension 2. Dimensions + * 0 and 1 are used to specify the circle center. + * @param radius + * the circle radius. The circle is written in a plane in + * dimensions 0 and 1. + */ + public CircleCursor( final RandomAccessible< T > rai, final Localizable center, final long radius ) + { + this( rai, center, radius, 0, 1 ); + } + + /** + * Iterates over a Bresenham circle in the target {@link RandomAccessible}. + * Each point of the circle is iterated exactly once, and there is no "hole" + * in the circle. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must at least contain dimensions specified + * dimX and dimY. + * @param radius + * the circle radius. + * @param dimX + * the first dimension of the plane in which to draw the circle. + * @param dimY + * the second dimension of the plane in which to draw the circle. + */ + public CircleCursor( final RandomAccessible< T > rai, final Localizable center, final long radius, final int dimX, final int dimY ) + { + this.rai = rai; + this.center = center; + this.radius = radius; + this.dimX = dimX; + this.dimY = dimY; + // Make an interval to signal where we will access data. + final int numDimensions = rai.numDimensions(); + final long[] minmax = new long[ 2 * numDimensions ]; + for ( int d = 0; d < numDimensions; d++ ) + { + if ( d == dimX || d == dimY ) + minmax[ d ] = center.getLongPosition( d ) - radius; + else + minmax[ d ] = center.getLongPosition( d ); + } + for ( int d = 0; d < numDimensions; d++ ) + { + if ( d == dimX || d == dimY ) + minmax[ d + numDimensions ] = center.getLongPosition( d ) + radius; + else + minmax[ d + numDimensions ] = center.getLongPosition( d ); + } + final Interval interval = FinalInterval.createMinMax( minmax ); + this.ra = rai.randomAccess( interval ); + reset(); + } + + @Override + public void reset() + { + x = 0; + y = radius; + f = 1 - radius; + dx = 1; + dy = -2 * radius; + octant = Octant.INIT; + ra.setPosition( center ); + hasNext = true; + } + + @Override + public void fwd() + { + switch ( octant ) + { + default: + case INIT: + ra.setPosition( center.getLongPosition( dimY ) + radius, dimY ); + octant = Octant.NORTH; + break; + + case NORTH: + ra.setPosition( center.getLongPosition( dimY ) - radius, dimY ); + octant = Octant.SOUTH; + break; + + case SOUTH: + ra.setPosition( center.getLongPosition( dimX ) - radius, dimX ); + ra.setPosition( center.getLongPosition( dimY ), dimY ); + octant = Octant.WEST; + break; + + case WEST: + ra.setPosition( center.getLongPosition( dimX ) + radius, dimX ); + octant = Octant.EAST; + break; + + case EAST: + x = x + 1; + dx = dx + 2; + f = f + dx; + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + ra.setPosition( center.getLongPosition( dimY ) + y, dimY ); + octant = Octant.O1; + break; + + case O1: + ra.setPosition( center.getLongPosition( dimX ) - x, dimX ); + octant = Octant.O2; + break; + + case O2: + ra.setPosition( center.getLongPosition( dimY ) - y, dimY ); + octant = Octant.O3; + break; + + case O3: + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + octant = Octant.O4; + // Stop here if x==y, lest the 45º will be iterated twice. + if ( x >= y ) + hasNext = false; + break; + + case O4: + ra.setPosition( center.getLongPosition( dimX ) + y, dimX ); + ra.setPosition( center.getLongPosition( dimY ) - x, dimY ); + octant = Octant.O5; + break; + + case O5: + ra.setPosition( center.getLongPosition( dimX ) - y, dimX ); + octant = Octant.O6; + break; + + case O6: + ra.setPosition( center.getLongPosition( dimY ) + x, dimY ); + octant = Octant.O7; + break; + + case O7: + ra.setPosition( center.getLongPosition( dimX ) + y, dimX ); + octant = Octant.O8; + // Stop here if dx would cross y. + if ( x >= y - 1 ) + hasNext = false; + break; + + case O8: + if ( f > 0 ) + { + y = y - 1; + dy = dy + 2; + f = f + dy; + } + x = x + 1; + dx = dx + 2; + f = f + dx; + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + ra.setPosition( center.getLongPosition( dimY ) + y, dimY ); + octant = Octant.O1; + break; + } + } + + @Override + public boolean hasNext() + { + return hasNext; + } + + @Override + public void localize( final float[] position ) + { + ra.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + ra.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return ra.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return ra.getDoublePosition( d ); + } + + @Override + public int numDimensions() + { + return ra.numDimensions(); + } + + @Override + public T get() + { + return ra.get(); + } + + @Override + public Sampler< T > copy() + { + return ra.copy(); + } + + @Override + public void jumpFwd( final long steps ) + { + for ( int i = 0; i < steps; i++ ) + fwd(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void localize( final int[] position ) + { + ra.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + ra.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return ra.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return ra.getLongPosition( d ); + } + + @Override + public Cursor< T > copyCursor() + { + return new CircleCursor<>( rai, center, radius, dimX, dimY ); + } +} diff --git a/src/main/java/varun_algorithm_region/Circles.java b/src/main/java/varun_algorithm_region/Circles.java new file mode 100644 index 0000000..2c56eeb --- /dev/null +++ b/src/main/java/varun_algorithm_region/Circles.java @@ -0,0 +1,193 @@ +package varun_algorithm_region; + +import net.imglib2.Cursor; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.operators.Add; + +/** + * Write circles in an image using the midpoint algorithm. + * + * @author Jean-Yves Tinevez + * @see Midpoint + * circle algorithm on Wikipedia. + * @see CircleCursor {@link CircleCursor} + */ +public class Circles +{ + + /** + * Writes a circle in the target {@link RandomAccessible}. The circle is + * written by incrementing the pixel values by 1 along the circle. + * The circle is written in a plane in dimensions 0 and 1. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must be at least of dimension 2. Dimensions + * 0 and 1 are used to specify the circle center. + * @param radius + * the circle radius. + * @param + * the type of the target image. + */ + public static < T extends RealType< T > > void inc( final RandomAccessible< T > rai, final Localizable center, final long radius ) + { + inc( rai, center, radius, 0, 1 ); + } + + /** + * Writes a circle in the target {@link RandomAccessible}. The circle is + * written by incrementing the pixel values by 1 along the circle. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must contain at least of dimensions + * dimX and dimY, used to specify the + * circle center. + * @param radius + * the circle radius. + * @param dimX + * the first dimension of the plane in which to draw the circle. + * @param dimY + * the second dimension of the plane in which to draw the circle. + * @param + * the type of the target image. + */ + public static < T extends RealType< T > > void inc( final RandomAccessible< T > rai, final Localizable center, + final long radius, final int dimX, final int dimY ) + { + final Cursor< T > cursor = new CircleCursor< T >( rai, center, radius, dimX, dimY ); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().inc(); + } + } + + /** + * Writes a circle in the target {@link RandomAccessible}. The circle is + * written by setting the pixel values with the specified value. The + * circle is written in a plane in dimensions 0 and 1. + * + * @param rai + * the target random accessible. It is the caller responsibility + * to ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must be at least of dimension 2. Dimensions + * 0 and 1 are used to specify the circle center. + * @param radius + * the circle radius. + * @param value + * the value to write along the circle. + * @param + * the type of the target image. + */ + public static < T extends Type< T > > void set( final RandomAccessible< T > rai, final Localizable center, final long radius, final T value ) + { + set( rai, center, radius, 0, 1, value ); + } + + /** + * Writes a circle in the target {@link RandomAccessible}. The circle is + * written by setting the pixel values with the specified value. + * + * @param rai + * the target random accessible. It is the caller responsibility + * to ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must contain at least of dimensions + * dimX and dimY, used to specify the + * circle center. + * @param radius + * the circle radius. + * @param dimX + * the first dimension of the plane in which to draw the circle. + * @param dimY + * the second dimension of the plane in which to draw the circle. + * @param value + * the value to write along the circle. + * @param + * the type of the target image. + */ + public static < T extends Type< T > > void set( final RandomAccessible< T > rai, final Localizable center, final long radius, final int dimX, final int dimY, final T value ) + { + final Cursor< T > cursor = new CircleCursor< T >( rai, center, radius, dimX, dimY ); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().set( value ); + } + } + + /** + * Writes a circle in the target {@link RandomAccessible}. The circle is + * written by adding the specified value to the pixel values already + * in the image. The circle is written in a plane in dimensions 0 and 1. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must be at least of dimension 2. Dimensions + * 0 and 1 are used to specify the circle center. + * @param radius + * the circle radius. + * @param value + * the value to add along the circle. + * @param + * the type of the target image. + */ + public static < T extends Add< T > > void add( final RandomAccessible< T > rai, final Localizable center, final long radius, final T value ) + { + add( rai, center, radius, 0, 1, value ); + } + + /** + * Writes a circle in the target {@link RandomAccessible}. The circle is + * written by adding the specified value to the pixel values already + * in the image. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the circle will be + * iterated. + * @param center + * the circle center. Must contain at least of dimensions + * dimX and dimY, used to specify the + * circle center. + * @param radius + * the circle radius. + * @param dimX + * the first dimension of the plane in which to draw the circle. + * @param dimY + * the second dimension of the plane in which to draw the circle. + * @param value + * the value to add along the circle. + * @param + * the type of the target image. + */ + public static < T extends Add< T > > void add( final RandomAccessible< T > rai, final Localizable center, final long radius, final int dimX, final int dimY, final T value ) + { + final Cursor< T > cursor = new CircleCursor< T >( rai, center, radius, dimX, dimY ); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().add( value ); + } + } + + private Circles() + {} +} diff --git a/src/main/java/varun_algorithm_region/EllipseCursor.java b/src/main/java/varun_algorithm_region/EllipseCursor.java new file mode 100644 index 0000000..82e652e --- /dev/null +++ b/src/main/java/varun_algorithm_region/EllipseCursor.java @@ -0,0 +1,423 @@ +package varun_algorithm_region; + +import net.imglib2.Cursor; +import net.imglib2.FinalInterval; +import net.imglib2.Interval; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.Sampler; +import net.imglib2.util.Util; + +/** + * Iterates over a Bresenham 2D ellipse using the mid-point algorithm. + *

      + * Each point of the ellipse is iterated exactly once, and there is no "hole" in + * the ellipse. A bitmap excerpt from such a ellipse looks like this: + * + *

      + * ..........
      + * OOO.......
      + * ...OO.....
      + * .....O....
      + * .....O....
      + * 
      + *

      + * The circle generated with equal X and Y radii is not strictly equivalent to + * the circle generated by the {@link CircleCursor}. + * + * @author Jean-Yves Tinevez + */ +public class EllipseCursor< T > implements Cursor< T > +{ + private final RandomAccessible< T > rai; + + private final RandomAccess< T > ra; + + private final Localizable center; + + private final long radiusX; + + private final long radiusY; + + private final int dimX; + + private final int dimY; + + private long x; + + private long y; + + private double px; + + private double py; + + private double p; + + private Octant octant; + + private boolean hasNext; + + private static enum Octant + { + INIT, + EAST, NORTH, WEST, SOUTH, + REGION1_Q1, REGION1_Q2, REGION1_Q3, REGION1_Q4, + REGION2_Q1, REGION2_Q2, REGION2_Q3, REGION2_Q4; + } + + /** + * Iterates over a Bresenham ellipse in the target {@link RandomAccessible}. + * Each point of the ellipse is iterated exactly once, and there is no + * "hole" in the ellipse. The ellipse is written in a plane in dimensions 0 + * and 1. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must be at least of dimension 2. + * Dimensions 0 and 1 are used to specify the ellipse center. + * @param radiusX + * the ellipse radius in the dimension 0. + * @param radiusY + * the ellipse radius in the dimension 1. + */ + public EllipseCursor( final RandomAccessible< T > rai, final Localizable center, final long radiusX, final long radiusY ) + { + this( rai, center, radiusX, radiusY, 0, 1 ); + } + + /** + * Iterates over a Bresenham ellipse in the target {@link RandomAccessible}. + * Each point of the ellipse is iterated exactly once, and there is no + * "hole" in the ellipse. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must be at least of dimension 2. + * Dimensions 0 and 1 are used to specify the ellipse center. + * @param radiusX + * the ellipse radius in dimension X. + * @param radiusY + * the ellipse radius in dimension Y. + * @param dimX + * the first dimension of the plane in which to draw the ellipse. + * @param dimY + * the second dimension of the plane in which to draw the + * ellipse. + */ + public EllipseCursor( final RandomAccessible< T > rai, final Localizable center, final long radiusX, final long radiusY, final int dimX, final int dimY ) + { + this.rai = rai; + this.center = center; + if ( radiusX > radiusY ) + { + // Swap X & Y if radiusX if larger than radiusY and avoid issues + // with slim ellipses. + this.radiusX = radiusY; + this.radiusY = radiusX; + this.dimX = dimY; + this.dimY = dimX; + } + else + { + this.radiusX = radiusX; + this.radiusY = radiusY; + this.dimX = dimX; + this.dimY = dimY; + } + // Make an interval to signal where we will access data. + final int numDimensions = rai.numDimensions(); + final long[] minmax = new long[ 2 * numDimensions ]; + for ( int d = 0; d < numDimensions; d++ ) + { + if ( d == dimX ) + minmax[ d ] = center.getLongPosition( d ) - radiusX; + else if ( d == dimY ) + minmax[ d ] = center.getLongPosition( d ) - radiusY; + else + minmax[ d ] = center.getLongPosition( d ); + } + for ( int d = 0; d < numDimensions; d++ ) + { + if ( d == dimX ) + minmax[ d + numDimensions ] = center.getLongPosition( d ) + radiusX; + else if ( d == dimY ) + minmax[ d + numDimensions ] = center.getLongPosition( d ) + radiusY; + else + minmax[ d + numDimensions ] = center.getLongPosition( d ); + } + final Interval interval = FinalInterval.createMinMax( minmax ); + this.ra = rai.randomAccess( interval ); + reset(); + } + + @Override + public void reset() + { + // Prepare for region 1. + x = 0l; + y = radiusY; + px = 0.; + py = 2. * radiusX * radiusX * y; + p = radiusY * radiusY - ( radiusX * radiusX * radiusY ) + ( 0.25 * radiusX * radiusX ); + + octant = Octant.INIT; + ra.setPosition( center ); + hasNext = true; + } + + @Override + public void fwd() + { + switch ( octant ) + { + default: + case INIT: + ra.setPosition( center.getLongPosition( dimY ) + radiusY, dimY ); + octant = Octant.NORTH; + break; + + case NORTH: + ra.setPosition( center.getLongPosition( dimY ) - radiusY, dimY ); + octant = Octant.SOUTH; + break; + + case SOUTH: + ra.setPosition( center.getLongPosition( dimX ) - radiusX, dimX ); + ra.setPosition( center.getLongPosition( dimY ), dimY ); + octant = Octant.WEST; + break; + + case WEST: + ra.setPosition( center.getLongPosition( dimX ) + radiusX, dimX ); + octant = Octant.EAST; + + // Special case: rx = ry = 1. + if ( radiusX == 1 && radiusY == 1 ) + hasNext = false; + + break; + + case EAST: + // Move to region 1. + x++; + px += 2. * radiusY * radiusY; + if ( p < 0 ) + { + p += ( radiusY * radiusY ) + px; + } + else + { + y--; + py -= 2 * ( radiusX * radiusX ); + p += ( radiusY * radiusY ) + px - py; + } + + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + ra.setPosition( center.getLongPosition( dimY ) + y, dimY ); + octant = Octant.REGION1_Q1; + break; + + case REGION1_Q1: + ra.setPosition( center.getLongPosition( dimX ) - x, dimX ); + octant = Octant.REGION1_Q2; + break; + + case REGION1_Q2: + ra.setPosition( center.getLongPosition( dimY ) - y, dimY ); + octant = Octant.REGION1_Q3; + break; + + case REGION1_Q3: + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + octant = Octant.REGION1_Q4; + + // Don't overwrite equator for small radii. + if ( y == 1 && x <= 2 ) + hasNext = false; + + break; + + case REGION1_Q4: + + if ( px < py ) + { + // Back to Q1 of Region 1. + x++; + px += 2. * radiusY * radiusY; + if ( p < 0 ) + { + p += ( radiusY * radiusY ) + px; + } + else + { + y--; + py -= 2 * ( radiusX * radiusX ); + p += ( radiusY * radiusY ) + px - py; + } + + octant = Octant.REGION1_Q1; + } + else + { + // Maybe go to Region 2. + p = radiusY * radiusY * ( x + 0.5 ) * ( x + 0.5 ) + radiusX * radiusX * ( y - 1 ) * ( y - 1 ) - radiusX * radiusX * radiusY * radiusY; + y--; + py -= 2. * radiusX * radiusX; + if ( p > 0 ) + { + p += radiusX * radiusX - py; + } + else + { + x++; + px += 2. * radiusY * radiusY; + p += radiusX * radiusX - py + px; + } + octant = Octant.REGION2_Q1; + } + + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + ra.setPosition( center.getLongPosition( dimY ) + y, dimY ); + break; + + case REGION2_Q1: + ra.setPosition( center.getLongPosition( dimX ) - x, dimX ); + octant = Octant.REGION2_Q2; + break; + + case REGION2_Q2: + ra.setPosition( center.getLongPosition( dimY ) - y, dimY ); + octant = Octant.REGION2_Q3; + break; + + case REGION2_Q3: + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + octant = Octant.REGION2_Q4; + if ( y <= 1 ) + hasNext = false; + break; + + case REGION2_Q4: + y--; + py -= 2. * radiusX * radiusX; + if ( p > 0 ) + { + p += radiusX * radiusX - py; + } + else + { + x++; + px += 2. * radiusY * radiusY; + p += radiusX * radiusX - py + px; + } + octant = Octant.REGION2_Q1; + ra.setPosition( center.getLongPosition( dimX ) + x, dimX ); + ra.setPosition( center.getLongPosition( dimY ) + y, dimY ); + } + } + + @Override + public boolean hasNext() + { + return hasNext; + } + + @Override + public void localize( final float[] position ) + { + ra.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + ra.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return ra.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return ra.getDoublePosition( d ); + } + + @Override + public int numDimensions() + { + return ra.numDimensions(); + } + + @Override + public T get() + { + return ra.get(); + } + + @Override + public Sampler< T > copy() + { + return ra.copy(); + } + + @Override + public void jumpFwd( final long steps ) + { + for ( int i = 0; i < steps; i++ ) + fwd(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void localize( final int[] position ) + { + ra.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + ra.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return ra.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return ra.getLongPosition( d ); + } + + @Override + public Cursor< T > copyCursor() + { + return new EllipseCursor<>( rai, center, radiusX, radiusY, dimX, dimY ); + } + + @Override + public String toString() + { + return super.toString() + " pos=" + Util.printCoordinates( this ) + ",\t" + octant + ",\tx = " + x + ",\ty = " + y; + } +} diff --git a/src/main/java/varun_algorithm_region/Ellipses.java b/src/main/java/varun_algorithm_region/Ellipses.java new file mode 100644 index 0000000..388424b --- /dev/null +++ b/src/main/java/varun_algorithm_region/Ellipses.java @@ -0,0 +1,209 @@ +package varun_algorithm_region; + +import net.imglib2.Cursor; +import net.imglib2.Localizable; +import net.imglib2.RandomAccessible; +import net.imglib2.type.Type; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.operators.Add; + +/** + * Write ellipses in an image using the midpoint algorithm. + * + * @author Jean-Yves Tinevez + * @see EllipseCursor {@link EllipseCursor} + * @see EllipseCursor {@link EllipseCursor} + */ +public class Ellipses +{ + + /** + * Writes an ellipse in the target {@link RandomAccessible}. The ellipse is + * written by incrementing the pixel values by 1 along the ellipse. + * The ellipse is written in a plane in dimensions 0 and 1. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must be at least of dimension 2. + * Dimensions 0 and 1 are used to specify the ellipse center. + * @param radiusX + * the ellipse X radius. + * @param radiusY + * the ellipse Y radius. + * @param + * the type of the target image. + */ + public static < T extends RealType< T > > void inc( final RandomAccessible< T > rai, final Localizable center, final long radiusX, final long radiusY ) + { + inc( rai, center, radiusX, radiusY, 0, 1 ); + } + + /** + * Writes an ellipse in the target {@link RandomAccessible}. The ellipse is + * written by incrementing the pixel values by 1 along the ellipse. + * The ellipse is written in a plane in specified dimensions. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must contain at least dimensions + * dimX and dimY, used to specify the + * ellipse center. + * @param radiusX + * the ellipse X radius. + * @param radiusY + * the ellipse Y radius. + * @param dimX + * the first dimension of the plane in which to draw the ellipse. + * @param dimY + * the second dimension of the plane in which to draw the + * ellipse. + * @param + * the type of the target image. + */ + public static < T extends RealType< T > > void inc( final RandomAccessible< T > rai, final Localizable center, + final long radiusX, final long radiusY, final int dimX, final int dimY ) + { + final Cursor< T > cursor = new EllipseCursor< T >( rai, center, radiusX, radiusY, dimX, dimY ); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().inc(); + } + } + + /** + * Writes an ellipse in the target {@link RandomAccessible}. The ellipse is + * written by setting the pixel values with the specified value. The + * ellipse is written in a plane in dimensions 0 and 1. + * + * @param rai + * the target random accessible. It is the caller responsibility + * to ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must be at least of dimension 2. + * Dimensions 0 and 1 are used to specify the ellipse center. + * @param radiusX + * the ellipse X radius. + * @param radiusY + * the ellipse Y radius. + * @param value + * the value to write along the ellipse. + * @param + * the type of the target image. + */ + public static < T extends Type< T > > void set( final RandomAccessible< T > rai, final Localizable center, final long radiusX, final long radiusY, final T value ) + { + set( rai, center, radiusX, radiusY, 0, 1, value ); + } + + /** + * Writes an ellipse in the target {@link RandomAccessible}. The ellipse is + * written by setting the pixel values with the specified value. + * + * @param rai + * the target random accessible. It is the caller responsibility + * to ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must contain at least dimensions + * dimX and dimY, used to specify the + * ellipse center. + * @param radiusX + * the ellipse X radius. + * @param radiusY + * the ellipse Y radius. + * @param dimX + * the first dimension of the plane in which to draw the ellipse. + * @param dimY + * the second dimension of the plane in which to draw the + * ellipse. + * @param value + * the value to write along the ellipse. + * @param + * the type of the target image. + */ + public static < T extends Type< T > > void set( final RandomAccessible< T > rai, final Localizable center, + final long radiusX, final long radiusY, final int dimX, final int dimY, final T value ) + { + final Cursor< T > cursor = new EllipseCursor< T >( rai, center, radiusX, radiusY, dimX, dimY ); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().set( value ); + } + } + + /** + * Writes an ellipse in the target {@link RandomAccessible}. The ellipse is + * written by adding the specified value to the pixel values already + * in the image. The ellipse is written in a plane in dimensions 0 and 1. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must be at least of dimension 2. + * Dimensions 0 and 1 are used to specify the ellipse center. + * @param radiusX + * the ellipse X radius. + * @param radiusY + * the ellipse Y radius. + * @param value + * the value to add along the ellipse. + * @param + * the type of the target image. + */ + public static < T extends Add< T > > void add( final RandomAccessible< T > rai, final Localizable center, final long radiusX, final long radiusY, final T value ) + { + add( rai, center, radiusX, radiusY, 0, 1, value ); + } + + /** + * Writes an ellipse in the target {@link RandomAccessible}. The ellipse is + * written by adding the specified value to the pixel values already + * in the image. + * + * @param rai + * the random accessible. It is the caller responsibility to + * ensure it can be accessed everywhere the ellipse will be + * iterated. + * @param center + * the ellipse center. Must contain at least dimensions + * dimX and dimY, used to specify the + * ellipse center. + * @param radiusX + * the ellipse X radius. + * @param radiusY + * the ellipse Y radius. + * @param dimX + * the first dimension of the plane in which to draw the ellipse. + * @param dimY + * the second dimension of the plane in which to draw the + * ellipse. + * @param value + * the value to add along the ellipse. + * @param + * the type of the target image. + */ + public static < T extends Add< T > > void add( final RandomAccessible< T > rai, final Localizable center, + final long radiusX, final long radiusY, final int dimX, final int dimY, final T value ) + { + final Cursor< T > cursor = new EllipseCursor< T >( rai, center, radiusX, radiusY, dimX, dimY ); + while ( cursor.hasNext() ) + { + cursor.fwd(); + cursor.get().add( value ); + } + } + + private Ellipses() + {} +} diff --git a/src/main/java/varun_algorithm_region/hypersphere/HyperSphere.java b/src/main/java/varun_algorithm_region/hypersphere/HyperSphere.java new file mode 100644 index 0000000..c4ad159 --- /dev/null +++ b/src/main/java/varun_algorithm_region/hypersphere/HyperSphere.java @@ -0,0 +1,256 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_region.hypersphere; + +import java.util.Iterator; + +import net.imglib2.IterableInterval; +import net.imglib2.IterableRealInterval; +import net.imglib2.Localizable; +import net.imglib2.Positionable; +import net.imglib2.RandomAccessible; +import net.imglib2.RealPositionable; + +/** + * TODO + * + * @author Stephan Preibisch + * @author Stephan Saalfeld + */ +public class HyperSphere< T > implements IterableInterval< T > +{ + final int numDimensions; + + long radius; + + final RandomAccessible< T > source; + + final long[] center; + + public HyperSphere( final RandomAccessible< T > source, final Localizable center, final long radius ) + { + this.numDimensions = source.numDimensions(); + this.source = source; + this.center = new long[ numDimensions ]; + center.localize( this.center ); + + updateRadius( radius ); + } + + public void updateCenter( final long[] center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center[ d ]; + } + + public void updateCenter( final Localizable center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center.getLongPosition( d ); + } + + public void updateRadius( final long radius ) + { + this.radius = radius; + } + + /** + * Compute the number of elements for iteration + */ + protected long computeSize() + { + final HyperSphereCursor< T > cursor = new HyperSphereCursor< T >( source, this.center, radius ); + + // "compute number of pixels" + long size = 0; + while ( cursor.hasNext() ) + { + cursor.fwd(); + ++size; + } + + return size; + } + + public void update( final Localizable center, final long radius ) + { + updateCenter( center ); + updateRadius( radius ); + } + + public void update( final long[] center, final long radius ) + { + updateCenter( center ); + updateRadius( radius ); + } + + @Override + public long size() + { + return computeSize(); + } + + @Override + public T firstElement() + { + final HyperSphereCursor< T > cursor = new HyperSphereCursor< T >( source, center, radius ); + cursor.fwd(); + return cursor.get(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return min( d ); + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min[ d ] = center[ d ] - radius; + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min.setPosition( center[ d ] - radius, d ); + } + + @Override + public double realMax( final int d ) + { + return max( d ); + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max[ d ] = center[ d ] + radius; + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max.setPosition( center[ d ] - radius, d ); + } + + @Override + public int numDimensions() + { + return numDimensions; + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return center[ d ] - radius; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min[ d ] = center[ d ] - radius; + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min.setPosition( center[ d ] - radius, d ); + } + + @Override + public long max( final int d ) + { + return center[ d ] + radius; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max[ d ] = center[ d ] + radius; + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max.setPosition( center[ d ] + radius, d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + final long size = radius * 2 + 1; + for ( int d = 0; d < numDimensions; ++d ) + dimensions[ d ] = size; + } + + @Override + public long dimension( final int d ) + { + return radius * 2 + 1; + } + + @Override + public HyperSphereCursor< T > cursor() + { + return localizingCursor(); + } + + @Override + public HyperSphereCursor< T > localizingCursor() + { + return new HyperSphereCursor< T >( source, center, radius ); + } +} diff --git a/src/main/java/varun_algorithm_region/hypersphere/HyperSphereCursor.java b/src/main/java/varun_algorithm_region/hypersphere/HyperSphereCursor.java new file mode 100644 index 0000000..d90eb8e --- /dev/null +++ b/src/main/java/varun_algorithm_region/hypersphere/HyperSphereCursor.java @@ -0,0 +1,265 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_region.hypersphere; + +import net.imglib2.Cursor; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; + +/** + * Iterate over all pixels in an n-dimensional sphere. + * + * @param + * + * @author Stephan Preibisch + * @author Stephan Saalfeld + * @author Stephan Preibisch + * @author Stephan Saalfeld + * @author Tobias Pietzsch + */ +public class HyperSphereCursor< T > implements Cursor< T > +{ + final RandomAccessible< T > source; + + final protected long[] center; + + final protected RandomAccess< T > randomAccess; + + final protected long radius; + + final int numDimensions, maxDim; + + // the current radius in each dimension we are at + final double[] r; + + // the current radius in each dimension truncated to long + final long[] ri; + + // the remaining number of steps in each dimension we still have to go + final long[] s; + + public HyperSphereCursor( final RandomAccessible< T > source, final long[] center, final long radius ) + { + this.source = source; + this.center = center.clone(); + this.radius = radius; + this.numDimensions = source.numDimensions(); + this.maxDim = numDimensions - 1; + this.r = new double[ numDimensions ]; + this.ri = new long[ numDimensions ]; + this.s = new long[ numDimensions ]; + this.randomAccess = source.randomAccess(); + + reset(); + } + + public HyperSphereCursor( final HyperSphereCursor< T > cursor ) + { + this.source = cursor.source; + this.center = cursor.center.clone(); + this.radius = cursor.radius; + this.numDimensions = cursor.numDimensions(); + this.maxDim = cursor.maxDim; + + this.r = cursor.r.clone(); + this.ri = cursor.ri.clone(); + this.s = cursor.s.clone(); + + this.randomAccess = source.randomAccess(); + this.randomAccess.setPosition( cursor.randomAccess ); + } + + public void updateCenter( final long[] center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center[ d ]; + + reset(); + } + + public void updateCenter( final Localizable center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center.getLongPosition( d ); + + reset(); + } + + @Override + public boolean hasNext() + { + return s[ maxDim ] > 0; + } + + @Override + public void fwd() + { + if ( --s[ 0 ] >= 0 ) + randomAccess.fwd( 0 ); + else + { + int d = 1; + for ( ; d < numDimensions; ++d ) + { + if ( --s[ d ] >= 0 ) + { + randomAccess.fwd( d ); + break; + } + } + + for ( ; d > 0; --d ) + { + final int e = d - 1; + final double rd = r[ d ]; + final long pd = s[ d ] - ri[ d ]; + + final double rad = Math.sqrt( rd * rd - pd * pd ); + r[ e ] = rad; + ri[ e ] = ( long ) rad; + s[ e ] = 2 * ( long ) rad; + + randomAccess.setPosition( center[ e ] - ri[ e ], e ); + } + } + } + + @Override + public void reset() + { + for ( int d = 0; d < maxDim; ++d ) + { + r[ d ] = ri[ d ] = s[ d ] = 0; + randomAccess.setPosition( center[ d ], d ); + } + + randomAccess.setPosition( center[ maxDim ] - radius - 1, maxDim ); + + r[ maxDim ] = radius; + ri[ maxDim ] = radius; + s[ maxDim ] = 1 + 2 * radius; + } + + @Override + public void jumpFwd( final long steps ) + { + for ( long j = 0; j < steps; ++j ) + fwd(); + } + + @Override + public void localize( final float[] position ) + { + randomAccess.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + randomAccess.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return randomAccess.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return randomAccess.getDoublePosition( d ); + } + + @Override + public int numDimensions() + { + return numDimensions; + } + + @Override + public T get() + { + return randomAccess.get(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + {} + + @Override + public void localize( final int[] position ) + { + randomAccess.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + randomAccess.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return randomAccess.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return randomAccess.getLongPosition( d ); + } + + @Override + public HyperSphereCursor< T > copyCursor() + { + return new HyperSphereCursor< T >( this ); + } + + @Override + public HyperSphereCursor< T > copy() + { + return copyCursor(); + } +} diff --git a/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood.java b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood.java new file mode 100644 index 0000000..b977db3 --- /dev/null +++ b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood.java @@ -0,0 +1,221 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_region.localneighborhood.old; + +import java.util.Iterator; + +import net.imglib2.IterableInterval; +import net.imglib2.Localizable; +import net.imglib2.Positionable; +import net.imglib2.RandomAccessible; +import net.imglib2.RealPositionable; +import net.imglib2.util.Util; + +/** + * TODO + * + */ +@Deprecated +public class LocalNeighborhood< T > implements IterableInterval< T > +{ + final int numDimensions; + + final long size; + + final long[] center; + + final RandomAccessible< T > source; + + public LocalNeighborhood( final RandomAccessible< T > source, final Localizable center ) + { + this.numDimensions = source.numDimensions(); + this.center = new long[ numDimensions ]; + center.localize( this.center ); + + this.size = Util.pow( 3, numDimensions ) - 1; + + this.source = source; + } + + public void updateCenter( final long[] center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center[ d ]; + } + + public void updateCenter( final Localizable center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center.getLongPosition( d ); + } + + @Override + public long size() + { + return size; + } + + @Override + public T firstElement() + { + final LocalNeighborhoodCursor< T > cursor = new LocalNeighborhoodCursor< T >( source, center ); + cursor.fwd(); + return cursor.get(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return center[ d ] - 1; + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min[ d ] = center[ d ] - 1; + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min.setPosition( center[ d ] - 1, d ); + } + + @Override + public double realMax( final int d ) + { + return center[ d ] + 1; + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max[ d ] = center[ d ] + 1; + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max.setPosition( center[ d ] + 1, d ); + } + + @Override + public int numDimensions() + { + return numDimensions; + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return center[ d ] - 1; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min[ d ] = center[ d ] - 1; + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min.setPosition( center[ d ] - 1, d ); + } + + @Override + public long max( final int d ) + { + return center[ d ] + 1; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max[ d ] = center[ d ] + 1; + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max.setPosition( center[ d ] - 1, d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < numDimensions; ++d ) + dimensions[ d ] = 3; + } + + @Override + public long dimension( final int d ) + { + return 3; + } + + @Override + public LocalNeighborhoodCursor< T > cursor() + { + return new LocalNeighborhoodCursor< T >( source, center ); + } + + @Override + public LocalNeighborhoodCursor< T > localizingCursor() + { + return cursor(); + } + +} diff --git a/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood2.java b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood2.java new file mode 100644 index 0000000..ad4e251 --- /dev/null +++ b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhood2.java @@ -0,0 +1,219 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_region.localneighborhood.old; + +import java.util.Iterator; + +import net.imglib2.IterableInterval; +import net.imglib2.Localizable; +import net.imglib2.Positionable; +import net.imglib2.RandomAccessible; +import net.imglib2.RealPositionable; +import net.imglib2.util.Util; + +/** + * TODO + * + */ +@Deprecated +public class LocalNeighborhood2< T > implements IterableInterval< T > +{ + final int numDimensions; + + final long size; + + final long[] center; + + final RandomAccessible< T > source; + + public LocalNeighborhood2( final RandomAccessible< T > source, final Localizable center ) + { + this.numDimensions = source.numDimensions(); + this.center = new long[ numDimensions ]; + center.localize( this.center ); + + this.size = Util.pow( 3, numDimensions ) - 1; + + this.source = source; + } + + public void updateCenter( final long[] center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center[ d ]; + } + + public void updateCenter( final Localizable center ) + { + for ( int d = 0; d < numDimensions; ++d ) + this.center[ d ] = center.getLongPosition( d ); + } + + @Override + public long size() + { + return size; + } + + @Override + public T firstElement() + { + return cursor().next(); + } + + @Override + public Object iterationOrder() + { + return this; // iteration order is only compatible with ourselves + } + + @Override + public double realMin( final int d ) + { + return center[ d ] - 1; + } + + @Override + public void realMin( final double[] min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min[ d ] = center[ d ] - 1; + } + + @Override + public void realMin( final RealPositionable min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min.setPosition( center[ d ] - 1, d ); + } + + @Override + public double realMax( final int d ) + { + return center[ d ] + 1; + } + + @Override + public void realMax( final double[] max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max[ d ] = center[ d ] + 1; + } + + @Override + public void realMax( final RealPositionable max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max.setPosition( center[ d ] + 1, d ); + } + + @Override + public int numDimensions() + { + return numDimensions; + } + + @Override + public Iterator< T > iterator() + { + return cursor(); + } + + @Override + public long min( final int d ) + { + return center[ d ] - 1; + } + + @Override + public void min( final long[] min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min[ d ] = center[ d ] - 1; + } + + @Override + public void min( final Positionable min ) + { + for ( int d = 0; d < numDimensions; ++d ) + min.setPosition( center[ d ] - 1, d ); + } + + @Override + public long max( final int d ) + { + return center[ d ] + 1; + } + + @Override + public void max( final long[] max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max[ d ] = center[ d ] + 1; + } + + @Override + public void max( final Positionable max ) + { + for ( int d = 0; d < numDimensions; ++d ) + max.setPosition( center[ d ] - 1, d ); + } + + @Override + public void dimensions( final long[] dimensions ) + { + for ( int d = 0; d < numDimensions; ++d ) + dimensions[ d ] = 3; + } + + @Override + public long dimension( final int d ) + { + return 3; + } + + @Override + public LocalNeighborhoodCursor2< T > cursor() + { + return new LocalNeighborhoodCursor2< T >( source.randomAccess(), center ); + } + + @Override + public LocalNeighborhoodCursor2< T > localizingCursor() + { + return cursor(); + } + +} diff --git a/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor.java b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor.java new file mode 100644 index 0000000..e93a319 --- /dev/null +++ b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor.java @@ -0,0 +1,287 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_region.localneighborhood.old; + +import net.imglib2.Cursor; +import net.imglib2.Localizable; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.iterator.LocalizingZeroMinIntervalIterator; +import net.imglib2.util.IntervalIndexer; + +/** + * Iterates all pixels in a 3 by 3 by .... by 3 neighborhood of a certain + * location but skipping the central pixel + * + * @param + * + * @author Stephan Preibisch + * @author Stephan Saalfeld + */ +@Deprecated +public class LocalNeighborhoodCursor< T > implements Cursor< T > +{ + final RandomAccessible< T > source; + + final protected RandomAccess< T > randomAccess; + + final LocalizingZeroMinIntervalIterator driver; + + final long[] positionMinus1, tmp; + + final int numDimensions, centralPositionIndex; + + /** + * Create new {@link LocalNeighborhoodCursor} on a {@link RandomAccessible} + * at a certain location. + * + * Note: the location can be updated without need to re-instantiate all the + * times. + * + * @param source + * - the data as {@link RandomAccessible} + * @param center + * - the center location of the 3x3x3...x3 environment that will + * be skipped + */ + public LocalNeighborhoodCursor( final RandomAccessible< T > source, final long[] center ) + { + this.source = source; + this.randomAccess = source.randomAccess(); + + this.numDimensions = source.numDimensions(); + this.tmp = new long[ numDimensions ]; + this.positionMinus1 = new long[ numDimensions ]; + + final int[] dim = new int[ numDimensions ]; + final int[] dim2 = new int[ numDimensions ]; + + for ( int d = 0; d < numDimensions; ++d ) + { + dim[ d ] = 3; + dim2[ d ] = 1; + positionMinus1[ d ] = center[ d ] - 1; + } + + this.driver = new LocalizingZeroMinIntervalIterator( dim ); + this.centralPositionIndex = IntervalIndexer.positionToIndex( dim2, dim ); + } + + /** + * Create new {@link LocalNeighborhoodCursor} on a {@link RandomAccessible} + * at a certain location. + * + * Note: the location can be updated without need to re-instantiate all the + * times. + * + * @param source + * - the data as {@link RandomAccessible} + * @param center + * - the center location of the 3x3x3...x3 environment that will + * be skipped + */ + public LocalNeighborhoodCursor( final RandomAccessible< T > source, final Localizable center ) + { + this.source = source; + this.randomAccess = source.randomAccess(); + + this.numDimensions = source.numDimensions(); + this.tmp = new long[ numDimensions ]; + this.positionMinus1 = new long[ numDimensions ]; + + final int[] dim = new int[ numDimensions ]; + final int[] dim2 = new int[ numDimensions ]; + + for ( int d = 0; d < numDimensions; ++d ) + { + dim[ d ] = 3; + dim2[ d ] = 1; + positionMinus1[ d ] = center.getLongPosition( d ) - 1; + } + + this.driver = new LocalizingZeroMinIntervalIterator( dim ); + this.centralPositionIndex = IntervalIndexer.positionToIndex( dim2, dim ); + } + + public LocalNeighborhoodCursor( final LocalNeighborhoodCursor< T > cursor ) + { + this.source = cursor.source; + this.randomAccess = source.randomAccess(); + this.randomAccess.setPosition( cursor.randomAccess ); + + this.numDimensions = cursor.numDimensions(); + this.tmp = cursor.tmp.clone(); + this.positionMinus1 = cursor.positionMinus1.clone(); + + final int[] dim = new int[ numDimensions ]; + for ( int d = 0; d < numDimensions; ++d ) + dim[ d ] = 3; + + this.driver = new LocalizingZeroMinIntervalIterator( dim ); + this.driver.jumpFwd( cursor.driver.getIndex() ); + + this.centralPositionIndex = cursor.centralPositionIndex; + } + + public void updateCenter( final long[] center ) + { + for ( int d = 0; d < numDimensions; ++d ) + positionMinus1[ d ] = center[ d ] - 1; + + reset(); + } + + public void updateCenter( final Localizable center ) + { + for ( int d = 0; d < numDimensions; ++d ) + positionMinus1[ d ] = center.getLongPosition( d ) - 1; + + reset(); + } + + @Override + public boolean hasNext() + { + return driver.hasNext(); + } + + @Override + public void fwd() + { + driver.fwd(); + + if ( driver.getIndex() == centralPositionIndex ) + driver.fwd(); + + for ( int d = 0; d < numDimensions; ++d ) + randomAccess.setPosition( positionMinus1[ d ] + driver.getLongPosition( d ), d ); + } + + @Override + public void jumpFwd( final long steps ) + { + for ( long j = 0; j < steps; ++j ) + fwd(); + } + + @Override + public void reset() + { + this.driver.reset(); + } + + @Override + public void localize( final float[] position ) + { + randomAccess.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + randomAccess.localize( position ); + } + + @Override + public float getFloatPosition( final int d ) + { + return randomAccess.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return randomAccess.getDoublePosition( d ); + } + + @Override + public int numDimensions() + { + return numDimensions; + } + + @Override + public T get() + { + return randomAccess.get(); + } + + @Override + public T next() + { + fwd(); + return get(); + } + + @Override + public void remove() + {} + + @Override + public void localize( final int[] position ) + { + randomAccess.localize( position ); + } + + @Override + public void localize( final long[] position ) + { + randomAccess.localize( position ); + } + + @Override + public int getIntPosition( final int d ) + { + return randomAccess.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return randomAccess.getLongPosition( d ); + } + + @Override + public LocalNeighborhoodCursor< T > copyCursor() + { + return new LocalNeighborhoodCursor< T >( this ); + } + + @Override + public LocalNeighborhoodCursor< T > copy() + { + return copyCursor(); + } +} diff --git a/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor2.java b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor2.java new file mode 100644 index 0000000..7639e0a --- /dev/null +++ b/src/main/java/varun_algorithm_region/localneighborhood/old/LocalNeighborhoodCursor2.java @@ -0,0 +1,204 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_region.localneighborhood.old; + +import net.imglib2.AbstractCursor; +import net.imglib2.RandomAccess; + +/** + * Iterates all pixels in a 3 by 3 by .... by 3 neighborhood of a certain + * location but skipping the central pixel + * + * @param + * + * @author Stephan Preibisch + * @author Stephan Saalfeld + * @author Benjamin Schmid + * @author Tobias Pietzsch + */ +@Deprecated +public class LocalNeighborhoodCursor2< T > extends AbstractCursor< T > +{ + final RandomAccess< T > source; + + private final long[] center; + + private final long[] min; + + private final long[] max; + + private final long span; + + private final long maxCount; + + private final long midCount; + + private long count; + + public LocalNeighborhoodCursor2( final RandomAccess< T > source, final long[] center, final long span ) + { + super( source.numDimensions() ); + this.source = source; + this.center = center; + max = new long[ n ]; + min = new long[ n ]; + this.span = span; + maxCount = ( long ) Math.pow( span + 1 + span, n ); + midCount = maxCount / 2 + 1; + reset(); + } + + public LocalNeighborhoodCursor2( final RandomAccess< T > source, final long[] center ) + { + this( source, center, 1 ); + } + + protected LocalNeighborhoodCursor2( final LocalNeighborhoodCursor2< T > c ) + { + super( c.numDimensions() ); + this.source = c.source.copyRandomAccess(); + this.center = c.center; + max = c.max.clone(); + min = c.min.clone(); + span = c.span; + maxCount = c.maxCount; + midCount = c.midCount; + } + + @Override + public T get() + { + return source.get(); + } + + @Override + public void fwd() + { + source.fwd( 0 ); + if ( source.getLongPosition( 0 ) > max[ 0 ] ) + { + source.setPosition( min[ 0 ], 0 ); + for ( int d = 1; d < n; ++d ) + { + source.fwd( d ); + if ( source.getLongPosition( d ) > max[ d ] ) + source.setPosition( min[ d ], d ); + else + break; + } + } + if ( ++count == midCount ) + fwd(); + } + + @Override + public void reset() + { + for ( int d = 0; d < n; ++d ) + { + min[ d ] = center[ d ] - span; + max[ d ] = center[ d ] + span; + } + source.setPosition( min ); + source.bck( 0 ); + count = 0; + } + + @Override + public boolean hasNext() + { + return count < maxCount; + } + + @Override + public float getFloatPosition( final int d ) + { + return source.getFloatPosition( d ); + } + + @Override + public double getDoublePosition( final int d ) + { + return source.getDoublePosition( d ); + } + + @Override + public int getIntPosition( final int d ) + { + return source.getIntPosition( d ); + } + + @Override + public long getLongPosition( final int d ) + { + return source.getLongPosition( d ); + } + + @Override + public void localize( final long[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final float[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final double[] position ) + { + source.localize( position ); + } + + @Override + public void localize( final int[] position ) + { + source.localize( position ); + } + + @Override + public LocalNeighborhoodCursor2< T > copy() + { + return new LocalNeighborhoodCursor2< T >( this ); + } + + @Override + public LocalNeighborhoodCursor2< T > copyCursor() + { + return copy(); + } +} diff --git a/src/main/java/varun_algorithm_stats/ComputeMinMax.java b/src/main/java/varun_algorithm_stats/ComputeMinMax.java new file mode 100644 index 0000000..299560f --- /dev/null +++ b/src/main/java/varun_algorithm_stats/ComputeMinMax.java @@ -0,0 +1,239 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import java.util.Vector; +import java.util.concurrent.atomic.AtomicInteger; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.type.Type; +import net.imglib2.util.Util; +import net.imglib2.view.Views; +import varun_algorithm.Algorithm; +import varun_algorithm.Benchmark; +import varun_algorithm.MultiThreaded; +import varun_multithreading.Chunk; +import varun_multithreading.SimpleMultiThreading; + +/** + * TODO + * + * @author Stephan Preibisch + */ +public class ComputeMinMax< T extends Type< T > & Comparable< T >> implements Algorithm, MultiThreaded, Benchmark +{ + /** + * Computes minimal and maximal value in a given interval + * + * @param interval + * @param min + * @param max + */ + final public static < T extends Comparable< T > & Type< T > > void computeMinMax( final RandomAccessibleInterval< T > interval, final T min, final T max ) + { + final ComputeMinMax< T > c = new ComputeMinMax< T >( Views.iterable( interval ), min, max ); + c.process(); + + min.set( c.getMin() ); + max.set( c.getMax() ); + } + + final IterableInterval< T > image; + + final T min, max; + + String errorMessage = ""; + + int numThreads; + + long processingTime; + + public ComputeMinMax( final IterableInterval< T > interval, final T min, final T max ) + { + setNumThreads(); + + this.image = interval; + + this.min = min; + this.max = max; + } + + public T getMin() + { + return min; + } + + public T getMax() + { + return max; + } + + @Override + public boolean process() + { + final long startTime = System.currentTimeMillis(); + + final long imageSize = image.size(); + + final AtomicInteger ai = new AtomicInteger( 0 ); + final Thread[] threads = SimpleMultiThreading.newThreads( getNumThreads() ); + + final Vector< Chunk > threadChunks = SimpleMultiThreading.divideIntoChunks( imageSize, numThreads ); + final Vector< T > minValues = new Vector< T >(); + final Vector< T > maxValues = new Vector< T >(); + + for ( int ithread = 0; ithread < threads.length; ++ithread ) + { + minValues.add( image.firstElement().createVariable() ); + maxValues.add( image.firstElement().createVariable() ); + + threads[ ithread ] = new Thread( new Runnable() + { + @Override + public void run() + { + // Thread ID + final int myNumber = ai.getAndIncrement(); + + // get chunk of pixels to process + final Chunk myChunk = threadChunks.get( myNumber ); + + // compute min and max + compute( myChunk.getStartPosition(), myChunk.getLoopSize(), minValues.get( myNumber ), maxValues.get( myNumber ) ); + + } + } ); + } + + SimpleMultiThreading.startAndJoin( threads ); + + // compute overall min and max + min.set( minValues.get( 0 ) ); + max.set( maxValues.get( 0 ) ); + + for ( int i = 0; i < threads.length; ++i ) + { + T value = minValues.get( i ); + if ( Util.min( min, value ) == value ) + min.set( value ); + + value = maxValues.get( i ); + if ( Util.max( max, value ) == value ) + max.set( value ); + } + + processingTime = System.currentTimeMillis() - startTime; + + return true; + } + + protected void compute( final long startPos, final long loopSize, final T min, final T max ) + { + final Cursor< T > cursor = image.cursor(); + + // init min and max + cursor.fwd(); + + min.set( cursor.get() ); + max.set( cursor.get() ); + + cursor.reset(); + + // move to the starting position of the current thread + cursor.jumpFwd( startPos ); + + // do as many pixels as wanted by this thread + for ( long j = 0; j < loopSize; ++j ) + { + cursor.fwd(); + + final T value = cursor.get(); + + if ( Util.min( min, value ) == value ) + min.set( value ); + + if ( Util.max( max, value ) == value ) + max.set( value ); + } + } + + @Override + public boolean checkInput() + { + if ( errorMessage.length() > 0 ) + { + return false; + } + else if ( image == null ) + { + errorMessage = "ScaleSpace: [Image img] is null."; + return false; + } + else + return true; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public void setNumThreads() + { + this.numThreads = Runtime.getRuntime().availableProcessors(); + } + + @Override + public void setNumThreads( final int numThreads ) + { + this.numThreads = numThreads; + } + + @Override + public int getNumThreads() + { + return numThreads; + } + + @Override + public String getErrorMessage() + { + return errorMessage; + } +} diff --git a/src/main/java/varun_algorithm_stats/Histogram.java b/src/main/java/varun_algorithm_stats/Histogram.java new file mode 100644 index 0000000..b75dfd2 --- /dev/null +++ b/src/main/java/varun_algorithm_stats/Histogram.java @@ -0,0 +1,251 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import java.util.ArrayList; +import java.util.Arrays; + +import net.imglib2.RealCursor; +import net.imglib2.img.Img; +import net.imglib2.type.Type; +import varun_algorithm.Algorithm; +import varun_algorithm.Benchmark; + +/** + * Implements a Histogram over an Image. + * + * @author 2011 Larry Lindsey + * @author Larry Lindsey + */ +public class Histogram< T > implements Algorithm, Benchmark +{ + /** + * Processing time, milliseconds. + */ + private long pTime = 0; + + /** + * Hold the histogram itself. + */ + private final int[] histogram; + + /** + * The Cursor from which the histogram is to be calculated. + */ + private final RealCursor< T > cursor; + + /** + * The HistogramBinMapper, used to map Type values to histogram bin indices. + */ + private final HistogramBinMapper< T > binMapper; + + /** + * Create a Histogram using the given mapper, calculating from the given + * Cursor. + * + * @param mapper + * the HistogramBinMapper used to map Type values to histogram + * bin indices. + * @param c + * a Cursor corresponding to the Image from which the Histogram + * will be calculated + * + */ + public Histogram( final HistogramBinMapper< T > mapper, + final RealCursor< T > c ) + { + cursor = c; + binMapper = mapper; + histogram = new int[ binMapper.getNumBins() ]; + } + + /** + * Create a Histogram using the given mapper, calculating from the given + * Image. + * + * @param mapper + * the HistogramBinMapper used to map Type values to histogram + * bin indices. + * @param image + * an Image from which the Histogram will be calculated + * + */ + public Histogram( final HistogramBinMapper< T > mapper, + final Img< T > image ) + { + this( mapper, image.cursor() ); + } + + /** + * Resets the histogram array and the Cursor. + */ + public void reset() + { + Arrays.fill( histogram, 0 ); + cursor.reset(); + } + + /** + * Returns the bin count corresponding to a given {@link Type}. + * + * @param t + * the Type corresponding to the requested + * @return The requested bin count. + */ + public int getBin( final T t ) + { + return getHistogram()[ binMapper.map( t ) ]; + } + + /** + * Returns the bin count given by the indicated bin index. + * + * @param i + * the index of the requested bin + * @return the bin count at the given index + */ + public int getBin( final int i ) + { + return getHistogram()[ i ]; + } + + /** + * Returns this Histogram's HistogramBinMapper. + * + * @return the HistogramBinMapper associated with this Histogram. + */ + public HistogramBinMapper< T > getBinMapper() + { + return binMapper; + } + + /** + * Returns the histogram array. + * + * @return the histogram array. + */ + public int[] getHistogram() + { + return histogram; + } + + /** + * Creates and returns the a Type whose value corresponds to the center of + * the bin indexed by i. + * + * @param i + * the requested bin index. + * @return a Type whose value corresponds to the requested bin center. + */ + public T getBinCenter( final int i ) + { + return getBinMapper().invMap( i ); + } + + /** + * Creates and returns a List containing Types that correspond to the + * centers of the histogram bins. + * + * @return a List containing Types that correspond to the centers of the + * histogram bins. + */ + public ArrayList< T > getBinCenters() + { + final ArrayList< T > binCenters = new ArrayList< T >( histogram.length ); + for ( int i = 0; i < histogram.length; ++i ) + { + binCenters.add( i, getBinMapper().invMap( i ) ); + } + + return binCenters; + } + + /** + * Returns the number of bins in this Histogram. + * + * @return the number of bins in this Histogram + * + */ + public int getNumBins() + { + return getBinMapper().getNumBins(); + } + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public String getErrorMessage() + { + return null; + } + + @Override + public boolean process() + { + final long startTime = System.currentTimeMillis(); + int index; + + while ( cursor.hasNext() ) + { + cursor.fwd(); + index = binMapper.map( cursor.get() ); + /* + * The following check makes this run for IntegerTypes at 3 to 4 + * longer than the manual case on my machine. This is a necessary + * check, but if this takes too long, it might be worthwhile to + * separate out an UncheckedHistogram, which would instead throw an + * ArrayOutOfBoundsException. + */ + if ( index >= 0 && index < histogram.length ) + { + ++histogram[ index ]; + } + } + + pTime = System.currentTimeMillis() - startTime; + return true; + } + + @Override + public long getProcessingTime() + { + return pTime; + } + +} diff --git a/src/main/java/varun_algorithm_stats/HistogramBinMapper.java b/src/main/java/varun_algorithm_stats/HistogramBinMapper.java new file mode 100644 index 0000000..7ac6c0a --- /dev/null +++ b/src/main/java/varun_algorithm_stats/HistogramBinMapper.java @@ -0,0 +1,88 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +/** + * An interface used by the Histogram class to map Type objects to histogram + * bins. + * + * @author 2011 Larry Lindsey + * @author Larry Lindsey + */ +public interface HistogramBinMapper< T > +{ + + /** + * Returns the minimum bin for the histogram. This value may not be relevant + * for histograms over Type's that do not have a natural order. + * + * @return the minimum bin Type for the histogram. + */ + public T getMinBin(); + + /** + * Returns the maximum bin for the histogram. This value may not be relevant + * for histograms over Type's that do not have a natural order. + * + * @return the maximum bin Type for the histogram. + */ + public T getMaxBin(); + + /** + * Returns the number of bins for the histogram. + * + * @return the number of bins for the histogram. + */ + public int getNumBins(); + + /** + * Maps a given Type to its histogram bin. + * + * @param type + * the Type to map. + * @return the histogram bin index. + */ + public int map( final T type ); + + /** + * Maps a given histogram bin index to a Type containing the bin center + * value. + * + * @param i + * the histogram bin index to map. + * @return a Type containing the bin center value. + */ + public T invMap( final int i ); +} diff --git a/src/main/java/varun_algorithm_stats/IntBinMapper.java b/src/main/java/varun_algorithm_stats/IntBinMapper.java new file mode 100644 index 0000000..1d86e68 --- /dev/null +++ b/src/main/java/varun_algorithm_stats/IntBinMapper.java @@ -0,0 +1,150 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import net.imglib2.type.numeric.IntegerType; + +/** + * A HistogramBinMapper over IntegerTypes, using the values themselves as the + * histogram bin centers. + * + * + * @author 2011 Larry Lindsey + * @author Larry Lindsey + */ +public class IntBinMapper< T extends IntegerType< T >> + implements HistogramBinMapper< T > +{ + + /** + * Given an IntegerType, returns a new Type containing its minimum possible + * value. + * + * @param + * the IntegerType in question. + * @param r + * a representative Type object. + * @return a new Type containing its minimum possible value. + */ + private static < R extends IntegerType< R >> R minType( final R r ) + { + final R type = r.createVariable(); + type.setReal( r.getMinValue() ); + return type; + } + + /** + * Given an IntegerType, returns a new Type containing its maximum possible + * value. + * + * @param + * the IntegerType in question. + * @param r + * a representative Type object. + * @return a new Type containing its maximum possible value. + */ + private static < R extends IntegerType< R >> R maxType( final R r ) + { + final R type = r.createVariable(); + type.setReal( r.getMaxValue() ); + return type; + } + + private final T minType, maxType; + + private final int numBins; + + private final int minVal; + + /** + * Create an IntBinMapper with the given minimum and maximum bins. + * + * @param min + * the minimum bin center. + * @param max + * the maximum bin center. + */ + public IntBinMapper( final T min, final T max ) + { + minType = min; + maxType = max; + numBins = max.getInteger() - min.getInteger() + 1; + minVal = min.getInteger(); + } + + /** + * Create an IntBinMapper with minimum and maximum bin centers corresponding + * to the minimal and maximal Type values. + * + * @param type + * a representative Type object. + */ + public IntBinMapper( final T type ) + { + this( minType( type ), maxType( type ) ); + } + + @Override + public T getMaxBin() + { + return maxType; + } + + @Override + public T getMinBin() + { + return minType; + } + + @Override + public int getNumBins() + { + return numBins; + } + + @Override + public T invMap( final int i ) + { + final T out = minType.createVariable(); + out.setInteger( i + minVal ); + return out; + } + + @Override + public int map( final T type ) + { + return type.getInteger() - minVal; + } +} diff --git a/src/main/java/varun_algorithm_stats/Max.java b/src/main/java/varun_algorithm_stats/Max.java new file mode 100644 index 0000000..86f2ec0 --- /dev/null +++ b/src/main/java/varun_algorithm_stats/Max.java @@ -0,0 +1,65 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; + +/** + * Find the maximum value and its position in an {@link IterableInterval}. + * + * @author Tobias Pietzsch + */ +public class Max +{ + /** + * Find the maximum value and its position in an {@link IterableInterval}. + * + * @param iterable + * input interval. + * @return a cursor positioned on the global maximum. If several maxima with + * the same value exist, the cursor is on the first one. + */ + public static < T extends Comparable< T > > Cursor< T > findMax( final IterableInterval< T > iterable ) + { + final Cursor< T > cursor = iterable.cursor(); + cursor.fwd(); + Cursor< T > max = cursor.copyCursor(); + while ( cursor.hasNext() ) + if ( cursor.next().compareTo( max.get() ) > 0 ) + max = cursor.copyCursor(); + return max; + } +} diff --git a/src/main/java/varun_algorithm_stats/Min.java b/src/main/java/varun_algorithm_stats/Min.java new file mode 100644 index 0000000..f7a16e7 --- /dev/null +++ b/src/main/java/varun_algorithm_stats/Min.java @@ -0,0 +1,66 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; + +/** + * Find the minimum value and its position in an {@link IterableInterval}. + * + * @author Tobias Pietzsch + */ +public class Min +{ + /** + * Find the minimum value and its position in an {@link IterableInterval}. + * + * @param iterable + * input interval. + * @return a cursor positioned on the global minimum. If several minima with + * the same value exist, the cursor is on the first one. + */ + public static < T extends Comparable< T > > Cursor< T > findMin( final IterableInterval< T > iterable ) + { + final Cursor< T > cursor = iterable.cursor(); + cursor.fwd(); + Cursor< T > min = cursor.copyCursor(); + while ( cursor.hasNext() ) + if ( cursor.next().compareTo( min.get() ) < 0 ) + min = cursor.copyCursor(); + return min; + } + +} diff --git a/src/main/java/varun_algorithm_stats/Normalize.java b/src/main/java/varun_algorithm_stats/Normalize.java new file mode 100644 index 0000000..cd0745b --- /dev/null +++ b/src/main/java/varun_algorithm_stats/Normalize.java @@ -0,0 +1,78 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.type.numeric.NumericType; + +public class Normalize +{ + /** + * Normalize values of an {@link IterableInterval} to the range [min, max]. + * + * @param iterable + * the interval to be normalized. + * @param min + * target minimum value of the normalized interval. + * @param max + * target maximum value of the normalized interval. + */ + public static < T extends NumericType< T > & Comparable< T > > void normalize( final IterableInterval< T > iterable, final T min, final T max ) + { + final Cursor< T > cursor = iterable.cursor(); + final T currentMax = cursor.next().copy(); + final T currentMin = currentMax.copy(); + for ( final T t : iterable ) + if ( t.compareTo( currentMax ) > 0 ) + currentMax.set( t ); + else if ( t.compareTo( currentMin ) < 0 ) + currentMin.set( t ); + + final T scale = max.copy(); + scale.sub( min ); + final T currentScale = currentMax; // no need to currentMax.copy(). We + // don't use currentMax after this. + currentScale.sub( currentMin ); + + for ( final T t : iterable ) + { + t.sub( currentMin ); + t.mul( scale ); + t.div( currentScale ); + t.add( min ); + } + } +} diff --git a/src/main/java/varun_algorithm_stats/RealBinMapper.java b/src/main/java/varun_algorithm_stats/RealBinMapper.java new file mode 100644 index 0000000..99b649a --- /dev/null +++ b/src/main/java/varun_algorithm_stats/RealBinMapper.java @@ -0,0 +1,132 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_algorithm_stats; + +import net.imglib2.type.numeric.RealType; + +/** + * A HistogramBinMapper over RealType, using arbitrary-width histogram bins. + * + * @author 2011 Larry Lindsey + * @author Larry Lindsey + */ +public class RealBinMapper< T extends RealType< T >> + implements HistogramBinMapper< T > +{ + + private final int numBins; + + private final T minBin; + + private final T maxBin; + + private final double binWidth; + + private final double halfBinWidth; + + private final double minVal; + + /** + * Creates a RealBinMapper for a histogram with the given minimum bin + * center, maximum bin center, and number of bins. + * + * @param minBin + * the minimal bin center. + * @param maxBin + * the maximal bin center. + * @param numBins + * the number of histogram bins to use. + */ + public RealBinMapper( final T minBin, final T maxBin, final int numBins ) + { + this.numBins = numBins; + this.minBin = minBin; + this.maxBin = maxBin; + + // Save a little computation time by calculating these only once. + binWidth = ( 1 + maxBin.getRealDouble() - minBin.getRealDouble() ) / + ( numBins ); + halfBinWidth = binWidth / 2; + + minVal = minBin.getRealDouble(); + } + + @Override + public T getMaxBin() + { + return maxBin; + } + + @Override + public T getMinBin() + { + return minBin; + } + + @Override + public int getNumBins() + { + return numBins; + } + + @Override + public T invMap( final int i ) + { + final T out = minBin.createVariable(); + double t = i; + + t *= binWidth; + t += minVal; + out.setReal( t ); + return out; + } + + @Override + public int map( final T type ) + { + double tVal = type.getRealDouble(); + tVal -= minVal; + tVal += halfBinWidth; + tVal /= binWidth; + + return ( int ) tVal; + } + + public double getBinWidth() + { + return binWidth; + } + +} diff --git a/src/main/java/varun_algorithm_tree/Forest.java b/src/main/java/varun_algorithm_tree/Forest.java new file mode 100644 index 0000000..ac84109 --- /dev/null +++ b/src/main/java/varun_algorithm_tree/Forest.java @@ -0,0 +1,58 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_tree; + +import java.util.Set; + + + +/** + * A forest (in contrast to a {@link Tree} has several {@link Forest#roots() + * roots}. + * + * @param + * the node type + * + * @author Tobias Pietzsch + */ +public interface Forest< T extends TreeNode< T > > +{ + /** + * Get the set of root nodes of this component forest. + * + * @return set of roots. + */ + public Set< T > roots(); + +} diff --git a/src/main/java/varun_algorithm_tree/Tree.java b/src/main/java/varun_algorithm_tree/Tree.java new file mode 100644 index 0000000..9dc63f6 --- /dev/null +++ b/src/main/java/varun_algorithm_tree/Tree.java @@ -0,0 +1,53 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_tree; + + +/** + * A tree. + * + * @param + * the node type + * + * @author Tobias Pietzsch + */ +public interface Tree< T extends TreeNode< T > > extends Forest< T > +{ + /** + * Get the root component. + * + * @return root component. + */ + public T root(); +} diff --git a/src/main/java/varun_algorithm_tree/TreeNode.java b/src/main/java/varun_algorithm_tree/TreeNode.java new file mode 100644 index 0000000..e5cb2be --- /dev/null +++ b/src/main/java/varun_algorithm_tree/TreeNode.java @@ -0,0 +1,63 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_tree; + +import java.util.List; + + + +/** + * A node in a {@link Tree} (or {@link Forest}). + * + * @param + * the node type + * + * @author Tobias Pietzsch + */ +public interface TreeNode< T extends TreeNode< T > > +{ + /** + * Get the parent of this node in the {@link Tree}. + * + * @return the parent of this node in the {@link Tree}. + */ + public T getParent(); + + /** + * Get the children of this node in the {@link Tree}. + * + * @return the children of this node in the {@link Tree}. + */ + public List< T > getChildren(); +} diff --git a/src/main/java/varun_algorithm_tree/TreeUtils.java b/src/main/java/varun_algorithm_tree/TreeUtils.java new file mode 100644 index 0000000..5883249 --- /dev/null +++ b/src/main/java/varun_algorithm_tree/TreeUtils.java @@ -0,0 +1,92 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ +package varun_algorithm_tree; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; + + + +public class TreeUtils +{ + /** + * Find all leaf nodes of a forest. + * + * @param forest + * the forest + * @return set of leaf nodes. + */ + public static < T extends TreeNode< T > > ArrayList< T > getLeafs( final Forest< T > forest ) + { + final ArrayList< T > leafs = new ArrayList< T >(); + final ArrayDeque< T > nodes = new ArrayDeque< T >( forest.roots() ); + while ( !nodes.isEmpty() ) + { + final T node = nodes.remove(); + final List< T > children = node.getChildren(); + if ( children.isEmpty() ) + leafs.add( node ); + else + nodes.addAll( children ); + } + return leafs; + } + + public static interface Consumer< T > + { + void accept( T t ); + } + + /** + * Call {@link Consumer#accept(Object)} on op for every node in the forest. + * + * @param forest + * the forest + * @param op + * the consumer + */ + public static < T extends TreeNode< T > > void forEach( final Forest< T > forest, final Consumer< T > op ) + { + final ArrayDeque< T > nodes = new ArrayDeque< T >( forest.roots() ); + while ( !nodes.isEmpty() ) + { + final T node = nodes.remove(); + op.accept( node ); + final List< T > children = node.getChildren(); + if ( !children.isEmpty() ) + nodes.addAll( children ); + } + } +} diff --git a/src/main/java/varun_multithreading/Chunk.java b/src/main/java/varun_multithreading/Chunk.java new file mode 100644 index 0000000..4ead524 --- /dev/null +++ b/src/main/java/varun_multithreading/Chunk.java @@ -0,0 +1,64 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_multithreading; + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +public class Chunk +{ + public Chunk( final long startPosition, final long loopSize ) + { + this.startPosition = startPosition; + this.loopSize = loopSize; + } + + public long getStartPosition() + { + return startPosition; + } + + public long getLoopSize() + { + return loopSize; + } + + protected long startPosition; + + protected long loopSize; +} diff --git a/src/main/java/varun_multithreading/SimpleMultiThreading.java b/src/main/java/varun_multithreading/SimpleMultiThreading.java new file mode 100644 index 0000000..0696356 --- /dev/null +++ b/src/main/java/varun_multithreading/SimpleMultiThreading.java @@ -0,0 +1,177 @@ +/* + * #%L + * ImgLib2: a general-purpose, multidimensional image processing library. + * %% + * Copyright (C) 2009 - 2016 Tobias Pietzsch, Stephan Preibisch, Stephan Saalfeld, + * John Bogovic, Albert Cardona, Barry DeZonia, Christian Dietz, Jan Funke, + * Aivar Grislis, Jonathan Hale, Grant Harris, Stefan Helfrich, Mark Hiner, + * Martin Horn, Steffen Jaensch, Lee Kamentsky, Larry Lindsey, Melissa Linkert, + * Mark Longair, Brian Northan, Nick Perry, Curtis Rueden, Johannes Schindelin, + * Jean-Yves Tinevez and Michael Zinsmaier. + * %% + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * #L% + */ + +package varun_multithreading; + +import java.util.Vector; + + + +/** + * TODO + * + * @author Stephan Preibisch + */ +@Deprecated +public class SimpleMultiThreading +{ + /* + * final int start = 0; final int end = 10; + * + * final AtomicInteger ai = new AtomicInteger(start); + * + * Thread[] threads = newThreads(); for (int ithread = 0; ithread < + * threads.length; ++ithread) { threads[ithread] = new Thread(new Runnable() + * { public void run() { // do something.... // for example: for (int i3 = + * ai.getAndIncrement(); i3 < end; i3 = ai.getAndIncrement()) { } } }); } + * startAndJoin(threads); + */ + + public static Vector< Chunk > divideIntoChunks( final long imageSize, final int numThreads ) + { + final long threadChunkSize = imageSize / numThreads; + final long threadChunkMod = imageSize % numThreads; + + final Vector< Chunk > chunks = new Vector< Chunk >(); + + for ( int threadID = 0; threadID < numThreads; ++threadID ) + { + // move to the starting position of the current thread + final long startPosition = threadID * threadChunkSize; + + // the last thread may has to run longer if the number of pixels + // cannot be divided by the number of threads + final long loopSize; + if ( threadID == numThreads - 1 ) + loopSize = threadChunkSize + threadChunkMod; + else + loopSize = threadChunkSize; + + chunks.add( new Chunk( startPosition, loopSize ) ); + } + + return chunks; + } + + public static void startTask( final Runnable run ) + { + final Thread[] threads = newThreads(); + + for ( int ithread = 0; ithread < threads.length; ++ithread ) + threads[ ithread ] = new Thread( run ); + + startAndJoin( threads ); + } + + public static void startTask( final Runnable run, final int numThreads ) + { + if ( 1 == numThreads ) + { + run.run(); + return; + } + + final Thread[] threads = newThreads( numThreads ); + + for ( int ithread = 0; ithread < threads.length; ++ithread ) + threads[ ithread ] = new Thread( run ); + + startAndJoin( threads ); + } + + public static Thread[] newThreads() + { + final int nthread = Runtime.getRuntime().availableProcessors(); + return new Thread[ nthread ]; + } + + public static Thread[] newThreads( final int numThreads ) + { + return new Thread[ numThreads ]; + } + + public static void startAndJoin( final Thread[] threads ) + { + if ( 1 == threads.length ) + { + threads[ 0 ].run(); + return; + } + + for ( int ithread = 0; ithread < threads.length; ++ithread ) + { + threads[ ithread ].setPriority( Thread.NORM_PRIORITY ); + threads[ ithread ].start(); + } + + try + { + for ( int ithread = 0; ithread < threads.length; ++ithread ) + threads[ ithread ].join(); + } + catch ( final InterruptedException ie ) + { + throw new RuntimeException( ie ); + } + } + + public static void start( final Thread[] threads ) + { + for ( int ithread = 0; ithread < threads.length; ++ithread ) + { + threads[ ithread ].setPriority( Thread.MIN_PRIORITY ); + threads[ ithread ].start(); + } + } + + public static void threadHaltUnClean() + { + final int i = 0; + + while ( i == 0 ) + {} + } + + public static void threadWait( final long milliseconds ) + { + try + { + Thread.sleep( milliseconds ); + } + catch ( final InterruptedException e ) + { + System.err.println( "MultiThreading.threadWait(): Thread woken up: " + e ); + } + } +} diff --git a/src/main/java/visualization/AbstractCovistoModelView.java b/src/main/java/visualization/AbstractCovistoModelView.java new file mode 100644 index 0000000..449e10e --- /dev/null +++ b/src/main/java/visualization/AbstractCovistoModelView.java @@ -0,0 +1,124 @@ +package visualization; + + +import java.util.HashMap; +import java.util.Map; + +import linkers.Model3D; +import utility.ModelChangeListener; +import utility.ThreeDRoiobject; + +/** + * An abstract class for ThreeDRoiobject displayers, that can overlay detected ThreeDRoiobjects and + * tracks on top of the image data. + *

      + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> Jan 2011 + */ +public abstract class AbstractCovistoModelView implements SelectionChangeListener, CovistoModelView, ModelChangeListener +{ + + /* + * FIELDS + */ + + /** + * A map of String/Object that configures the look and feel of the display. + */ + protected Map< String, Object > displaySettings; + + /** The model displayed by this class. */ + protected Model3D model; + + protected final SelectionModel selectionModel; + + /* + * PROTECTED CONSTRUCTOR + */ + + protected AbstractCovistoModelView( final Model3D model, final SelectionModel selectionModel ) + { + this.selectionModel = selectionModel; + this.model = model; + this.displaySettings = initDisplaySettings( model ); + model.addModelChangeListener( this ); + selectionModel.addSelectionChangeListener( this ); + } + + /* + * PUBLIC METHODS + */ + + @Override + public void setDisplaySettings( final String key, final Object value, final int trackID ) + { + displaySettings.put( key, value ); + } + + @Override + public Object getDisplaySettings( final String key ) + { + return displaySettings.get( key ); + } + + @Override + public Map< String, Object > getDisplaySettings() + { + return displaySettings; + } + + /** + * This needs to be overriden for concrete implementation to display + * selection. + */ + @Override + public void selectionChanged( final SelectionChangeEvent event ) + { + // Center on selection if we added one ThreeDRoiobject exactly + final Map< ThreeDRoiobject, Boolean > ThreeDRoiobjectsAdded = event.getThreeDRoiobjects(); + if ( ThreeDRoiobjectsAdded != null && ThreeDRoiobjectsAdded.size() == 1 ) + { + final boolean added = ThreeDRoiobjectsAdded.values().iterator().next(); + if ( added ) + { + final ThreeDRoiobject ThreeDRoiobject = ThreeDRoiobjectsAdded.keySet().iterator().next(); + centerViewOn( ThreeDRoiobject ); + } + } + } + + @Override + public Model3D getModel() + { + return model; + } + + /** + * Provides default display settings. + * + * @param lModel + * the model this view operate on. Needed for some display + * settings. + */ + protected Map< String, Object > initDisplaySettings( final Model3D lModel ) + { + final Map< String, Object > lDisplaySettings = new HashMap<>( 11 ); + lDisplaySettings.put( KEY_COLOR, DEFAULT_ThreeDRoiobject_COLOR ); + lDisplaySettings.put( KEY_HIGHLIGHT_COLOR, DEFAULT_HIGHLIGHT_COLOR ); + lDisplaySettings.put( KEY_ThreeDRoiobjectS_VISIBLE, true ); + lDisplaySettings.put( KEY_DISPLAY_ThreeDRoiobject_NAMES, false ); + lDisplaySettings.put( KEY_ThreeDRoiobject_COLORING, new DummyThreeDRoiobjectColorGenerator() ); + lDisplaySettings.put( KEY_ThreeDRoiobject_RADIUS_RATIO, 1.0d ); + lDisplaySettings.put( KEY_TRACKS_VISIBLE, true ); + lDisplaySettings.put( KEY_TRACK_DISPLAY_MODE, DEFAULT_TRACK_DISPLAY_MODE ); + lDisplaySettings.put( KEY_TRACK_DISPLAY_DEPTH, DEFAULT_TRACK_DISPLAY_DEPTH ); + lDisplaySettings.put( KEY_TRACK_COLORING, new DummyTrackColorGenerator() ); + lDisplaySettings.put( KEY_COLORMAP, DEFAULT_COLOR_MAP ); + lDisplaySettings.put( KEY_LIMIT_DRAWING_DEPTH, DEFAULT_LIMIT_DRAWING_DEPTH ); + lDisplaySettings.put( KEY_DRAWING_DEPTH, DEFAULT_DRAWING_DEPTH ); + return lDisplaySettings; + } + + + +} diff --git a/src/main/java/visualization/CategoryJComboBox.java b/src/main/java/visualization/CategoryJComboBox.java new file mode 100644 index 0000000..408240b --- /dev/null +++ b/src/main/java/visualization/CategoryJComboBox.java @@ -0,0 +1,290 @@ +package visualization; + + +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.AbstractAction; +import javax.swing.Action; +import javax.swing.ActionMap; +import javax.swing.InputMap; +import javax.swing.JComboBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JList; +import javax.swing.KeyStroke; +import javax.swing.ListCellRenderer; + + +/** + * A JcomboBox that displays categories, and return the category the selected + * item belong to. + *

      + * We have to extends JComboBox<Object>, because we want to + * be able to insert both objects of type K and V. + * + * + * @author Jean-Yves Tinevez, adapted from + * http://java-swing-tips.blogspot.fr/2010 + * /03/non-selectable-jcombobox-items.html + * + * @param + * the type of the category objects + * @param + * the type of the items + */ +public class CategoryJComboBox< K, V > extends JComboBox< Object > +{ + + private static final long serialVersionUID = 1L; + + protected static final String INDENT = " "; + + /** Indices of items that should be displayed as a category name. */ + private final HashSet< Integer > categoryIndexSet = new HashSet< >(); + + private boolean isCategoryIndex = false; + + private final Map< V, String > itemNames; + + private final HashMap< V, K > invertMap; + + private final Map< K, String > categoryNames; + + /* + * CONSTRUCTOR + */ + + public CategoryJComboBox( final Map< K, Collection< V >> items, final Map< V, String > itemNames, final Map< K, String > categoryNames ) + { + super(); + this.invertMap = new HashMap< >(); + this.itemNames = itemNames; + this.categoryNames = categoryNames; + init(); + + // Feed the combo box + for ( final K category : items.keySet() ) + { + addItem( category, true ); + + final Collection< V > categoryItems = items.get( category ); + for ( final V item : categoryItems ) + { + addItem( item, false ); + invertMap.put( item, category ); + } + } + if ( items.size() > 0 ) + setSelectedIndex( 1 ); + } + + /* + * METHODS + */ + + public K getSelectedCategory() + { + final Object obj = getSelectedItem(); + return invertMap.get( obj ); + } + + public void setDisableIndex( final HashSet< Integer > set ) + { + categoryIndexSet.clear(); + for ( final Integer i : set ) + { + categoryIndexSet.add( i ); + } + } + + @Override + public void setPopupVisible( final boolean v ) + { + if ( !v && isCategoryIndex ) + { + isCategoryIndex = false; + } + else + { + super.setPopupVisible( v ); + } + } + + @SuppressWarnings( "unchecked" ) + @Override + public V getSelectedItem() + { + return ( V ) super.getSelectedItem(); + } + + @Override + public void setSelectedIndex( final int index ) + { + if ( categoryIndexSet.contains( index ) ) + { + isCategoryIndex = true; + } + else + { + super.setSelectedIndex( index ); + } + } + + /* + * PRIVATE METHODS + */ + + private void addItem( final Object anObject, final boolean isCategoryName ) + { + super.addItem( anObject ); + if ( isCategoryName ) + { + categoryIndexSet.add( getItemCount() - 1 ); + } + } + + /** + * Called at instantiation: prepare the {@link JComboBox} with correct + * listeners and logic for categories. + */ + private void init() + { + final ListCellRenderer< Object > r = getRenderer(); + setRenderer( new ListCellRenderer< Object >() + { + @Override + public Component getListCellRendererComponent( final JList< ? extends Object > list, final Object value, final int index, final boolean isSelected, final boolean cellHasFocus ) + { + JLabel c; + if ( categoryIndexSet.contains( index ) ) + { + c = ( JLabel ) r.getListCellRendererComponent( list, value, index, false, false ); + c.setEnabled( false ); + c.setText( categoryNames.get( value ) ); + } + else + { + c = ( JLabel ) r.getListCellRendererComponent( list, value, index, isSelected, cellHasFocus ); + c.setEnabled( true ); + c.setText( INDENT + itemNames.get( value ) ); + } + return c; + } + } ); + + final Action up = new AbstractAction() + { + private static final long serialVersionUID = 1L; + + @Override + public void actionPerformed( final ActionEvent e ) + { + final int si = getSelectedIndex(); + for ( int i = si - 1; i >= 0; i-- ) + { + if ( !categoryIndexSet.contains( i ) ) + { + setSelectedIndex( i ); + break; + } + } + } + }; + final Action down = new AbstractAction() + { + private static final long serialVersionUID = 1L; + + @Override + public void actionPerformed( final ActionEvent e ) + { + final int si = getSelectedIndex(); + for ( int i = si + 1; i < getModel().getSize(); i++ ) + { + if ( !categoryIndexSet.contains( i ) ) + { + setSelectedIndex( i ); + break; + } + } + } + }; + + final ActionMap am = getActionMap(); + am.put( "selectPrevious", up ); + am.put( "selectNext", down ); + final InputMap im = getInputMap(); + im.put( KeyStroke.getKeyStroke( KeyEvent.VK_UP, 0 ), "selectPrevious" ); + im.put( KeyStroke.getKeyStroke( KeyEvent.VK_KP_UP, 0 ), "selectPrevious" ); + im.put( KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, 0 ), "selectNext" ); + im.put( KeyStroke.getKeyStroke( KeyEvent.VK_KP_DOWN, 0 ), "selectNext" ); + } + + /** + * Demo + */ + public static void main( final String[] args ) + { + // + final List< String > fruits = new ArrayList< >( 5 ); + fruits.add( "Apple" ); + fruits.add( "Pear" ); + fruits.add( "Orange" ); + fruits.add( "Strawberry" ); + // + final List< String > cars = new ArrayList< >( 3 ); + cars.add( "Peugeot" ); + cars.add( "Ferrari" ); + cars.add( "Ford" ); + // + final List< String > computers = new ArrayList< >( 2 ); + computers.add( "PC" ); + computers.add( "Mac" ); + // + final LinkedHashMap< String, Collection< String >> items = new LinkedHashMap<>( 3 ); + items.put( "Fruits", fruits ); + items.put( "Cars", cars ); + items.put( "Computers", computers ); + // + final Map< String, String > itemNames = new HashMap< >(); + for ( final String key : items.keySet() ) + { + for ( final String string : items.get( key ) ) + { + itemNames.put( string, string ); + } + } + // + final Map< String, String > categoryNames = new HashMap< >(); + for ( final String key : items.keySet() ) + { + categoryNames.put( key, key ); + } + // Ouf! + + final CategoryJComboBox< String, String > cb = new CategoryJComboBox< >( items, itemNames, categoryNames ); + cb.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( final ActionEvent arg0 ) + { + System.out.println( "Selected " + cb.getSelectedItem() + " in category " + cb.getSelectedCategory() ); + } + } ); + + final JFrame frame = new JFrame(); + frame.getContentPane().add( cb ); + frame.setVisible( true ); + + } +} diff --git a/src/main/java/visualization/ColorByFeatureGUIPanel.java b/src/main/java/visualization/ColorByFeatureGUIPanel.java new file mode 100644 index 0000000..cc8e126 --- /dev/null +++ b/src/main/java/visualization/ColorByFeatureGUIPanel.java @@ -0,0 +1,548 @@ +package visualization; + + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; + +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import linkers.Model3D; +import utility.ActionListenablePanel; +import utility.ThreeDRoiobjectCollection; + + +public class ColorByFeatureGUIPanel extends ActionListenablePanel implements MinMaxAdjustable +{ + + /** The key for the manual painting style. */ + public static final String MANUAL_KEY = "MANUAL"; + + /** The key for the default, uniform painting style. */ + public static final String UNIFORM_KEY = "UNIFORM"; + + /** The name of the default, uniform painting style. */ + public static final String UNIFORM_NAME = "Uniform color"; + + /** The name of the manual painting style. */ + public static final String MANUAL_NAME = "Manual color"; + + /* + * ENUM + */ + + public static enum Category + { + SPOTS( "spots" ), EDGES( "edges" ), TRACKS( "tracks" ), DEFAULT( "Default" ); + + private String name; + + private Category( final String name ) + { + this.name = name; + } + + @Override + public String toString() + { + return name; + } + + } + + /* + * FIELDS + */ + + private static final long serialVersionUID = 1L; + + /** + * This action is fired when the feature to color in the + * "Set color by feature" JComboBox is changed. + */ + public final ActionEvent COLOR_FEATURE_CHANGED = new ActionEvent( this, 1, "ColorFeatureChanged" ); + + private JLabel jLabelSetColorBy; + + private CategoryJComboBox< Category, String > jComboBoxSetColorBy; + + private JComponent canvasColor; + + protected InterpolatePaintScale colorMap = InterpolatePaintScale.Jet; + + protected final Model3D model; + + private final List< Category > categories; + + private double min; + + private double max; + + private boolean autoMode = true; + + /* + * CONSTRUCTOR + */ + + public ColorByFeatureGUIPanel( final Model3D model, final List< Category > categories ) + { + super(); + this.model = model; + this.categories = categories; + initGUI(); + } + + /* + * PUBLIC METHODS + */ + + /** + * Forward the enabled flag to all components off this panel. + */ + @Override + public void setEnabled( final boolean enabled ) + { + jLabelSetColorBy.setEnabled( enabled ); + jComboBoxSetColorBy.setEnabled( enabled ); + canvasColor.setEnabled( enabled ); + } + + /** + * Returns a key to the color generator category selected in the combo box. + * Will be a {@link Category} enum type, as set in constructor. + * + * @return the selected category. + * @see #getColorFeature() + */ + public Category getColorGeneratorCategory() + { + return jComboBoxSetColorBy.getSelectedCategory(); + } + + /** + * Returns the selected feature in the combo box. + * + * @return the selected feature. + * @see #getColorGeneratorCategory() + */ + public String getColorFeature() + { + return jComboBoxSetColorBy.getSelectedItem(); + } + + public void setColorFeature( final String feature ) + { + if ( null == feature ) + { + jComboBoxSetColorBy.setSelectedItem( UNIFORM_KEY ); + } + else + { + jComboBoxSetColorBy.setSelectedItem( feature ); + } + } + + /* + * PRIVATE METHODS + */ + + /** + * Forward the 'color by feature' action to the caller of this GUI. + */ + private void colorByFeatureChanged() + { + super.fireAction( COLOR_FEATURE_CHANGED ); + } + + private void repaintColorCanvas( final Graphics g ) + { + if ( null == jComboBoxSetColorBy.getSelectedItem() + || getColorGeneratorCategory().equals( Category.DEFAULT ) + || ( getColorGeneratorCategory().equals( Category.SPOTS ) && getColorFeature().equals( ManualSpotColorAnalyzerFactory.FEATURE ) ) + || ( getColorGeneratorCategory().equals( Category.EDGES ) && getColorFeature().equals( ManualEdgeColorAnalyzer.FEATURE ) ) ) + { + g.clearRect( 0, 0, canvasColor.getWidth(), canvasColor.getHeight() ); + return; + } + + /* + * Compute min & max + */ + + final double[] values = getValues( jComboBoxSetColorBy ); + + if ( null == values ) + { + g.clearRect( 0, 0, canvasColor.getWidth(), canvasColor.getHeight() ); + return; + } + double dataMax = Float.NEGATIVE_INFINITY; + double dataMin = Float.POSITIVE_INFINITY; + double val; + for ( int i = 0; i < values.length; i++ ) + { + val = values[ i ]; + if ( val > dataMax ) + { + dataMax = val; + } + if ( val < dataMin ) + { + dataMin = val; + } + } + + if ( autoMode ) + { + min = dataMin; + max = dataMax; + } + + /* + * The color scale. + */ + + final float alphaMin = ( float ) ( ( min - dataMin ) / ( dataMax - dataMin ) ); + final float alphaMax = ( float ) ( ( max - dataMin ) / ( dataMax - dataMin ) ); + final int width = canvasColor.getWidth(); + final int height = canvasColor.getHeight(); + for ( int i = 0; i < width; i++ ) + { + final float alpha = ( float ) i / ( width - 1 ); + final float beta = ( alpha - alphaMin ) / ( alphaMax - alphaMin ); + + g.setColor( colorMap.getPaint( beta ) ); + g.drawLine( i, 0, i, height ); + } + + /* + * Print values as text. + */ + + g.setColor( Color.WHITE ); + final FontMetrics fm = g.getFontMetrics(); + + final Category category = jComboBoxSetColorBy.getSelectedCategory(); + final String feature = jComboBoxSetColorBy.getSelectedItem(); + boolean isInt; + switch ( category ) + { + case TRACKS: + isInt = model.getFeatureModel().getTrackFeatureIsInt().get( feature ); + break; + case EDGES: + isInt = model.getFeatureModel().getEdgeFeatureIsInt().get( feature ); + break; + case SPOTS: + isInt = model.getFeatureModel().getThreeDRoiobjectFeatureIsInt().get( feature ); + break; + default: + isInt = false; + } + + final String dataMinStr; + final String dataMaxStr; + final String minStr; + final String maxStr; + if ( isInt ) + { + dataMinStr = String.format( "%d", ( int ) dataMin ); + dataMaxStr = String.format( "%d", ( int ) dataMax ); + minStr = String.format( "%d", ( int ) min ); + maxStr = String.format( "%d", ( int ) max ); + } + else + { + dataMinStr = String.format( "%.1f", dataMin ); + dataMaxStr = String.format( "%.1f", dataMax ); + minStr = String.format( "%.1f", min ); + maxStr = String.format( "%.1f", max ); + } + + final int dataMinStrWidth = fm.stringWidth( dataMinStr ); + final int dataMaxStrWidth = fm.stringWidth( dataMaxStr ); + final int minStrWidth = fm.stringWidth( minStr ); + final int maxStrWidth = fm.stringWidth( maxStr ); + + g.drawString( dataMinStr, 1, height / 2 + fm.getHeight() / 2 ); + g.drawString( dataMaxStr, width - dataMaxStrWidth - 1, height / 2 + fm.getHeight() / 2 ); + + final int iMin = ( int ) ( ( width - 1 ) * ( min - dataMin ) / ( dataMax - dataMin ) ); + final int iMax = ( int ) ( ( width - 1 ) * ( max - dataMin ) / ( dataMax - dataMin ) ); + + if ( ( iMin - minStrWidth ) > dataMinStrWidth + 2 && iMin < ( width - dataMaxStrWidth - 2 ) ) + { + g.drawString( minStr, iMin - minStrWidth, height / 2 ); + } + if ( ( iMax + maxStrWidth ) < ( width - dataMaxStrWidth - 2 ) && iMax > dataMinStrWidth + 2 ) + { + g.drawString( maxStr, iMax, height / 2 ); + } + + } + + private void initGUI() + { + + { + final BorderLayout layout = new BorderLayout(); + setLayout( layout ); + this.setPreferredSize( new Dimension( 270, 45 ) ); + + final JPanel jPanelByFeature = new JPanel(); + final BoxLayout jPanelByFeatureLayout = new BoxLayout( jPanelByFeature, javax.swing.BoxLayout.X_AXIS ); + jPanelByFeature.setLayout( jPanelByFeatureLayout ); + jPanelByFeature.setPreferredSize( new java.awt.Dimension( 270, 25 ) ); + jPanelByFeature.setMaximumSize( new java.awt.Dimension( 32767, 25 ) ); + jPanelByFeature.setSize( 270, 25 ); + { + jPanelByFeature.add( Box.createHorizontalStrut( 5 ) ); + jLabelSetColorBy = new JLabel(); + jPanelByFeature.add( jLabelSetColorBy ); + jLabelSetColorBy.setText( "Set color by" ); + } + { + jComboBoxSetColorBy = createComboBoxSelector( categories ); + jPanelByFeature.add( Box.createHorizontalStrut( 5 ) ); + jPanelByFeature.add( Box.createHorizontalStrut( 5 ) ); + jPanelByFeature.add( jComboBoxSetColorBy ); + jComboBoxSetColorBy.addActionListener( new ActionListener() + { + @Override + public void actionPerformed( final ActionEvent e ) + { + new Thread( "ColorByFeatureGUIPanel color scale recalc Thread" ) + { + @Override + public void run() + { + if ( !autoMode && !jComboBoxSetColorBy.getSelectedCategory().equals( Category.DEFAULT ) ) + { + // Enforce recalculation of min & max when + // we change feature. + max = Float.NEGATIVE_INFINITY; + min = Float.POSITIVE_INFINITY; + final double[] values = getValues( jComboBoxSetColorBy ); + if ( null != values ) + { + for ( int i = 0; i < values.length; i++ ) + { + final double val = values[ i ]; + if ( val > max ) + { + max = val; + } + if ( val < min ) + { + min = val; + } + } + } + } + colorByFeatureChanged(); + canvasColor.repaint(); + } + }.start(); + } + } ); + } + add( jPanelByFeature, BorderLayout.CENTER ); + } + { + final JPanel jPanelColor = new JPanel(); + final BorderLayout jPanelColorLayout = new BorderLayout(); + add( jPanelColor, BorderLayout.SOUTH ); + jPanelColor.setPreferredSize( new java.awt.Dimension( 10, 20 ) ); + jPanelColor.setLayout( jPanelColorLayout ); + { + canvasColor = new JComponent() + { + private static final long serialVersionUID = -2174317490066575040L; + + @Override + public void paint( final Graphics g ) + { + repaintColorCanvas( g ); + } + }; + jPanelColor.add( canvasColor, BorderLayout.CENTER ); + canvasColor.addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( final MouseEvent e ) + { + canvasColor.repaint(); + } + } ); + } + } + { + addMouseListener( new MouseAdapter() + { + @Override + public void mouseClicked( final MouseEvent e ) + { + canvasColor.repaint(); + } + } ); + } + } + + /** + * Return the {@link CategoryJComboBox} that configures this selector. + * Subclasses can override this method to decide what items are in the combo + * box list. + * + * @return a new {@link CategoryJComboBox}. + */ + protected CategoryJComboBox< Category, String > createComboBoxSelector( final List< Category > lCategories ) + { + final LinkedHashMap< Category, Collection< String >> features = new LinkedHashMap<>( lCategories.size() ); + final HashMap< Category, String > categoryNames = new HashMap< >( lCategories.size() ); + final HashMap< String, String > featureNames = new HashMap< >(); + + for ( final Category category : lCategories ) + { + switch ( category ) + { + case SPOTS: + categoryNames.put( Category.SPOTS, "Spot features:" ); + final Collection< String > spotFeatures = new ArrayList< >( model.getFeatureModel().getThreeDRoiobjectFeatures()); + features.put( Category.SPOTS, spotFeatures ); + // Deal with manual coloring separately. + spotFeatures.remove( ManualSpotColorAnalyzerFactory.FEATURE ); + + featureNames.putAll( model.getFeatureModel().getThreeDRoiobjectFeatureNames() ); + break; + + case EDGES: + categoryNames.put( Category.EDGES, "Edge features:" ); + final Collection< String > edgeFeatures = new ArrayList< >( model.getFeatureModel().getEdgeFeatures() ); + // Deal with manual coloring separately. + edgeFeatures.remove( ManualEdgeColorAnalyzer.FEATURE ); + + features.put( Category.EDGES, edgeFeatures ); + featureNames.putAll( model.getFeatureModel().getEdgeFeatureNames() ); + break; + + case TRACKS: + categoryNames.put( Category.TRACKS, "Track features:" ); + final Collection< String > trackFeatures = model.getFeatureModel().getTrackFeatures(); + features.put( Category.TRACKS, trackFeatures ); + featureNames.putAll( model.getFeatureModel().getTrackFeatureNames() ); + break; + + case DEFAULT: + categoryNames.put( Category.DEFAULT, "Default:" ); + final Collection< String > defaultOptions = new ArrayList< >( 1 ); + defaultOptions.add( UNIFORM_KEY ); + defaultOptions.add( MANUAL_KEY ); + features.put( Category.DEFAULT, defaultOptions ); + featureNames.put( UNIFORM_KEY, UNIFORM_NAME ); + featureNames.put( MANUAL_KEY, MANUAL_NAME ); + break; + + default: + throw new IllegalArgumentException( "Unknown category: " + category ); + } + } + return new CategoryJComboBox< >( features, featureNames, categoryNames ); + } + + /** + * Returns the feature values for the item currently selected in the combo + * box. + * + * @param cb + * the {@link CategoryJComboBox} to interrogate. + * @return a new double array containing the feature values. + */ + protected double[] getValues( final CategoryJComboBox< Category, String > cb ) + { + + double[] values; + final Category category = cb.getSelectedCategory(); + final String feature = cb.getSelectedItem(); + switch ( category ) + { + case TRACKS: + values = model.getFeatureModel().getTrackFeatureValues( feature, true ); + break; + case EDGES: + values = model.getFeatureModel().getEdgeFeatureValues( feature, true ); + break; + case SPOTS: + final ThreeDRoiobjectCollection spots = model.getThreeDRoiobjects(); + values = spots.collectValues( feature, false ); + break; + case DEFAULT: + throw new IllegalArgumentException( "Cannot return values for " + category ); + default: + throw new IllegalArgumentException( "Unknown category: " + category ); + } + return values; + } + + @Override + public double getMin() + { + return min; + } + + @Override + public double getMax() + { + return max; + } + + @Override + public void setMinMax( final double min, final double max ) + { + this.min = min; + this.max = max; + } + + @Override + public void autoMinMax() + { + canvasColor.repaint(); + } + + @Override + public void setAutoMinMaxMode( final boolean autoMode ) + { + this.autoMode = autoMode; + } + + @Override + public boolean isAutoMinMaxMode() + { + return autoMode; + } + + @Override + public void setFrom( final MinMaxAdjustable minMaxAdjustable ) + { + setAutoMinMaxMode( minMaxAdjustable.isAutoMinMaxMode() ); + if ( !minMaxAdjustable.isAutoMinMaxMode() ) + { + setMinMax( minMaxAdjustable.getMin(), minMaxAdjustable.getMax() ); + } + } +} diff --git a/src/main/java/visualization/CovistoModelView.java b/src/main/java/visualization/CovistoModelView.java new file mode 100644 index 0000000..5be3e9f --- /dev/null +++ b/src/main/java/visualization/CovistoModelView.java @@ -0,0 +1,311 @@ +package visualization; + +import java.awt.Color; +import java.util.Map; + +import linkers.Model3D; +import linkers.TrackModel3D; +import utility.ThreeDRoiobject; + + + + +public interface CovistoModelView +{ + + /* + * KEY-VALUE CONSTANTS FOR LOOK & FILL CUSTOMIZATION + */ + + /* + * KEYS + */ + + /** + * Defines the key for the main color. Accepted values are color. + */ + public static final String KEY_COLOR = "Color"; + + /** + * Defines the key for the highlight color, used to paint selection. + * Accepted values are color. + */ + public static final String KEY_HIGHLIGHT_COLOR = "HighlightColor"; + + /** + * Defines the key for the track display mode. Possible values are + * {@link #TRACK_DISPLAY_MODE_WHOLE}, {@link #TRACK_DISPLAY_MODE_LOCAL}, + * {@link #TRACK_DISPLAY_MODE_LOCAL_BACKWARD}, + * {@value #TRACK_DISPLAY_MODE_LOCAL_FORWARD}, + * {@value #TRACK_DISPLAY_MODE_SELECTION_ONLY}. + */ + public static final String KEY_TRACK_DISPLAY_MODE = "TrackDisplaymode"; + + /** + * Defines the key for the track display depth. Values are integer, and they + * defines how many frames away the track can be seen from the current + * time-point. + */ + public static final String KEY_TRACK_DISPLAY_DEPTH = "TrackDisplayDepth"; + + /** + * Defines the key for the track visibility. Values are boolean. If + * false, tracks are not visible. + */ + public static final String KEY_TRACKS_VISIBLE = "TracksVisible"; + + /** + * Defines the key for the track coloring method. Values are concrete + * implementations of {@link TrackColorGenerator}. + */ + public static final String KEY_TRACK_COLORING = "TrackColoring"; + + /** + * Defines the key for the ThreeDRoiobject visibility. Values are boolean. If + * false, ThreeDRoiobjects are not visible. + * */ + public static final String KEY_ThreeDRoiobjectS_VISIBLE = "ThreeDRoiobjectsVisible"; + + /** + * Defines the key for the ThreeDRoiobject name display. Values are boolean. If + * false, ThreeDRoiobject names are not visible. + */ + public static final String KEY_DISPLAY_ThreeDRoiobject_NAMES = "DisplayThreeDRoiobjectNames"; + + /** + * Defines the key for the ThreeDRoiobject radius ratio. Value should be a positive + * {@link Double} object. ThreeDRoiobjects will be rendered with a radius equals to + * their actual radius multiplied by this ratio. + */ + public static final String KEY_ThreeDRoiobject_RADIUS_RATIO = "ThreeDRoiobjectRadiusRatio"; + + /** + * Defines the key for the ThreeDRoiobject coloring method. Accepted values are + * implementation of {@link FeatureColorGenerator} + */ + public static final String KEY_ThreeDRoiobject_COLORING = "ThreeDRoiobjectColoring"; + + /** + * Defines the key for the color map to use for painting overlay. Acceptable + * values are {@link InterpolatePaintScale}s, the default is + * {@link InterpolatePaintScale#Jet}. + */ + public static final String KEY_COLORMAP = "ColorMap"; + + /** + * Defines the key for the drawing depth, for views that can use this + * settings. The draw depth is the max distance between the view plane and + * any object for this object to be drawn. Accepted values are + * double. The values must be specified in image units. + */ + public static final String KEY_DRAWING_DEPTH = "DrawingDepth"; + + /** + * Defines the key for limiting the drawing depth. Values are boolean. If + * true, drawing depth will be limited by the values specified + * by {@link #KEY_DRAWING_DEPTH}. + */ + public static final String KEY_LIMIT_DRAWING_DEPTH = "LimitDrawingDepth"; + + /* + * VALUES + */ + + /** + * Track display mode where the whole tracks are drawn, ignoring the value + * of {@link #KEY_TRACK_DISPLAY_DEPTH}. + */ + public static final int TRACK_DISPLAY_MODE_WHOLE = 0; + + /** + * Track display mode where the only part of the tracks close to the current + * time-point are drawn backward and forward. Edges away from current time + * point are faded in the background. How much can be seen is defined by the + * value of {@link #KEY_TRACK_DISPLAY_DEPTH}. + */ + public static final int TRACK_DISPLAY_MODE_LOCAL = 1; + + /** + * Track display mode where the only part of the tracks close to the current + * time-point are drawn, backward only. How much can be seen is defined by + * the value of {@link #KEY_TRACK_DISPLAY_DEPTH}. + */ + public static final int TRACK_DISPLAY_MODE_LOCAL_BACKWARD = 2; + + /** + * Track display mode where the only part of the tracks close to the current + * time-point are drawn, forward only. How much can be seen is defined by + * the value of {@link #KEY_TRACK_DISPLAY_DEPTH}. + */ + public static final int TRACK_DISPLAY_MODE_LOCAL_FORWARD = 3; + + /** + * Track display mode similar to {@link #TRACK_DISPLAY_MODE_LOCAL}, except + * that for the sake of speed, edges are not faded. + */ + public static final int TRACK_DISPLAY_MODE_LOCAL_QUICK = 4; + + /** + * Track display mode similar to {@link #TRACK_DISPLAY_MODE_LOCAL_BACKWARD}, + * except that for the sake of speed, edges are not faded. + */ + public static final int TRACK_DISPLAY_MODE_LOCAL_BACKWARD_QUICK = 5; + + /** + * Track display mode similar to {@link #TRACK_DISPLAY_MODE_LOCAL_FORWARD}, + * except that for the sake of speed, edges are not faded. + */ + public static final int TRACK_DISPLAY_MODE_LOCAL_FORWARD_QUICK = 6; + + /** + * Track display mode where only the content of the current selection is + * displayed. + */ + public static final int TRACK_DISPLAY_MODE_SELECTION_ONLY = 7; + + /* + * DESCRIPTIONS + */ + + /** + * String that describe the corresponding track display mode. + */ + public static final String[] TRACK_DISPLAY_MODE_DESCRIPTION = new String[] { + "Show all entire tracks", + "Show local tracks", + "Show local tracks, backward", + "Show local tracks, forward", + "Local tracks (fast)", + "Local tracks, backward (fast)", + "Local tracks, forward (fast)", + "Show selection only" + }; + + /* + * DEFAULTS + */ + + /** + * The default color for ThreeDRoiobjects. + */ + public static final Color DEFAULT_ThreeDRoiobject_COLOR = new Color( 1f, 0, 1f ); + + /** + * The default track color. + */ + public static final Color DEFAULT_TRACK_COLOR = new Color( 250, 250, 0 ); + + /** + * The color to use to paint objects for which a feature is undefined. + * E.g. the numerical feature for this object calculated + * automatically, but its returned value is {@link Double#NaN}. + */ + public static final Color DEFAULT_UNDEFINED_FEATURE_COLOR = Color.BLACK; + + /** + * The color to use to paint objects with a manual feature that has not been + * assigned yet. E.g the coloring uses a manual feature, but this + * object did not receive a value yet. + */ + public static final Color DEFAULT_UNASSIGNED_FEATURE_COLOR = Color.GRAY.darker(); + + /** + * The default color for highlighting. + */ + public static final Color DEFAULT_HIGHLIGHT_COLOR = new Color( 0, 1f, 0 ); + + /** + * The default track display mode. + */ + public static final int DEFAULT_TRACK_DISPLAY_MODE = TRACK_DISPLAY_MODE_WHOLE; + + /** + * The default track display mode. + */ + public static final int DEFAULT_TRACK_DISPLAY_DEPTH = 10; + + /** + * The default color map. + */ + public static final InterpolatePaintScale DEFAULT_COLOR_MAP = InterpolatePaintScale.Jet; + + /** + * The default drawing depth, in image units. + */ + public static final double DEFAULT_DRAWING_DEPTH = 10d; + + /** + * The default drawing depth limitation mode.. + */ + public static final boolean DEFAULT_LIMIT_DRAWING_DEPTH = false; + + /* + * INTERFACE METHODS + */ + + /** + * Initializes this displayer and render it according to its concrete + * implementation. + */ + + + public void render(final int trackID); + /** + * Refreshes the displayer display with current model. If the underlying + * model was modified, or the display settings were changed, calling this + * method should be enough to update the display with changes. + * + * @see #setDisplaySettings(String, Object) + */ + public void refresh(); + + /** + * Removes any overlay (for ThreeDRoiobjects or tracks) from this displayer. + */ + public void clear(); + + /** + * Centers the view on the given ThreeDRoiobject. + */ + public void centerViewOn( final ThreeDRoiobject ThreeDRoiobject ); + + /** + * Returns the current display settings map. + */ + public Map< String, Object > getDisplaySettings(); + + /** + * Set a display parameter. + * + * @param key + * the key of the parameter to change. + * @param value + * the value for the display parameter + */ + + /** + * Returns the value of a specific display parameter. + */ + public Object getDisplaySettings( final String key); + + /** + * Returns the model displayed in this view. + */ + public Model3D getModel(); + + /** + * Returns the unique key that identifies this view. + *

      + * Careful: this key must be the same that for the + * {@link ViewFactory} that can instantiates this view. This is to + * facilitate saving/loading. + * + * @return the key, as a String. + */ + public String getKey(); + + void setDisplaySettings(String key, Object value, int trackID); + + + +} diff --git a/src/main/java/visualization/Draw3DLines.java b/src/main/java/visualization/Draw3DLines.java new file mode 100644 index 0000000..ec06baa --- /dev/null +++ b/src/main/java/visualization/Draw3DLines.java @@ -0,0 +1,94 @@ +package visualization; + +import java.awt.Color; +import java.awt.Graphics; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import ij.ImagePlus; +import ij.gui.Line; +import ij.gui.Overlay; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import net.imglib2.Cursor; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +public class Draw3DLines { + + + public final InteractiveMethods parent; + + public Draw3DLines(final InteractiveMethods parent) { + + this.parent = parent; + } + public void DrawLines(HashMap> trackidlist ) { + + + if (parent.imp == null) { + parent.imp = ImageJFunctions.show(parent.CurrentView); + + } + + else { + + final float[] pixels = (float[]) parent.imp.getProcessor().getPixels(); + final Cursor c = Views.iterable(parent.CurrentView).cursor(); + + for (int i = 0; i < pixels.length; ++i) + pixels[i] = c.next().get(); + + parent.imp.updateAndDraw(); + + } + Overlay o = parent.imp.getOverlay(); + + if( parent.imp.getOverlay() == null ) + { + o = new Overlay(); + parent.imp.setOverlay( o ); + } + + + + for(Map.Entry> current: trackidlist.entrySet()) { + + + ArrayList currenttrack = current.getValue(); + int trackID = current.getKey(); + + for(int index = 0; index < currenttrack.size() - 1; ++index ) { + + double[] XYZ = new double[]{currenttrack.get(index)[0], currenttrack.get(index)[1], currenttrack.get(index)[2]}; + + int time = (int)currenttrack.get(index)[3]; + + double[] XYZnext = new double[]{currenttrack.get(index + 1)[0], currenttrack.get(index + 1)[1], currenttrack.get(index + 1)[2]}; + + int timenext = (int)currenttrack.get(index + 1)[3]; + + + + if(parent.originalimg.numDimensions() > 3) { + + Line newline = new Line(XYZ[0], XYZ[1], XYZnext[0], XYZnext[1]); + newline.setStrokeColor(parent.colorTrack); + newline.setStrokeWidth(0.8); + + o.add(newline); + } + + } + + parent.imp.updateAndDraw(); + } + + + } + + +} diff --git a/src/main/java/visualization/DrawCircles.java b/src/main/java/visualization/DrawCircles.java new file mode 100644 index 0000000..3f6b1f6 --- /dev/null +++ b/src/main/java/visualization/DrawCircles.java @@ -0,0 +1,237 @@ +package visualization; + +import net.imglib2.Cursor; +import net.imglib2.IterableInterval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessible; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.Img; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.RealType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; + +public class DrawCircles { + + + + public static void drawCircle(RandomAccessibleIntervalimgout, double[] min, double[] max, double[] center, double radius) { + int n = imgout.numDimensions(); + double[] realpos = new double[n]; + double[] size = new double[n]; + double[] location = new double[n]; + double[] position = new double[n]; + double[] iniposition = new double[n]; + double[] backini = new double[n]; + double[] newpos = new double[n]; + double[] backpos = new double[n]; + double[] sigma = new double[n]; + final RandomAccess outbound = imgout.randomAccess(); + double stepsize = 0.1; + int[] setpos = new int[n]; + for (int d = 0; d < n; ++d) + size[d] = imgout.dimension(d); + + Cursor cursor = Views.iterable(imgout).localizingCursor(); + while (cursor.hasNext()) { + cursor.fwd(); + cursor.localize(location); + realpos = transformfwd(location, size, min, max); + + // To get a starting point on the circle + if (Math.pow(realpos[0] - center[0], 2) + Math.pow(realpos[1] - center[1], 2) + - radius * radius <= 1.0E-50) { + for (int d = 0; d < n; ++d) + position[d] = realpos[d]; + break; + + } + + } + + for (int d = 0; d < n; ++d) + iniposition[d] = position[d]; + + double initheta = Math.atan2(iniposition[1] - center[1], iniposition[0] - center[0]); + double increment = Math.acos((2 * radius * radius - stepsize * stepsize) / (2 * radius * radius)); + + backini = transformback(iniposition, size, min, max); + sigma[0] = 1; + sigma[1] = 1; + while (true) { + + // Move the current point along the curve + + newpos[0] = center[0] + radius * Math.cos((initheta - increment)); + newpos[1] = center[1] + radius * Math.sin((initheta - increment)); + initheta = Math.atan2(newpos[1] - center[1], newpos[0] - center[0]); + + // Transform the co-ordinates back as double[] + backpos = transformback(newpos, size, min, max); + + setpos[0] = (int) Math.round(backpos[0]); + setpos[1] = (int) Math.round(backpos[1]); + + // To set the pixel intensity + addGaussian(imgout, backpos, sigma); + + // To make sure that the values transformed back are not out of + // bounds + if (backpos[0] < imgout.realMax(0) - imgout.realMin(0) || backpos[0] > imgout.realMin(0) + || backpos[1] < imgout.realMax(1) - imgout.realMin(1) || backpos[1] > imgout.realMin(1)) + + outbound.setPosition(setpos); + + // Stopping criteria of moving along the circular arc + if (Math.abs(setpos[0] - (int) Math.round(backini[0])) == 0 + && Math.abs(setpos[1] - (int) Math.round(backini[1])) == 0) + break; + + // General Stopping criteria of moving along a curve, when we hit a + // boundary + if (newpos[0] >= max[0] || newpos[0] <= min[0] || newpos[1] >= max[1] || newpos[1] <= min[1]) + + break; + } + } + + public static > double[] transformfwd(double[] location, double[] size, double[] min, + double[] max) { + + int n = location.length; + + double[] delta = new double[n]; + + final double[] realpos = new double[n]; + + for (int d = 0; d < n; ++d){ + + delta[d] = (max[d] - min[d]) / size[d]; + + realpos[d] = location[d] * delta[d] + min[d]; + } + return realpos; + + } + + public static > double[] transformback(double[] location, double[] size, double[] min, + double[] max) { + + int n = location.length; + + double[] delta = new double[n]; + + final double[] realpos = new double[n]; + + for (int d = 0; d < n; ++d){ + + delta[d] = (max[d] - min[d]) / size[d]; + + realpos[d] = (location[d] - min[d]) / delta[d]; + } + return realpos; + + } + + final public static void addGaussian( final RandomAccessibleInterval< BitType > image, final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + final double[] two_sq_sigma = new double[ numDimensions ]; + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = 2 * getSuggestedKernelDiameter( sigma[ d ] ); + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ] / 2; + max[ d ] = min[ d ] + size[ d ] - 1; + two_sq_sigma[ d ] = sigma[ d ] * sigma[ d ]; + } + + final RandomAccessible< BitType > infinite = Views.extendZero( image ); + final RandomAccessibleInterval< BitType > interval = Views.interval( infinite, min, max ); + final IterableInterval< BitType > iterable = Views.iterable( interval ); + final Cursor< BitType > cursor = iterable.localizingCursor(); + + + + while ( cursor.hasNext() ) + { + cursor.fwd(); + + + + + + cursor.get().setOne();; + + + } + + + + + } + + + + + + + final public static void addGaussian( final IterableInterval< FloatType > image, final double Amplitude, + final double[] location, final double[] sigma) + { + final int numDimensions = image.numDimensions(); + final int[] size = new int[ numDimensions ]; + + final long[] min = new long[ numDimensions ]; + final long[] max = new long[ numDimensions ]; + + + for ( int d = 0; d < numDimensions; ++d ) + { + size[ d ] = getSuggestedKernelDiameter( sigma[ d ] ) * 2; + min[ d ] = (int)Math.round( location[ d ] ) - size[ d ]/2; + max[ d ] = min[ d ] + size[ d ] - 1; + + } + + + final Cursor< FloatType > cursor = image.localizingCursor(); + while ( cursor.hasNext() ) + { + cursor.fwd(); + + double value = Amplitude; + + for ( int d = 0; d < numDimensions; ++d ) + { + final double x = location[ d ] - cursor.getIntPosition( d ); + value *= Math.exp( -(x * x) / (sigma[ d ] * sigma[ d ] ) ); + } + + + cursor.get().set( cursor.get().get() + (float)value ); + + + } + + } + + public static int getSuggestedKernelDiameter( final double sigma ) + { + int size = 0; + int cutoff = 5; // This number means cutoff is chosen to be cutoff times sigma. + if ( sigma > 0 ) + size = Math.max( cutoff, ( 2 * ( int ) ( cutoff * sigma + 0.5 ) + 1 ) ); + + return size; + } + + + + +} diff --git a/src/main/java/visualization/DummyThreeDRoiobjectColorGenerator.java b/src/main/java/visualization/DummyThreeDRoiobjectColorGenerator.java new file mode 100644 index 0000000..7874ec9 --- /dev/null +++ b/src/main/java/visualization/DummyThreeDRoiobjectColorGenerator.java @@ -0,0 +1,75 @@ +package visualization; + + +import static visualization.CovistoModelView.DEFAULT_ThreeDRoiobject_COLOR; +import java.awt.Color; + +import utility.ThreeDRoiobject; + +/** + * A dummy spot color generator that always return the default color. + * + * @author Jean-Yves Tinevez - 2013 + */ +public class DummyThreeDRoiobjectColorGenerator implements FeatureColorGenerator< ThreeDRoiobject > +{ + + @Override + public Color color( final ThreeDRoiobject obj ) + { + return DEFAULT_ThreeDRoiobject_COLOR; + } + + @Override + public void setFeature( final String feature ) + {} + + @Override + public void terminate() + {} + + @Override + public void activate() + {} + + @Override + public String getFeature() + { + return ColorByFeatureGUIPanel.UNIFORM_KEY; + } + + @Override + public double getMin() + { + return Double.NaN; + } + + @Override + public double getMax() + { + return Double.NaN; + } + + @Override + public void setMinMax( final double min, final double max ) + {} + + @Override + public void autoMinMax() + {} + + @Override + public void setAutoMinMaxMode( final boolean autoMode ) + {} + + @Override + public boolean isAutoMinMaxMode() + { + return false; + } + + @Override + public void setFrom( final MinMaxAdjustable minMaxAdjustable ) + {} + +} diff --git a/src/main/java/visualization/DummyTrackColorGenerator.java b/src/main/java/visualization/DummyTrackColorGenerator.java new file mode 100644 index 0000000..c63696a --- /dev/null +++ b/src/main/java/visualization/DummyTrackColorGenerator.java @@ -0,0 +1,79 @@ +package visualization; + +import static visualization.CovistoModelView.DEFAULT_TRACK_COLOR; + +import java.awt.Color; + +import org.jgrapht.graph.DefaultWeightedEdge; + +/** + * A dummy track color generator that always return the default color. + * + * @author Jean-Yves Tinevez - 2013 + */ +public class DummyTrackColorGenerator implements TrackColorGenerator +{ + + @Override + public Color color( final DefaultWeightedEdge obj ) + { + return DEFAULT_TRACK_COLOR; + } + + @Override + public void setFeature( final String feature ) + {} + + @Override + public void terminate() + {} + + @Override + public void activate() + {} + + @Override + public void setCurrentTrackID( final Integer trackID ) + {} + + @Override + public String getFeature() + { + return ColorByFeatureGUIPanel.UNIFORM_KEY; + } + + @Override + public double getMin() + { + return Double.NaN; + } + + @Override + public double getMax() + { + return Double.NaN; + } + + @Override + public void setMinMax( final double min, final double max ) + {} + + @Override + public void autoMinMax() + {} + + @Override + public void setAutoMinMaxMode( final boolean autoMode ) + {} + + @Override + public boolean isAutoMinMaxMode() + { + return false; + } + + @Override + public void setFrom( final MinMaxAdjustable minMaxAdjustable ) + {} + +} diff --git a/src/main/java/visualization/FeatureColorGenerator.java b/src/main/java/visualization/FeatureColorGenerator.java new file mode 100644 index 0000000..1d4d5d2 --- /dev/null +++ b/src/main/java/visualization/FeatureColorGenerator.java @@ -0,0 +1,51 @@ +package visualization; + +import java.awt.Color; + +/** + * Interface for color generator that can color objects based on a + * feature identified by a String. + * @author Jean-Yves Tinevez - 2013 + * + * @param the type of object to color. + */ +public interface FeatureColorGenerator< K > extends MinMaxAdjustable +{ + + /** + * Returns a color for the given object. + * + * @param obj + * the object to color. + * @return a color for this object. + */ + public Color color( K obj ); + + /** + * Sets the feature to generate the color from. + * @param feature the feature. + */ + public void setFeature(String feature); + + /** + * Returns the feature that this color generator use. + * @return the feature set. + */ + public String getFeature(); + + /** + * When this color generator is replaced by another one, calling this method ensures + * that it gets correctly unregistered and cleaned, should it be a model listener + * or have a heavy memory footprint. + */ + public void terminate(); + + /** + * When this color generator is reused, calling this method ensures that it + * is registered again, and will follow whatever changes subsequently + * happening to the model. + */ + public void activate(); + + +} diff --git a/src/main/java/visualization/HyperStackDisplayer.java b/src/main/java/visualization/HyperStackDisplayer.java new file mode 100644 index 0000000..e69de29 diff --git a/src/main/java/visualization/InterpolatePaintScale.java b/src/main/java/visualization/InterpolatePaintScale.java new file mode 100644 index 0000000..492182e --- /dev/null +++ b/src/main/java/visualization/InterpolatePaintScale.java @@ -0,0 +1,147 @@ +package visualization; + +import java.awt.Color; +import java.io.Serializable; +import java.util.Set; +import java.util.TreeMap; + +import org.jfree.chart.renderer.PaintScale; + +/** + * This class implements a {@link PaintScale} that generate colors interpolated + * within a list of given color, using a linear scale. + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> - Sept 2010 + */ +public class InterpolatePaintScale implements PaintScale, Serializable +{ + + private static final long serialVersionUID = 2977884191627862512L; + private static final Color DEFAULT_COLOR = Color.BLACK; + private final double lowerBound; + private final double upperBound; + private final TreeMap colors = new TreeMap<>(); + private final Color defaultColor; + + /* + * INNER CLASSES + */ + + /** + * A {@link InterpolatePaintScale} that map a typical "Jet" colormap going from + * blue to red to the range [0, 1]. + */ + public static final InterpolatePaintScale Jet; + static { + Jet = new InterpolatePaintScale(0, 1); + Jet.add(0.00, new Color(0.0f, 0.0f, 1.0f)); + Jet.add(0.16, new Color(0.0f, 0.5f, 1.0f)); + Jet.add(0.33, new Color(0.0f, 1.0f, 1.0f)); + Jet.add(0.50, new Color(0.5f, 1.0f, 0.5f)); + Jet.add(0.66, new Color(1.0f, 1.0f, 0.0f)); + Jet.add(0.83, new Color(1.0f, 0.5f, 0.0f)); + Jet.add(1.00, new Color(1.0f, 0.0f, 0.0f)); + } + + + /* + * CONSTRUCTORS + */ + + /** + * Create a paint scale with given lower and upper bound, and a specified default color. + */ + public InterpolatePaintScale(final double lowerBound, final double upperBound, final Color defaultColor) { + this.lowerBound = lowerBound; + this.upperBound = upperBound; + this.defaultColor = defaultColor; + } + + /** + * Create a paint scale with a given lower and upper bound and a default black color. + */ + public InterpolatePaintScale(final double lowerBound, final double upperBound) { + this(lowerBound, upperBound, DEFAULT_COLOR); + } + + /** + * Create a paint scale with a lower bound of 0, an upper bound of 1 and a default + * black color. + */ + public InterpolatePaintScale() { + this(0, 1); + } + + /* + * PUBLIC METHODS + */ + + /** + * Add a color to the color list of this paint scale, at the position given by + * value. If value is greater than the upper bound or lower + * than the lower bound set at construction, this call will be ignored. + */ + public void add(final double value, final Color color) { + if (value > upperBound) return; + if (value < lowerBound) return; + colors.put(value, color); + } + + + @Override + public double getLowerBound() { + return lowerBound; + } + + /** + * Return a color interpolated within the color list of this paint scale. + * The interpolation is a linear one between the two colors in the list + * whose associated values frame the one given. + */ + @Override + public Color getPaint(double value) { + if (colors.isEmpty()) return defaultColor; + if (colors.size() == 1) return colors.get(colors.firstKey()); + + if (value > upperBound) + value = upperBound; + if (value < lowerBound) + value = lowerBound; + final Set keys = colors.keySet(); + double bottom = colors.firstKey(); + double top = colors.lastKey(); + for (final double key : keys) { + top = key; + if (value < key) + break; + + bottom = top; + } + + double alpha; + if (top == bottom) + alpha = 0; // we reached the end of the list + else + alpha = (value-bottom) / (top - bottom); + + final Color colorBottom = colors.get(bottom); + final Color colorTop = colors.get(top); + final int red = (int) ((1-alpha) * colorBottom.getRed() + alpha * colorTop.getRed()); + final int green = (int) ((1-alpha) * colorBottom.getGreen() + alpha * colorTop.getGreen()); + final int blue = (int) ((1-alpha) * colorBottom.getBlue() + alpha * colorTop.getBlue()); + return new Color(red, green, blue); + } + + @Override + public double getUpperBound() { + return upperBound; + } + + @Override + public InterpolatePaintScale clone() { + final InterpolatePaintScale ips = new InterpolatePaintScale(lowerBound, upperBound); + for(final double key : colors.keySet()) + ips.add(key, colors.get(key)); + return ips; + } + +} diff --git a/src/main/java/visualization/ManualEdgeColorAnalyzer.java b/src/main/java/visualization/ManualEdgeColorAnalyzer.java new file mode 100644 index 0000000..1b7aed3 --- /dev/null +++ b/src/main/java/visualization/ManualEdgeColorAnalyzer.java @@ -0,0 +1,157 @@ +package visualization; + + + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.ImageIcon; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.scijava.plugin.Plugin; + +import analyzers.EdgeAnalyzer; +import linkers.Dimension; +import linkers.Model3D; + +@Plugin( type = EdgeAnalyzer.class ) +public class ManualEdgeColorAnalyzer implements EdgeAnalyzer +{ + + public static final String FEATURE = "MANUAL_COLOR"; + + public static final String KEY = "MANUAL_EDGE_COLOR_ANALYZER"; + + static final List< String > FEATURES = new ArrayList< >( 1 ); + + static final Map< String, String > FEATURE_SHORT_NAMES = new HashMap< >( 1 ); + + static final Map< String, String > FEATURE_NAMES = new HashMap< >( 1 ); + + static final Map< String, Dimension > FEATURE_DIMENSIONS = new HashMap< >( 1 ); + + static final Map< String, Boolean > IS_INT = new HashMap< >( 1 ); + + static final String INFO_TEXT = "A dummy analyzer for the feature that stores the color manually assigned to each edge."; + + static final String NAME = "Manual edge color analyzer"; + + private static final Double DEFAULT_COLOR_VALUE = Double.valueOf( CovistoModelView.DEFAULT_UNASSIGNED_FEATURE_COLOR.getRGB() ); + + + static + { + FEATURES.add( FEATURE ); + FEATURE_SHORT_NAMES.put( FEATURE, "Edge color" ); + FEATURE_NAMES.put( FEATURE, "Manual edge color" ); + FEATURE_DIMENSIONS.put( FEATURE, Dimension.NONE ); + IS_INT.put( FEATURE, Boolean.TRUE ); + } + + private long processingTime; + + @Override + public long getProcessingTime() + { + return processingTime; + } + + @Override + public String getKey() + { + return KEY; + } + + @Override + public List< String > getFeatures() + { + return FEATURES; + } + + @Override + public Map< String, String > getFeatureShortNames() + { + return FEATURE_SHORT_NAMES; + } + + @Override + public Map< String, String > getFeatureNames() + { + return FEATURE_NAMES; + } + + @Override + public Map< String, Dimension > getFeatureDimensions() + { + return FEATURE_DIMENSIONS; + } + + @Override + public Map< String, Boolean > getIsIntFeature() + { + return IS_INT; + } + + @Override + public void setNumThreads() + {} + + @Override + public void setNumThreads( final int numThreads ) + {} + + @Override + public int getNumThreads() + { + return 1; + } + + @Override + public String getInfoText() + { + return INFO_TEXT; + } + + + @Override + public ImageIcon getIcon() + { + return null; + } + + @Override + public String getName() + { + return NAME; + } + + @Override + public void process( final Collection< DefaultWeightedEdge > edges, final Model3D model ) + { + final long start = System.currentTimeMillis(); + for ( final DefaultWeightedEdge edge : edges ) + { + if ( null == model.getFeatureModel().getEdgeFeature( edge, FEATURE ) ) + { + model.getFeatureModel().putEdgeFeature( edge, FEATURE, DEFAULT_COLOR_VALUE ); + } + } + final long end = System.currentTimeMillis(); + processingTime = end - start; + } + + @Override + public boolean isLocal() + { + return true; + } + + @Override + public boolean isManualFeature() + { + return true; + } +} diff --git a/src/main/java/visualization/ManualSpotColorAnalyzerFactory.java b/src/main/java/visualization/ManualSpotColorAnalyzerFactory.java new file mode 100644 index 0000000..38997e4 --- /dev/null +++ b/src/main/java/visualization/ManualSpotColorAnalyzerFactory.java @@ -0,0 +1,160 @@ +package visualization; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.ImageIcon; + +import net.imagej.ImgPlus; +import net.imglib2.type.NativeType; +import net.imglib2.type.numeric.RealType; +import utility.ThreeDRoiobject; +import utility.ThreeDRoiobjectAnalyzer; +import utility.ThreeDRoiobjectAnalyzerFactory; + +import org.scijava.plugin.Plugin; + +import linkers.Dimension; +import linkers.Model3D; + + + +@Plugin( type = ThreeDRoiobjectAnalyzerFactory.class ) +public class ManualSpotColorAnalyzerFactory< T extends RealType< T > & NativeType< T >> implements ThreeDRoiobjectAnalyzerFactory< T > +{ + + public static final String FEATURE = "MANUAL_COLOR"; + + public static final String KEY = "MANUAL_SPOT_COLOR_ANALYZER"; + + static final List< String > FEATURES = new ArrayList< >( 1 ); + + static final Map< String, String > FEATURE_SHORT_NAMES = new HashMap< >( 1 ); + + static final Map< String, String > FEATURE_NAMES = new HashMap< >( 1 ); + + static final Map< String, Dimension > FEATURE_DIMENSIONS = new HashMap< >( 1 ); + + static final Map< String, Boolean > IS_INT = new HashMap< >( 1 ); + + static final String INFO_TEXT = "A dummy analyzer for the feature that stores the color manually assigned to each spot."; + + static final String NAME = "Manual spot color analyzer"; + + private static final Double DEFAULT_COLOR_VALUE = Double.valueOf( CovistoModelView.DEFAULT_UNASSIGNED_FEATURE_COLOR.getRGB() ); + + static + { + FEATURES.add( FEATURE ); + FEATURE_SHORT_NAMES.put( FEATURE, "Spot color" ); + FEATURE_NAMES.put( FEATURE, "Manual spot color" ); + FEATURE_DIMENSIONS.put( FEATURE, Dimension.NONE ); + IS_INT.put( FEATURE, Boolean.TRUE ); + } + + @Override + public String getKey() + { + return KEY; + } + + @Override + public List< String > getFeatures() + { + return FEATURES; + } + + @Override + public Map< String, String > getFeatureShortNames() + { + return FEATURE_SHORT_NAMES; + } + + @Override + public Map< String, String > getFeatureNames() + { + return FEATURE_NAMES; + } + + @Override + public Map< String, Dimension > getFeatureDimensions() + { + return FEATURE_DIMENSIONS; + } + + @Override + public String getInfoText() + { + return INFO_TEXT; + } + + @Override + public Map< String, Boolean > getIsIntFeature() + { + return IS_INT; + } + + @Override + public boolean isManualFeature() + { + return true; + } + + @Override + public ImageIcon getIcon() + { + return null; + } + + @Override + public String getName() + { + return NAME; + } + + @Override + public ThreeDRoiobjectAnalyzer< T > getAnalyzer( final Model3D model, final ImgPlus< T > img, final int frame, final int channel ) + { + return new ThreeDRoiobjectAnalyzer< T >() + { + + private long processingTime; + + @Override + public boolean checkInput() + { + return true; + } + + @Override + public boolean process() + { + final long start = System.currentTimeMillis(); + for ( final ThreeDRoiobject spot : model.getThreeDRoiobjects().iterable( false ) ) + { + if ( null == spot.getFeature( FEATURE ) ) + { + spot.putFeature( FEATURE, DEFAULT_COLOR_VALUE ); + } + } + final long end = System.currentTimeMillis(); + processingTime = end - start; + return true; + } + + @Override + public String getErrorMessage() + { + return ""; + } + + @Override + public long getProcessingTime() + { + return processingTime; + } + }; + } +} diff --git a/src/main/java/visualization/MinMaxAdjustable.java b/src/main/java/visualization/MinMaxAdjustable.java new file mode 100644 index 0000000..42d3970 --- /dev/null +++ b/src/main/java/visualization/MinMaxAdjustable.java @@ -0,0 +1,76 @@ +package visualization; + + +/** + * Simple interface for objects whose effects depend on a minimal and maximal + * double value that can be adjusted manually or automatically. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> Mar 20, 2014 + * + */ +public interface MinMaxAdjustable +{ + /** + * Returns the min value currently set. + * + * @return the min value. + */ + public double getMin(); + + /** + * Returns the max value currently set. + * + * @return the max value. + */ + public double getMax(); + + /** + * Sets the min and max of this object. + * + * @param min + * the min value. + * @param max + * the max value. + */ + public void setMinMax( double min, double max ); + + /** + * Automatically computes the min & max values from this object current + * content. + */ + public void autoMinMax(); + + /** + * Sets the behavior of this object regarding the min & max calculation. + *

      + * If the flag passed is true, then the min & max will be + * recalculated by the instance everytime it is meaningful to do so, by + * calling the {@link #autoMinMax()} method. + *

      + * If false, the min & max will not be recalculated. They + * will keep their current value unless {@link #setMinMax(double, double)} + * is called. + * + * @param autoMode + * the behavior flag. + */ + public void setAutoMinMaxMode( boolean autoMode ); + + /** + * Returns whether the automatic scaling mode is activated or not for this + * object. + * + * @return true if the automatic scaling mode is on. + */ + public boolean isAutoMinMaxMode(); + + /** + * Copies the min, max and auto fields from the specified + * {@link MinMaxAdjustable} to this object. + *

      + * Not that if {@link #isAutoMinMaxMode()} is set to true, the + * min and max value will not be copied over. + */ + public void setFrom( MinMaxAdjustable minMaxAdjustable ); + +} diff --git a/src/main/java/visualization/NearestRoi.java b/src/main/java/visualization/NearestRoi.java new file mode 100644 index 0000000..5dfdc1c --- /dev/null +++ b/src/main/java/visualization/NearestRoi.java @@ -0,0 +1,72 @@ +package visualization; + +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import ij.gui.OvalRoi; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import linkers.FlagNode; +import linkers.NNFlagsearchKDtree; +import net.imglib2.KDTree; +import net.imglib2.RealPoint; +import utility.PreRoiobject; + +public class NearestRoi { + + + public static Roi getNearestRois(ArrayList roi, double[] Clickedpoint, final InteractiveMethods parent ) { + + + + Roi[] Allrois = new Roi[roi.size()]; + + for(int index = 0; index < roi.size(); ++index) { + + Allrois[index] = roi.get(index).rois; + } + + Roi KDtreeroi = null; + + final List targetCoords = new ArrayList(Allrois.length); + final List> targetNodes = new ArrayList>(Allrois.length); + for (int index = 0; index < Allrois.length; ++index) { + + Roi r = Allrois[index]; + Rectangle rect = r.getBounds(); + + targetCoords.add( new RealPoint(rect.x + rect.width/2.0, rect.y + rect.height/2.0 ) ); + + + targetNodes.add(new FlagNode(Allrois[index])); + + } + + if (targetNodes.size() > 0 && targetCoords.size() > 0) { + + final KDTree> Tree = new KDTree>(targetNodes, targetCoords); + + final NNFlagsearchKDtree Search = new NNFlagsearchKDtree(Tree); + + + final double[] source = Clickedpoint; + final RealPoint sourceCoords = new RealPoint(source); + Search.search(sourceCoords); + final FlagNode targetNode = Search.getSampler().get(); + + KDtreeroi = targetNode.getValue(); + + } + + return KDtreeroi; + + } + + + + + + +} diff --git a/src/main/java/visualization/SelectionChangeEvent.java b/src/main/java/visualization/SelectionChangeEvent.java new file mode 100644 index 0000000..d979c27 --- /dev/null +++ b/src/main/java/visualization/SelectionChangeEvent.java @@ -0,0 +1,84 @@ +package visualization; + +import java.util.EventObject; +import java.util.Map; + +import org.jgrapht.graph.DefaultWeightedEdge; + +import utility.ThreeDRoiobject; + + +/** + * An event that characterizes a change in the current selection. + * {@link ThreeDRoiobject} selection and {@link DefaultWeightedEdge} selection are dealt with separately, + * to keep the use of this class general. + */ +public class SelectionChangeEvent extends EventObject { + + private static final long serialVersionUID = -8920831578922412606L; + + /** Changes in {@link DefaultWeightedEdge} selection this event represents. */ + private final Map edges; + + /** Changes in {@link ThreeDRoiobject} selection this event represents. */ + protected Map ThreeDRoiobjects; + + /* + * CONSTRUCTORS + */ + + /** + * Represents a change in the selection of a displayed TM model. + *

      + * Two maps are given. The first one represent changes in the ThreeDRoiobject + * selection. The {@link Boolean} mapped to a {@link ThreeDRoiobject} key specifies if + * the ThreeDRoiobject was added to the selection (true) or removed from + * it (false). The same goes for the + * {@link DefaultWeightedEdge} map. nulls are accepted for the + * two maps, to specify that no changes happened for the corresponding type. + * + * @param source + * the source object that fires this event. + * @param ThreeDRoiobjects + * the ThreeDRoiobjects that are added or removed from the selection by this + * event. + * @param edges + * the edges that are added or removed from the selection by this + * event. + */ + public SelectionChangeEvent(final Object source, final Map ThreeDRoiobjects, final Map edges) { + super(source); + this.ThreeDRoiobjects = ThreeDRoiobjects; + this.edges = edges; + } + + /* + * METHODS + */ + + /** + * Returns the ThreeDRoiobjects that have been added or removed from the selection. + * The {@link Boolean} + * mapped to a {@link ThreeDRoiobject} key specifies if the ThreeDRoiobject was added to the selection (true) + * or removed from it (false). + * @return added or removed ThreeDRoiobjects, can be null if no changes on ThreeDRoiobject selection happened. + */ + public Map getThreeDRoiobjects() { + return ThreeDRoiobjects; + } + + /** + * Returns the edges that have been added or removed from the selection. + * The {@link Boolean} mapped to a {@link DefaultWeightedEdge} key specifies + * if the edge was added to the selection (true) + * or removed from it (false). + * @return added or removed edges, can be null if no changes on edge selection happened. + */ + public Map getEdges() { + return edges; + } + + + + +} diff --git a/src/main/java/visualization/SelectionChangeListener.java b/src/main/java/visualization/SelectionChangeListener.java new file mode 100644 index 0000000..2cbbf52 --- /dev/null +++ b/src/main/java/visualization/SelectionChangeListener.java @@ -0,0 +1,22 @@ +/** + * + */ +package visualization; + +import java.util.EventListener; + +/** + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> - Jan 29, 2011 + */ +public interface SelectionChangeListener extends EventListener { + + /** + * Called whenever the value of the selection changes. + * @param event the event that characterizes the change. + */ + public void selectionChanged(SelectionChangeEvent event); + + public void selectionChanged(SelectionChangeEvent event, int trackID); + + +} diff --git a/src/main/java/visualization/SelectionModel.java b/src/main/java/visualization/SelectionModel.java new file mode 100644 index 0000000..5b45c24 --- /dev/null +++ b/src/main/java/visualization/SelectionModel.java @@ -0,0 +1,320 @@ +package visualization; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.traverse.GraphIterator; + +import linkers.Model3D; +import utility.ThreeDRoiobject; + +/** + * A component of {@link Model} that handles ThreeDRoiobject and edges selection. + * @author Jean-Yves Tinevez + */ +public class SelectionModel { + + private static final boolean DEBUG = false; + + /** The ThreeDRoiobject current selection. */ + private Set ThreeDRoiobjectSelection = new HashSet<>(); + /** The edge current selection. */ + private Set edgeSelection = new HashSet<>(); + /** The list of listener listening to change in selection. */ + private List selectionChangeListeners = new ArrayList<>(); + + private final Model3D model; + + /* + * DEFAULT VISIBILITY CONSTRUCTOR + */ + + public SelectionModel(Model3D parent) { + this.model = parent; + } + + /* + * DEAL WITH SELECTION CHANGE LISTENER + */ + + public boolean addSelectionChangeListener(SelectionChangeListener listener) { + return selectionChangeListeners.add(listener); + } + + public boolean removeSelectionChangeListener(SelectionChangeListener listener) { + return selectionChangeListeners.remove(listener); + } + + public List getSelectionChangeListener() { + return selectionChangeListeners; + } + + /* + * SELECTION CHANGES + */ + + public void clearSelection() { + if (DEBUG) + System.out.println("[SelectionModel] Clearing selection"); + // Prepare event + Map ThreeDRoiobjectMap = new HashMap<>(ThreeDRoiobjectSelection.size()); + for (ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectSelection) + ThreeDRoiobjectMap.put(ThreeDRoiobject, false); + Map edgeMap = new HashMap<>(edgeSelection.size()); + for (DefaultWeightedEdge edge : edgeSelection) + edgeMap.put(edge, false); + SelectionChangeEvent event = new SelectionChangeEvent(this, ThreeDRoiobjectMap, edgeMap); + // Clear fields + clearThreeDRoiobjectSelection(); + clearEdgeSelection(); + // Fire event + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void clearThreeDRoiobjectSelection() { + if (DEBUG) + System.out.println("[SelectionModel] Clearing ThreeDRoiobject selection"); + // Prepare event + Map ThreeDRoiobjectMap = new HashMap<>(ThreeDRoiobjectSelection.size()); + for (ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectSelection) + ThreeDRoiobjectMap.put(ThreeDRoiobject, false); + SelectionChangeEvent event = new SelectionChangeEvent(this, ThreeDRoiobjectMap, null); + // Clear field + ThreeDRoiobjectSelection.clear(); + // Fire event + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void clearEdgeSelection() { + if (DEBUG) + System.out.println("[SelectionModel] Clearing edge selection"); + // Prepare event + Map edgeMap = new HashMap<>(edgeSelection.size()); + for (DefaultWeightedEdge edge : edgeSelection) + edgeMap.put(edge, false); + SelectionChangeEvent event = new SelectionChangeEvent(this, null, edgeMap); + // Clear field + edgeSelection.clear(); + // Fire event + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void addThreeDRoiobjectToSelection(final ThreeDRoiobject ThreeDRoiobject) { + if (!ThreeDRoiobjectSelection.add(ThreeDRoiobject)) + return; // Do nothing if already present in selection + if (DEBUG) + System.out.println("[SelectionModel] Adding ThreeDRoiobject " + ThreeDRoiobject + " to selection"); + Map ThreeDRoiobjectMap = new HashMap<>(1); + ThreeDRoiobjectMap.put(ThreeDRoiobject, true); + if (DEBUG) + System.out.println("[SelectionModel] Seding event to listeners: "+selectionChangeListeners); + SelectionChangeEvent event = new SelectionChangeEvent(this, ThreeDRoiobjectMap, null); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void removeThreeDRoiobjectFromSelection(final ThreeDRoiobject ThreeDRoiobject) { + if (!ThreeDRoiobjectSelection.remove(ThreeDRoiobject)) + return; // Do nothing was not already present in selection + if (DEBUG) + System.out.println("[SelectionModel] Removing ThreeDRoiobject " + ThreeDRoiobject + " from selection"); + Map ThreeDRoiobjectMap = new HashMap<>(1); + ThreeDRoiobjectMap.put(ThreeDRoiobject, false); + SelectionChangeEvent event = new SelectionChangeEvent(this, ThreeDRoiobjectMap, null); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void addThreeDRoiobjectToSelection(final Collection ThreeDRoiobjects) { + Map ThreeDRoiobjectMap = new HashMap<>(ThreeDRoiobjects.size()); + for (ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects) { + if (ThreeDRoiobjectSelection.add(ThreeDRoiobject)) { + ThreeDRoiobjectMap.put(ThreeDRoiobject, true); + if (DEBUG) + System.out.println("[SelectionModel] Adding ThreeDRoiobject " + ThreeDRoiobject + " to selection"); + } + } + SelectionChangeEvent event = new SelectionChangeEvent(this, ThreeDRoiobjectMap, null); + if (DEBUG) + System.out.println("[SelectionModel] Seding event "+event.hashCode()+" to "+selectionChangeListeners.size()+" listeners: "+selectionChangeListeners); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void removeThreeDRoiobjectFromSelection(final Collection ThreeDRoiobjects) { + Map ThreeDRoiobjectMap = new HashMap<>(ThreeDRoiobjects.size()); + for (ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjects) { + if (ThreeDRoiobjectSelection.remove(ThreeDRoiobject)) { + ThreeDRoiobjectMap.put(ThreeDRoiobject, false); + if (DEBUG) + System.out.println("[SelectionModel] Removing ThreeDRoiobject " + ThreeDRoiobject + " from selection"); + } + } + SelectionChangeEvent event = new SelectionChangeEvent(this, ThreeDRoiobjectMap, null); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void addEdgeToSelection(final DefaultWeightedEdge edge) { + if (!edgeSelection.add(edge)) + return; // Do nothing if already present in selection + if (DEBUG) + System.out.println("[SelectionModel] Adding edge " + edge + " to selection"); + Map edgeMap = new HashMap<>(1); + edgeMap.put(edge, true); + SelectionChangeEvent event = new SelectionChangeEvent(this, null, edgeMap); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + + } + + public void removeEdgeFromSelection(final DefaultWeightedEdge edge) { + if (!edgeSelection.remove(edge)) + return; // Do nothing if already present in selection + if (DEBUG) + System.out.println("[SelectionModel] Removing edge " + edge + " from selection"); + Map edgeMap = new HashMap<>(1); + edgeMap.put(edge, false); + SelectionChangeEvent event = new SelectionChangeEvent(this, null, edgeMap); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + + } + + public void addEdgeToSelection(final Collection edges) { + Map edgeMap = new HashMap<>(edges.size()); + for (DefaultWeightedEdge edge : edges) { + if (edgeSelection.add(edge)) { + edgeMap.put(edge, true); + if (DEBUG) + System.out.println("[SelectionModel] Adding edge " + edge + " to selection"); + } + } + SelectionChangeEvent event = new SelectionChangeEvent(this, null, edgeMap); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public void removeEdgeFromSelection(final Collection edges) { + Map edgeMap = new HashMap<>(edges.size()); + for (DefaultWeightedEdge edge : edges) { + if (edgeSelection.remove(edge)) { + edgeMap.put(edge, false); + if (DEBUG) + System.out.println("[SelectionModel] Removing edge " + edge + " from selection"); + } + } + SelectionChangeEvent event = new SelectionChangeEvent(this, null, edgeMap); + for (SelectionChangeListener listener : selectionChangeListeners) + listener.selectionChanged(event); + } + + public Set getThreeDRoiobjectSelection() { + return ThreeDRoiobjectSelection; + } + + public Set getEdgeSelection() { + return edgeSelection; + } + + /* + * SPECIAL METHODS + */ + + + /** + * Search and add all ThreeDRoiobjects and links belonging to the same track(s) that of given ThreeDRoiobjects and + * edges to current selection. A direction parameter allow specifying + * whether we should include only parts upwards in time, downwards in time or all the way through. + * @param ThreeDRoiobjects the ThreeDRoiobjects to include in search + * @param edges the edges to include in search + * @param direction the direction to go when searching. Positive integers will result in searching + * upwards in time, negative integers downwards in time and 0 all the way through. + */ + public void selectTrack(final Collection ThreeDRoiobjects, final Collection edges, final int direction) { + + HashSet inspectionThreeDRoiobjects = new HashSet<>(ThreeDRoiobjects); + + for(DefaultWeightedEdge edge : edges) { + // We add connected ThreeDRoiobjects to the list of ThreeDRoiobjects to inspect + inspectionThreeDRoiobjects.add(model.getTrackModel().getEdgeSource(edge)); + inspectionThreeDRoiobjects.add(model.getTrackModel().getEdgeTarget(edge)); + } + + // Walk across tracks to build selection + final HashSet lThreeDRoiobjectSelection = new HashSet<>(); + final HashSet lEdgeSelection = new HashSet<>(); + + if (direction == 0) { // Unconditionally + for (ThreeDRoiobject ThreeDRoiobject : inspectionThreeDRoiobjects) { + lThreeDRoiobjectSelection.add(ThreeDRoiobject); + GraphIterator walker = model.getTrackModel().getDepthFirstIterator(ThreeDRoiobject, false); + while (walker.hasNext()) { + ThreeDRoiobject target = walker.next(); + lThreeDRoiobjectSelection.add(target); + // Deal with edges + Set targetEdges = model.getTrackModel().edgesOf(target); + for(DefaultWeightedEdge targetEdge : targetEdges) { + lEdgeSelection.add(targetEdge); + } + } + } + + } else { // Only upward or backward in time + for (ThreeDRoiobject ThreeDRoiobject : inspectionThreeDRoiobjects) { + lThreeDRoiobjectSelection.add(ThreeDRoiobject); + + // A bit more complicated: we want to walk in only one direction, + // when branching is occurring, we do not want to get back in time. + Stack stack = new Stack<>(); + stack.add(ThreeDRoiobject); + while (!stack.isEmpty()) { + ThreeDRoiobject inspected = stack.pop(); + Set targetEdges = model.getTrackModel().edgesOf(inspected); + for(DefaultWeightedEdge targetEdge : targetEdges) { + ThreeDRoiobject other; + if (direction > 0) { + // Upward in time: we just have to search through edges using their source ThreeDRoiobjects + other = model.getTrackModel().getEdgeSource(targetEdge); + } else { + other = model.getTrackModel().getEdgeTarget(targetEdge); + } + + if (other != inspected) { + lThreeDRoiobjectSelection.add(other); + lEdgeSelection.add(targetEdge); + stack.add(other); + } + } + } + } + } + + // Cut "tail": remove the first an last edges in time, so that the selection only has conencted + // edges in it. + ArrayList edgesToRemove = new ArrayList<>(); + for(DefaultWeightedEdge edge : lEdgeSelection) { + ThreeDRoiobject source = model.getTrackModel().getEdgeSource(edge); + ThreeDRoiobject target = model.getTrackModel().getEdgeTarget(edge); + if ( !(lThreeDRoiobjectSelection.contains(source) && lThreeDRoiobjectSelection.contains(target)) ) { + edgesToRemove.add(edge); + } + } + lEdgeSelection.removeAll(edgesToRemove); + + // Set selection + addThreeDRoiobjectToSelection(lThreeDRoiobjectSelection); + addEdgeToSelection(lEdgeSelection); + } + +} diff --git a/src/main/java/visualization/ThreeDRoiobjectOverlay.java b/src/main/java/visualization/ThreeDRoiobjectOverlay.java new file mode 100644 index 0000000..32226f7 --- /dev/null +++ b/src/main/java/visualization/ThreeDRoiobjectOverlay.java @@ -0,0 +1,279 @@ +package visualization; + +import static visualization.CovistoModelView.KEY_ThreeDRoiobject_COLORING; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + + +import ij.ImagePlus; +import ij.gui.Roi; +import linkers.Model3D; +import utility.CovistoUtils; +import utility.ThreeDRoiobject; +import utility.ThreeDRoiobjectCollection; + +/** + * The overlay class in charge of drawing the ThreeDRoiobject images on the hyperstack + * window. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> 2010 - 2011 + */ +public class ThreeDRoiobjectOverlay extends Roi +{ + + private static final long serialVersionUID = 1L; + + private static final Font LABEL_FONT = new Font( "Arial", Font.BOLD, 12 ); + + private static final boolean DEBUG = false; + + public ThreeDRoiobject editingThreeDRoiobject; + + protected final double[] calibration; + + protected Composite composite = AlphaComposite.getInstance( AlphaComposite.SRC_OVER ); + + protected FontMetrics fm; + + protected Collection< ThreeDRoiobject > ThreeDRoiobjectSelection = new ArrayList<>(); + + protected Map< String, Object > displaySettings; + + protected final Model3D model; + + /* + * CONSTRUCTOR + */ + + public ThreeDRoiobjectOverlay( final Model3D model, final ImagePlus imp, final Map< String, Object > displaySettings ) + { + super( 0, 0, imp ); + this.model = model; + this.imp = imp; + this.calibration = CovistoUtils.getSpatialCalibration( imp ); + this.displaySettings = displaySettings; + } + + /* + * METHODS + */ + + @Override + public void drawOverlay( final Graphics g ) + { + final int xcorner = ic.offScreenX( 0 ); + final int ycorner = ic.offScreenY( 0 ); + final double magnification = getMagnification(); + final ThreeDRoiobjectCollection ThreeDRoiobjects = model.getThreeDRoiobjects(); + + final boolean ThreeDRoiobjectVisible = ( Boolean ) displaySettings.get( CovistoModelView.KEY_ThreeDRoiobjectS_VISIBLE ); + if ( !ThreeDRoiobjectVisible || ThreeDRoiobjects.getNThreeDRoiobjects( true ) == 0 ) { + return; + } + + final boolean doLimitDrawingDepth = ( Boolean ) displaySettings.get( CovistoModelView.KEY_LIMIT_DRAWING_DEPTH ); + final double drawingDepth = ( Double ) displaySettings.get( CovistoModelView.KEY_DRAWING_DEPTH ); + final int trackDisplayMode = ( Integer ) displaySettings.get( CovistoModelView.KEY_TRACK_DISPLAY_MODE ); + final boolean selectionOnly = ( trackDisplayMode == CovistoModelView.TRACK_DISPLAY_MODE_SELECTION_ONLY ); + + final Graphics2D g2d = ( Graphics2D ) g; + // Save graphic device original settings + final AffineTransform originalTransform = g2d.getTransform(); + final Composite originalComposite = g2d.getComposite(); + final Stroke originalStroke = g2d.getStroke(); + final Color originalColor = g2d.getColor(); + final Font originalFont = g2d.getFont(); + + g2d.setComposite( composite ); + g2d.setFont( LABEL_FONT ); + g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + fm = g2d.getFontMetrics(); + + final double zslice = ( imp.getSlice() - 1 ) * calibration[ 2 ]; + final double lMag = magnification; + final int frame = imp.getFrame() - 1; + + // Deal with normal ThreeDRoiobjects. + @SuppressWarnings( "unchecked" ) + final FeatureColorGenerator< ThreeDRoiobject > colorGenerator = ( FeatureColorGenerator< ThreeDRoiobject > ) displaySettings.get( KEY_ThreeDRoiobject_COLORING ); + g2d.setStroke( new BasicStroke( 1.0f ) ); + + if ( selectionOnly && null != ThreeDRoiobjectSelection) + { + // Track display mode only displays selection. + + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectSelection ) + { + if ( ThreeDRoiobject == editingThreeDRoiobject ) + { + continue; + } + final int sFrame = ThreeDRoiobject.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( sFrame != frame ) + { + continue; + } + + final double z = ThreeDRoiobject.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + if ( doLimitDrawingDepth && Math.abs( z - zslice ) > drawingDepth ) + { + continue; + } + + final Color color = colorGenerator.color( ThreeDRoiobject ); + g2d.setColor( color ); + drawThreeDRoiobject( g2d, ThreeDRoiobject, zslice, xcorner, ycorner, lMag ); + } + + } + else + { + // Other track displays. + + for ( final Iterator< ThreeDRoiobject > iterator = ThreeDRoiobjects.iterator( frame, true ); iterator.hasNext(); ) + { + final ThreeDRoiobject ThreeDRoiobject = iterator.next(); + + if ( editingThreeDRoiobject == ThreeDRoiobject || ( ThreeDRoiobjectSelection != null && ThreeDRoiobjectSelection.contains( ThreeDRoiobject ) ) ) + { + continue; + } + + final Color color = colorGenerator.color( ThreeDRoiobject ); + g2d.setColor( color ); + + final double z = ThreeDRoiobject.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + if ( doLimitDrawingDepth && Math.abs( z - zslice ) > drawingDepth ) + { + continue; + } + + drawThreeDRoiobject( g2d, ThreeDRoiobject, zslice, xcorner, ycorner, lMag ); + } + + // Deal with ThreeDRoiobject selection + if ( null != ThreeDRoiobjectSelection ) + { + g2d.setStroke( new BasicStroke( 2.0f ) ); + g2d.setColor( CovistoModelView.DEFAULT_HIGHLIGHT_COLOR ); + for ( final ThreeDRoiobject ThreeDRoiobject : ThreeDRoiobjectSelection ) + { + if ( ThreeDRoiobject == editingThreeDRoiobject ) + { + continue; + } + final int sFrame = ThreeDRoiobject.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( DEBUG ) + { + System.out.println( "[ThreeDRoiobjectOverlay] For ThreeDRoiobject " + ThreeDRoiobject + " in selection, found frame " + sFrame ); + } + if ( sFrame != frame ) + { + continue; + } + drawThreeDRoiobject( g2d, ThreeDRoiobject, zslice, xcorner, ycorner, lMag ); + } + } + } + + drawExtraLayer( g2d, frame ); + + // Deal with editing ThreeDRoiobject - we always draw it with its center at the + // current z, current t + // (it moves along with the current slice) + if ( null != editingThreeDRoiobject ) + { + g2d.setColor( CovistoModelView.DEFAULT_HIGHLIGHT_COLOR ); + g2d.setStroke( new BasicStroke( 1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 1.0f, new float[] { 5f, 5f }, 0 ) ); + final double x = editingThreeDRoiobject.getFeature( ThreeDRoiobject.XPOSITION ); + final double y = editingThreeDRoiobject.getFeature( ThreeDRoiobject.YPOSITION ); + final double radius = editingThreeDRoiobject.getFeature( ThreeDRoiobject.Size ) / calibration[ 0 ] * lMag; + // In pixel units + final double xp = x / calibration[ 0 ] + 0.5d; + final double yp = y / calibration[ 1 ] + 0.5d; + // Scale to image zoom + final double xs = ( xp - xcorner ) * lMag; + final double ys = ( yp - ycorner ) * lMag; + final double radiusRatio = ( Double ) displaySettings.get( CovistoModelView.KEY_ThreeDRoiobject_RADIUS_RATIO ); + g2d.drawOval( ( int ) Math.round( xs - radius * radiusRatio ), ( int ) Math.round( ys - radius * radiusRatio ), ( int ) Math.round( 2 * radius * radiusRatio ), ( int ) Math.round( 2 * radius * radiusRatio ) ); + } + + // Restore graphic device original settings + g2d.setTransform( originalTransform ); + g2d.setComposite( originalComposite ); + g2d.setStroke( originalStroke ); + g2d.setColor( originalColor ); + g2d.setFont( originalFont ); + } + + /** + * @param g2d + * @param frame + */ + protected void drawExtraLayer( final Graphics2D g2d, final int frame ) + {} + + public void setThreeDRoiobjectSelection( final Collection< ThreeDRoiobject > ThreeDRoiobjects ) + { + this.ThreeDRoiobjectSelection = ThreeDRoiobjects; + } + + protected void drawThreeDRoiobject( final Graphics2D g2d, final ThreeDRoiobject ThreeDRoiobject, final double zslice, final int xcorner, final int ycorner, final double magnification ) + { + final double x = ThreeDRoiobject.getFeature( ThreeDRoiobject.XPOSITION ); + final double y = ThreeDRoiobject.getFeature( ThreeDRoiobject.YPOSITION ); + final double z = ThreeDRoiobject.getFeature( ThreeDRoiobject.ZPOSITION ); + final double dz2 = ( z - zslice ) * ( z - zslice ); + final double radiusRatio = ( Double ) displaySettings.get( CovistoModelView.KEY_ThreeDRoiobject_RADIUS_RATIO ); + final double radius = ThreeDRoiobject.getFeature( ThreeDRoiobject.Size ) * radiusRatio; + // In pixel units + final double xp = x / calibration[ 0 ] + 0.5f; + final double yp = y / calibration[ 1 ] + 0.5f; // so that ThreeDRoiobject centers + // are displayed on the + // pixel centers + // Scale to image zoom + final double xs = ( xp - xcorner ) * magnification; + final double ys = ( yp - ycorner ) * magnification; + + if ( dz2 >= radius * radius ) + { + g2d.fillOval( ( int ) Math.round( xs - 2 * magnification ), ( int ) Math.round( ys - 2 * magnification ), ( int ) Math.round( 4 * magnification ), ( int ) Math.round( 4 * magnification ) ); + } + else + { + final double apparentRadius = Math.sqrt( radius * radius - dz2 ) / calibration[ 0 ] * magnification; + g2d.drawOval( ( int ) Math.round( xs - apparentRadius ), ( int ) Math.round( ys - apparentRadius ), ( int ) Math.round( 2 * apparentRadius ), ( int ) Math.round( 2 * apparentRadius ) ); + final boolean ThreeDRoiobjectNameVisible = ( Boolean ) displaySettings.get( CovistoModelView.KEY_DISPLAY_ThreeDRoiobject_NAMES ); + if ( ThreeDRoiobjectNameVisible ) + { + final String str = ThreeDRoiobject.toString(); + + final int xindent = fm.stringWidth( str ); + int xtext = ( int ) ( xs + apparentRadius + 5 ); + if ( xtext + xindent > imp.getWindow().getWidth() ) + { + xtext = ( int ) ( xs - apparentRadius - 5 - xindent ); + } + + final int yindent = fm.getAscent() / 2; + final int ytext = ( int ) ys + yindent; + + g2d.drawString( ThreeDRoiobject.toString(), xtext, ytext ); + } + } + } +} diff --git a/src/main/java/visualization/TrackColorGenerator.java b/src/main/java/visualization/TrackColorGenerator.java new file mode 100644 index 0000000..eb3ce6a --- /dev/null +++ b/src/main/java/visualization/TrackColorGenerator.java @@ -0,0 +1,37 @@ +package visualization; + +import org.jgrapht.graph.DefaultWeightedEdge; + +/** + * Interface for functions that can generate track colors, with a + * different color for each vertex and edge. This allow more + * detailed views, but is most likely more costly in memory and computation + * time. + *

      + * The spot coloring can seem to be redundant with individual spot coloring + * defined elsewhere. However, it must be noted that this interface is intended + * for track coloring, and is applied to spots in tracks only. + * Concrete implementations of {@link TrackMateModelView} decide whether they + * abide to individual spot coloring or spots within tracks coloring (this interface). + * + * @author Jean-Yves Tinevez + */ +public interface TrackColorGenerator extends FeatureColorGenerator { + + /** + * This is a hack for performance: + *

      + * For color generators that depends only on the track edges and spots are + * in, we can skip some computations by specifying first the trackID, and + * returning always the same color - ignoring the spot and edge argument of + * the {@link FeatureColorGenerator#color(Object)} methods - while we + * iterate through the track. + *

      + * Color generators that return a color per object can otherwise ignore this + * method. + * + * @param trackID + */ + public void setCurrentTrackID(Integer trackID); + +} diff --git a/src/main/java/visualization/TrackOverlay.java b/src/main/java/visualization/TrackOverlay.java new file mode 100644 index 0000000..ac54f61 --- /dev/null +++ b/src/main/java/visualization/TrackOverlay.java @@ -0,0 +1,400 @@ +package visualization; + +import java.awt.AlphaComposite; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Composite; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.jgrapht.graph.DefaultWeightedEdge; + +import ij.ImagePlus; +import ij.gui.Roi; +import linkers.Model3D; +import utility.CovistoUtils; +import utility.ThreeDRoiobject; + +/** + * The overlay class in charge of drawing the tracks on the hyperstack window. + * + * @author Jean-Yves Tinevez <jeanyves.tinevez@gmail.com> 2010 - 2011 + */ +public class TrackOverlay extends Roi +{ + private static final long serialVersionUID = 1L; + + protected final double[] calibration; + + protected Collection< DefaultWeightedEdge > highlight = new HashSet< >(); + + protected Map< String, Object > displaySettings; + + protected final Model3D model; + + private TrackColorGenerator colorGenerator; + + private static final Stroke NORMAL_STROKE = new BasicStroke(); + + private static final Stroke SELECTION_STROKE = new BasicStroke( 4.0f ); + + /* + * CONSTRUCTOR + */ + + public TrackOverlay( final Model3D model, final ImagePlus imp, final Map< String, Object > displaySettings ) + { + super( 0, 0, imp ); + this.model = model; + this.calibration = CovistoUtils.getSpatialCalibration( imp ); + this.imp = imp; + this.displaySettings = displaySettings; + colorGenerator = new DummyTrackColorGenerator(); + } + + /* + * PUBLIC METHODS + */ + + public void setHighlight( final Collection< DefaultWeightedEdge > edges ) + { + this.highlight = edges; + } + + @Override + public final synchronized void drawOverlay( final Graphics g ) + { + final Graphics2D g2d = ( Graphics2D ) g; + + final double magnification = getMagnification(); + + // Painted clip in window coordinates. + final int xcorner = ic.offScreenX( 0 ); + final int ycorner = ic.offScreenY( 0 ); + final double minx = xcorner; + final double miny = ycorner; + final double maxx = minx + ic.getWidth() / magnification; + final double maxy = miny + ic.getHeight() / magnification; + + final boolean tracksVisible = ( Boolean ) displaySettings.get( CovistoModelView.KEY_TRACKS_VISIBLE ); + if ( !tracksVisible || model.getTrackModel().nTracks( true ) == 0 ) + return; + + final boolean doLimitDrawingDepth = ( Boolean ) displaySettings.get( CovistoModelView.KEY_LIMIT_DRAWING_DEPTH ); + final double drawingDepth = ( Double ) displaySettings.get( CovistoModelView.KEY_DRAWING_DEPTH ); + final double zslice = ( imp.getSlice() - 1 ) * calibration[ 2 ]; + + // Save graphic device original settings + final AffineTransform originalTransform = g2d.getTransform(); + final Composite originalComposite = g2d.getComposite(); + final Stroke originalStroke = g2d.getStroke(); + final Color originalColor = g2d.getColor(); + ThreeDRoiobject source, target; + + // Normal edges + final int currentFrame = imp.getFrame() - 1; + final int trackDisplayMode = ( Integer ) displaySettings.get( CovistoModelView.KEY_TRACK_DISPLAY_MODE ); + final int trackDisplayDepth = ( Integer ) displaySettings.get( CovistoModelView.KEY_TRACK_DISPLAY_DEPTH ); + final Set< Integer > filteredTrackKeys = model.getTrackModel().unsortedTrackIDs( true ); + + g2d.setStroke( NORMAL_STROKE ); + if ( trackDisplayMode == CovistoModelView.TRACK_DISPLAY_MODE_LOCAL || trackDisplayMode == CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_QUICK ) + { + g2d.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER ) ); + } + + // Determine bounds for limited view modes + int minT = 0; + int maxT = 0; + switch ( trackDisplayMode ) + { + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_QUICK: + case CovistoModelView.TRACK_DISPLAY_MODE_SELECTION_ONLY: + minT = currentFrame - trackDisplayDepth; + maxT = currentFrame + trackDisplayDepth; + break; + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_FORWARD: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_FORWARD_QUICK: + minT = currentFrame; + maxT = currentFrame + trackDisplayDepth; + break; + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_BACKWARD: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_BACKWARD_QUICK: + minT = currentFrame - trackDisplayDepth; + maxT = currentFrame; + break; + } + + float transparency; + switch ( trackDisplayMode ) + { + + case CovistoModelView.TRACK_DISPLAY_MODE_SELECTION_ONLY: + { + for ( final DefaultWeightedEdge edge : highlight ) + { + source = model.getTrackModel().getEdgeSource( edge ); + target = model.getTrackModel().getEdgeTarget( edge ); + if ( !isOnClip( source, target, minx, miny, maxx, maxy, calibration ) ) + continue; + + source = model.getTrackModel().getEdgeSource( edge ); + final int sourceFrame = source.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( sourceFrame < minT || sourceFrame >= maxT ) + continue; + + final double zs = source.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + final double zt = target.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + if ( doLimitDrawingDepth && Math.abs( zs - zslice ) > drawingDepth && Math.abs( zt - zslice ) > drawingDepth ) + continue; + + final Integer trackID = model.getTrackModel().trackIDOf( edge ); + colorGenerator.setCurrentTrackID( trackID ); + g2d.setColor( colorGenerator.color( edge ) ); + + transparency = ( float ) ( 1 - Math.abs( ( double ) sourceFrame - currentFrame ) / trackDisplayDepth ); + drawEdge( g2d, source, target, xcorner, ycorner, magnification, transparency ); + } + + break; + } + case CovistoModelView.TRACK_DISPLAY_MODE_WHOLE: + { + for ( final Integer trackID : filteredTrackKeys ) + { + + colorGenerator.setCurrentTrackID( trackID ); + Set< DefaultWeightedEdge > track; + synchronized ( model ) + { + track = new HashSet< >( model.getTrackModel().trackEdges( trackID ) ); + } + for ( final DefaultWeightedEdge edge : track ) + { + source = model.getTrackModel().getEdgeSource( edge ); + target = model.getTrackModel().getEdgeTarget( edge ); + if ( !isOnClip( source, target, minx, miny, maxx, maxy, calibration ) ) + continue; + + final double zs = source.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + final double zt = target.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + if ( doLimitDrawingDepth && Math.abs( zs - zslice ) > drawingDepth && Math.abs( zt - zslice ) > drawingDepth ) + continue; + + g2d.setColor( colorGenerator.color( edge ) ); + drawEdge( g2d, source, target, xcorner, ycorner, magnification ); + } + } + break; + } + + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_QUICK: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_FORWARD_QUICK: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_BACKWARD_QUICK: + { + + g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF ); + + for ( final Integer trackID : filteredTrackKeys ) + { + colorGenerator.setCurrentTrackID( trackID ); + Set< DefaultWeightedEdge > track; + synchronized ( model ) + { + track = new HashSet< >( model.getTrackModel().trackEdges( trackID ) ); + } + for ( final DefaultWeightedEdge edge : track ) + { + source = model.getTrackModel().getEdgeSource( edge ); + final int sourceFrame = source.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( sourceFrame < minT || sourceFrame >= maxT ) + continue; + + target = model.getTrackModel().getEdgeTarget( edge ); + if ( !isOnClip( source, target, minx, miny, maxx, maxy, calibration ) ) + continue; + + final double zs = source.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + final double zt = target.getFeature( ThreeDRoiobject.ZPOSITION ).doubleValue(); + if ( doLimitDrawingDepth && Math.abs( zs - zslice ) > drawingDepth && Math.abs( zt - zslice ) > drawingDepth ) + continue; + + g2d.setColor( colorGenerator.color( edge ) ); + drawEdge( g2d, source, target, xcorner, ycorner, magnification ); + } + } + break; + } + + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_FORWARD: + case CovistoModelView.TRACK_DISPLAY_MODE_LOCAL_BACKWARD: + { + + g2d.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); + + for ( final Integer trackID : filteredTrackKeys ) + { + colorGenerator.setCurrentTrackID( trackID ); + final Set< DefaultWeightedEdge > track; + synchronized ( model ) + { + track = new HashSet< >( model.getTrackModel().trackEdges( trackID ) ); + } + for ( final DefaultWeightedEdge edge : track ) + { + source = model.getTrackModel().getEdgeSource( edge ); + final int sourceFrame = source.getFeature( ThreeDRoiobject.Time ).intValue(); + if ( sourceFrame < minT || sourceFrame >= maxT ) + continue; + + transparency = ( float ) ( 1 - Math.abs( ( double ) sourceFrame - currentFrame ) / trackDisplayDepth ); + target = model.getTrackModel().getEdgeTarget( edge ); + if ( !isOnClip( source, target, minx, miny, maxx, maxy, calibration ) ) + continue; + + g2d.setColor( colorGenerator.color( edge ) ); + drawEdge( g2d, source, target, xcorner, ycorner, magnification, transparency ); + } + } + break; + + } + } + + if ( trackDisplayMode != CovistoModelView.TRACK_DISPLAY_MODE_SELECTION_ONLY ) + { + // Deal with highlighted edges first: brute and thick display + g2d.setStroke( SELECTION_STROKE ); + g2d.setColor( CovistoModelView.DEFAULT_HIGHLIGHT_COLOR ); + g2d.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER ) ); + for ( final DefaultWeightedEdge edge : highlight ) + { + source = model.getTrackModel().getEdgeSource( edge ); + target = model.getTrackModel().getEdgeTarget( edge ); + if ( !isOnClip( source, target, minx, miny, maxx, maxy, calibration ) ) + continue; + drawEdge( g2d, source, target, xcorner, ycorner, magnification ); + } + } + + // Restore graphic device original settings + g2d.setTransform( originalTransform ); + g2d.setComposite( originalComposite ); + g2d.setStroke( originalStroke ); + g2d.setColor( originalColor ); + + } + + private static final boolean isOnClip( final ThreeDRoiobject source, final ThreeDRoiobject target, final double minx, final double miny, final double maxx, final double maxy, final double[] calibration ) + { + // Find x & y in physical coordinates + final double x0i = source.getFeature( ThreeDRoiobject.XPOSITION ); + final double y0i = source.getFeature( ThreeDRoiobject.YPOSITION ); + final double x1i = target.getFeature( ThreeDRoiobject.XPOSITION ); + final double y1i = target.getFeature( ThreeDRoiobject.YPOSITION ); + // In pixel units + final double x0p = x0i / calibration[ 0 ] + 0.5f; + final double y0p = y0i / calibration[ 1 ] + 0.5f; + final double x1p = x1i / calibration[ 0 ] + 0.5f; + final double y1p = y1i / calibration[ 1 ] + 0.5f; + + // Is any ThreeDRoiobject inside the clip? + if ( ( x0p > minx && x0p < maxx && y0p > miny && y0p < maxy ) || ( x1p > minx && x1p < maxx && y1p > miny && y1p < maxy ) ) { return true; } + + // Do we cross any of the 4 borders of the clip? + if ( segmentsCross( x0p, y0p, x1p, y1p, minx, miny, maxx, miny ) ) { return true; } + if ( segmentsCross( x0p, y0p, x1p, y1p, maxx, miny, maxx, maxy ) ) { return true; } + if ( segmentsCross( x0p, y0p, x1p, y1p, minx, maxy, maxx, maxy ) ) { return true; } + if ( segmentsCross( x0p, y0p, x1p, y1p, minx, miny, minx, maxy ) ) { return true; } + return false; + } + + /* + * PROTECTED METHODS + */ + + private static final boolean segmentsCross( final double x0, final double y0, final double x1, final double y1, final double x2, final double y2, final double x3, final double y3 ) + { + final double s1_x = x1 - x0; + final double s1_y = y1 - y0; + final double s2_x = x3 - x2; + final double s2_y = y3 - y2; + + final double det = ( -s2_x * s1_y + s1_x * s2_y ); + if ( det < Float.MIN_NORMAL ) + return false; + + final double s = ( -s1_y * ( x0 - x2 ) + s1_x * ( y0 - y2 ) ); // / det; + final double t = ( s2_x * ( y0 - y2 ) - s2_y * ( x0 - x2 ) ); // / det; + + return ( s >= 0 && s <= det && t >= 0 && t <= det ); + } + + protected void drawEdge( final Graphics2D g2d, final ThreeDRoiobject source, final ThreeDRoiobject target, final int xcorner, final int ycorner, final double magnification, final float transparency ) + { + // Find x & y in physical coordinates + final double x0i = source.getFeature( ThreeDRoiobject.XPOSITION ); + final double y0i = source.getFeature( ThreeDRoiobject.YPOSITION ); + final double x1i = target.getFeature( ThreeDRoiobject.XPOSITION ); + final double y1i = target.getFeature( ThreeDRoiobject.YPOSITION ); + // In pixel units + final double x0p = x0i / calibration[ 0 ] + 0.5f; + final double y0p = y0i / calibration[ 1 ] + 0.5f; + final double x1p = x1i / calibration[ 0 ] + 0.5f; + final double y1p = y1i / calibration[ 1 ] + 0.5f; + // Scale to image zoom + final double x0s = ( x0p - xcorner ) * magnification; + final double y0s = ( y0p - ycorner ) * magnification; + final double x1s = ( x1p - xcorner ) * magnification; + final double y1s = ( y1p - ycorner ) * magnification; + // Round + final int x0 = ( int ) Math.round( x0s ); + final int y0 = ( int ) Math.round( y0s ); + final int x1 = ( int ) Math.round( x1s ); + final int y1 = ( int ) Math.round( y1s ); + + g2d.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, transparency ) ); + g2d.drawLine( x0, y0, x1, y1 ); + + } + + protected void drawEdge( final Graphics2D g2d, final ThreeDRoiobject source, final ThreeDRoiobject target, final int xcorner, final int ycorner, final double magnification ) + { + // Find x & y in physical coordinates + final double x0i = source.getFeature( ThreeDRoiobject.XPOSITION ); + final double y0i = source.getFeature( ThreeDRoiobject.YPOSITION ); + final double x1i = target.getFeature( ThreeDRoiobject.XPOSITION ); + final double y1i = target.getFeature( ThreeDRoiobject.YPOSITION ); + // In pixel units + final double x0p = x0i / calibration[ 0 ] + 0.5f; + final double y0p = y0i / calibration[ 1 ] + 0.5f; + final double x1p = x1i / calibration[ 0 ] + 0.5f; + final double y1p = y1i / calibration[ 1 ] + 0.5f; + // Scale to image zoom + final double x0s = ( x0p - xcorner ) * magnification; + final double y0s = ( y0p - ycorner ) * magnification; + final double x1s = ( x1p - xcorner ) * magnification; + final double y1s = ( y1p - ycorner ) * magnification; + // Round + final int x0 = ( int ) Math.round( x0s ); + final int y0 = ( int ) Math.round( y0s ); + final int x1 = ( int ) Math.round( x1s ); + final int y1 = ( int ) Math.round( y1s ); + g2d.drawLine( x0, y0, x1, y1 ); + } + + public void setTrackColorGenerator( final TrackColorGenerator colorGenerator ) + { + this.colorGenerator = colorGenerator; + } + +} diff --git a/src/main/java/visualization/Visualize3D.java b/src/main/java/visualization/Visualize3D.java new file mode 100644 index 0000000..04766bc --- /dev/null +++ b/src/main/java/visualization/Visualize3D.java @@ -0,0 +1,277 @@ +package visualization; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.Insets; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.awt.event.MouseMotionListener; +import java.text.NumberFormat; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JScrollPane; +import javax.swing.JTable; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; +import javax.swing.table.DefaultTableCellRenderer; + +import ij.gui.ImageCanvas; +import ij.gui.Roi; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import utility.PreRoiobject; +import utility.ThreeDRoiobject; +import zGUI.CovistoZselectPanel; + +public class Visualize3D { + + final InteractiveMethods parent; + + public Visualize3D(final InteractiveMethods parent) { + + this.parent = parent; + + } + + + public void CreateTable() { + + + + parent.row = 0; + NumberFormat f = NumberFormat.getInstance(); + + + + parent.panelThird.remove(parent.PanelSelectFile); + parent.PanelSelectFile.removeAll(); + parent.panelThird.remove(parent.controlprevthird); + + parent.PanelSelectFile.repaint(); + parent.PanelSelectFile.validate(); + parent.panelThird.repaint(); + parent.panelThird.validate(); + + for (Map.Entry> entry: parent.Timetracks.entrySet()) { + + ArrayList currententry = entry.getValue(); + parent.table.getModel().setValueAt(entry.getKey(), parent.row, 0); + parent.table.getModel().setValueAt(f.format(currententry.get(currententry.size() - 1).geometriccenter[0]), parent.row, 1); + parent.table.getModel().setValueAt(f.format(currententry.get(currententry.size() - 1).geometriccenter[1]), parent.row, 2); + parent.table.getModel().setValueAt(f.format(currententry.get(currententry.size() - 1).geometriccenter[2]), parent.row, 3); + parent.table.getModel().setValueAt(f.format(currententry.get(currententry.size() - 1).totalintensity), parent.row, 4); + parent.table.getModel().setValueAt(f.format(currententry.get(currententry.size() - 1).averageintensity), parent.row, 5); + parent.row++; + + + parent.tablesize = parent.row; + + } + + + parent.PanelSelectFile.add(parent.scrollPane, BorderLayout.CENTER); + + Border trackborder = new CompoundBorder(new TitledBorder("Select track"), new EmptyBorder(parent.c.insets)); + parent.PanelSelectFile.setBorder(trackborder); + + + parent.panelThird.add(parent.PanelSelectFile, new GridBagConstraints(0, 4, 3, 1, 0.0, 0.0, GridBagConstraints.EAST, + GridBagConstraints.HORIZONTAL, parent.insets, 0, 0)); + parent.panelThird.add(parent.controlprevthird, new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0, GridBagConstraints.ABOVE_BASELINE, + GridBagConstraints.HORIZONTAL, new Insets(10, 10, 0, 10), 0, 0)); + + + + + + parent.controlnextthird.setEnabled(true); + + parent.panelThird.validate(); + parent.panelThird.repaint(); + + } + + public void set() { + + + if (parent.mvl != null) + parent.imp.getCanvas().removeMouseListener(parent.mvl); + parent.imp.getCanvas().addMouseListener(parent.mvl = new MouseListener() { + + @Override + public void mouseClicked(MouseEvent e) { + + if (SwingUtilities.isLeftMouseButton(e) && e.isShiftDown()) { + + + + + + + } + + + } + + @Override + public void mousePressed(MouseEvent e) { + + } + + @Override + public void mouseReleased(MouseEvent e) { + + } + + @Override + public void mouseEntered(MouseEvent e) { + + } + + @Override + public void mouseExited(MouseEvent e) { + + } + }); + + + + } + + public void mark() { + + if (parent.ml != null) + parent.imp.getCanvas().removeMouseMotionListener(parent.ml); + + parent.imp.getCanvas().addMouseMotionListener(parent.ml = new MouseMotionListener() { + + final ImageCanvas canvas = parent.imp.getWindow().getCanvas(); + Roi lastnearest = null; + + @Override + public void mouseMoved(MouseEvent e) { + + int x = canvas.offScreenX(e.getX()); + int y = canvas.offScreenY(e.getY()); + + final HashMap loc = new HashMap(); + + loc.put(0, new double[] { x, y }); + + Color roicolor; + ArrayList currentobject; + + roicolor = parent.colorSnake; + + currentobject = parent.ZTRois.get(parent.uniqueID); + + + parent.nearestRoiCurr = NearestRoi.getNearestRois(currentobject, loc.get(0), parent); + + if (parent.nearestRoiCurr != null) { + parent.nearestRoiCurr.setStrokeColor(parent.colorConfirm); + + if (lastnearest != parent.nearestRoiCurr && lastnearest != null) + lastnearest.setStrokeColor(roicolor); + + lastnearest = parent.nearestRoiCurr; + + parent.imp.updateAndDraw(); + } + + double distmin = Double.MAX_VALUE; + if (parent.tablesize > 0) { + NumberFormat f = NumberFormat.getInstance(); + for (int row = 0; row < parent.tablesize; ++row) { + String CordX = (String) parent.table.getValueAt(row, 1); + String CordY = (String) parent.table.getValueAt(row, 2); + + String CordZ = (String) parent.table.getValueAt(row, 3); + + double dCordX = 0, dCordZ = 0, dCordY = 0; + try { + dCordX = f.parse(CordX).doubleValue(); + + dCordY = f.parse(CordY).doubleValue(); + dCordZ = f.parse(CordZ).doubleValue(); + } catch (ParseException e1) { + + } + double dist = DistanceSq(new double[] { dCordX, dCordY }, new double[] { x, y }); + if (DistanceSq(new double[] { dCordX, dCordY }, new double[] { x, y }) < distmin + && CovistoZselectPanel.thirdDimension == (int) dCordZ && parent.ndims > 3) { + + parent.rowchoice = row; + distmin = dist; + + } + if (DistanceSq(new double[] { dCordX, dCordY }, new double[] { x, y }) < distmin + && parent.ndims <= 3) { + + parent.rowchoice = row; + distmin = dist; + + } + + + } + + + parent.table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer() { + @Override + public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, + boolean hasFocus, int row, int col) { + + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col); + if (row == parent.rowchoice) { + setBackground(Color.green); + + } else { + setBackground(Color.white); + } + return this; + } + }); + parent.scrollPane.getVerticalScrollBar().setValue(parent.table.getY()); + parent.scrollPane.getHorizontalScrollBar().setValue(parent.table.getX()); + parent.table.validate(); + parent.scrollPane.validate(); + parent.panelThird.repaint(); + parent.panelThird.validate(); + + } + + } + + @Override + public void mouseDragged(MouseEvent e) { + + } + + }); + } + + public static double DistanceSq(final double[] pointA, final double[] pointB) { + + double distance = 0; + int numDim = pointA.length; + + for (int d = 0; d < numDim; ++d) { + + distance += (pointA[d] - pointB[d]) + * (pointA[d] - pointB[d]); + + } + return distance; + } + +} diff --git a/src/main/java/watershed3D/WatershedAll.java b/src/main/java/watershed3D/WatershedAll.java new file mode 100644 index 0000000..3e4a94f --- /dev/null +++ b/src/main/java/watershed3D/WatershedAll.java @@ -0,0 +1,112 @@ +package watershed3D; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ExecutionException; + +import javax.swing.JOptionPane; +import javax.swing.SwingWorker; + +import org.jgrapht.graph.DefaultWeightedEdge; +import org.jgrapht.graph.SimpleWeightedGraph; + +import common3D.CommonWater; +import distanceTransform.DistWatershed; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import linkers.PRENNsearch; +import net.imglib2.Cursor; +import net.imglib2.Interval; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +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.integer.IntType; +import net.imglib2.type.numeric.integer.UnsignedByteType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.util.Intervals; +import net.imglib2.util.Pair; +import net.imglib2.view.IntervalView; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; +import preProcessing.GlobalThresholding; +import timeGUI.CovistoTimeselectPanel; +import utility.PreRoiobject; +import utility.ThreeDRoiobject; +import watershedGUI.CovistoWatershedPanel; +import zGUI.CovistoZselectPanel; + +public class WatershedAll extends SwingWorker { + + final InteractiveMethods parent; + + public WatershedAll(final InteractiveMethods parent) { + + this.parent = parent; + + } + + @Override + protected Void doInBackground() throws Exception { + + + + parent.apply3D = true; + + RandomAccessibleInterval newimg = new ArrayImgFactory().create(parent.originalimg, new FloatType()); + + RandomAccessibleInterval bitimg = new ArrayImgFactory().create(newimg, new BitType()); + + RandomAccessibleInterval intimg = new ArrayImgFactory().create(newimg, new IntType()); + + for (int t = CovistoTimeselectPanel.fourthDimensionsliderInit; t <= CovistoTimeselectPanel.fourthDimensionSize; ++t) { + + + for (int z = CovistoZselectPanel.thirdDimensionsliderInit; z <= CovistoZselectPanel.thirdDimensionSize; ++z) { + + CovistoZselectPanel.thirdDimension = z; + CovistoTimeselectPanel.fourthDimension = t; + CommonWater.Watershed(parent, newimg, bitimg, intimg, t, z); + + } + + + } + + if(parent.displayBinaryimg) + ImageJFunctions.show(bitimg).setTitle("Binary Image"); + + if (parent.displayWatershedimg) + ImageJFunctions.show(intimg).setTitle("Integer Image"); + parent.intimg = intimg; + + if (parent.displayDistTransimg) + ImageJFunctions.show(newimg ).setTitle("Distance Transform Image"); + + + + + + return null; + } + + + + + @Override + protected void done() { + try { + + parent.apply3D = false; + get(); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + + } + +} diff --git a/src/main/java/watershedCurrent/WatershedCurrent.java b/src/main/java/watershedCurrent/WatershedCurrent.java new file mode 100644 index 0000000..15a9495 --- /dev/null +++ b/src/main/java/watershedCurrent/WatershedCurrent.java @@ -0,0 +1,80 @@ +package watershedCurrent; + +import java.util.concurrent.ExecutionException; + +import javax.naming.InitialContext; +import javax.swing.SwingWorker; + +import common3D.CommonWater; +import distanceTransform.DistWatershed; +import distanceTransform.WatershedBinary; +import interactivePreprocessing.InteractiveMethods; +import interactivePreprocessing.InteractiveMethods.ValueChange; +import net.imglib2.Cursor; +import net.imglib2.RandomAccess; +import net.imglib2.RandomAccessibleInterval; +import net.imglib2.img.array.ArrayImgFactory; +import net.imglib2.img.display.imagej.ImageJFunctions; +import net.imglib2.type.logic.BitType; +import net.imglib2.type.numeric.integer.IntType; +import net.imglib2.type.numeric.real.FloatType; +import net.imglib2.view.Views; +import preProcessing.GetLocalmaxminMT; +import preProcessing.GlobalThresholding; +import timeGUI.CovistoTimeselectPanel; +import watershedGUI.CovistoWatershedPanel; +import zGUI.CovistoZselectPanel; + +public class WatershedCurrent extends SwingWorker { + + final InteractiveMethods parent; + + public WatershedCurrent(final InteractiveMethods parent) { + + this.parent = parent; + + } + + @Override + protected Void doInBackground() throws Exception { + + + + parent.apply3D = false; + RandomAccessibleInterval newimg = new ArrayImgFactory().create(parent.originalimg, new FloatType()); + + RandomAccessibleInterval bitimg = new ArrayImgFactory().create(newimg, new BitType()); + + RandomAccessibleInterval intimg = new ArrayImgFactory().create(newimg, new IntType()); + + int t = CovistoTimeselectPanel.fourthDimension; + int z = CovistoZselectPanel.thirdDimension; + + CommonWater.Watershed(parent, newimg, bitimg, intimg, t, z); + if(parent.displayBinaryimg) + ImageJFunctions.show(bitimg).setTitle("Binary Image"); + + if (parent.displayWatershedimg) + ImageJFunctions.show(intimg).setTitle("Integer Image"); + parent.intimg = intimg; + + if (parent.displayDistTransimg) + ImageJFunctions.show(newimg ).setTitle("Distance Transform Image"); + + return null; + } + + + + @Override + protected void done() { + try { + + parent.apply3D = false; + get(); + } catch (ExecutionException | InterruptedException e) { + e.printStackTrace(); + } + + } +} diff --git a/src/main/java/watershedGUI/CovistoWatershedPanel.java b/src/main/java/watershedGUI/CovistoWatershedPanel.java new file mode 100644 index 0000000..1a6eb43 --- /dev/null +++ b/src/main/java/watershedGUI/CovistoWatershedPanel.java @@ -0,0 +1,99 @@ +package watershedGUI; + +import java.awt.Checkbox; +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.Scrollbar; + +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoWatershedPanel { + + + + public static JPanel WaterPanel = new JPanel(); + public static final int scrollbarSize = 1000; + public static final String waterstring = "Threshold for Watershedding"; + public static int thresholdsliderWaterInit = 125; + public static float thresholdMinWater = 1f; + public static float thresholdMaxWater = 255f; + public static int thresholdInitWater = 0; + public static float thresholdWater = 255f; + + public static Label watertext = new Label(waterstring + " = " + thresholdInitWater, Label.CENTER); + public static JScrollBar thresholdWaterslider = new JScrollBar(Scrollbar.HORIZONTAL, thresholdsliderWaterInit, 10, 0, + 10 + scrollbarSize); + public double getInitialThresholdWater() { + return thresholdInitWater; + } + + public void setInitialThresholdWater(final float value) { + thresholdWater = value; + thresholdInitWater = scrollbar.Utility.computeScrollbarPositionFromValue(thresholdWater, thresholdMinWater, thresholdMaxWater, + scrollbarSize); + } + + public static boolean disttransform = true; + + public static final Checkbox displayWater = new Checkbox("Display Watershed image", true); + + public static final Checkbox displayBinary = new Checkbox("Display Binary image"); + + public static final Checkbox displayDist = new Checkbox("Display DTimage"); + + public static final Checkbox autothreshold = new Checkbox("Auto Thresholding"); + + public static final Checkbox dodist = new Checkbox("Distance transformed Watershed", disttransform); + + public static final JButton Water3D = new JButton("Watershed in 3D/4D"); + + public static final JButton tryWater = new JButton("Watershed Current"); + + + + public static JPanel WaterPanel() { + + + thresholdWaterslider.setValue(scrollbar.Utility.computeScrollbarPositionFromValue(thresholdsliderWaterInit, + thresholdMinWater, thresholdMaxWater, scrollbarSize)); + + thresholdWater = scrollbar.Utility.computeValueFromScrollbarPosition(thresholdWaterslider.getValue(), + thresholdMinWater, thresholdMaxWater, scrollbarSize); + layoutManager.Setlayout.LayoutSetter(WaterPanel); + Border waterborder = new CompoundBorder(new TitledBorder("Watershed detection"), new EmptyBorder(layoutManager.Setlayout.c.insets)); + + watertext = new Label(waterstring + " = " + thresholdWater, Label.CENTER); + WaterPanel.add(watertext, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.add(dodist, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.add(thresholdWaterslider, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + WaterPanel.add(displayWater, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.add(displayBinary, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.add(displayDist, new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.add(autothreshold, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.add(Water3D, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + WaterPanel.add(tryWater, new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + WaterPanel.setBorder(waterborder); + + + return WaterPanel; + + } + +} diff --git a/src/main/java/zGUI/CovistoZselectPanel.java b/src/main/java/zGUI/CovistoZselectPanel.java new file mode 100644 index 0000000..61c71cd --- /dev/null +++ b/src/main/java/zGUI/CovistoZselectPanel.java @@ -0,0 +1,70 @@ +package zGUI; + +import java.awt.GridBagConstraints; +import java.awt.Label; +import java.awt.Scrollbar; +import java.awt.TextField; + +import javax.swing.JPanel; +import javax.swing.JScrollBar; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.TitledBorder; + +public class CovistoZselectPanel { + + public static final int scrollbarSize = 1000; + public static JPanel Zselect = new JPanel(); + public static final String zstring = "Current Z"; + public static final String zgenstring = "Current Z / T"; + + + public static Label zText = new Label("Current Z = " + 1, Label.CENTER); + public static Label zgenText = new Label("Current Z / T = " + 1, Label.CENTER); + public static int thirdDimension; + public static int thirdDimensionSize; + + public static int thirdDimensionslider = 1; + public static int thirdDimensionsliderInit = 1; + public static TextField inputFieldZ; + + public static JScrollBar zslider = new JScrollBar(Scrollbar.HORIZONTAL, thirdDimensionsliderInit, 10, 0, + 10 + scrollbarSize); + + + public static void setZ(final int value) { + thirdDimensionslider = value; + thirdDimensionsliderInit = value; + thirdDimension = value; + } + + + public static JPanel ZselectPanel(int ndims) { + + layoutManager.Setlayout.LayoutSetter(Zselect); + setZ(thirdDimension); + inputFieldZ = new TextField(); + inputFieldZ = new TextField(5); + inputFieldZ.setText(Integer.toString(thirdDimension)); + + Border zborder = new CompoundBorder(new TitledBorder("Select Z / T"), new EmptyBorder( layoutManager.Setlayout.insets)); + if (ndims > 3) + Zselect.add(zText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + else + Zselect.add(zgenText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + Zselect.add(zslider, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Zselect.add(inputFieldZ, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Zselect.setBorder(zborder); + + return Zselect; + } + + +} diff --git a/target/classes/batchMode/ExecuteBatch.class b/target/classes/batchMode/ExecuteBatch.class index 309f3aec144edd9c0a02c9adf4162cf8873bebd7..ed552cb9e470ff6fe0c80a50095e07fcd8f7318c 100644 GIT binary patch delta 443 zcmb`DODlw77>2JYiHyT%K{6XPVxg3cS=b;lF-|LyQw)YAXA)u@vl#gj4Kc}z#;n53RNLx!5>S&uO$jwE z)QnKGJTb=$^L((tmjvT1NeS9X)BD2-r<(d%p)8lpx10otHA$>X!p}V$MA;<97O!mc z$qwIY=9fLQIwxKbz|KDT9H`kt$~nTtvDyuqrAD~;j0|1MBSaAwlo6(qOKNo4!!;e; V(8sO$XSAh1X!DV5BBndn@efaxVf_FA delta 443 zcmb`DT`PlO9EY!!MCRpjK(Z4xaiEkFJ8(jx$=e(WAsL3Kd6%ZldkQ@zOl{_^DYJUC zBu>bI)c6uR_7(g-#7^$}-`&6ay1LTNv@?;kdq>Ek8G{xav{HqeI@)NUT`wJU(@7s) zcs0%9VTFDI3=m|HJ%%`Bm~%!6YvYF+ delta 17 ZcmbO+k#WXE#tpL+nT#?w&r$rN1^_-12JZj> diff --git a/target/classes/curvatureUtils/DisplaySelected$1.class b/target/classes/curvatureUtils/DisplaySelected$1.class index c592e7c80452aa9e43ada9cd6b49e3408c72915f..47a345b27fb681012f67b915054ad9a5da9dce36 100644 GIT binary patch delta 22 dcmbQnKaGDwJ1eIe11Ez9gE)iQ<_WB9OaMH<1atrZ delta 22 dcmbQnKaGDwJ1Zw611p0X11E#V<_WB9OaM9(1U&!% diff --git a/target/classes/curvatureUtils/DisplaySelected$2$1.class b/target/classes/curvatureUtils/DisplaySelected$2$1.class index 5c31413da20db80080c7af23d624069c37c5b6eb..50881d05515084a8475d905d12f50ca2db1b3eda 100644 GIT binary patch delta 23 ecmdnSwTWv3FAJmPWIh%JRyGDk28PMzEdK#P3k4Pc delta 27 icmdnQwT){7FAJmXWIh%JPA&!}1_lO32Cm7*EdK#d7X>N+ diff --git a/target/classes/curvatureUtils/DisplaySelected$2.class b/target/classes/curvatureUtils/DisplaySelected$2.class index 78dca3a782b69c9320cd0c91605a00e6d4511160..72eeeb6156e4ce7b70f44dd8edc436d78bb44a6a 100644 GIT binary patch delta 22 dcmdley-|9@R8G!#22O@V262Y?&GR^gSpZ8)1)u-` delta 22 dcmdley-|9@R8CGt23Cf622O^=&GR^gSpY?T1uy^r diff --git a/target/classes/curvatureUtils/PointExtractor.class b/target/classes/curvatureUtils/PointExtractor.class index 27da514a4791e02ca061e735df5148d2b2b3f9c9..7dc4976b91ebf5890ea8c9d4a3f8d19bb896c7b7 100644 GIT binary patch delta 313 zcmWNMO(?^09L7K2->;SRFNvI1Lk?qJYpj-5-jbzdli0;>{L9N^IjQxZmnmtH#nnrU zlA;}?lyX`MS39~WZVv8#m*=JDc@9s-SMlAxS>h?ZTV?IE;HH&Y{CH@inRbI61n8uX zE{5o4%(w(S%+gDmAPymNCfz5@5&c{-z@uqCG0Za~yfDfK5x&gwn<&4Gi;I|e3^x#$ z022~19A#2s4pWj~TINW|Dl?KJDS5(jj4c<;%L5DY%A$O-B!8ron`Px?MJ2ISijZ0( zqp~=vKvor5SNCkFGMlQxmil2^`^o4ayE;x@+wAEK2UsA3G=kotbXaqznoiK1;^K@d V&Z(wIJ(qZm_Hk{l-W2S*^gjVrLo@&Y delta 313 zcmWNNO(=tL9DtwS|KG}5N#ryQIV|(tW@@xDr1@$inaiBm3ZpkCWt)#Bg;sA|g@%Jf zJ4h+zv=pv(bWz+K+`Xsgxjs+XU3TBTIi#-m?kMR@Gj=>w(}I&$nrO4E9WNd9(#Zf_ zjN&6kH}m*0=pjRZ9DU>oa!fy0gm`3-Cx&=tm>0r)Fv1sOd^5o>5wQ^!r^R|E#mkh0 zEsirSkqk2uBPI*X${KOWF((HE<%FaZSd<5rRGC#|w% zmC2^MU`ySztxD{uGCB3buJ(}De)e^gL!BV6H!yL4glGW0NAalYPBosQIm5;|6%?rC Tl3FXvWx1PcYs8J2s4@NlEs#Td diff --git a/target/classes/drawUtils/DrawFunction.class b/target/classes/drawUtils/DrawFunction.class index 35e248dbcf3ccda102a5b618eac7545c32669178..f6ccdfc04fee302a1b7e7ee7812fec7451b249c9 100644 GIT binary patch delta 210 zcmdn2wpne%VqR8N1{Maj$t!q`SQUXRrOEesgLpN7EG-6325kl@2A#=vd}j>}7&sV= z77=##18I%~zfGi6JM+Qp(MoXOLki0-9M2G^q?|N(E3y6@w3u9mdeOd73~Tqvak3E{44fA`JV1N)Iro xG8|;kXE+43IX!%+q=hT{yu3@3rgPctMjoMp&nILA;8;p~4qKYr(!K{NP0R<;mU!um@YwsRrB-m52dA#}wF*PIg3Ip_Aqz|Jj- zI35!CxRaG4Pnt(EypZLM93S#`DbS;&Gp>=Ptp8oZKvmkBbal@7D` vOy3;3tCr*h2UEPvM4c00L6jv)Ruoxt&kq$gqHlTSmv{bX%O0@PStYn~hmL83EK22FL&a diff --git a/target/classes/hashMapSorter/SortTimeorZ$1.class b/target/classes/hashMapSorter/SortTimeorZ$1.class index 9d3d490054cee4c3ef743eb70a99feb51c2b16c4..6a59e40a7dd3643727efa4a7f4c0e6b18484a577 100644 GIT binary patch delta 43 rcmZ3>wUTQC3yYjS10xVJFtBQEXW-fhWH2%C07*8mpu%P~7IsDek4^x$ diff --git a/target/classes/hashMapSorter/SortTimeorZ$2.class b/target/classes/hashMapSorter/SortTimeorZ$2.class index 0c1d3a3241cbd72e7a4039a11d26646b81556621..797d74304cac146f2a78deae68bbda79fc27265b 100644 GIT binary patch delta 43 rcmcb?eT93&Y8E+t21X!cU|`kS&cL-1$Y5gN0g`NBLDS7=S-2Sixu^#0 delta 47 xcmcb@eS>?$Y8EA921X!cU|`kS&cL-1$Y5gN0g_xmnt{QTfrWu_^GOzNMgY*T2KoR1 diff --git a/target/classes/hashMapSorter/SortTimeorZ$3.class b/target/classes/hashMapSorter/SortTimeorZ$3.class index ddc6055b93272ebe1848ff56a2430e47589311f4..596be362e78a4a0eb55252646b304296a5476045 100644 GIT binary patch delta 43 rcmey!{g!*f5f(Xp21X!cU|`kS&cL-1$Y5gN0g`NBLGR7aShyJh#(D>4 delta 47 xcmaFM{gHda5f&w521X!cU|`kS&cL-1$Y5gN0g_xmnt{QafrWu_^CK2+MgZRi2Xp`c diff --git a/target/classes/kalmanForSegments/KFSegmentsearch$1.class b/target/classes/kalmanForSegments/KFSegmentsearch$1.class index 4a50c1578c6f6f25f427ed22c0dd1bc0584b4cd3..a54ca738f25b5706c8880ce58a4591193c8288e5 100644 GIT binary patch delta 43 scmcb}b(U*`D2tpv10xVJFtBQEXW-fhWH2%C07*8GAfw!7a~3H^0G@~ delta 47 xcmX@hb&+d>D2tLY10xVJFtBQEXW-fhWH2%C07)(&&A=$fz{0?|*_cI&5dg9w1-AeI diff --git a/target/classes/kalmanForSegments/KFSegmentsearch$ComparableRealPoint.class b/target/classes/kalmanForSegments/KFSegmentsearch$ComparableRealPoint.class index 71b6fbaf931261f7cd1b398fe3f1438e05bd2d1a..9805b3fc80781bebd63a3e82d5f1bd641e08cab4 100644 GIT binary patch delta 151 zcmeywafgHJ)W2Q(7#J8#7)&>E?P26#WKiK@P-Re?{Dje!QD?Fi(|Q(TMh3-+0*aIW zG5u3kXAoduWME=2W6)qw0@9j5tsD%@e;8O9WtjvV8H5?MCd;#k+i0%=N-#0-0!cQY k1_mJJ1B);)Ff-_aS$YimVA_De5J+I#0fQl!HexUa(p*3tj6i3Z HF-QRbOUWE! diff --git a/target/classes/kalmanForSegments/SegmentobjectCollection$1.class b/target/classes/kalmanForSegments/SegmentobjectCollection$1.class index cab467225c7ca62f935a6e21ad59f44733e9b8de..6b850ab11d3464e70be4a74799d7b6f855f89115 100644 GIT binary patch delta 26 hcmey%_LFUcEfb^uWIHA?b~Xk^1_qX_&GAffi~wO~1-bwL delta 30 lcmey#_LpsgEfb^hWIHA?ZY~BU1_qWa1{Ma!&CyJ9i~w|c1;_vZ diff --git a/target/classes/kalmanForSegments/SegmentobjectCollection$2.class b/target/classes/kalmanForSegments/SegmentobjectCollection$2.class index cb899286dad85d36ab81397b6bf427a0bdeb8735..2a2848c2c8a1b691af22e67d3c4f47b67342149b 100644 GIT binary patch delta 23 ecmaFH^N44IEi0qlWII+>RyGDk28PKwtiJ$IssPA&!}1_lO32L8zztiJ$a4+by* diff --git a/target/classes/kalmanForSegments/SegmentobjectCollection$3.class b/target/classes/kalmanForSegments/SegmentobjectCollection$3.class index b4eb2764ed2234fa0e53ee425a17004404d0affa..ee36b8326cad3ad8c8c26de71f475f4f5e0b43c3 100644 GIT binary patch delta 24 fcmZ1_v`lD2GaIAb*eaL+R^J97 delta 28 jcmZ1`v`T10GaIARI|ZI|VCe$)uv delta 27 jcmdnawVi82Gz;e+1{TJD44jPrC#SF!FtSX(%8~>Ce+CHQ diff --git a/target/classes/kalmanForSegments/SortedSegmentDepthFirstIterator$FlyweightVertexEvent.class b/target/classes/kalmanForSegments/SortedSegmentDepthFirstIterator$FlyweightVertexEvent.class index 6d49f9f1669052807fb1d7dccb178b4972e131c7..50763d98c3d581d04f822511966ebfad5d04b538 100644 GIT binary patch delta 27 jcmaFI@s4A|RA$!u3~Y=KCeLCnWqrlK%J_P+2ulH7iivUWH7iivUGR%Nt{b;;WFFk*6AJ&aD@}} za++)8F%-B?A2+o2YaQSggWP6_JKSVU-2;7{MT|HfTtP|Rr4%bjD(M#y zeiLJXP5dFnU$*kkBt6|$3B|IMaw#+KJw4V6buN~R*U5Q9vb~nYvR1=&gd|8z>Pg9Z znk7tDdxs^$DT&f48#ylxbV-bYG%_enj7pqIN$^yX%xeBtQqGsk7O{3mZ|7m4E rB{D#T4C#+yof%Q$C~M>{5xGZP#!PKsxbz{V;!r7%2*`w)51jiCiKln) diff --git a/target/classes/kalmanForSegments/TrackSegmentModel$1.class b/target/classes/kalmanForSegments/TrackSegmentModel$1.class index 161cadb19ec805b07be97f927c0efd0237c707b5..1bb978629aa1242706dae19174c59bb9e11881b4 100644 GIT binary patch delta 24 gcmZ3?HJ@w4duB$H$sd@dS=ksE85lN;u<$bi09|7Rwg3PC delta 28 kcmbQwwU}$eduB$9$sd@dIk^~^7#J8B8CW+9u<$bi0CBAa#Q*>R diff --git a/target/classes/kalmanTracker/IntersectionobjectCollection$1.class b/target/classes/kalmanTracker/IntersectionobjectCollection$1.class index ca83115d0925920e862aa95403ddd26f72a5815c..d20b36b3fb650b79fb56584788579f2a3b0069bf 100644 GIT binary patch delta 26 hcmeBY?`GfN&%~%dIen{LBFa^T^ delta 27 icmZqS?cm)I&&udDIe}G`lZ%0gfq{XMfq!xr>n{LQy#?F= diff --git a/target/classes/kalmanTracker/IntersectionobjectCollection$3.class b/target/classes/kalmanTracker/IntersectionobjectCollection$3.class index 0d8617e456e1fbe87c91600d98eebb48806b0131..8062f9b0caa3a3c8d015a97d50da455b89ef1f06 100644 GIT binary patch delta 24 gcmX>jbV6vuQZ`1r$;;SOS=ksE85lNSVXI&Q0Ab|@$^ZZW delta 28 kcmX>hbVg{yQZ`1X$;;SOIk^~^7#J8B8TdC}V5?vP0Co!o;{X5v diff --git a/target/classes/kalmanTracker/IntersectionobjectCollection$4.class b/target/classes/kalmanTracker/IntersectionobjectCollection$4.class index c6d96fe07685262ed7373b476620fd99a9b6062d..f62146298f9a7b493c92300ccb6a477f0db4726c 100644 GIT binary patch delta 24 fcmZn?{wK8I7aOD9~V|$UbY4| delta 28 jcmZn{>=xYckB!l3@_#l}PA&!}1_lO32L8>O>~V|$bCd>9 diff --git a/target/classes/kalmanTracker/KFsearch$1.class b/target/classes/kalmanTracker/KFsearch$1.class index 325d34b255a8f50454ca7145422e0e92f7848998..4f6e4c744d2646bf443ba80da2e6783a006a36d4 100644 GIT binary patch delta 43 scmX@hd6IL(OlCQK21X!cU|`kS&cL-1$Y5gN0g`MWK}L3zfCJQoZZM4c}dyOuM3oAV$fsI2h#=&hCrGNsGpI+2xzwy0A$}58vp3zfCJQoZZM4c}lv80)V`orhWDv+oEKAhSNz6;v z_fN`7O)lYKP-oDXyq3|GQE&2R#<`59nFjX91P5V z7+4u)nFJgegc)>z@|-|XO`t3jkml6VKES}ZjX`$}ki*2l3nbZqN*I8c4=lpKz|5cz YW*IOTf@vcLV<621RKW;zvKfOE07`HhlK=n! diff --git a/target/classes/kalmanTracker/TrackModel$1.class b/target/classes/kalmanTracker/TrackModel$1.class index dd14a8b23b50c6762a10d20357f01ff1d8066f5f..e20ad418baa9e69751defcd3b473409625b83456 100644 GIT binary patch delta 46 ycmey(`JHpaab`sm1|}e6VBpfy-patEwVi=)Bap|$AP6Mc7#JBCHosx!X9NK7KL}(1 delta 50 ycmey)`I~dYab{Hu1|}e6VBpfy-patEwVi=)Bap|$AP5oz%P=yqZhpbc&je;{82@z=GRe{;62c`%f^uvEG8nlRkz|uVa04M5gF&v&ZnHaKj&+U=i;AM+ zjW;SNPd-@1n-*Ctt@01>-G9Xwsw}^r*@WE~%T)O=)7@Wx$FINh_rHGm4Zv>P)lkQ< z+B7_0+S2v(UOQ*XROP6lf#I{$Yk4@kL8@ zjWaYPove(Z9?LkI(ZWz4?;39gVdv1$#L${DY)wW)vc2SIFA;2nEJx|bZ+l39k1cxpjk3@E=O6pl4`T*F$1ZHx2-$2zQMSm6Z) z>5j^5kdfK6P~IYNDxz={c_N05=+w|bQX!byYmQCID@!t_6t2SaB*$iK zA&MV)qdio5Mfau+3iI*M9i(#`x-`VeSxRT^El0Of+3ZToDau~cRNOl_o>ElJl?7qg zzA~kA>{7339;3KlR*qHF9*$?Qn>b0>HARU+l`^LETwv%YBMO<{tDdxvp{4dl$sgc2 z2nt6%B@r60!(?EH|tLYp^&`Usa(oYJzD9D+s;$5?5L-B~7P!^IL z{TLt%R?%eGI6ub2PTNt+$c?wMAsv1FtP5;QA8iq>#40D{o zNrpO$uxwrs%dk^jfFYt&9HU56K37?;Wuu&2*nTZr1;q;-<2X$o3=`9$nqy#Rl(Qx{ z&fq16rH*S*tqaqzatvEjw)Az{n#N?0J}T_2WA$b-((??exv&)!g&BJL9LITFpnNc$ z>4NJ_lHx(7Sa3`#dQ;xW<<&(DSzOXUmfKa18-7XeBZIDSRjBuKshlecd0!6~J!*8PCB49x*`w-8 zgR>xOzq}*@)1T!X$mXc6nRr>XHJ6@T30ByQlcw;zN@RSF07deD)OAC<6x-CIz$7$W zVOajCZyXa|epnP@n8q^o%vU&G#cNbeRoc%`LRWR7iU_6M8obW&hUx+>XM{-MZCfBj`HI2s^sx3$MN&(_C1dG!_*3WJWKx&H#K}zGn~>T znqd~l$M}S*)(I%+m0oE$`iM*IDD6{LScxWlMg<*$BUqi8S%96EumfRsDqJyqfm<5B zob9KzDmcEv*OaUUrPZ{_P<3F)4C$rFP!dy~uAg77(Ws2Z1H&=`lDSOouFRlV+6mkY z4a9It)r{kvDn!)E>3Q@mQS(iWHyY%1v~Na?{!8gJNas3QW8DwHySLAv^~aEBIsIEm z7TAsk>eS8Hi8eZ|iny@??Q|l!P%ELwO1cYm)@?we;$j!O@1ylTR+q6M+C5Rm*16sG zXt#6j_USVAM6~^79Ex^*Wem=zrOFtIXk)iAq;}Ei#b`NuGg>d+{27@U80z#Z%!xMp z0SYtlqVx2`1C(ZPRh`PX7KvQH4NL8!)7#N9Pv5(b82*pJAJ8AEzkUY?qutmY>{7e` z1p5;{X#2E`&sC)Ap(1q+9rWK#(bz+A*-Ha{AL7`L9vq+uAEbtxpk8!Hf(!&x?nbL=>+BRJ1a qQ@_1HZFDX(*u~k*pxrlQhv5z(TZ;OJxc3m=LmVN}w?wMLcYgyOEYyAg diff --git a/target/classes/listeners/DegreeListener$1.class b/target/classes/listeners/DegreeListener$1.class index 0324d3f4c372ccff2a2653a42c3182b36cfa527a..f4d6791bfb3645c680a4c296d9963929e3d7bb67 100644 GIT binary patch delta 24 fcmdnXxs!8)H8Z2hWE*B_RyGDk28PWE%(09BOn3zu delta 28 jcmdnVxtDW;H8Z2dWE*B_PA&!}1_lO32G-3n%(09BTj~WU diff --git a/target/classes/listeners/IncrementListener$1.class b/target/classes/listeners/IncrementListener$1.class index 388f21d4565fce63a502be5b4bc0d7142843ecda..611ae7868394f2555dd6f4b514437d112a9d5c7c 100644 GIT binary patch delta 24 fcmX@ld75*BFEgXbWItwURyGDk28PW=%(09BQceYP delta 28 jcmX@jd7g8FFEgXXWItwUPA&!}1_lO32G-4a%(09BV&DaW diff --git a/target/classes/listeners/InsideLocListener$1.class b/target/classes/listeners/InsideLocListener$1.class index 15c79a1170abc499e0673a73699fdcc7ec5732b4..05f0173e578d824b88c53506426216db7b2392ad 100644 GIT binary patch delta 46 ycmaFG^^9x7F=j;*1|}e6VBpfy-patEwVi=)Bap|$AP6Mc7#JBCHos=BX9NK2PzbUB delta 50 ycmaFH^@?l5F=kZ@1|}e6VBpfy-patEwVi=)Bap|$AP5oz%P=yqZhp>O&jaXzE9f`hfHUi*=hY!k0wH~vk8|gqGxwgEKYuSS0er@r042gZt#T)gw0W{G%vk6>$3nA!n0#B5s4P&DEZbqmVFmshOkyd#V?mgPfE7>cZr0Vk+2*mw9=6tA1QN~Wjara zTLkA$WJe80TJC7AM!DQoZi7i*lu_<0;Zt*JR;x>>Y-a-*qKu~zYM3LGo2_mQTziB7 zRYII9BM-*Ip0tOerzK%N&H6%jg;gG32P^KU$_ek&Y0TkzW8X<;zDxTfvqL!`G@9vy z#_V{LHd@^fOL!GvnebssTOzFZj_MsJth@hI1mbR1AG?(hcCxYUv&p{m#(k0h@4fD8 z$6Ge8%;SkS(EG>Vro{UiLY`+C?J_R$8n(|7wLktwd{X2*=Xs8~K!ghKYKSq<=;c*5 zUSNR{^9ofi9gBQ+rMbzg*Q~Zr5uc%cs~8sXMdsA+s8w&&s#wJuvk9-6Uqa&_JyE(V diff --git a/target/classes/listeners/MinInlierLocListener$1.class b/target/classes/listeners/MinInlierLocListener$1.class index cdc9ac56a876b6c7dee0e644b63a2f4a97d085bc..356a82d0ef16d6dadd4201c9585945576cafd2f0 100644 GIT binary patch delta 24 fcmaFJd6#oTJTs%oR)5TZ#Cl{j$%i9doP7bLEg5VP+&NSs%rE*5;D9a_4wJn5`(zZ!V6m5K;MN#I3q zy2;&XlVIG&WxUQ{xxrRL1!YRSVC;^KH;g?|m$katNMRD!EL>$cX{wca+nJ*SmHd<& zgl)Wu>x6es39d!7vppw?Ps{1{ikY9!wC8B5o3e4!T)SI%4ZUrFUiw0yYR*PY@kU)c zk#J(hJ`{8VEvk3TmWA6#y3UJ7yy@zF-+Z-3{Je#?3_stdE4N4!byd#gVc25IoQ)jj z3IDi#qH~Ekg|>>Bwy?m^f53oyJgnd6jTG|eHP>9U@ebZ)un%P~5oZo@sue3-JCTGKc;??4iJ_OOsC+`#X*FxA7&KLdCWmR~{=;Ky!XQALr#V1hx0wTG4A8vm zpmh={`X3{0f%FcNsq7x??D!7)zKMML>3^JJffMM$AVmyefV835o2(~Eqqrz56;D1x zaZdI%dW;uKWS^n$8D8GSNW2<*fBT8_(#6?4;=o_fnQ=KPQUaNlksZoDbi;3pT!zPJ7Eyr zz$yBlCe%gAP|G)%t01ipfUZ%OXb5cv^T z@H4LB7s~J}uH!e{!0#mgz!U>+GHS_#*J6qN*P_)2Sf^JiQ58vazCh^(!YML*NQMr4 F^cM*s*U10? diff --git a/target/classes/listeners/ResolutionListener$1.class b/target/classes/listeners/ResolutionListener$1.class index 272af543a99a82b6c2dc051ad3af97f5f62f1e4a..a6573638303cf1f4ff6cc5730408f9cfd9cd82eb 100644 GIT binary patch delta 46 xcmcc0d69ENFtefw0}~K3FmP#UZ)M=o+RnhY5y)d=5CoEJ42%p6n=6=O83E552Yvtm delta 50 ycmcb}d6jcRFte%!0}~K3FmP#UZ)M=o+RnhY5y)d=5Cn;VWf&P)H=WM2*iRyGDk28PXL9J`nRP}l{? delta 28 jcmZ1@wn1!z4+o>|WM2*iPA&!}1_lO32CmIT9J`nRVL%1k diff --git a/target/classes/pluginTools/ComputeAngles$1.class b/target/classes/pluginTools/ComputeAngles$1.class deleted file mode 100644 index cf3188e81d0cd2028faf3bfe038a55f1a42129ec..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1327 zcmbtUT~8B16g{^sT_{`mYO8<rl14SE$_R^zi13~R-1&ewr#mWg;DP3p}E^}uIAlVzU^8Gn)WnZB`xVUc7{H2 z*W7Az97WtWP$J9>so%r{%o0W|sg8sba;=3*wXd#t9t{KYgsGb`CB@}1hXoUlu*iIq zP91uLQo?tDNYs>+w@xSy?O7Du;MBokU37Z`OBicCT(|$OZ6LhpFDYSln`gC|1K%|< zi77&^?S+0vzOl8629&!N^@8y6Hbft}C?l5be-*#OI-J;dJB}ByMh>Jp^-c_|5@!FZ zT@!V@B$$EctJfELvMlyWzYO1s;FSB=NT#j(+VgQ{({-i4=ZGMXfq_jzen35h5v_kL zalg{MM)-qI^WtxiPh)It@=m;u)V?A2m5mfk{>||Yw2|Rpo-H%JgnO9a6aN`tif>G_ zox+R;>arC2@IGxP%aH9(vpY3We~)jz zm?%bL4A1^3=klvF~afM(TiMTa7 zwk89q*BRzYe>vyuGGsPYTV%m7V#C7u9MTxIkwu;%T`~to8!`}2dYz^ScX-nm3}X$| z;{GlVrP&XZ8FMj)+2nA=DqYNKQ3iNOD66GLAhatxo-dnKH)i^b%dnQ9cIph-ZRrJE z_l*CW1pl?h?|g3SPzGMDA$(tUBPH7pg{B0n(5(krgb~S71uD*ZZ9O6K833L7FbcS0 zVTNJiRJJx|F-KMKgnmFpFF8kU-|vQ+g$0JF zw|Th3BWd@C%ACCz!*qJM@+w`-X;TJxL@3LZrV_@N`#~sMD}KWC371hlMeQ{h@>?=c z-1Myf%M|~$=C6EinMkUj-V|XdyRnv?d%{qHb>ugc5m8LC)Ji8=udk;>{s%y>F^Cc_ zIhbM?JC&`AY0OX+0%7h_(JS7O+xNSn?qH6g{5KTxn8g(rS5anQh8CraZ7@uvq?D`_ zbo_WZpLc(h^y%qt|Bw%c`N~OxCpBK9KpnZ@VhrO9g)QBS+Twvsn(x>ME!tW$yf_Kb z*Zej=YyX|mI`#DMebo+iOr7>nm|fj*u);8zI-V{)4K!Vjohu$tC%X-$M6?<5I2JMH zhfgc5Ph437zdYxQlO=@6fkYRN&w}6l&Vq{KtxI@QmfuK$;#boyBpbSeiaua zJ|qr^Gk=5&zX37pT*c$E3_|4my@%#Db+jjtM?0U#CtdCV1Nqu6BN?KiWF;1;G z(2){J*!EChsGRfnyrsB~T1SI(F|;0v3@ceSwNhn8Jm%KK3`Kh;lkh3g_UH0;2(~A+ zwEGO3)&JmXrwoOz83`W@O94DwEg_F`03Q{GeANNUeW}IU^kN|56FyLaVYzRHT%Gb* zx_Fu_xW*VZ=3f`KsWERz8{jdq998>5DLGDz9K95l<~DJwr>%$+@}g9hxl#(y za7yr;26#j*1wE}r+*LeDM1n0EEl+j@tWt6EA>HT6e7K>nk@o^QdVx~C@fkt=E3SW} zD2E#^mwj>7zpzYkKs7FV6DxEwx3MY98$vqXQsWcuG|5IED4MVov#Q@2!`1{fWpG>@ z*qpN7CofkyAXeA4>us0Nj&-Tt_<_|X*%v&RB__FNR{;e)%DfPdvvV6ebQVxW9ZmAw J$Zi07{sR0T`S}0< diff --git a/target/classes/pluginTools/ComputeAngles.class b/target/classes/pluginTools/ComputeAngles.class deleted file mode 100644 index ba8e16cfe68850730ee877001b61ca866cf178e7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8296 zcmb_h33y!9b^gz2q$iD@cgsd#gE3wtc^M4G7|Ai%k{20Sb}SWRsVC}o6(Gs z`Sg7$#`EsG@7#0G`k!;JpLp|gUj(pP4p|5ZY6s)lUN5mbnT)5S&B=kmY{uP?=#9H+ z3t_?B1I{7mNHl%eOY}xN^mk7()$gVR6@yO7O=JZ1ZN)5F6B#$<^klq4ZihE876E#^Q-R^gqxEw zH-a)uu~CJol%lS_vkHRgHp)?c01j1mtWeFJx;vSNqPDn zOolUkURrQjiGU@>qassd$=1Y1r>DO+mCPn$WK~yh1dYZW8nZL$#Yh#|bfm`}%y`KJ z4StzmmYT+=D152Hd|YASa!Ne@wc}_y;||zZfGep{GD}a?`0Tx8bZ5#-WI8e_*BNNC za8=+(pDm~9#*JAo9&=NbSS*;+nmCl~=i~z8y|hdBB@+uGScs)+vO2P^D-zx3(4ZJR&JA+LTT#aiitP;#H zdKZ7RaV=vQ;k0)ry+n_;s<>-xT#vO3B4=>WRh!SNYb`cQP`zZ%!VbYD#qJ6+bT`wsKZ16= zQ+*!MQHPD)nlLj8ap5FlhOQD!tl1`R+*nE2&=~{HAU(t1z0^P99?mFdP5X=O$~>4; znrBA`^SRde*1%Zz7Kbzy&8V!sZmMZLxmI;}y$L6iO)*9eO<v7qPu~HYVqD^D9oaFizSv~rLegQ3I(t-*X#HSWUQ7Jh?J zI?f^g)y8{p4+T%SM<{q{uI=bPKr;~u-uv4Jp`U`-Cn;`e#*wD@fo60vy<`*U)~=D zhMTnQtN)x#<%cos%)yEpMpXAWpZLeD&-tCc^JXdc+$dm zN|2}U*}^{&Hcf$y8dmJWHfIy20ZT`49{;RbeowG`^4+kn@iR951wWw733`PMg0xoS zSq0$dXzX$LWPOR?I9{;vL+z(3y@U^Hy7!AVevE%3xshytAeL5z`nF`MH+rBqncCgb zwwz(3b3eE7vchi}1IEHH1(zFT3doyEWe2%`p?S9OE4HWO3il)vJ=v5VU80+gxIOe% zGEpF=xr{Fuux%G{3bCuq%Mf_wP9|5{oNwQ{u2d5eP`J{V`F1w$9Viy1`%7T#b(SIOSiT#NM{l%iwc7_Kb9~6-lna&CL&XW zC%Gz_!B{BGl9C2Un-rP#w!E&5$V{1DDRUSH#y;%ih#>hY<*1=NN6h`(8{bQ zwu@l}QLvo-jYo@@4>(8k8PcEN7+(^POzYi&*gfXR;H9-gm{XMKMMnwp*qnDzF?pqb zE2Sr-3-1avz*&k7fyXRitqCBW7cF{;vt)8ON*>+X9r%kPe*3INoJyMO3c-mYp$o@{ ziTd72!RY@tzH4Lc6-hX5)^#O)pUGNH*oh>c3=puE=w8LA!2nDqsgkZP?$*bT0F&IF zr>P=E{UgaFms-NmE;^BqTZ#RJBy7yZCA`n+U=;T7-qPFxDox>LbftC^dF}JfdC}!) zu~p4ApD(FN3iGbo+pUR&n`(|b=``m_3boqgdEAbI~xCG@QY-GpNrYn!|N7 zZ^)s^pIPtE&G^vakEji!527CbsY_{2P>)24lKc~ z9EG@IDlNH}yl*4dIh^sSZZA;X&bf%*!SVSn(u8n_nnP;1rkwA$j$&U|Lk`X`+_q(- z=<6EBff4kN;vh%q?aR)A&Tb#V(iP{x9`egL))-!18$O5M)R+5obia;{=kTFDr1^;8 z!LjqpRpYSHc%kwBPT1(6WA@<+IH>16(rUPr%0VZ!P{*$Js(dBu|kDL1W z^~-YjQDfMrerf&)eo`A&50KV);3@j_zlShS$xMu%!AoIMEmtD_#=rlwX9$(tAiVuC z+UPs@9?xLEFwf`kzk&C!#(uc zd+`w7$7_MRaTfRC9Nv%5^9c1gua~}sk8p>NajysP9KHEGDb@Sy4OYAm#CkV5ynB6U=jb}1(#A_Hhd>dEl}X0`UPtjNjLdz68Zdn8z1TS+i6 zV_6+OW#EyB`;FQvP8XECt|q*uVu<11XsxKJ7?JBK)>rZ5D4y;rybnubV`VKhY*G#1 zq01``HZ@k2t%(fbKrM}}07coWYa$QAs;zFcd?`+!ll5#EPhnnd^%>dVi_`Kj)+oun z5gEdo62edVgj;;V)`u~rKsZIWF&ci<|JtU;q0kqzjHL;8Z-vxU(BWeXg8Y2rOS*bQ zc4)!#aq>mx76?BU9_GKGoh0I&;=dX_g0&bTa(O)xH7#+SIMNMU`tYK3kzWP1J}*6-v#9R~ZSesAQS~HwAK{8B z>U~07(nYBGZDwt?L7A=ekdavSQ(}Mr-opO9h5h^T`-_Kae+M~`-+^;gibi5##u&?F znMGa%Bw&nAujHGh78LSyvfxZ2ZO2=Dh0 zqD#BB#WaidcI6E@8PJ}>Zw|GWRJhj45#rx6N$N-j{T@dSN{a6djG1GSmMs6(ypQkv PPYCJe$@nwRCja?=jj{Hy diff --git a/target/classes/pluginTools/ComputeAnglesCurrent$1.class b/target/classes/pluginTools/ComputeAnglesCurrent$1.class deleted file mode 100644 index bd652cc210cda8c918efa1178a6bd36210148ba8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1355 zcmbtUOHUgy5dNHmEQF=um9)?orD>b+N_q4YghV5%MhfLX#N7mUS+sZ~ueTEFU+Se7 zs`gNE?vJWE_AU@aB#?UWdOYL#X1uQN0DBiRPefNc$TLh^ZLIyk`f|l}s2K8_zV!7LL$+Ew zV8}JYBT+yWqb?i_GZfpt6rbYLL!mnSFc1ur?Xb&(1Fn2?PL(-*>_-fh_T4!)7@RI; z&lQ0!SKHTl?ebn_M{0G}7^tkZ7)JJePjVd_->(CGHxpk=e_tzKdMTRKw4@fEP>rpD zA@1v2YmK6WhYrdNQv>pM@dz^v!=BIwJcudR>1wSnucR#v2ag#huDg{cSHLWux_E}? z#5b)}CuAt6d~XmjMahJ#45fiJGsZP?z1g#~-euoX#&#^O%75Q7FudtcDP?t=XRTEL z-E}dJ35LRc7^|-M;G1EZ88pW$rX&oXZzJ__G>*v-{}+cX8pq;}>;_>(qp~aXad_ll znPKj(Np?}kTLw1@m41I_`%EvO+CP*Zd2~!wZKS=oRld}bXG=<4?Z6&#WE>;OO)lX9igcns z1SaUlB*_^}S)d+C2D-fX6VnVAm|eOQ&hg|MS+W*6Z;?6{buRHpQOTDQePQio%#)PC zOM^&zVyj%)D#(m)Qx>mCV`wZBl8HXHuy}#e60LK*HbE0QNzjCf0n4FkZMBrb0v4@y U2`gA7X_EX(Si?qs#D*l*KVr;P-T(jq diff --git a/target/classes/pluginTools/ComputeAnglesCurrent$2.class b/target/classes/pluginTools/ComputeAnglesCurrent$2.class deleted file mode 100644 index 2635fb1492fc30277e76b25ab36473b54619a527..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1461 zcmb_cTTc@~6#k~ItSyUF>mVV;PL#~66VMOo9sI)@p ztzqzWI=v>9)awij<>AMjZH7!E=!z^D#%x$PpF7GXrP3{;SmqP{*L@)-b~ z<|qodYT*jQ)TwN3T*GxLh9~qx>V&d$?DqW@saq&gdjEw&7IT=laT6s5_J$UvjBPN? z3`i+iDd_Z{Rejz2S=pDTyPY8)42$KH1W#(5W{w(j$;K3>8FHIJ9CpMLX?k$>wDDRt zC1-eh5~;8GYqr+@JF#_Y?eWK|;|CEn+*6@G(ODx8F9}~2< z$;W6fVUoVolbEIxGbE>QIYHAT8T>-^GiDhMG4}yCmX68Y_hd~c=pzZ*L4sWDdmSN@ zKNfH+u@|sNQVM1AGtv4s=ahKA~!Y_9^v)(p!{h cfDAbp+wu_&7b~>SU=??8k0g^OLz;d+0MHSEnTr%ieW_Wim1Fy z=&fP!bvnH+q|h4-^OfPpogIcuBkb}l7{+W^IGaNnc^g?27}6DUAm0*_KaTg?Ty1;p zfHRD@!j2d0cuJW4p)zBx$xt2|wY)~xv^ta>PZ8Kkr6sv`#l9bi_NtrEf9eTUAE0)c z4B0K=OHaqf|J4Bh_15nsZ)qi@UvKds5WOfA-TPcq)Se!5cw2< zPIDART()qDVe&+_Hm=|*6~pKH9(6*+Idc1ci_|TYD82teA&Xhe*|?4}1Jkr9Wo(mS zdO%9aNKpN z5qMF=Ba|o)XYx15PeQUmV_+{r9jJPC~2Fo5TpYBN40@aN={-U$FGDVF^p~ZwG%cbUm9&vg}Te3 z>iq)NaW}vQwXzIvhM4+f8@v#_tVusy}&Sj4E%L9=96z{+JmQ}l;q$qMiiTqEx#GV}t)M)M28 z#y4F5L{SDed@lXstAAsa;*e^5_9oV7WnRA-%UeQv-eU7J?zG6pA1Ru$0p*`zLqxlo-EwZn8JWovjK)Mx>!;{pjVk=#@u|sPP OH8jv7&mY+zfS!L1JPUdN diff --git a/target/classes/pluginTools/ComputeAnglesCurrent.class b/target/classes/pluginTools/ComputeAnglesCurrent.class deleted file mode 100644 index 2788d4f1d345b2691f2887378ba835357eb516c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9251 zcmb_i4}4qIb^eYl``PkK;+QzZkTeiNu$>q~2!`1qkl0CZkWIkBsgu(1BE2|DWXU|q z`9sTq5(-^6Xj#iPwi5oNbJ<2)mc(}IwWDLBqm;EkVcnpxb%S*qE2CvyO9A&g_et`T zarV>J`4>y~-FNP}=l}Wcb-wxDSH1>dmE2(=BsgOzIXsX^ZAqt-nU;?9;Lvc^U7H$6 zx|xn)&vR2*3t_>jdz^jFftJkvL~5X=S3kF=y}hm{s2FmTO3)Y$a_UNDUC)VS6Z_oW z#NbfUU7t)QhBEGkM7CW}wr6Ok;IwFv>!!_KdcbotnRSjwl8V)dR3f`Zu%V%3#wX|3 z*e3{gq~mS`Wte573bQFiLt|eR1T{9wQK6$+8x|@BbE1iq+cP}4)AhDEJCiQoMAI=R z+2?o(ea=mWv%3=+!IB~YXR;ynnH^7erPevIy#ro)I29+yhDIZAOVXiD`_hRxsUqtS z#N455BAv=uI9+gxTF9p;ys5$>EVl3g!8s*b+{PI=Q&67ub(vReC!j@>BwH?bGP}E- zA@Ucju+fZj1$HLwW!D|-bCSbK-q0`uea<*VJapIdY@DyVMhd%bvv7f6LC_YtVB5k7 z+#A74T&Nz8=;$IF7pp1@On_$GSk_6!4P7qW85V=i5Tk{k+XR&T2D7TskL?y1F|((3R-QIKgllPu zgw{iHW2h8aqANGwUX2}aEL@jMK!2nBTN^tOqu?p`00p0y=U_tU%AI>?t#%6x62D#; z8JToT*Q@!f5yyay-O8Y1z|CIa9<;EBWzPs2h(aYy+89*AN)np;C5C;4T*f44>n{QpTKX>4;eSxm5RHJ*}()06C*C% zuuZ$@BK)R}-@=`&Ssjk&cY?e>3Jf=Be_-B!3U^!h^z=?+-rD$WEzgzvoMa--4J>?? zq9^vWbPYNKu2DfZ`yCstbNkuU_J!ikB@uN2WRBc`!OS+DygvV?g$0$`c zjnPlRVD7B-Jm;Xv!wAOlsD%?nm8bApc}>{(GklfYQ?wt8g>+Tp&$U!OMzc(nN;ZTD zQuqrSkK-@3T&Dc;V^CU`f`=z;d;@>YMMkJy!zt4tw2C)I)80VKo&nDp+MR7NUoAf9 z-0CIZndO3B_H7?1k_N~;W8+yh znWZ?99#{FkYvXyXoMrU2g})ViV5+{F(DR0eC`tjwS@=65xG4+7(y5q_xLejAaAPb! z=~RK3Y!g}5L%D%5pD}>ih)xbADo+4W8s+M!YoIy-X(tms#-7z;5SBd{-RvAl1@D_fqV!W+E`~Hw^>{E(& zY`ly2hyk3TAy-cz%|X%``m>)0x$PMDZ!vA~uE?~NISBjyR0*yuV|qz=I&{e`rDLSR z7E5^ave;sJ*e0fq1wq&m1j_k+soX0Pv87sU2Ac0zJ$)9%EB%~5W)$%YdS0=FdOsqw zS)gQ29`_g=kb7y#T(*r;GQ^TocxDJT7{kYas?AQqiy$F&w#=7PsWjWtuzTe$wy=_X zLJ@1Fo*tLeSOI2O5{QAkV`-*wS!l~5;mNVgi8BC-VkJ+vja6IDu;omx+NbPF4}00& zo%z<+o4A3+E+uEF;7b+D7L`b?6MA;hwy?>Pb4m&IRS{W+H`tS<$(H3>{AZOxyum1;H*>0Z3RPhVYZ%dcq@sNSH^ z!S99qB!eKp*+d8hu%R{7$nL< zUOuSsKo6BCtBi_U39=IOQ~98CK)*x!kvm)$MKO!mmYkL`b8bmw6sF7%l(oQlhCDXp z-5MmX^zVK2v^M<&uW3O%U-6-iJ@bBQ20 z91ywy(q{tSB{hxz?{22(J`s>`%B-u4z^BP|P4k%~FQt62bLw8fBWbP#m8g=g4jvh% z&LJgk&m&zxQU6?2;!;aK$WlLrZKkXwI8Z8HD^brGfWS$Bh-@dGE>5@grhj&%GuaI} zKpBy3a;;X@4>KeiIy()*mg{VBWG6G~nvCnkcBeCLD!7P8fM|?Nx*cBU93cLm;%92% zEUh}OExWYp)U1t0gMcby$!>Bk7VL@*8G`ZHZe6&?mc6Flu?+N@SnE!7ZT@u*nf6svj47u6HsaU|9&t4Yq7;nxxD3&c7aiAVDyE0IUc#9?{FD)1 zhft&U=p5DPEjqs~zTv$qKdbrFPYR`*(=-OVY58#uRvgFNM>!S3Dg3NOn4cH$^Fl7W z2(x)rJC8GG_{&g-`FtV+vyGUMZWQ7QT87{CoC|Zi)69lXaI73RG#$g7V^}zfCI0A~ zaV(GOkV2f}PqlEWTZa^>WdbYvb#;DAT1p;oX;Pez}o(%QFM%?jN{4)T*J}U?&T-IQrt6wbDB?p zCG9c9+rlgA!zVzn`4|#9;&s7eNR47>D{0t?@11;Mg=!lPXd5N;zmitoj>Wi^_x?Mu z3J$tC?nBIIyULfx=m=6raR3LY$70UhV6qyQ zE7b!2%9{%-Z{f<1k#-J3@1fB`zlBZzQQYPaKRJrK zbTom_7(XAwJ!L0wZwPnz@9&{e^l2RTkKqrG<6(dLkx_iv9~~XVm_PbT#Sv`whb{EN z34Bcu9YHhg?pu4xykEbBe)_8(pZLjz!%nwgN)B1`oyEZ zvh>_OTu=XG>6uT^BX`ml_wjiIH}fXx7JL!6;V9ocj!%;NUEJqWcn){tySOLd>*sTk zeLbW3@5x2DqE6vAWxJC_;3a&Y9y`hvIuhe+&1IkdX3qFjFBhm@=A8OAkrt1>WB zkLQ-xSF%!=u`CWBG4+mh@@Rb(rwb~!)`eRuMtH<-vzqHF#$^e`dIC>RV7$NZJSJzi zRn}Kd$TBJ@4Z6J26rQ%Kvew85_S9DuWUsD^+zYF|y3O*XxaWj4@?dlX3+tklV>n& zboINB>*{f7)7IoC)oV;6fO7Ndco9Cwa(tM7Or(%_5LfYzcRN1MztD^zi7z0-xf}5a z4)M12FdoIjOo*d+l8O2hUdGdSl}Yy?_&HmNnqJ<3MR zw8|y-qENG3#_}LIH?&aJ$~q?N%FqJopjKhDhUQ5pN9DA_5?L=Bn3RVxmK!~cM`WX1 z&USee4@nnCRkXkX*(6u6slAHpCCaiYF;&@HglV1h3enGC}oJD=dWiwkz zB{hDKe@*)+k?jM#k-v&`5o*5OtgSZX<#Kw+NGw-VVt@bM!v4L5{kP=zmupb%??9X9 zcHmrIo(DXOQBoPIO;sOTg zW~!N*Jl+;wULV%#l*b+~Psl+3azB4}_+N?Ji@%fR5iS1Lp)pB!uNW0?D?f&Nnx~*k z6TJ+Xj~ZD(;9SorI~|wFLTu!Cvn;`Fau)xpwG?;BG86f0s5KSJMg9UDlmi&0@)t5A z6s{=vvg9D=!fcQ>FXKPLxx4u5w>kwg3i`}S(S~SN%8mJzqL9a@*QTefZXRgt

      *h zjr);tdQB&rc#Nxx0qxo3sIa%ZX;f}fHGSM%VJ3OtHZw%2a*K}SV>y4_Du?*oL~q6A ZHo2Yu(o! diff --git a/target/classes/pluginTools/ComputeCurvature$2.class b/target/classes/pluginTools/ComputeCurvature$2.class index c3eeb918050ba709ae27ea9cbdfc47d6d5f3113c..5f672a94a0fece1e4934de9f928c316346b9bd52 100644 GIT binary patch delta 43 scmaFM^^$9Y2aB9O10xVJFtBQEXW-fhWH2%C07*8GAdBMWEEZ-)0I!e+HUIzs delta 47 xcmaFK^_FXc2aA$110xVJFtBQEXW-fhWH2%C07)(&&A_6_z{0?|IgN#x5dg((21Eb= diff --git a/target/classes/pluginTools/ComputeCurvature$3.class b/target/classes/pluginTools/ComputeCurvature$3.class index 3a81dbde2a9625190da3f418033ebc2832d28b55..9c57889e692fd0c403a403c50f14dbbf2a7af479 100644 GIT binary patch delta 42 rcmaFL{*Zmc7bZD<21X!cU|`kS&cL-1$Y5gN0g`MWK^C{kV$A0OzI6vh delta 46 wcmaFJ{*-;g7bYcR21X!cU|`kS&cL-1$Y5gN0g_xmnt{cQfrWu_vJmrm0M{o6Qvd(} diff --git a/target/classes/pluginTools/ComputeCurvature$4.class b/target/classes/pluginTools/ComputeCurvature$4.class index be460540d2627312ec51888e5202ef8c723aacbc..0eb12ee94f350a15470632fe6650ff765d7371c8 100644 GIT binary patch delta 42 rcmaFL{*Zmc7bZD<21X!cU|`kS&cL-1$Y5gN0g`MWL6(5YV$A0OzNZII delta 46 wcmaFJ{*-;g7bYcR21X!cU|`kS&cL-1$Y5gN0g_xmnt>&NfrWu_vJmrm0M~&BT>t<8 diff --git a/target/classes/pluginTools/ComputeCurvature$5.class b/target/classes/pluginTools/ComputeCurvature$5.class index aae4ce94667aa59b3fb57513331fa3569f27cf1e..bb20043636890bfc85a916f24b1cfb61ca1e0c63 100644 GIT binary patch delta 26 hcmeyw^^R+UFAJmoWIq-$b~Xk^1_qXCoAX(i83ALi1@iy^ delta 30 lcmaFI^@(ePFAJmbWIq-$ZY~BU1_qXC3@i+co3mM%83A|71_1y7 diff --git a/target/classes/pluginTools/ComputeCurvature$6.class b/target/classes/pluginTools/ComputeCurvature$6.class index 1427ca2d0fa81c04e5dd7ee02747abe287c995d6..b33a2aae30779f72be42d14b7aae994ef28e78bf 100644 GIT binary patch delta 26 hcmeyw^^R+UFAJmoWIq-$b~Xk^1_qWzoAX(i83AMV1^)m5 delta 30 lcmaFI^@(ePFAJmbWIq-$ZY~BU1_qWz3@i+co3mM%83A}g1`PlJ diff --git a/target/classes/pluginTools/ComputeCurvature$7.class b/target/classes/pluginTools/ComputeCurvature$7.class index 45999d60908f293a6cf73a6ba21cae4e577e48b0..e36b16fe72eeea799f868f4e1ed6386d13c902e1 100644 GIT binary patch delta 42 scmaFG{)~OYPbN8i21X!cU|`kS&cL-1$Y5gN0g`MWL6(z~rJ2tI0K;Yn+yDRo delta 46 wcmaFH{)&CWPbMW}21X!cU|`kS&cL-1$Y5gN0g_xmnt|mc0}BJ=WO3&60N!8+>Hq)$ diff --git a/target/classes/pluginTools/ComputeCurvature$8.class b/target/classes/pluginTools/ComputeCurvature$8.class index 5460393f8050ce495574d0bf22caa876807dac5e..523600d2d4249d431f61d6e376d6d08790ca5f70 100644 GIT binary patch delta 42 scmaFG{)~OYPbN8i21X!cU|`kS&cL-1$Y5gN0g`MWL6*ytrJ2tI0K=FE<^TWy delta 46 wcmaFH{)&CWPbMW}21X!cU|`kS&cL-1$Y5gN0g_xmnt|mq0}BJ=WO3&60N%O>^Z)<= diff --git a/target/classes/pluginTools/ComputeManual$1.class b/target/classes/pluginTools/ComputeManual$1.class deleted file mode 100644 index 12c15248e707f2aa3544891ba9bd714c0141d623..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1419 zcmb_cTTc@~6#k~ItSyUF+_8{hnCkhlCxb1e{K#Eb zd%akT4IaeYFV`*P7>c|68Fzgic<$r&uIOkB0}P{yHrCQloOsB!3K<6Uj*QAn1lvl* zZP2k58A!d#FjM)>C7h{-C4_B_!#jFlxfQN)que1W8UAgD^vc2pkOrLNWHd541lOew; zy@2c3_`gZO0WvuW}rnFkt|i9lB_pYQzD-N&}j~$ zgi98t7{*RyYhxNSR0U7ydsOs_bLjT{ZfIDTV<`U%g*;|)#l}^X8Q2S2lrpx)Fp-i{ zvQp6Tqvd?w`&rVbC%c^@9}M%A;{=auoMwSKa>2$J#u*BmDh@m1p-h_Z$OtW(S~I*n z4$;^AF+VH+ozW`w^zehAw6s_lRvKE zdSWkOo}>&a5x(EvGe ZFt)`58ZMS-pF2cG++y(c=_!T`f)qK&om6DJ;WtwM$Yy(^>g62Z1o zaT|1OMFvu@GR#!|a?aUd$gQic$b(_XhK2J5WHD?bk0L|1Vh#+qWFQ{L`)v_!^R_P- zMp~-F{T&`kvp-bk%*7a{)5DePbTO+#8Q>wIELU2A(5~F~eA%wM3Dc)shK&@p(`3kR zNiX0!HvVr?{8w7P^SPx%8F-DB@O{~fl1)?Vw6s{lRs|Y zW@0a4o}>&a5x(EvGe ZFt)`*8ZMS-pTjb4<1R@iO@=i6egJ|ya4!G= diff --git a/target/classes/pluginTools/ComputeManual$3.class b/target/classes/pluginTools/ComputeManual$3.class deleted file mode 100644 index ed9a818ce267517e1185bfdbfc1ad01d3d5a49f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1027 zcma)4-%k@k5dNmEoUMnghzcTzTA{zNDn6K$keI5mNf8qW@7s1$mMpu?-JXAy51NQ3 zJ{X_eM@A`uB^Bf^3^RdM+#({^X_wivdjjB@*g zjmm#;)e}OlZ3i+B!eWR3R}09Z7$T1nAzSf)VpkdYJ~{8n=vef$BrJ7pU+5DNDIZUg zIo}vzefD+bU2ZJs^9Dr3EQgh@)LM;Vs|K&7=-Abn0;l9O_$M{4^{2tv4Xn+ zRte>Kctfn=9)TQNhmf()2wU^r<{qqY`tEMsi)Xj!%^pi;@phjpK2%fk8aVf5sBWA=miqO)T?Ceq$4scZ~GBh5A?AX|N4GaWrOGNUJVVhRrc*!r-~qu`yx2 z&t9hVidlWvzPBAld)CET{TEgmY~S!;nwaIDUIpavDD}#CoSs|Q<}-&1YG|p(15Db|RzD(CJ0dk;vGN70JX7**&q*RNUSXkH=DJ zyFHd^Q79Wp4Jw@1>BYKhx04)l>~wm&hpEu&O<7?)n=75kh!yX%oS1y)C;ge> zSX$xy5(Z0@M?n@ulO2ieR^;H2lguWfL{;CQ84blPDzh&cixMiZV=Q8)GO=VLZQy)` zb3`>RL~+Hy3Wa4}E$5ZmOHCgPU^y-n$pSLE$i&4WV~uM>ZH_~<6A36(ChWr**&{YM z8$L4Sk0uj#71pB0K)u3RFX-DCIDkg1H?WS(p3T4}ny`VIPurP}MAW9QjK&gHhKlyr z?-i@8#ib@TVv|ButK~!wIMyf^my}w#U1nPXxE##}uAs0bAaHeMiHt0Cr%ffp!L?HokROXp>9E zV?60}(R<@2u0sa}7_u{ac5aW_2dr$IvA3e&QgiL0g08~#;u)O^E9*NtT<@C`)Kz`A zi5sw+T1zn=MMXLyygF+UrR_1%gI@YBp~yy?V;!dBy4bUsSUhx{l^*W0Qe>bH?=aA> zuvkmxT{JNu1}cxlZOaj?`%JtOH&Z{_tK+dW<*W4|-R3yfkxot%?S4UfYav=R+Tya{ z8Z;3BL#{kQWiwJ$hs+)@F{JsXEg6y-JGxgmjF}h#MXh9*I7fPH(p_B8t47T!+Ikd8 z0}P_-Sy8xPB8A(yHzsK>lVoCCTC%qzKTiZ{WDO9x_STTbTv#=62-KlJVUH1DNdbdS z6gLcx*pW<&fg>gIQopNq2*W{);aw(vOAu7jKl-eAmhS%B1*K~w;f;f&h{#77m3VJqFOHOvY?jubv-;*Z4(4axhAIGOu|i9Zot zmQj}mKBaJhHh58uj+0GMUQc#D#uPd06p>^il69muhIWkE5!yMKC;}60EVH+e&`O%@L=c#!5U1_B{k}6cIJ)R&cINWB>T+x&AlU_@BbewmgmM#3NaAlfvnBVaRv9<5goEM;cA_`$+ND-2L6=Q=AacH zwG!HCZ6~DPI%^vEGlkXj^4tJU;9<#?f3DD2KLcC<0`)Ud31bSAm<=;`gV6el(E92bBnsTTg~7Z; z%pZ^A?9#ouY5uYs8nCcv<&cSwRcij#ypla2*hFVHBi56p-BcWEu zX4kB9tf`i%I#TpZ^~G#P@A}f*r1Sb&j(&0utI+)wi=VfvD;6Ktl=4w&b>(bwwuG*M zcs`_P<)hY^ynXV>H=o5C%~pq?HNAQ1BibusY3XH_&h0J4(O&QnFL>p)#8BL3iYOA% zepZNFwyPRI+CO$Y`NGfhotz!l%&+(n$Dp*^?xuivxg8b1U{3Tft6iOP>(*7_m}jY| z#JMW?yvpDIcb%3NA?D$5HZC`poUODnB{|K-`FxUIz8z`t(8}9po+1gS-fn2NLz*Xf zHm4rqVx@UgHq_-TP_xNkR!j7Wp4bpi##x7sG&bMNNm5C8*0;6k%&V?3)fROv1Ma4@?L>x?X*=QF#C+NrA(AeOM}aXO zb!YLh*z8BOB&+SFYL#TQXj^Bew{1)tY6mfwGIo7?nn%gVux#9Es_S%`WR~dBvyK&w zWtmXds$HhKUea#n8J+B!TAAl}D#bRDvAb5?T4 z#r}cRcAM%(eJDveNx4~&xgJyX%K58Gj`{j*&0aFXLKxTCpj;&nbN ztU}bkJsltlk;?XiJAi*v~Qb%W*z+hM(7$3q5nMwp6S#rCeFn&-dOZ7 z$47V>sjv1V+=|zo8-z=RQoDI>JiQ4@-`%-P>5`+rMI~lOB4InNaVwp+>7#Tq&usy9 z#Q4x>c3i}RW*JWnJ}iPxh{v5>LgcOx(hV^CbGSO^e)vjm%2OiG#62nOR$QV{hBW=kop>D zh2w7s?o(J(mP3O(4CQc{jHYpwCVUFvvMFrw;VySQ?03H=(Kdlyr_tq3-aEfw3v@p#21n=5WU~jXAuh+Xa5#@=3hEDTjOd z3Hf1O1i3JdkHU(X zZlSWlE6E_Prg~cV+|0|v7TySMK_|BI7HT^zwBa_iBg4DCyRi%RaOFOJkE0t8@nY^_ z-nu=CemqI9cp5j8&MkNmx8m!FYV}`E6H~Ey_1}gt?#BaE|J|HnUr65Wp}8K!ahmQ? z?vRnfXShe4#fASYXI!Z9BGfqNK1ZmfFx~*KMA8kfW8pI1SbnnAAcI`x9SWcH*YXjX zx0*h(FR!bD_PfS?;q?XWcTVHP05z;_@X!Q4-(7RE*QgkPYw(VnaI*PJ}2o+-ullo#7Hasv#h$#CT~re*Y4 zxT>kHVj7PR)I2ePFADN6k;P}kO`gx;%Q<{4hp!VPhi``ib%7kd6Aq4}tuB~n@#V1T z%Di_P-yaY}nnC)?B>ssQ&p=jZPT)s5yw)!WG}n{)?RAxt_&0a_OaE~lfDDwqbyb`$ zir8H1Z>|_;&mT6LYAYu3Uu5ekyfBUZ1I6zNydJKsqkwOUfHShaa*F2&gsaM$1LGK} zt19vytPOkuMqMy$xRk?Fs*D}`aV)P3o>CQhQdQlLW&yb~pvKW$0{EZ{=&C?1xF6L; zz-l3*CH%09X0a$om_CbU42i8fD^zU-%{{Y{ZX>(jd{TB#swI;9+@N}uVFU~_amKI$ z2bqaRu?Yz@^G^0gIM~O#zd>XX=k02mb4T$m+{x!L+`$9<2N(n&;}yWi@e(imUcsmE zW3K#?K{JB~)N0(X8hHD686H&E;<(y{&!|2eRkz}V8seqX2p&?0@OgDR9#-$+`!1YR zALWJ3eY}u)0FSBTymC3oo0Ze}qME{!>KWdtd>da<-@#MrWjw8ZfM?au@f;6>&-=>p zg0B)^_66~nuNL3(ZN&-SE)4s6@RDyYzNdpb$4t+#$_MufUz0jdoljeC@GV!%)d~jd zCf_o30mIynX5SKZAxGuZ!o_N(x`;t}jF(RC=mfdFSY5&z@F-E$|F$mMtgIdL zYyDi)q5IObY8zPR#_wkL)&9QHcZdFj?Y`DbsLrnSIdwxnAA7o+q;qhiwvko_e#lbv zD*KNg;R^hO1@JZOpL2GJt;I4V>7`Sht<^Y*gTAipmr=`j^iHh+fB P@mu~(yDwfD=|8^#PE6tD diff --git a/target/classes/pluginTools/Computeinwater.class b/target/classes/pluginTools/Computeinwater.class index 546ed5e3dcbe7775f3ec5a8f7390c1700eb677be..e4a0711d8cae96661b04d63b10f19d0afad63691 100644 GIT binary patch delta 2317 zcmaJ?dr*{B6#w02cfb9XZ+Xh&LkUPoL>vedaq*EM2)Lqxiuh!OFIZU@*oTTzYnEod zO0Sxlue749d=#!CJ+z0fw6d3Fn$|Q;Gfh)7{Zlg~oo{zp^q*#T_MUU^Ip_Y)IlpuF zLfO$=gM)5nk?6A==^Wpl`fnzXEz*q*8KY+(=v*%PRZVBV@5O2~yU?{-D5{faA z@6hL_mZD6+B!-xN<8rs+w7OfI63S7*f7Yi~Ss0@G(Ud#fN~7X5VlpZvOvN+?gU#Wd zWwm=0hOo?`-#Y`_D`$6sDXoX9H8x<6yQ3(`Dz*2^E?u}13 zs$E~=Fnyju$WjS3(1)U>cm0GkqQ$mtj_SF)ez>^fw-BRs# z+8m8)V&!~`DK^!JwOA+MDTb*2js=V)JdO1{cfd%47aP@rZQ>nf=spBN3ybqwea3#pZGs@r`jVmd;sJ zJpUsyiVX=$0>2U|@zs&T+2GJla=c+{bT%7H8=3rCYz$A0zmZn$CbCiWXIK~66#-`$ zBKo_n-oXEk?@qx->V%x@BfWpb{G|6gUEo&}5;eHQe@&1@T*PGwSMUipC8mowkIy7r z#pm3dcw2+pe30DCw0xahn>HR=nkf+z5rGoKq7*VFAq8c~pfMUogQQ8ioMs`bsbn>c ztfrGy6Vn*bk=-a_FzZzIk-ZB^&4?3EqTVdYk`IrVO|r>{#Q`*t zok)=*gIBbgBV>ad(G911p~r`1HR{<)hTXk4Wdh=ts(Rp6??^j^$4_C+No*h`g%lq) z`>@prsdX zYMVk0YNUs$33IWSG&>FN60Cxgrl%Df;K4R5!|Pa%E*jgjB%i}dVn_#mp~?LV9gOIITCNM!kBH4$M*V_7&vk``Gk z4KI?ULm`XBOL!T2g7^mlFH`u$1RnyiYlb1Vn=m)WKuZNz#+vF{~RO(jaMNZIHL;&ARL}xJX}F*Nu-g z7~ZdWN$tdhyKQA%b~irNU=O12b(3|&{RjIHeOCkupnChc-_uXtJZc`Wm1?;i1L#S< zpPHCUuW>&1(x3Z&R1lwLK;(J<+6s;Av@Pl6b4@S!yqW}N^ literal 9288 zcmcgx3wT^ro&TM@?qudBZPSJ_)Y25%(mcu_Em)JZkd{7D(uYZCX%))t4UDTh@t}O?xH9{6_(vDEDC6orql;2YGGwx>bfs?7gm&gsI083?jqga|J*w_cbcS| z?^}egBryEvo440Hg${+- z+G0+iwmbF#g(W?%T}U|T!IZP-@L#x!iD2v*zY47ygKn6ni& zP7A2B>{j7W)=s1{gHAHB#wqmkt7A3M)`z$uHHF?XsHqs51h-pE+c{!)DE*+FOAuQ? zMG>*;=KqHkB5CVj%GzV66pXB$OKJtybULn6Sgmkm>9kcjEk--0cdMKKHE&mV_H`yd ziJs23!L%5h1kcp^E(ue$HZx#HQHi-G8gRZs<vY^=k&pEd7bSseRmLVc;bF@rE0wZS4C%a;jN&p} zZs5%dv&QpUZ_zC{Y1_ndW<^8??Sj9f(73d@XTrq}!E&XEw_v5hY^Fztft|9q^6uY$B=U^)EK07z;B<&6ZSCgA@N?o~}b*RV57oxZdb8E2{>kM3@aLF`twu$xF zzyzEnOHAZ>=CT={E;J{7cA+DNZd_|%lS17%T^^X|!DgmBM+yQZq^FnksA0SP{SM)3 ztBGxR8(Gg6nHPKQfnw4g=(D7NP?3!xl7U1d+=uH7^iyf$giBaWY{w3TD%U8m=20|2T%PZhqcSlJmU|;B*i)_qR4ATE&T$7j znzxJ64w*PCdYKj0i`Ga4N0_|NVkd36MJuIQs3r6}P5d_AMXUEYhs8M8h;eQ;@g7Oq zX4-M-Ta0lULJr<*;(gwMbue^b>i4x)p0P9KR`_+HZg|` z=9om*SUF~yyKs+`u#YRe_ByBJj!;-hj_By_4(*Hw+8et$sT#U>iGQ@?UK5|heT+0# zTb*wet_V95bG9pKxMPZZxz-0`@;Hi58~7B9!+7!#C75^s57IE!z`$9K>UF*y&ATM= zJY?bz@P~BjAuGQ{=NM-DT@m~d6Qq~ZY09R8mA^S3!y|aqz#l6t8YeYL3Gj2ciO=A( z%rcHUoVnTVN-=zw`XcJQxi-T}#s)j%mI$c*za>B$tZ+=JFmu6p!I^;!K}c zXq&z>iRvH2h>200U@qNY7jy=o-v^i#+%S*7S>KU%)xb$+Ppwa#)4eRRrs$*wB!!P* zuKf6di7( zOSx=Hi~{*}6W_o$>BEJLcmM4ArF;&QAn3Lq0Dwk~%}*p`b1$8vi)lcdXWr&z2Be5H6iP z&c4K8(MkBP=H85>FJE%b%UfxY(#=KZyeaP%jd11U*SwmRaR#nokxa5_OZ4;2&sVC~ zv}n*((*+HDfPNZ+zjbo>@F+m%xowt{>+Bh}az%Hyl^V?CoWf9gcTRWkyS?J-tBL$} zO#7mz{cw3GIa=6M!aI9GE%5#j!=A;b;5EG6Yg;M(uyYRUdQ{fT>3Kxy4a;ULl~|w3 zSf!DHj3(;(3Rd#w%~sZb3(hWo!LU0TW`h$A@&Fr#5-j;8`SLr&g(pXQTBMObxIt*~EuONI(w zM^wdBouSO~r-JUxO;xXEFiA7X6z#2i(kz))4W&lL)GYC$19U+(+f;K@Bhh#D^n|%& znw}kUfsi`YTvMH|;%vn@jZ|1U2~`g;oLhayVTb0d#I2#O zVDB{E43BecsJGBd!i7oK9w8l5SE{#~s$J1;mF&Ry3@sly1UEJL6>62KR;#Pn6N&;k z{O{)gq3k7J>ZiNupnPjf)cOrw8Mi`@?`N5OPR6o67Q<8t1Jde^-zo|0>4tcyo8X2{ zxj`}4T4%}Y({Jwej!bE1i(pyHYiS)pyrpdf^IDdTV17$u)d&`}G**vbVN0Vif{R-kYeum63Er>7 zCH%QW<646%j_zu)fpj*KLO0^rgax=3i#eBT#bzw!-CmCln$f~9Qq*!Oy%NbP^h#tB zx!bffReB}zRO^*UBD!d!wDr_SE7#}wkOp;9!eJ7JNZZC8PugW#+UNLf5T^D2lD2Y6 zVYgD)ULx%y1N{{CItsg;!tTH#4)a>DQwzJ%BNGVQ8VK8}%_xkvY6FU}t%0zu@`nH| zY^yd~O-Mw6^h6X$PsB@cJ_V&AE5GNNSv@_5#3-)loHvHIZa#&pcAh}j7XNp9RcTRO zTGW&lMrjc#EoxhiBYqrfPGX}%dsM#sqY-R5hM%1 z5AKG0QRl6!PtdE5V}4_73evXgk2C#Ue3MtuTLw8u)k zkLxlX8z)jCJBs7Su%JCgRHraT`#c$sjp9_pmq+oHQ9MoLPe7>~)kEqSPSe{Y&>qxY zbpt(MH!Xh?=3o!za}c})14v*HD>>rr!ajywieBhqFEVhDMIJfa%8}^DZ~zbB5FX~p z^HChZ3A__u=B)BGZpVvwzpB6oR0BS!+Hi+jg*(-?T=#Qodn4{vdpSIG@kw%M<-;(XCwlHNJQ51C6tj$~8dWtr zRH;LgiZ$E>taQzMX|8tChm-zFS_!c8>OquvLBknt@1K(Af=$ld|AN0)XM^Hqq z)$zc`411^f^!*C{fqA*QkgG~8R4r-&S5;_H3snTctFQUl$^RcMq=c#Q(deUF@m1M6>70+)E+!jEm4>7N#BMAYN=|br%T*q z89yF(FVHDH?w$|0dp_V!Vo7tSS_1BV$XN2Y`$52+#FFMjNp9tqIt2~!gE845uo>EuxU%dLHT3LZ*Wnah86}}p;Zab+uD{voXl|#j= zm+6C}m{pEIptaYZ8u#1FX*u8pYVt)^h#%o1>g+NmjOF+-pXNVhb$FS+@H5tpSFoS^ zx8Ud6=T>_%1l`IJb+ziERVkNRLl2a0<%qh5t4i%dJ~ z0px}NazhDHZ44kc5JGHPhss}LIn7|G6^oeZR5v#()Fu`oFqtY2C3@7Bn%e&bY%5RJ diff --git a/target/classes/pluginTools/EllipseTrack.class b/target/classes/pluginTools/EllipseTrack.class index 78995425bf96501a122403300637b0d4e9f902cf..d58af6afea1cc42390dcca8c450b7c3c5a2d6d01 100644 GIT binary patch literal 16713 zcmc&*34B!5)j#KDGB24tmJk9AYXp>R5Jd%=00N0fkOU+l7{p~r9$|1Y6K5vuR;^o0 zMJ;Gm+_%Q6wQ3;*Q?1>oTU~3ltG3lv+iF{=))l`0xp(H}4N3Th{@U+rG4H+m?sD$` z?C0Hi=HO#b5Yb7-Jd+Hj5pALNmSDI(5(&l1P7Q^EZ85t(8fZG-BoEWzwSjekvQQx0 zQnq;2TDvLEE*{0|6|=FVcV;jgjL&A8Uzqk@`rkrP1CysR z(ro)EgK{kzKtr(c!lH(3Vj5~uCVAy&JDJkPuR!cd7((t`Ah9KI}opIkFLX9-omQtYN5z?>V_t}Egp=7 zVt^(y>ef>K9Zmt*WZ7X^phU zK*7l=6sI{?g^wmtkx7M2lhP!`qGBq+^6hQSfw;XSYOf30>zQVx%~JPXDvBDsa~h!@ z_+K_iDYNK!IsvGyjw=rGMXomKL`Z87q*}PXG zt!^vUK=wP?O07cQ{V0>elOY<#SOiP#s*4c4aE7S3s@;;44Rj(a@Z$j}>i|VKz zd$3#E;v1QcOR*431L5XK>)fU$*qk6VOv&WBKnN5z(3vJJ2N_8U>q5aMJ8DrQtzgP+ zu|==M>@`5R<^^H>mKLTrcQyW3lwMca!AgtHmK_X^M1wHPfe?&fti8>S3XSJlbY36y zU>_#IycW8RW_)9tT^4R{wWGnNvLK9eiybXP&w8}`C_uSBvZ=+S)euqlIMJRm*=5Y4 zH53GUVUnZTK*3D9yY`D`>VB&e<6Uqwoo`WyTA{X0NtgwP+o|dRX>` zI22B|F>OfOm{g>PP`V=FgTvTp(I(mqnofg?1UJ~BMZp+$QIsw-8cezn4#aIvwN0{v z;q@??5UnkAk=UY(nKu8YcbX<1;;e*@FIjY{_|3c}U~(vAhahCJKvTl2nNWl#6?||N zOx-5@2PAS_x(O9_JwfEa{MKNYsW^p@BWVX5YDTNbq6Ik-onI7KWrtt|Y>^YY*=~+U z;z}`@%!K&DGN-t^RDdz#uvFg;B~1AD1u1m(r&2RhdMWBdkrrq+Y>{ZR3TKw4bUKQu z`Y{t~(gAgG1cNn!wgkOd^>!>iw>=(#l`RymiPgH%qMO8OFxK*1p}MJLpctkFhn8^~*(`Ar&iR^}8*)M-(SpwaRNS(Ip*q zicW+`i8e@!N%t{LOvlK<*}0-F0R*$GvxTAuEP7DWvg+1Ai@hY&9)qKQmVRo|bI@&fyG75_3r+^2VsXUnkfE386_Z|On&OrSc;vF? zV5}_^*oYJbQzuqfIKMrN{0D)yML(lgkql^&o0i_L0k4}TMeA!8y-vRXwa_0#M-sS1 zAl_pTW}$DJZb|Gf`5FU%MZYoW*G&F?J6ZHwdILL_dBvq5!$^(Y5-d0AcTC1vbvg70 zdfTM8VCURWBpr0pwOjN@`V-_3hEowb41~J-Lq*VDkwfp&UrhQlq^9>wn34vH*ii7W@>lC62$62Nhj=yC6j!0I#N zr#XGRIw}c=x+dI-SQwQ+z?`txVpG{%SEQIj9h_~kPa>ZI&>1IYkI4wj;sLV10b;FU z@jw(?X|;Vkh;vOI4BOa~#|T3aFe1WdK%d`N)*;51hgzJ^!{9LMtKpD`^p4A=(+G?G zJQ7Sm=nZ3t41aYr&}u{G^7H@+y2};>Vr$@LDukZVutz)wUNd#!1XbuVj*l_9AW5=& zyv5^r0tiCRrINs&JtaiiMPna!Eztz`!N*!Oibnf*5*L|_gfvssbD_n>Vk-vNv8F(q zq?@sr-7K9-EuJiLnu8`geyVycyDzg?rI0y-I$}z}T-v8ve4>JGMRk=tpKS3oLCNQ! zgv?`D6=JB3DTelAZKRjMZi?aX2d5RiL zhRTj}EAmo15LzPUq%I&OEeX7RT+Xu)EHiR#55%(prjh4ZJXZ|gVC8;-n~?2mQ)hA| z(^&VEy<;~Y&*M`qo-a!4gF#M6xycKldG4VOH9kI#7YYrhGZh@h5Kyc_&EO`<*^n2* z*m8|zLvIM_i!ENlXFxv!%?LkAdk|Z%YGI&FhD@Xw@zbm7 zqc)7nGCor%Y-Bn%b&;gckln~uVbVe%)8wc@4tUuftCh%t}PnqFpxQnSX z!LR@v?Z}i#+z6vnW$Me`%A1BcVVagQi`L_RW1!x5S_4<6tavoYq&0nNCpTuPZ+e?1 zl>wRwA!FK}DzEOD-1W3HX6;|yj&dTCzXl8A);bB#1JNjg^SSc3y0v9KK1)QR8-08& zs$M)#{#?t~Tl{stL1NV?s^*Z|xv87$37z^fhA9k_6|oG$lAHKulXo6wwpDbp3?Yui zw=k075x`TCwm4d8heACBGS&4@gL{jLQMCd2)}oUCR5TOhBz6s|Zs+Swz5}}Bo+GuF z#5;FcJe_BVmAJ>^d&RsA0HvboRgrf15(!c|EWVGw3HuR>tVj9*OUm=5{Q-*~l=iGO zu-WoLrg_NXZ;P>k^@=YJ17fQw(zY=fu40nanlVy5>7O*{wTW~~TPB~}H6^l(Ybr_FnNnp+0dVyS5&0}-)}#r9qG-O2Bm4PnZ`^$?&il$MmZqA zEs981rI+$y?sPjEMoMV$6Wj%+X;kJ1L#nir84I?w29(oZk31<1`pZnG5DsrV zwYdc`KsW!;;vdN*c}^?*ZVkT3sT%q%|1nh3V?-aI^j}yo`6o={MZk~>1?^Z_J$4%g zKB$;T(?-Xv?Bk~;^ms=6>_AT@hki@9Tp_#Wm;5tR1*~)sdt6RKLKI2zsj!*3@BI3I^v2I zD~_b?5~sY%zq05WMg=3Do`j^Ez@#kPuezH2Tlf?)E-9H;SruBGio-m7oDrY$8-5eJ zrpYX#KuRr3?O1yVX=&jR!cDjTlaNYmBubJ;VoK7|BT7;d zgTly>x?@(aFi^f?b5!;P@L-~`nARm9gABj4C3}*FF%k85I(g6Eise&Ob+e}N+gSet9O{WBLxHY{g`@kTH zDkuz<%@0Ka$qygtuHMI-hvRDULBQpXMN$}=y0ni!=X)f&!O?&w=)@WSE_eIg8Pp_Y z&VF-|erG^zW_T<_5V6Z*_LOBFD%we5R3&m`@eVlrZ6iW#p)|w+W|c zqS9#q>IcR^7>+(e69mWenU z$T13x36?R@I2Kg{MCULise0-3RGS!UOtOqZqX=AWQV7}gs2ubu0@Wav>!esJWd*4P z^pKX^ILyE>S91@=c#-IzvPCp|gdlv-a1Be&rV+Qw+9Ox_@4GAbyuOvhGf`A$KV5I22LSm&|o<^hP%OX zZVXpBnIxY#b8u&nMR~X%Km+l25Lz11CgTPb@1eoPCA%rFxO6w=KcYGh$KN63!QUD9 zTaJllV&E(qftCV2jQlhbPnbY0%j!-RHn0Hd;kb@Ln+H#0WkJJ-SuC_1DBeYRk5WM= z9XDxE(*iu@@1v!18A_#-&X>2lXo}~&D_Dopf3kov!ZG-Pvy;NpvSkXtH$S_JqUF9~zps-vbkmkb)tu?~<$K0Vx_S?Nu{_70(@B>Is2&nf z&ryG?!Iy;q^mNL{nWrMEp?a#tyBZ4MxtW&W%*z=#oJ=hc&%)4PA@XzJ|F) zEc9&Ko!NGvhwi@v{jWo>OyW-Z~z_&ORhah)aLX}Qo0x?z;QR*lxzDPUf0 z@#Ie0S)O3$Z8uSNu^+;5R}aGDmF%y^dO?WrTIffRj-@b_QiP__1rX$Rh0;lSQ3s`2 z3L8Obmcquss1bB8cA9~)x%3U(dDKpaLA3R|?asR$cqjYIA^x0Zn$&@R*dMgMmmXXU z5C;(P$vyl1f7OE<`p|=AwHPVp%yBxJh-hcz9=tq`6Tl51;5}xLhQTe{z zbiW?{t!{dxamaV}&~E*F|K*+3*+q|a(v#BQH}id>Njb%$E^bZAaWu*DTPd0p1++w) z!RHI1NneB}T?9?K7+kspnshNV=^_Z)rO>3yph=ga^>WDf70{&1A?sVg&ux(JYp9cU z(D%T*ry!EA(hc-$^mvcHu6m zUAr9Zxy(~lVeM9>XW3Z2QR$fvYim8r!RlJiEG2!#N^50at!Fat zcJ$Em?jSQUatCBlBr=x_Xxu?MLkp#rOB{EBTx!SgW6?rxz_g-;cW4If+Xtrr&g`On zo%GC3%9V$bE_!h{?dzZmomW5aq+dQsZ`R;d^IrNr4(?=3^K{Vg(tOY4z4Q*#lN5kJ z>}a^m!}*@lC+?L69echp{+B zzXLXN=v>q+#kE$_ST>Zg6f-Bv50ub<2m0$?8R#WY524KA0}fJwNpG6?@{{)wRbdJb z+7I`I!+j;^SZ*ySSu-axcr^q?X@v=%vdH)+Uvw+;iyG4!TZH zt7*u*heGaW^DbJcpM|E%#436wGR{Qs}?KHh&u{{ZLr| z9z-ATa2)ZFXlDW3Ao!G!6oZFB-A@4LM?z9W$wg978qrrZpNw4kz%IhAJe7eq6SF_07=D0#6&$;dQMHi24u<9VH2<++1K`7^uuv_`*o4_D(IFM>MCyIOfz%Cz&) zi`~4e(Vw}8mm}Wc6^(v#53elGn!J}G{pH!xuZblfkUO0A@~ftxLGJ0QcG5Ule{Uy8 z{MmaM`3Y8mAK+LgukWD2J&m2b*;)GG9!+kbd|-^A7P^uL?8f3?$}yZnQ`y3~aUTw^ z41zZ1(ORG%g+boPqiG9|p-Z`duHp$W)W^}?Ttd5fGIhaR{uu9H;1d=9BoF|0j;6oJ zUsRfoHogdVqRlU|xQz^O>M6biyaE;UIDd&Rg_e$mCHW(0^CHN)fG^|AArEVD82SRf z0{x?O4qwS%hDR6MuvCQ}vch&qh+bj4vw{Q*dfu-%^KNzKm2gYXdzCZqHq0w_WfWx| z1Ry4t!>kk@q!UbDX7UP?*CMfCzM5`zMCKZ0%%8^=Ahy)i&DS>W;q4{eJP+3$oqSzQ z^1h32teu>E=;YfFQosNA4CMaaw8&uOH!il*2@2lZ8zKo zG3@=~{%tqjA^~$!Iy(7VA{egc%`!^K?A2k?G3*Yv0}4nZc9moDX> zQK_bT`C%mvJ81LJTpab@1tG}94;do78w~d6zxJ>2E+c(3pnBBmYo z*xo!Y_mmWN@nhv4jiffD;&O=VfMh+8tfQg4j7Guji;1gUFR&_c`R} z^AwRf?;Pb2Im%&OmLkn?f^*RrO%Rwhig~4w`35uRG#Vg_%p>K{&IOY|3+Eh zH_A=qj{Ljj^7pOsEPqzdwr4A?wJDodQ!cmA1Po5K7Kzl*Ks!fgGS{{pBdz@+|_-vei+(;R*u ze9eR-Embgj;XViR-}vvaAaV4SoG{CYJONe$T0K%?P(4y&P~CqA`ZNE-1-G_J!_mV8 z+~@u`;r4rbV4bQ$^-BQjmjLUffc0{~dIey;9I##qSicNduLP`H0qa)*>o%8>IM;#l zTnEYq2g+Ip%9#$7g$|TM6Htn>Xt|X+L|LZeCu{HHPGFN>0vq#wKH$jaKb0`7O~~fO z|4p#^lg)1apiw;f9{yJkn)4w*#Mc4M>w)Iif#!`s^CqBqBhcIlG;ao)JAvk{K=U@B zc_)PQZV2hU5Yqc-6Mqw4`#zVjUhE)zv4ik>2jPf=@YxQ+)egcZJHqOBgw^ULtTL}g zT}EouWuz8X>92+L6I3OVQP*b*|7IwM*oLirDY-Z<36M;c_Ne$P3zZpvmiCaS6lo8M zi}H}4ck-b^xW?n;h0`*^qnx67m{W8;Z#M-fbZ2NJa#oY(_S z?4=5RjLzfl0`7$4=R&Al-4PS=tacc zs$v}c*b)UMiG|$e0CN~fl;8xUzu>C-Z-c6<{)U16Iy=fZNF~a_twkL3DOJI-$uE|Y z$dk6pwLsnkB`5=^N?c9iT3ak=m!kf&*2y-@J$MXH(-l1gB{5}&1!{2b!; z=YjPLkocFWoL@lz@-zG_@m2gL@HPAh?iax7WdsXvxbWWK;Jv{Zr0~wdAxkN+8ORdW zsKh@5*=wCK7}$EK1Z6-ewPm9GH%4WiI@r*er_3Rlr|zGD{(|jDG7h0k>rDm`0h93W zU9fg4%FnrmWS}aiF!EIJAWm6a{h2K%t!nRK-+za? z?Z;1_576=aA%4yK5e(iZNKZbcGg0zu;)6&JKBUdiA&IDTWVpq_Ws6Z_l&ZaNp{>Sb z>=8Spt8gc2M;u>xrhp+sX^l~awoK)3b%jz!9`B5lgj0`{lv0nJ>Wmbj5pftm8K2XM zrZL(y#!ego2{^HL4T2s(XM}__W1!UFQR(@3w!ZG4rpv3wNjFhW@uV@RzVk$_HPf9I zqg+7J!l6p8MkX1CiHtUj@=)s^V+^2Uja(`-@@YE0OZ9}AGZLT}sJ7_RnUAIh*t literal 20987 zcmdUX3w%_?_5V4uo4v{AhIfFKR}_#C0j?qqiyZ6h$CxmG+oW) z+t9w2>4H3U3+Xr zM>-m-SR9V(T8+`xcsSjWj4++jcdZ)Tiy8DROe+$OS9^*Y(@FGReG0k^)I{Ua^eIep zOLLw}|5KQ}oXI~s(Gm%gj|SURL_;v~(#gvUh-sKjc@&Vl;Wk;6&or<;8jmz|w5^RK zo5E{jn9q>GieIwn#h`O~f0c=V6LmXVEc$U=Q*QVrF$jV=a+nJ{`+c22`hkqLPR)m{dtP z-cr(>Xln;nQ49^nBB_$5l9NkZv?d_2`EMRnL z-Q+_mJ42|>CoDQwNW+t*ArL&ZFVTk)FoP+tWEI9I3YlfoY*EM%U@h5_r4Q+2j!kpv zG^YG?!l(jpEY;Fd*L}5xAryIjDjIfcGS=5AKWJcH+i#A7+ zHZ7+WOoLk^>De7g;kV@g+^i_fX=!P0vg`2AP_qf;L0V0NgS3`fENTYz zc-x6ZRmdz;HbrP1mKWTcG)x3O^j7UxpV{-RbJ}~<&9vU8C~bf#8|#QHjK&v+H!;oj z0yfjq%hR)AO<_HreoDo1R#VKTHj0C2v2>bl>w?ryNsG>98r9s9+^C(!GT;&JZBsjt zF)NXT9ov*5G_WI^(l9tv#P77~YN`)}Bv!OQM;}YH!l*$* zV$3>ZS(;VpV~Fa9A+*q<#xw%Hh2eH*yn{UP1T?)7UNJalQ*)#}4KJCp=o&D5k9&0A zZztO1238gt@fT%^^W}1)-sUkA;sNX1LSg#VTG8yB&Hhot-cW^Bh zur-IMqx#5q&eCaj?GOZ z+rsI5+K06mKW+T@@j=={$IG7wZ2AE`2*t0{RV1dF-)AWBFgoP$j9jki~b8?ipE~nL~Qzz1Xxxol9tIaEz1oLeb`+8hGIgG zAKUaaJp+e^m^&QPyo&t}IH^9mp`xxW+!|RN>qsHg_zC^YqMw3dyyZ6ioSt(xJ38#g z76GVwfnK!e7fjQ<7(q->(GpFy$HJSj8-i%3hxv0m;@E1z7;X9`y@Y*-0bylsvxa)T zW71>&wM{S6Z!lU|6yh@poe}{27zBE#+ipsn`DI?C;cw|xi~gG_)Ndu5UZdABW05OP z4V#(l=+Rkni+;zX&S)&8KhU2n`XlJptBOpBkO?kqdXxSP95#0(^+1PXpx*j05tLUK z(p&VlMSlfq4t;CWJ2EtX9FURq`kPJf2|+pAj+yb6+GMyhpWcTa98-_9l;|}sNn_Vg z(w>OM(K`yr1F65h#P}ozs-4q<$S?% zG%Xv$K)a(jhJPH^*EG>XlxapEuRa#SL1P}KSs0FYgk#_nF0y$555(LMQ-fo$M_-o= zw?(j&Lk+7)G+i+-oLUbjQXS;MJk(++at zNO$oxn=52SMc^EF*OihMpR)N_!AFtse<~eL0t#)BARo__76Tkcu{tj!<3PxdJsO`N zblk_jQQR4yWOEfu+|yKtxN69uJ1dcn?c{#Oc5-;O7^e@tvNLR+$+I8_fu-J&_e27Y zC3{5oLFZ19=(5(PQ8ZcxH`nIVWWI$mU&P}wTPd%zdA=U>s=8WvuD5xi=r=!%dZJzt zgU#S~XtbbaiOoy7F|(CP3xNqQ^q@$y2+D!6W#9!y%EovT4Zh4Y_1LoV@ zCSxp$q?*I+l4(h$A}vxWVRO5XVIhhl={fo})LDx?NjTmtIl_2Kf&fj2OdUD8o-B&$ zFMiE&b0rzKU*^_tA2irIxXx*0|ATTSFSodZX`I(@Esca@i^b_Qh9$=;8^j>CN1fQz zF#=LQ%yu~(D4%EZr^P)E(m_h}JcQ%MAzF;R-&n86LpK^h-ooeGe1QNu2$Va>viNgA zuD7W>njl}u7t0tfVJhjhAQ-XU7e&vL+ye49aLK$?cI~gr&@Z$3a{dD7B;0}^wfxY= zdb-{eK!#JapfwhMsh4kGP}`J@fGe-y?J}eEv2L&)qiP z!}pqaT(m66ti`?`ukN#XmyB9M^;MB1c6;DVNt?D|7Z+HIap{|cQ^FXeKc@2_M`jJx z#a@pkfcX4rEPd}7$~P7Tk@>ha@fCMWUPmApF3esd@n>HLJ{fQ;$NY`l6Uc@w=kNbTR;KBgdxu_c}8 zwL00gg_B9_*=EY$y0+H2_%N18ZVvL}NXPOP`SUnGW%G~uX@I^tiSIT*?ab`S^+bey zv0v7LNss6T@!7NdQ;UDni>GxMu?!))%|GLxgCn3%J+wv1+4zFdGeD-s{>R{*qF$(m zuzc;)si|yjI>T{hjZr<%Pg(o|Y{x4jyOu;KFWP)ACxs0NJLH9tmS`B1w$4aMH6Nlhkg+JOVMVRRt_je=%JMO*;PnWDZdI> zDvxQ(|G)Kwj5J$W3g4yk=hfBM)-}zZhmVT&AEPJpR-^Qm(@EG#*%(`mRY;3S25xC2 z)e*zCz4S2Qrq};TNF^MJ-1XrWCByWHk~CtFFFRayNDasW%IDSM`aYHt6$Zp3o!OS1 zinQWGJy;ldP@E*F&q+PP-IzCwAG-_L%t(R(`zNMTb6nVL!`80UIg9d9ZW8*i`QBU4 ze)`C6*&kZ6YiZBiKd0n`zdj`P9`Px#!)d)7y4H=(eu#pCYN|R$%<)rLtUglwp45fA zqXU1>*1?d$Hq+wVR9sz~qf-JLuFp+mA3Dfr6(fvQ%#9_&na^^$_TGog-g>oECDi4O zMY0w&yX&Bu&Pe_TQaH!b44YW;udZ&tGlQ9A$?SJ8(ys)>W~#8obgYNp=fHsjD%5(zyu8`%McbY<4bnG9TzCkw!o5o$p+Zp`2i7@QVKR@mxvwG!*mtmhVK!Un(3#e+KX^u}Ly>pi2&ObmuP+*8~$SiErR zYr4s#Y*Fl&vEWOTr&>Q4^*BHMupi+GlEv)CnPPTHkR($m+--aF_xGCaLPY5rju< zwNCQV1DfS?xjx6^B>&ZVTSetnk$&aLTdQ+yx?4Uah+{^#8ppv#ZOe#tF_ppIbAHoA zNg&9&cs!Dv9SZ~5DNCIT!9D41RBqn+BCl*+oOk%6C!mJ-YG;{2fAm z{5=tWt01Tb4Ns<#C@C?`Anph}L4fX-^__IA&;h;!m~K=MZEd`Jr|do&dM}OXrty;o zHqXOT@nf`9F2iW@q;ur$eKf_tW@};}RjeHHsoiv3vA>%thg5Y_jq_F>PsJmIc*|$v zv72i5kVu7;hB4+kv)J!6+(S#N^346|eY`xQDzJ}MuiQ;*s;taSepx86hr%H%l;2IQ zA*-9tsVoo?p+IrL9!gXN%R<3!>exq{SL))tP_WoPX402-)2FKnLxtV+Sy82*M3t-c z-?>q5=DX&HT= zR?wq#20e@OWY44Jn{+n4O>6YBwCi<#$6c0pAmwvd1gM@aqG@{3-l2|-e zkiLvA#a#e>)YDe_Jg{>&jt^djyL|LjL6@UN0eU=&zJPi`^ckQp(iQ0KOOP#KFw(ZU z(zc_9slOfdzl>UW#Q($uk?;X76kUZPKc21@ApWZe`O5!&gk0MjA?u*8Rv=_O5E2DK zHelV)!8&gMLZVP$8xRr)LfWA71lFh>2uWZK&&BE`v2H0Kqywwg3H_f3glxfTeFg|Q z|Njm`zDCyp8A4Dm05*BJo-F2Nn8TRx^`p#n=4f-B0`qPto7zn`RXGNK+YTxy3xPiF z=+W>|niDUAhA#!RZ3S;$N)v(Ua-g{qSgrv$h2D-gow{~(vbG+vqm#AuD6|?$--Ipr z&~`9=3wM5P3&R0z^KP5_ZadzI1r!p0L^T%Og?}+WeBd;*R1Gk$A|fvAndSc~KVIJl zKQ65bfDJ{zmeIJ;FF5fImo76m(7PqPnI9?;-o&4CDudE0WEBU6Hw(*z9KF0*=<=o= zva@(|gLX03V9mY?tGE_cu>)4I6ISsxSjA3Q#SU1-^{|Q?fc5KP6*s~vZbIpeD7g&> zzHWzA+=0_m-@-YkyXbNHHV*9EO)t^C^n2Pxf1z%i&e+XC+N;<8JaJVx5a75r+&LVI zJBLGYm*LRMT@G#2cnF}kh{l73-VB@Oqff)81?bhVX%KxIHZ4T&hD~japfb(PBG<5~ z$lZn-M(%cSvuU{WpL3B%8G-# z=pF;o`}WbEl|$~|P2V%m_gvCV-`_(Ib<+<&79Kq;orG$M9snL61Rfs(9v=oC9|0a8 z1s)#(9v=oC9|InL2s}Op^gIqc{ul80IPmxs*6|sj? z2=1wFdUhubmIsjg^SkJ&F8Yl7>Q~+L%7gUALc9v^rQhSUsISuBMI*|K{ZseSUl@zC z8i2TJ`Ne)N_Lo0!^TFS_ZQph7bddRXgb?QW1BoajQ3A+C)oX6WVgWkoDD*lG>>|KE1?{M1hL!}?Um%R@v`pDy0ZpthoXi8iM zJ`Yk93;#RlW(Ix@U~xX2$7~wQ1v*rdAWIwzw4_zdPA&OWl2E`9${SI1fbi3xH!OTb z9{N7j;?oPxG&%mI#l+vNW9Y2-TjK2UgYjT;@;} zxXU=D8rS%19OHk&4eZ}=1N*n!!2W|wV9!OFz#eDtoWPzd9h2_~?2R-rc_R&hy;DCE z*ayS`wbwu;WI_nTtDw9B1YV!GM{05PcbUJQV0Q?wHZu8A09~kXe8o@#M@1Chai$&-=J+ zFHc3D={Ab%G4U~{bkQ=o9kYjz+r=lEQrlckL}5)gpSr>?{4!1oi~YOs!vv|ni#{lu zgvh0vXKS9(O=#wA2Y-37j}10?&TR*8@1jjcn&~?4HdnrR&hu7)ly zbtb=)$C|rIJ9$_yHJopr4c{%sX%Z~)>X3ggH^G7MiiW9s8Huk4`S@-=v(h9F*pQf1 zwz_Ci$jVf%3WNgQ>iIxMesLavE_Kdk+IjHjB3j?w&_$p3jsp^{5Txj_s8?u& ze(P6nL5jQ$_=+-;eZw=7Gg%y`hX{HwawCvyWJEy25^8LxQ6JGHAAd+k^QeE&fPYZY zhjauVi|fb_ab}~B@=*ur1oB%1S83shE*->{*^)<#SD*l2^Q3hAL5x4EmQ6}GLW63` zlUt|iQ3tU_bKZ@F+gS(F@10N*mjcJZBYisP@sNI}v9n`NhN;W#=)OoFg@*LO&t+0; zj6?aOh4M#j9W%O{Tla7jIfgwP@1nsjGYR`Yd$D4Nl)?VjchS|RPnZ2;-e>C;I@3JM zm^5dP&h*xoH^__)!x#;B;2lPJR>ym~$nhX1e{!FY2^xVNTE*IZ+WDS`AC(i|4QrP} ze7k83@4@f1_u?1G_tR|t9xdSSQzQR?&fteB%#TnDKMJeApE^+YB7TA{uxVK8^uWg3!k^Q*>ZZ>EFlcmBs$uys;{;T=o~>6{-q!Q8^f1 z?=5&sH-FZD6O9Vx?c)nqh620!B4Pha#aZCp=jGvxLix|^<11E%@^`oeoWJ;i$yFvwf`A*#na?)-|aFwehCfM;y3Z%D0mCO)jQZ&yhlg!-@)kb(|p`FAvJq8AEXURQBvj6 zW|dD{6!=8Rk%@tHof<;7su8qHg=i0sBkaffr`0&Uev+?*?ws|*9e3+3joj_Voorj4 z0j4BZh8-u6CE4ep^nJdc_rXfXA`kN>LgWDQ*yr;1fHn)c&UHAW`T*)DX*K_V9|SkZ zCTJ;2Ob^?D5Yxjpw+G3%8MzO+avyf(KH|!K)Rns*a)s4KQQiS4#A5uY@2%1U^eKz4 zu=q-gZ>hEcnaAi>7cxK8yVf6L3VU;_!(nyup8$e9d>Bdan^^FGUhq?Jg_d>FAtOT;+{pN|D`T50 z<6&3EezcXvJ&Jq>vHcO3Ck8X3{7%OKZjKV#c~!8S7|;V2XdoCmKb=u=9Ce z>!N43FSP{d=AF!u`GekAL>)gy01zKK&V^G3?BRbbEbl$Yc^G6J200JQJ)c6VUJtUw zh;auw${FNP@P-Uc=_N6V2ze(%Gw)1V*0Wm;tjgT?7|fGZAlKg3RTY>r5L2qk3;9ENU_>=!#XeOm46H^} zS@=Y!LM!C&=&Jlse$TW|(L}$53e-{>tQu*&T1HdUayk~*6VwViRh>?Aab2KR(Nc8= zP`;X4)f#G3YbmWFT0Li)skrLtbktKoaaE#@#MB4VI&~Cm$VcO;Nlkzy`4RfefHlcy zwgptK)rc$s4N{X}TNb5JS3bSvYtBz(^8Ucrv7%+XV_(rx(zFhKn-D~ zvp-37hXOrn#r^=~(FV1~q1FV{dM?zOgj&yqT2oMK8fr~Jts9}%PN;RWR;Bj&;QI_o zob9S|xvRn#^CX{bHBtFO+4~dI- zNHx7%olpwbc(lB5OGf&&Q^fY$giMZ8^gMr8>T9RiWpHhrqQUibpzs@LjJlCZ)y-Im zTd@*fr)qUOtx))VubtioW@3yM%cyN8WtM$v(i7;-7R4(V=XquP1S8?YDsc+N{ zF+gLGE|eI@qEh^KT_hIr(g9?^wTcsv`l44;e}AUFszQBnbVJB@fToCR<698N{FCrw zOqT!tNJjHyQtl1tU1(yH^Sy4Nb8RRSZI|OCYO9-8ukz#Z94Ggos`si>C0ebNoP&fa zxQMoOuDvo)94OAakwQ9DQPsr(Y@+t6S&CK^2P#3>j;0^G2YnkoSK$i^sa^0!-JrzX zbcEVN6V?6D^*$i}0jg3zKmhU}EykX9g?fadYCrV;0ClP#dGx)>)%PYfN9#KuU&ZAd zgTgLhz5c@CLz=!(&4q6L*ljP=-#YSevSN%*v72DScs7x-9eOkMgQzdMJ%ZFhoPh~s zf{4`?;a?m=r(?k`r>S`v9gAN@eR0Z?xE$BNjzzKGLKPD|=#w_;X{h2EsNz|u;-`9w z#wUz(r#Q}?;s|$&1Dz=f8OoBQTW1oCP1FW=X!Et}F7*qQN z=KT`P?bmdadYO(M6sFI&_q_-&c{ z7MSCin4{cXhCA7Iqfd(#1$=wlu>3R7;zlzXLJ@JY~2{>1(!uUdL0-)e}W z)h$sQ1Q%vb`lyC)ASvHq3i^i7P~R{b;~RnQM`2FmFsGwEb2!nRL!~>1Y3>{*dgf3- z%FJOfrV#UlPT-TJiM7K`Vef}nC?xrCJXGrD1FkE_uZ#xx$}zfWp3xM$ zs4Rv+aR4~~rk$vQMwND=K2I<$wM;NA@3vzI3PBZ^QN0W+ih*;4`wd_;M)p& diff --git a/target/classes/pluginTools/InteractiveSimpleEllipseFit$2.class b/target/classes/pluginTools/InteractiveSimpleEllipseFit$2.class index 661f649de61f5cb122ccd722721bd62b8401ffe3..06c82443e23dd9534faceac5e0d890f35bc9c860 100644 GIT binary patch delta 39 vcmX@av5#Xz4l|?u4l|?k2pj+a delta 23 dcmZ3^x14W-EgS28h7FASCOfg20LgN;4FF$L2p<3d diff --git a/target/classes/pluginTools/InteractiveSimpleEllipseFit.class b/target/classes/pluginTools/InteractiveSimpleEllipseFit.class index 90094776c75245555a893e1be43a6b3820ef221d..6f55ee61ec1420034e6b0e2fa206e4b13a115412 100644 GIT binary patch literal 106769 zcmeFa2Vfk<^#?p#?q+XyoaKf*5R<&)r7#V#O=q^JEz{AI%VoQ2>N~pA`BY1HW@WlD)83g)&&*~s zon7g=Ot%!aAvRi`TA5m1)3qwo(N;5OL8>F2l_GA4L7C+>I-i;a$gaKvQC;bLCY4>3 z&Ua;U9sAYS8p4=kh^iU6j;`)hNB5#swkI7!Md+Vlh(u3UdTB1(n$Dw`1}di}n?fm# z-FXx?833u))~4LNRC^k^bhYGj*=%zv-!g|{fPJ3$g%zQqFOyaHSwtN~N zy-Y`EPj?sRgKWCeEy0&T?HROyTiBV)baZziC)9HwqFXZgmTWqRO?0Q4v*|7_E78@S z%XKe9rBK|oY_=oUnnuE5y3Re_nQYCpd_J|NA=A}O)o#qRb)>p`@~Fu^F5TuUmOIgIKtc`E)hge1WBRHq?^MDm@T z>RL88)rs#1_KlsQqoN)AFPBd3W#l)7@z-?Hm?o9LuCTk7l@->YeF)3kD2x zzYZ&_7*Rj39_=g%Ps_Jxkj6Apr6{xb-b4L0EZnYi7A2%xX|byen%A11(bJtc0(BG#Jj>|Umur_qB1g!b88ohkIS z(mjar25v>yV_J0av_Oxb6N7k}&8_Op=bC|3mSx(OX|GIoc4e|OVy^!gnE|5(5FB!N z-T_^szMZ-3nvPsM5bx3qqq8MQs&Sc)7|@E2PIa`TQJrPER^-)^Yj4g4p&>0;G28{w zL?b#gtJB%CpQ_rjx#m=MZfbQ)E|2U+ZxC^zqw?uipjs@8MAx$1s{K>kDPikUtD~)> z(7@bG)!RhSiZXnBZbOQ zINi~zL#37DQc5dFQBFRW$#k@)sWH7)EJmz!*<Y=F|IwY21VfKFSN=CO@p`tA`~N?*6$`XEC$%np;x zTHcANy9wu4BoOjBY{6Y!(^Gk@omiGIu_1<*0x^EZXK#TI%|lZRQ5RZRE9gj&2>GR% zESfq%S3HxesY4j4ovzeMkSX+S;PN```!kkd%<8C}gshpKNUiFw;TXV=>;*(v9_zMy z4LWqa-=-U4M6YxUiYs^sHoxv(pABJl0Q-&573fZ{?ykWcY)NPb7EN(;r>8=@|b~N&)o}z-nWP^)UFB3OCj2jeIHH>nB z3_7u$agT=q>grevb)O+dMe4*+HFLtwMNI~BT(JUuV$fp)7$z(n3%@zl-LkA5#0&h? z6u&Glm(PF_1(nkd^ssd)B1v_$=GrmgFq<+!*;FKmrY!2PZ@&Ob_lNE^o#`4NV)SiG zO&-)$OTW_blU4(Qsx+osxHf}=2!y(NfVK^>@ZT*rqX`=#y2FA8N6KjiJxo;N zP!O3jKq96oK_29*-oy9QbE=f!oYJ7_*<7Zzmbd#{UK90xvRw0Wq~|uq^4-W7vz_+1 z_H?QXBq}J)uAXdnR}+(&7#=2_{Uyi#)=VB5NA9$Q?M>6Jt=&dp(g&&^Tuh#W|Ffa;^QzY@S>$^u8g26UuTz%|_q za~X6N#zzGd8e)*mCjNI+H|)d^HbTv%vKrnCh#3SiiMjE0nRK?bo_8#b#A*noG+|Zq z7nBm?gF+u17xbq@kLPn4VBRgukDgCdQWc0GsUiZF#@;_b>`*=x%>kgUnh-KF%<&~n z$gTglKFAA+BDuJ$GQD{qR8SJB^rEEz5%WJ*ZbqBNKRCsB5sNy4|aC zz-S#fSsG&F{jg<$4)02L%m5it*VEyz-jNM02+vJ{_FavmQt=Z;y={Hp`nkIMj{`;* ztB0csP9)vYsu<#+_19ztMkCdMZOl*DRpWd7S<~=!05rdyRJL|Ad546S4*lR7@;A(Mo6EBHtH42W%h}rl6t2HBnG)p zEG7o#BB2`8X;CH(`f6|_)W}Q%?1z!K`Pkn;;6-Avz39jwsm8xLyDIFBOEdZQbgN## z5h@1cK#p*u=dTbA0J;FtGcfnsux%hYLiHU%m{=n|1%_Q0pXOshVF=id&4LaJ6y1RS z1QApWU=P&A%+JrSOz8Yu#`cbnn1q8sgI@5p}UmNM)%YL95z8QFo}<~7$7&U z{9x>Ky{kB@d}d6Wb2mm#^As+>rRjY!n`v2*)@T~lOz;VfwgO-eoX)f_(L`tYEOQo? zl`lIwWRa2V!+z{9O675D#YrL^DO)z@R%@0(6&SM z?R*VokMkr^t}~dE>#wPylyS7-x-Sm-H;V3Gqdko-r**W^5w$i_-oMb%2i9r~F;Hh3 zK@rFQdl{Etsj};V#;X@Mw%7n&FI~5d8he`iRPVz7h3e4=m!AH5<7SO;d7xXjR% z?`>+pplJ;awAKQC3y6$ih~ftIiuVxjZz+HpLJiRqJc2`}ZMdcVz^XV)0;87AeE)Bp zdH--CiY%5_iW9$vaa`J-QjBJZ;U&2eY`}S4&lg~|(wamt+W=zN6XRE(I}K64;ob;j zNPWpC%BT@s_Eq)vU(aBVs4})>O07Hqj|5tNzs`aY1T&mbS)`lcK-_bx>ZIE z9LgDoG+EG${w@OQzw)#{@d3JKfME-iOniV`20RFe>cH+B#r?^;3^mM(sllOmuKtQD zIXxYjLwnLBnT&O}cam!@;ol~XV+NEVW|vSvI0=^JI+v0Ye45MV+Q3Slo++|&J z04qfa&JE0<=$7A?N}{tq$ZJe~5+GtM_^hw_DMwbFsYoY^tj}vx3;HK#;r?LowQnW$Bg`({rm) zN3F}k+~y|isLYk_rc*9QVVa@GX&6}89L7Zi&3>HAQ*SBpvyrJ5N&BC?7Sv(sQa>~R z8ER>F@L4O*1fL;SD2a$v5F31)&*iA8O(Iib^5`kxrzX)j%?Lj@sK{go9P1}kWoQr1 z%CCY3VUZ-N5Q0QlZ-KZn z3K=PbGh>)QXutt2G>D8>2?$Bn{RoHnfrR^kr>JWV@ZH6jVO^N=SwFHEz!#c_%{|@S z#1GYaNa5U>&e51TgKD*`F9ND(X^@aOm73EO#8ml7g7C(GAgZ|!piA3k0Od_5@m>(B zqEA3YLD&V$E*Aup=8^_MZ$=ipc3^ZY4=i%MPG;oVp}hrc;}TN`(j`_NkcS06_pmmQ z4?#56t_DzAZ6F8+N+&^hBaMT#!axv7dS4l>+2QBnWt+1zGTp4`0?bA=AZZWAicWU&D3T$O??~34jhHL=X|>ai;I3K|zG=4E+&sRX$vI zraQ|#dR`KN0$I5NwB`j?iw`98fiGAf{s$FZ5PCPJ85ls*((sj@`~=~O3c|sx@WaJ{ z_ag@K{@2LcB{hq-Oxj}^T~KH77vk6vaUAU|z4zBlXA*$>l|cz>A|2y;AA9P6kIM(@ zj;k$7td)+L>#IKX-eyS=N^i5nsO!~+>;AyUPwhVnILqfj)lT9{X?K~&I7e!dIJ=)mbu37#K* z#z;jfHB85xZ{wJkW;zahist4pWqeGPg0erWSxxpw+ur;R6X8 zNc~|^3YCGCt^E9E;(FEkjZ9Xg zknHvlf^E|!Vfk^d)l&)kD6xX`MKzXC#5^Uo(?u>&VtegdsKjytQUJof2w1fAXh zp~1f(2emsAALeuIi*&u^xVpN!`tkLy*=)8-6I<=Z(fweKPK&)wnQ0Rf!a*(lq#%~M zi+dGSYnd|M&{bWoL`qjRt3|#!}KwCH5pRiGEyyPNcXlC9>2r z*sr?%EsvsCDltdry;_O6+IN@|2ckpp^%c?cRI`OEAg849jC-^sU8E1c}yc^3QBpR5+7@*la+Wu`+lIri`sXZG7jdHXq8_& zr^JXA^O_FqPg$L*#NTzYA1m=2?K@kEm$dI(Wqg~m8c@uN5@J6>P66dd*<7f^K03+8 zO3c%~OO^N*^{$7+0c>%8@OaXk=rd6;9^>k1e$;_{>k2M{1k;EV#xfnQd5w9UG_M6| z+TNLIX>RlHQq(YSZa*-XlzF{*1D1-fen2xDx<9e5Oj~=(6_$B3;f?W*`4gZH@EY3E z%U$y}vmnje0R`4w{TyYkHP>N@p!yLSW9u^L#YY-Zj6H@(czkcC81+jfMu_3` z@hK%X5yPeV40c(@nA{hktj0B;Hg6`lXO-AgjFRT_=-win&oy$*=QPxBl-NuprTH>c zHH%OnO2CpTfB(ZZU(!i`tHc&!ODgI$CAJbt^1Y!%wQkm%N=($gx0KjX``%V!Co#$1 zYwM*;3HVP+?2O>lqlBQ~`%3Jhr_={Z>?(%a(>nEszbY{qaC`5qgMU|IiiZ1CiK)8W z&y|?2%lL;9Gqmq3C2HAcVSHw4pQ%Kh_Ss6z(!RJ7v$fArqF(!4B^tO4ODVBH?2GzY z1C&^(eS?%})V__BXwtr+O6;e78!NF$`$j0SMEf>T;$WTMC?yWjWsFe*tInSO2sg{x zT!|J0XMC=MW0h#rDYjN3r&EkmqEif#RyDSy5=_c;vtwZsP2i4KH_p}*Cb-skYXWA! zRRgN4+-#;T%bKXfDnS_0+DVBux|p4nI86I?RpL9^x4RNQ(oLDH#D!uMrPx!6%c%jg zb&dB{;tE~k=}KIwlhrD56^3rcSsG-P5?5=GdLn2Ji%DVv!j2%4!zx z0Etg;*l#8^r&Wo!#XAJPR9S8GDZY4SL&N<2>0?HTclE~=%F1%OSqrBvrcWJ8{7HZ6 zRMw&N$yzwGmcDc;@t*$DqpX#jXvX4c^XL<{m-qFj!<6+M`V^bBaOO<@_FX0Zf^UF2 zQi%_=?`S1H)V}X2@sUn>tg?=ylwjI|eZh#{IzfrQig)6Bj}1|Wb&3*y!)n?KF_hW9jhW=yBJGnG-VN8`uJXwqNLR>nbMkcGv`$<9-TKW{HkMoNQRq>N_myF?i+ zyb`U;l+mi=u24o=`>s;PQti7&8ErI!uj@U7nZfH=K;;`ln#Wr8ASRMpp_%jrUF%wF z0@d|qWh^r?3a zKUYSV_C27CZexYC9>UToS!e+d*Rg(q<6k4Lz9gB|-?a<}uzDH?*Lu);l$PCNXiAx7 zw}dG#>j`D_7%QpnPb%X_y40tY@nhXhzf#6o+V`w7&ep!?m2nPr-HSfn4|x#NnK$TI zFM*W-&IRcjCVlLk1o@AXmVf8KwO-I&_^L9_HO_Oa-=TT_k)#xo>h-!Z&L^PXBU>Pq z6=h*GpZQ^uu~>OQ#*(^xW@!>cl`^g(h=JGwg49j)VX!i;26bW&iF}~*f;~(b z*U*RIAen;{OOiE7PBl^)*V4yLfdm)wF`tr*R>pM{yBWG?28$O$9t3^fTp8Ch#ba-& zj2j4WE6mk}RL5j)X)@GDBuc^FMj1B}$as)mI726iAwI6BohE?2tuk(+Z`&K<4^x(P zw`ZsBk1MnJR?21mVaW{UU{W8WlHED*J@_P&WIe?sRB1!((Yz*!>vFUmCiMYVPhO2w zC>F6cJ?DsYN2enKB{@(T2p zlRN6C&;rfmDVR@@j=f~#sj)RAkZGv*cSxaFSHK?+GE-W0_~TgO4^qJb%$iI7L~a6 zrS=tsORmIVkE_Q&CQiV2gH2vA)68a@izHz?y*#;>J)6Dr&r6eJ1Q z!YoX2?He^bD7R;g=cIibI+frN7k<(*i$TuZ;2py^&_ z#*ht~?p5srP4`>v15Nju_JO8*UHcwV#_zT77s_~3`yN%sAGGgrWxU1onEgv-{1H^3 z{S??4(OPQmC$xRqJ88d|&H@c-<3PSy8L%dj^7ufG{cCJ@n)j>e3fF$xnjmc)(#MoZ zJCmLq(D^6q8||xI`#JkJ)Cr*VhWaat&NIHuYy2YDe$sx00A4l3?iwKITff#iH?8Jb z{*xa4-znp5;~mF-9h@D%!ZWbr(IWc2GTx<6e*j5A1J(h`Y7hGGM`iqpwlJJY_iM;v95XqUm3zLM2 z3g(SB0H#P@YkyP5r^Y*!^6$#{Ob_R$%J^LSK3B#UqKfv=e<=G)+6#y^NN|%pIF?i_5nUf8!U{L$aEoI~nF-UCu>k=uh7X~n4+6P{ z6C?f~-NMjU{*$1#yWCh+Y$FV040PLsGPtwIzb+|pvOZqKhAGoCl@kMTH(Ohp-Qqs+m4>Wqz7=0;*)C$=rneQ+*_ieCKes8_5dzwVvq}L3JFZ{fW&ht+-nIGGJ%Z=bV_8GD3M%g zG#s0%%;AiNV|y!e1aNl@L{l$WTWip@MRA>lJZDGfNG+w@01=O>%3%uOiS9FP%Z zsB9*ZVhz|vV<4hNjjLV%eoIZAugpy;w|$EXo=#-+sZp7uu;63+0rmR0fQ}Hc{gpYI z;-R}T3Ur`8O6g-F2yeQ=0#cM&H48 z9;vTxadxbM6_EIO0&G!ck^s}Z-TS&OB@k`O+?*gX=uR!l!gS9PIjY5xJj{G{)#q4! z42!K$<`$F;BDo>{A&cjruiZsu=aji6qm|g9${dSxd8{keBV(Xp=0mFb%Ptj}D?a z`9s?$10rH$;36F>j-$ekRkeV^z7}kkI)>Xdb8+?9^!9B`S7l z3>29JY9MK|A?l)y=u=J+>4zCl)*~P6ETYU_4QhVcR>0M5lMn7ReBoJ+-b-Pq4! zKbNulK*ABNR^wM!#qK8(A^{QtTqYd(D8++HT&Br{hm|->obAOPi9IG`k7DVRXy>j+ zHCe?TH|w!?q2}RGGa7{)=m8Sd*+l99Z!oe6CDVBL!k13(*}u*TyFNPmsk4{$Fa5vL z*}aRJ+P|Vu)g|R8My7ume6zZ)r>uTX!pZ%e_+^SI-^eHa=Ns9tI{w?eR<1d;LS9hf z0s$O;QU4m&&mQepn`kPP>G(2HSPDk6e-->D9o4T|{wFic;7o=47dS`;Hy^%17tv_G zti*XVn%IT=gWvi&npBDYkCb@2e}(@iI~JE*N?#@VMoprg{hbmgiWA89x)LX8-|v+; zS^NH=#3@8tLGZF^aKsBwFv$p+^$zy>@WV994&wJ8iqpyWXC;mnM>(;-L}5rU82eC( zGc*i@@W&9$$B}{-U{__KO0=L)uoZ?MD38ySI8*2Gg%aNrM;YRh{*6;VCrSS%+&AiX z!`PyKRs3I_O8sj125MM-(SL*W_Hh3?>3?(_`_*Vh@cnOy(bd0(Y@jdutn+@=^Z(Ns z{+rVWs3C4BaW3r|7Gz=+ww3q+A3EYt$~hHUPI0IVZx!88@NipAir(ebuk+Tox7FeN ziigbHxHNB;aY#tFiNbH-jTSfVnl~d^e1M7%jDx0*<7pz+^PN^Y9f}lq^k5hYK%&J_ z^-w1|fWKlRB3*pNK+&}TZw|M(i|o3)YWWI;%@DO{_j27 z2mRllYTrU-#KjYown-T^Iu1I&J7^z-n-jHfi86N7K1eYqY2U%h*hTvwuG~%gnw7D; z_CY$ihxRR1#zxu)8RSsyTds^M?SqtYp!RhrV}|y1Dr2Vh<&`l@`?{5p&~00(3{U%3 zD`SxM9j1)UH5`O}TWa4CO60ZgC?&eJ?-(W4iBVA1jelR6-DVT{j#K9O+INC78@2Bw zWiBKC3`xvxl}9Iv&jP%H{os5 zCHPBnquuzm@#|&$I&e?}4oM_?6~BR$5hbtQYzGa_P0HNUoJzi1l-U5E`K|b^Dt=r1 zb}+2aU%p}rAxb>e*-xCvBFqh2A9?gdCB9HCVoem=TpG1u3H&8_6}HErtBn6H6HU zzW5U={!8LfS?CTY{uIV5xbx~O!o{D7|60a>1^HF9Nwh=y>`uK<{M9rkcy|mN;^_nZ2UKd@;?5{}%H6 zY2cwlXsK5YiA|tvfniVXk#yp(;hG_wH_)Wyl0A}c{LT1VGX4i_`2i-O`lrgU z2$mSjI8JP1qCMC5fdV(KC8}{qR2S&!`o1z2Y9B(9J`XEE z%n--9ZVV98`hj?@e4=3eVIO3Qy(#{A{2wy@g&}s***1Nh?TN8`8DIuKY^dU2#=pYk z!Ob8;?A+f=G0_|*W0Fguq#;dA@}5pShm5zv(nMI{-Mn%CQkIz6X0x=FjM2x-pkD^&}f-Z(&f50&YT6URH>A#BC15@(|7}cHC5$6B?nQQ10<87n&B>jRP-~D zsy$T6VZ?p6>BR$z7_Q_9f**rTC11RmO-m8fMhCrPMVw_vZi2})0gPx?xRRrlI6^=` zWT*0N5QUobio(Q__0kWbYBH8?1kVDA7^1o)lMP`rq{fv)t{p3@VJvuK#ETF>iR-bV<#Z)yNZbV?DZq?W$I28&Vq?9YC=DY99Y5KX zb#k_pvrtPv$H-SD>m}s5co2bV$xkWMD?jw!6|_+N?jtH7^44+yFqsJf36k&{aV?cH zPs#am0Z5ZbZlw=1sAMBeTS8J8m+rKb`{8`nuji?x+`lYnnYWu< z*(4W(^pQ)7S}rU!n1RtUHD4i*k zY>|*)4%DyC@Rv1s=EV>T`ndtrLVO;@#_QdhsY;-Uw`Yn}f+pTh`#=*PBW{HfjLa(8 z9w?nO;n?TOoIF&@&ft8(ZKb^&99bpv5>v^+j@(82J4F4_{oB9zIKG}r?NP?}Py%vW z1rScgG`?ITA`$?ZC>bUW=|&yrlK`Cm!-P1r0%+`K7|Rq{A_JeG5&%a@L#AI6Pe zLf=kQ@+5gO&>ogL4s80dCr!@N?&-e;#cMkJj6VN?JWWcRG)Ar$NXgSB#NRAPq+7}u z_fo}Fzmugr6S)4sI#2#so-O5B(7uSy%z&n&8AL>`lIO^CiM!F0$74MjEk)sh`{}hR zPo6I?l=1?grM_R4yojo1)5Ub~P2?pY{^X_HCw0^(mn(S%p-_Jxp4QQ--zZFI!ib*) z_X0o8PN)uFJ*Os?48C6(Oqz@#_A2w~n~Wtb3aBPFupn06q>OpmcZ)LSL;p+ODsPwa zHeiL~q-6sFw0a7(ddg@L6B#6Nw`49dK*R4;@-BHd_~w`l^8>3w8x;}1AIGU6MIO%^ z3X%VB%KMc3xx61qflb4tLwJ5iwi!abKy42y`4Ay66-8iFm0u|FGf<)OQQ9lhZRJQ% zB3d3-@(ChZ;tOXs?${VB2!b}uMYOh4rO19dU&|jjKIW=9fx-C2=7ojxs^i0KfSA z1NS8!vj_~1j7P`3=orc|39(5W@8@ALOUK61pwAfaETDmK9m|PhFLq>+RQQ@L05tTs zoP=^*2h7+Z7(A?LB3=pgH-J#Z;-BBoO&1d|TD1-BxlWZ5Kc}n0d(x=Xf@Uw5T`a-GpmlDd6!R{!kh0gE3%prl(WXDn0H zbcXIgcBuT;%Gri0pJ)aDCeuRHn*)p;-H!O_lW@;+opH{#%Gr)dW6ZlWCd-5<8O$~| za7Ej};|$k36R|x&$8e78Z13zuRiA`eHm(-`2!rkE?1IJN>HSA9&N+^%Ll{1aH=EsX{S*+ zO^iDANx*SHN=dF%J>67w4p8Dy>;?85X9@I0odbJa*a&cIgy7SZXLw-)%kCiRyMuvg zquuKhs3Ez>OOb{W3BC;;j5(fs&B|%NiQy}sh z6@F*AiknsqI1x@(IqeSEw!|-_{+rgtm*HuqjXyBx37TuJ{KV;`N*+qntK?ICJv19} z?sU48)6FX<&=*EsRw`!|)upOwcKyQI+WNU{Qqd>9!W(*+dHf=<)#9}Q*q}h=X?j`tJ6&{Dc09wA#DVO)m|;e95-*KdS0xYONc~> zW7^9S%Wy-mnc3CHjuBX>waZ+-wRL)h*4a)e|PHA+n!Oren+vkAguM^L?-nmga zH=w81w|yz+Cg*0%YC=o?%PFK8df*(wCO-kv?=6|rcaCb7K^&Iv>cqQi>6+Q(Z)AU9 zfz@)ITbu&*`&vUx-taD4Dz9TjLfhiN_(pCNX`V0^P0 z`h>mf+~eFUou3+F(!Y{kIX|P-X)RsVN}c?3>PC>zqe@#EZ42fU0KsbirTn3E9xjoK z=;zs8=OO125N$obgu7rVafC|Hb74eNY6=-W6@nHwOl0R93!~+XG{IibfFSa~3#puk z{wjS~w1PGffR}6#=(rgg9AD`OLdX#Osi?i+5J@8Q7~L($MA^hQpcD`QMO+CWmv+Za zrblUD@XB!u$568MAr2%nGdKwPPoI@=Z3y=ff!$32HV$X)$hHg1Hk;OgI4RoK<72Vy z^k&@F{j-aLnW|s=r5@80$YXq@agZT)_=Y%v27z%4MuO4GF5fT%3<&o{J-ehtsai zyN3GK!q;uQ|8JNMm|4=D(UaRZ;ounJi2k;;NZ1sIsiY2Nd!7(b(GUs`MlYisQ~$k8 z$bk1Z+v&bp|D?WQ?t005lQKZOCWeEgNQ^MVe*Gs!dE7z~3{I7SX3Jz>&pILmLJ6Cw z#HLJmr&rS>b!nLx4S`3L?(I!2vrNfNj7kirpQK7`&YNj#t{&p1z#^HO*b=%%iLn7S z%Of1>6VYD9A~tLfV@inZ-i2WuZ@EDLt_`3JWaV!PrDfsUg{5RlbWudDp?;IW>91I# zOrE{px(7Qsu^mW0c`jJN&b{>WAgG_j4k|H`Xd~8N0rlfkP4TxAJE;WA)Z(Cv=Hpm~ zs9jWIR~|-0Edh^;axt)9cER>JJvOgea-C~}NA$5x@}_5q(&|M!ru0i+8930POiab- zY-p>a7bSJS5mzi#0$4qTA>OH!Q-^bk`3R*Tfy~ zcb-oH&+o{t0mYqYQ{o13qmuwBwsrBfg{5@)O+WCH#Y@Z}eE8iKJ`Yl=70URCp1gQF z(T-VQPNle)o$Ww2o8jmI94T&OOS9171f`hlOJK1={Q_r#kn94mVlEu5qN1LB|RBTxWp-V zhBN|Bs18gOyw$3=T1Hy|#|Mc|MdBTu#OY|IzPw3_9$fv9T z#@1-xRm%9j_FbclW3}%(WgM=3kO3X0eUJftNBbZHI#T;?RmSn!ce^r9)V{UKI6?dF zP{vU-$T$aYQruROtm{CJ6=d2vVBVSPdXF+r*AwGjWt>83?!&GEovR7SR5zW%y1OU= zU5pOiKc_2wiTjoDL(Iy=gHp=GLpaO^OF%y+89to3A67<}llTRY=nOngjR(gXa=A{# zJc?D{m~PK(AB~oY$I-Dw6oq>*2Abc$b`y^!eo1H$N7{|S2V=^qbkwO{Pb=en<1bDE zf+2fWKE1|CJc}bS9{zU{&m(F&-llaDFT%GV(?M6b6E7*_LrU-pra3now-;d}old|$ zcHs8@RvAB_)d0PyVKYfasc&J|*9LDtP+o5+;|v0Nla~wu(d!douaoWvfxV@SGj*T8 zt&EeX&))?yhN{~HdRm?yY|@HY-1FHCPU9UxT_`#9rcR?3_kIamK+kS2D0B7PA<7*}N$GZK z7O%ay8>5--a2BpWD-*oah#sr|XbE427M43wxtq9~YOEG$!9$1=oxvIj2QSDIud~@5 zt=uueMdq0uq_x-z1n-WPE<`7rfx2tXK_Y{v2neO)wT&7b!jogO_N#Z@q`M_m5%gh` zj}mzy1P8Kc>BaYoeMCu(r(F4=`bZql(#d$+7BxfxTPt@PcN}I1Gt2#rtSB)JmnT1? zKUuofK+pXoO~y`i-0d(fZ=7^1slK`Hc$c31bfHLyTebbn#IJd@-Ke7 zG4}Km$X$-R0Lyy@FjqQH4PB_*Mz;xIC>pe1PkRS*+DWLadp9V(o92EEtGY$tCVTE8 z_Wsh;C^nf2<09^lDolZFlOu0 zG`MfAT6~9grj*<4wg8i%o#9t{6tK8bc#y)&I1uMdbkoXR>b9Yl(LD8zN!=0jx6UVc zGM=VmF&jPCfiIS$B%;Gf*RHFEIAslf$aOO=UYefhf}e)V=HF~xQE3_@L^fITTZ#1F2q&CNV?y;yr-+1-joaKRn#n?LbOI;Uns0p>4Kp-q}aG6K@P5) zceB!6S@LyZ@oNu#T?71GObfIPbgCa(D*~=2z{5)b^&6KO-4+4ALx9jK+pL(Q|5|(R zERP^a(88M*A+fP*^CiXnj&^a?W1@R3KUwfUI5Ha;80mue9#^c@!k!KyPWYIiA4ekQ zZ?THUxhJBE&i9}~>7K0IQ(VZ_24X;=CDR^28Mx$8+*0*WM!)kQLiDzMNwKU$wZXDa zbx)M;871RF)unB*w30t0z#n4?mlQy+QkBB}h~UmCf#cg^`jyb2Ij(!Qdm`<_=PUOD z_d?xX+A9Nqa_xL0wtW3w+>* zMKbKiVWnL63ioQ7P1k%Q4FGx+$Gxs3_nH1PjitF?OVzj$P0xmCW+59s3o28r@eS_P z(!B-kY9=#Wgz{_XL0aZA6`^iMCu5_&O}V$bP*@vGSb}%w0EF;&?`c&E!5zJ>r1GSO zP5nYo(gYc~?pmrmPC;8_iQmt z<^_Uz8CyrTI9|C-@%u~k9b&}Dp0@hK&}qPBF)~sABJyG!CapQllusyhAX5E``ZpV~WDOM=&4Y*q#kLpf;U_ zHKcTBF{8f_&|gby6|qBB1pJ5qamI^uS5rxMeN659tfWeOc+5n`LcriRo1l7*3Oso+M@AVm^bsAwa_3P`<17 zjkffFMh#7U0lT@0$K2bbheK$jub7KkEOBFRi1aoo89U#SXAx#3;o1k?lchJN1Q1WT zLb%af1RM>z#oN53ujmoiQlKOO;nW!!Vn(~*3hKA#TyIP2r)?kv$%gY+4I;1PP;LaoZfsj!sC2m1g()%o=t_O4Bw?07ge+6^!khD%M}MCI)mycFl_ zB`|ksJ|-)(KnQ4(@^&U>zuk#pMnnwzro9Z*A(+A~S)iWc)>C9-f*I_#2Q6qMRtDL- zfhR_$%HTRQ8=9co0;2RlokR3&isK0Z@CX99C6}dVK)ZSY`}|7)=X0yT=CFSDshCBvaOvSN zS)ySb7VUZSy@kqa^l-McaC*XgEtO7%kX-y+Z;|r$_YOdZq6g5nbW;vlM{6FijO&RP z)xnoWhL&jhX~*(G%}dy&{wWR|b`Ow3 zdgzidQG_*93tW6OguGax3KGsfNd0ifQU7zc5+V8;Yaipd~x5juZHP<9qx30G` z0o=1Hm;ohUk*k}cTn|&;cj!D!oH~8zrK^FM`Z1P`z=bq}^iq-~r`W#rL}$8@H?cdI3@L@*2g3(*O8gzhl-R|)gY3ej61%XY#4d~{u{+X_ zhY2MP!$cBt84bTF#=y6kNQPmm87v%y;j!>-75Tn3!nh&G`QTy)yRd%5?zVmyCfPU) zgKX@=J{-F>zKa_e9EPnVc3~-rUD!!t7Y35pg=HkTgUK!uO4a?d8+?2C?S+9PhJ$4! zc46?0-KmlK?ge=0sc}54C9w-zN$l48F6<<67*>+lg^eV3VIhfK*hgX)R?^soIWTr% zEs0&&Ok)?u!PvzOX?9^liCtJwVi)$4*oE~Zc40e-UA#2O?*8HccJZbMyRf6gE-bOJ z3zK8)!e|}4u&%@|Y}Bv|%S!CRQXIRm6UQ!0EU}As9oWUI4(!6@9lNl(#O^Xb9=7{9 zyxezTbC1I?9>;FG-@XpNemOrLcAEG*w5r+7N5-KGVTg%c zm|8ANtrvWs09>|z%$a)jm6e89ULC1JeIQ4+=*9VOvE_6ozY7v~EjUhKkR z7rU^}#qOW|dci0chhcz=U6|iu7sj{Ph3PGJVPuP4Sk__}h8o$0l|^>(6brjBwZ-n| z;tO{1l2BOBFX6+hL}8fb6W$?;h7G(`6od`P;pxSqFpOQ3-Y5#gF@*8l50!(L5x88# z@m)Nw6T)F%r6+bu!peuM65;-53;^7~UST}Q6Q;xdOHcoV;UNeQjf96Gym176IKm?$ z;gJY$5(#gL@Tf?5G{R$gg*WRJPWB3K9w~ndq~9_U#{O+=Wo*s;gS+Ely~iWIIs&&X z!rS!9kPl|+hMtGMlNO;S9p(J;mMKk z6r|s?R~TW+YsP@T^F9Ho`Cy$La722)hj$57Tv64&#O` zySQJ=E}kU`%iS00@i<9I7%!v+VS#l{#^*R4o-PUFu|LoY)EtlZr`g5p)9m{6fw4r> z3-o$dkk0rP(!qos!@*Q4yFPtj`1FC1^5bF0j=#4=($l#5bOKqjcyD&h9l?>_Q`kU1+$l3#ATrp|!y-Oy98!!*}e$>>ax> zddDtI-mwdNb?m|<9lNkL$1cptu?vH7?7|)#yRiPoF3h>H3%hOX!WtX9Fu2Ap%&M^q z<7w=|RvNo7jm9plps@?PXY9hj8M`<5E^L@_81~B8g$XZqVRnmMSlD70hP2p)eJpli z_=;Uvmtq&Dqu7OAD0X4`iCq|VVi%^G*oCDdxP!=^5xcNv#4c|zy*oA=}c43}}U6`L{7e;#6g^3<^Ve6S)80ujcW_s9#ksfwo zqK91==wTPOYS?|(cVUNy!|(CD9BjaP42Pl7gmy4pYJ7ltL1C#R4E;ccgT5ZSP{?B! zDu3)kSCCyO{;>-yGwi~~3|v7LX5b7W`!ZpF{R3fGl;L>TlL_KYz>!s%Fl-?V+cG6# zSeOaJ5`zUZGL#QlcVQRCTi^~R3oY!z6brjBu>#jHVPJ(_m{(yJR#Vu8brg1C2!&l( zIe{z4iV3^0VZtu#lCTRyBXA8e5yCF)fv^kvAMC=m2fMK0!7fa6unR*R?834JyD*;t zSCH`xI3vkUJG-#F4tHDOnBz?B$7VG`dkcN7ZPeZ7wu^_D+gpR&b!N?N=1#YoI}LKy znmgZS?*0?NndW5tx;ViL`5a#9d)}NvvG_CSbvZw`1&E=zLDicU%)P4Zf;o+TW)ySS zNZ3g705}m33Ky$lpz$#0w6RWS&NST#6;BUox z3warZ;AmjHj6z;5Ux?Z67t#^-(_a1%%IZyzE10=r&3WvN*G1fTBa)d6Yb?VNJGAVG z<$I6VTbxbr5yOj;eshMaXqPjxjK+k?MV_*;WUuQk7e3icE$H~=gzVZ4j+ z`;!=LycenDn6Q##P)XgbhnwH^%h|(kahwYH0UcGN^Xh|0KJhT0c$m)-Vb3*(^~K~r zlIwfqame#%{C!VXxL_VztnbFO^o);D-@l8Y&~hFQceL?oq`cC4>*a8K*guQ?@w5?gE1~kL zv6~(@%s#GTtFcR$H{Vn`na$Ch-6Rbdj(U|z938@}RVMLt2$5C6DETB>Q1B5y*hVpj znBVpbOW_L@HgLcbyn2*4D ztYAJ-J)mGdS$((p^x}2quL|a`;e8I?7YgQ!@V*4^D+Tk_V(jl|Z+M-4id%)hpE(8d z_tk^O6wE(Vdj<24sKh%3^IbUa70f?Z4;8x=%)cP?VZrFu$xGWV4cNW1mQ4Wj9cK~(sk-)I@?Pv~QfA}7?c8MRdzEypzb#TFIfs-y+%it_8SnY6f z1uGAyyI`$?v!-Aj4hJAdXy+(6067NE_Y2nX)kCcl3)abK!w(4avU5hYS+Gv6HVW41 zRGTxX=|9qyKMQpuU7- zwdnWj*I73ftedLu78WM#O$F=l%8lQ zXGXsNSbx7D`u*(cA;v=m`&>AW6zuciJYKLbg!5#YLckerQ(??T((>0mFi2ADhe* z+`2Tf3}btK&3BDbbb6o_LK~+>)6a;cpQ*o}6a79b^1WVve{yYXp8yTS=Ha_PZDI?` zj$~|M1f;1v$f5|y0p&rKL_oe(9^~K%$hXUbG)F*M%Y!V9fGjHyvOEHkEf3OB?G$32 z)wjm-cf?i~Vn<-G@n>DVHg*(wt+g>A!PxPboF^7yC(-OUX-MppA+b}3#7@5>c19s~ z4#k~wckH~yAh_`Vy4VGG#4awxE=Tm`NPcDYz(VXg`ndtrLF^{_xrKgirJvjBXDvvO z*d5`y#GVTAk}$Uc0e?LdJZ=!fAcP$yZh}h4&0=d@^4u0owjJ@X^X{;FF;%P;^$@={ zi95t05W6mis5J*W7u|T#eGQ(#JVM+D<5KrSeEk3{$2|8`mIXxaAl(&M~BMi80E!(%90t1yk2hABry5!F$40w(fsr#WH7+)W>28Mm4D7`K}b z7&yroYt1K(b>?%%9p-Dso#r2nyUafucbgx9z4wXS(u&Ek)*!i+wW-|N+FEX7O_bxT zDRR6uOIBM8<+j#Aayx68+}`Sv6Rcz96zg=kr**cRYF#Avwyu=ZtQ+JE>n>SqJtAjX z&&xXNO*zZ@NY1u}thWcqIrb>Ik6kSr?A_!%yH3uxo8$t!S?+6RL~lGi&s$QzwHd9(8^d5e>mw>d}1 z+nr3xM7X=kC)_3SNjD>(a#z9qu6)KlMgGb? z5AG%MS@%kFoO?4~0KChrcJDX0b$@AY=e}-k?|x=Z@C>uYi<>)m9yEamnLB#J%$>YV z%t_v6=FZ+&a~E&CxhtRGnMdbU`zLqTL_LhpM-|GxiGuOeOHd{=Fmm29N(Zq2E+Jjt=o7z$oHVe4hjSNN}@Rbw%f8_~&vU|Nb#l79!)4dbsJ0CFja-TH!cHc0kxu2WUJ=2`w zDYMpt@(@n$bvU`t!pVKMS7X+Dlgv5Z?&dz;RI|aGVb1mH&3SyHp9cKW{jx0Ki8V9-yw}&51pUD5*4f1E1-!TCKT zJ^zm~1WI`Ud;i`+-e{oPm1 z1Kf|z#qO6T%#fM~dK;PFf;j&mFa-|wwlWWa0RP)y3Z%dkXa-ZD1x$ffuhvW#mGr%t zAwyth$PkzrG6ZIZ41t-<28c)400bYf0e%&-0e;PFfPeVP0d*BNK!puZVFOgy02MYs zMSWkZ_gB~e6*fSH4NzeN&<&K>{}J{5|7|wFFr4<+!v`p*7|>SX1623`6+Xa!B_ALI zKEQJD0akzykOd#09ejWe@Bwn*19XB9a47fydGG{kA}bBT(T8R5$|v=Qsi*akt;s48Vg9 z&;N(+3ykNh0x`NOFbt>v_3#A}CHesijiIz8h z98@?570yA0b5P+NR5%9}&H;8q+^w!~4l103|9hMRJVohq4z@4EUaI~NvJZxVX>n8a zFz^R%;Rgrs>;SY2_}Kyf$-rSAy%{)Cm2eOi8Nu168x1o3#3!f#P$`r4CXN?<}oDw+yWj0l|?)Tf<^xLeLEkhVA^M|8n!j;5f9EuxQkqZuruiJvpL3cGzR%c4--0+Ow=f6g z*6LwkOsYyaCKth2fiQ2aa7-#3lM2V=zmj9J795jx;F#P2j>(58?jB!{C%WgvS|w;T>o`;E@oRvZ1e|a1Hmv_K_c^4Whe}cx!d(c?;qefO{CUtsF; z18<1=A$Tw!;XRDM;Z4ks@ean{ao6P&+;jQVn+Lba{LIUmpL^ZrKfEK&FTG>no?w3E zU1I*zyT&rS+bq*t3-=z&@}9M9?-eWVy=6)7L(B0#vl8AvEmy@XPkEM7gRLqx+!~-p zTLYN~^L@I#qQ-TlyRc_Q^}txQer$M?6B7 z%D>2+!D}7h&fv9<8;UA1|NrC8;7$>47J)AV?HKT7z?}j63)~s-Wx$;oSi+sT$`~(% zHMqi^sc>g1+?oGI?u<3uz0Mlp-eQe(3)Uv?UDl@Veby-VA#1ezlr_eE$=b~QgOzkY zvo`kzTU&S|tu4LHtg+r!)>hsQ*4Ex+Ya6f58t3h6jrW@1F0rb;ytS=&n6~26E&83HKZjomU%VAp&84(8sgB5WnK+&XvQ+HhB!21nO8%1j>a;tW+3xws)0Ev zCg9ajO*F5DYH|+i>rhS32b>W%ts2~B!u=&@UvP}TGm4#5J)jV`ss|S0F{nY_be?eG z+=wA6$LA^`qk{_Kx_F>f7Uk?}(4QS~zpJTN~f3 z+A74y!hZd8i`SVi6yjSKz2o408nM4B#H)+m?TX$BMeh#q`Z?@ajGa{U?t;4Qh6?Rb z_%R8f&zzY#x#WHsMhGzph1~VXt%-8&5sGq6LMTNNDWQmxsYkAv zb7ne1^=N? zbl!l@=6SpYI&VT}t32L1#@i+)tX<66A?EB9bKa84Ht0OwCFavL=IkDG;@@bDW-uq_ z>=kp~5p(v5Is3(&17gm*V$MM^=ir!gXv}$U%sD*fydRyBx*ZvFj*dCU#(2lYoa1B8 z2{Gp+bVh_tMtfO%bdXOq_(G6BYVg+}f6U;SX+b_6wO|iN%PwqpyS@1}!u44K*Jsi^ zyQTTn#q&1$!V35kHau=vQ9zeG{*=M?d3=tG;+{P6vWYL2K%WcZUlNsV<55W?Ow5)M zNh6G;FrS-aVi1qTIH~8%=g)@t^Lc!+o5nJTU&ojD`0^lsbuIdQx&s^(#JBqVS#>e6_E}0w++T(+EKMdJvLSDcJ^}${e z3o(&`f}m$KO1|KED8t0@0-`b8fEO~?3F&5ptO^PTM@NJNgmpoI`7w}^NrD2$N>d<{ zHymC?W>BQz9$-_BPXt1uOr9va7XMUsJ!l1Ocm<&L^>}Al4W{GTxD_zc8Z>DG8KfQ5 zBORd?=?rbj-=HJu23<&Z=uLXSAkq^?klSDa$$`ho?J$@0g2kjayhiST_2f?2MEbxk z(iirUe(*Kv568&>I7tRt7P-rcBY(GiWRO*!+-=n)gRKT+h}E17wc3+=tnTDqt1lU5 z4JE^^G30)0G8tjbBqOco$SCV2GTK^6##s4ethJRqV7*VqS^LO?)*&+9Izb+?&XNf> zBNOc;@~~ZoOtveNDRwO~)xMTIW;Z3%?N;OoyB(QncP3BSJ;)rp7kS$5OXk{lk!S5u zWS;#HdCq=>%(rKe=k0l9k^MYbY%d{8?A2tcy^*|RZznI??~`Tr_v97(1mdscRr@UY z2O(qyp=1?t$ZFyvYe*TgmQ*I|NCWZ)X^hy6zm#=h$>|-d&rtHLD6X zBf2(WGemj3PqLsbP;s@W05;hL)kQ_KN+BP?)%J2x2?*H_X)qpLlpF^xD&zePb5|Ak z2izn4&x0+h;GcA_2q_uE1g}5~ZL4`93`kCBd{xDYh0wYXwpS{Id+Ok8(xR%U<}EM8 z!pQQ1{NM7Tc&+e1vb^}eHO2qbngVB#|IZf@?y><_GyhY|hW~9V0BUCWps4Q7?KF=x z&a4#_nU~D#)>)VcIK=~_1TJtcY7+m&3=O3ZqKH@>}Tm6_A+{>{RZu0hiPAX zEA3~0N&DM}5RcHi>=Sg5eVX2FpP@sEMeiYs4kK65;iNob6?z}3Pwywq=?KyWu@fCh zy3tXjA015wBMzqzkTLW@GL?=ePaw{r6Ult~FnNJaCa)u|p;Jgc&eCtwN62nEjr@~7 zPClbE$+vVCO`>yXP5LywiO!|H=(Dsxoku6q`S^bUeSt2d>*(`z3%a%;?xu_AXLK?B z67djSLXXpB^k=%9{!Vl0Y5FStlfK4my5fSlniS8~L}bB)i{|Pz#dCFj@m&3O@m&4S zOXg~iKRQ>JgM`RjZJl)I>f)(-InLBLPuH|-dh@jP8_v`Ca*uUQ=6X>lDC&C95c@PL zaU+BV9#jj8CK#3-6iv}{r00nn8eDjb=Gy@my8P3ixY1BS(b`a6Sci+kIs`>0T>oa_ za<~g(Js0uYY}Ig+$nc0jbi?IvkC3=6PsDc9;tpJTvu!w>j(!q@DJFUs*GLt*X)2gT5QG0f~3*Yl?qfy9lbyAKtkq9JokNYh+ML!4A5 zUyQ+LTu?mJ^AXD`gMM?C6&+oRD1Mkyq*r&I{ z0lEVY(w*=NeHTvCUGOK}Z8>xg_Ureo%7}I72d+-JU&^(v@pQ^cg7V^FT+LbT+ARTC zA#^HJ2y@SY-%3H>$zn>>dPht(=Rp?1lOjA1f?H~B2aUtlBMZU7{p05ybDdjvY$o*x6!zN&J!z88{N=6 z;hrL5+E_$GMPmjrrs-_?c_Q05_q)ywrg!du0Cplg4-*?rAhH?X zpxA`40o&Dp+k?f1i&!zT+5VfrO|@`4yBt2oQsZWP4+3slw^^p!#tu6C$d4x)u@1Qv z!^QUPkZQnH9%Nyw7T}KEC{do+o(rwKu>VApyqGUtl!to8(><;bJFnol0*ke4IW>n9 zS8zTg>Y(uVaCG`$Gac3`av;_@g4q;Hk0ap6*&-L#1w?`P43%Gek!1lgyj~oA?A9E5p~rFbU>AD)Nb231!!Poo^==J{UwWF7Ug&?kSg|hj zJzA_`*)m}A*)AZ1;`hzs)Ov9yD9-i7>`LrJ3ya!^CCS0Vp^#+Rc=F4~)ebN-$YC9C zD_}b;;LEH=s9x^C+a+I0(~L5XHsZN>D6`(2qRF_ROz`udObJSVP-Z;BEk8%C7_qGA z8&{&6@)vHEY_>m))YvD{8k1$Sc|=(;vz*kM)kp<6MbH7x>NRl!oY!j-&l@F%Ww{)D z&)%;?^6KoQKvGavGK#<0uiUh&Zk9Fj$ZR}onH`iEAmdD-H-%+ZPKM)8d~8s@ykp^d_;J zOb%KW*J+Jh+nhb(QDU-Nb6%C@Vka=Z9u+Myr;Sj3E~-gPD_WDn@(ZlV_NCXP%(V^% zq^EmWlOiBY&8dLEn$rP+H5bPlB^a&otC*??tmT-BL@PEV+gw?-@@Tbce?p{M9kbH{X@S(`kmNR1DmGOhHQR@BJY8QK@NI{LKnnJOfG;R-MI|pS@izZ? z;&HLWqY39l)5H!ODam919mSGf0nP`){7{ZN*2(VfSZ4+(+0zVAL79W|iBZXr>>ZSS zyl&aA);c+GIXqVC>8Vd*uNaKcI(e(5lR-JOQ&^nJscohud|KkjFdPqoxf$74WO+ja zpMb<_6B=0(VfZBUT4^H1yxPU01my_K%IH^cO|ecsu$=sXF-8XE_{Cr*h}c=Jz-F9? zQceZsBa3ZZ#)&~e`DlThZb9cAwq;?C*r?YACIW#W_^XNTQ zHVd!<`7Ewec!1KNT;Phz#$Ize+H1t{Y+>p&Bo_s+>lvwwQ7RqgO|;o~^JTK*Wg^?j zwVDK+Xg0ESKu6iL;{tI3WyX9f-T0cUdCHuS3x$CMFAmNR@m?*B$xcLFc+n&si1Vg+ z>>2K|!1M?EL$@#D^pawxmy}5K!#F}Eua`?vt>e8BRSXZxWsOpeMvwQWE`w<}YHo0H zNWkaE9+10PmL>I``WsF-#WY2_K>ovquw0W9z=w2{c)xRE?8>DS?Hy4 zt$EmsjVlECq1R77!hV9S;#Dg*L#3Aqn&mnEL^U=mni6jgo-RJq=gE;PnPk&=__W8T zB|hH$4=#fK{k0C;7PIJ)Nu6y-TTFsb#y3eHO)$)~G z6cuj)$UOKCw*ZH6e{=-*N8jWA=omDlKS57=0{YXR;a>U+?uCBEz0hxvOMiz|^duC} zQ}7Nggx&NUd_@0*eY6M;;Z^zx#^59q7G=`nOk1hUv8u6ns~JnMZe~eVJC{@#Y;xyLKUcwsLZy@Hg>+JWC=QGySKFDsce`3w;Le_%B2}R`9)SCEw3h@o(80evGZ<=hz!UF}yNkA#n{W5KUQFv|yV= zXZE)EJKHLTupMF)dsjTbc8OVRw^)LA%DL=4G`rg)LhOC9nSCe@uz!j(>|>e6K9QB! zURj%cD%-QqWmoov9LWyI>Fi56n|&=`WZ%ly*g?6O9g^=Oe#{Qbee8(*67e8ADvz;a z@<;ZA{FVJA3)#=A3OlB1vjeIT`&6}HAFKAPQ1xPes)4LX4dRv>!)-N%Q}sA!Y9Zn) zT&UN%qqeaR)d%cd^*K*gzjL1|@Quwt_ z3x1v1ZrgtYJk4-!jBdA`8|7F0JXjXTQT}VQ-9{^wYrXBZ)gNkj+imNv=yuz-=z94L z-Z`f_!(Vy!3KE@YGCL+kmrUfG7?tdyG%)V7dj4JmB(>=?vP3FlKcT(@nj0T2+J@u zk<5lU@<;g-B#|SK4QpUEB-5UDmMf3b+asw7y4?E2O-<01)_bmGMc=S?xRNz3u-IiWZt%h~XP66oiXFT#`VaY`z-V>>04!ZtewHz}qtz71bTz_Jk3t1EhWBmdmmn#kL3jWY(HlH~@pMGRMRi2QM|DKuS(T?F zDiKTO)__XF8t}?JKU!{;4BRWDO2IO^B~v~unOC00SRS_&n1(Ns_tfIN`!Vlcj{gCU zISe9)p>@MTm=$0498?i(XL7nekD6V4G87+w7P5+*L=aBfNQ2|o*pO7HL4VEhco8+RjQ3Qs0Qi|YH zNG&m3$g-G4ixA6XT9y+sONdhfYb=uUHboGCl>cU2D1xj0(*#=tOf003v#?2pToDa#0&$VzUK}pOaKlcAMH#P! z#LF`ZPe3e2Gw@wu47c@6Vn&PHrYM5pkp3UU7UHnUw*8?cV}SpcjHv&1 z!PS-4=~bXFUq+mT&f^&3^}W>7kWd5}|E59JUo}9+)TB&}iWMuCNpR}&G9(Mtq;}Lq z0gmATGulo9E`v$qg-|gpC*_#cGu~d1#3N4_esvNl-sAE2P#+!oRH{f2XIh5x&LM7f3s`55)9d8Fsc}Hl$J3%Xc z3$*3k+;i^+#!wX6MJHlt7r7h?fKpXeH9W_@4wBJT9bK(}sv4>$P8H@vI{>6=5tKs; z%ZbY&RTfcIK5HyL#Q-r7Ba}lnTVyIA{*#hVIH;O!c8;o_ss5D)H2&J zRSTUm7rNs%N@ZCP5N1DRHi$^qaW{xTbxn=u7DKg*d@|_#7r_lN8G0O-r_f_wSj;XXbH zCScfO{2sURTq_tTwt|5fstL}O=F(sWw8vEfUUi|X1G*^M(2h~r7|O9$?p4?0Oe%1_ z(O=BRiv$_7t#xJ0M;R`^W$b2Meud2%;r|B?@7|RHIS;kG!3OUX3ZWI+(TI%bWgB$H zR80<2!s-T3nL?_0$67&kli4HL?tM~%vDoFKAfAtbWIom{m3NJi5>bhGVA%XEF-Mjh z6_srtL&v(f$&*TQsgX zvmH(2OlC1%aV8SCeI)M0pt|j^aq&#tO*a?f@bm|3bm93!W^;=dSKc-`#oIJHnfWRQ zpWelLS+uvVitRKWyY>WZr%9-tlc6D>irx7U=)kAJt^6^#jZcSudsDF5 z%BqTV1HRt6j!&{$@O-Ncf79yD!&VO8VBNttyZYk(9S!Twh`!Xa;#41830lz5vfcG5 zf$mUC^~JR^g?^Bt`oT&ZbY?((cjZRWRy;-ZR|C**d$HeV#t&E0%(A0?me2U%iniEh z4BWKTK-7BEA9_Gy5gLw}@hCdQHNpiPhI;d+IE6j4+*wfPAkM0E7MzQ)2(9fejEe1J z1mU{WQg;>W{ho;4i7rHh5r+7 z=O4oWz8CJ{`(QNsP2mS%I{y-$;$Ooe{tYbW-@#zi@S_USnFtdVSladVS3$dVLPA5~@8ab=nnc zHpdbD^QdN9Z9UD#L;PaR?qo)EcmNv;UEaZTCvV_d0If^X?~;b@yGv7G*8n^+^eylV zmC*>`(xETgyapo_c4-aCA`a?_1h`%#!HpsX+6y0a6{*l0>A~VExKEUUiJ~k_5#?Z- zsOUDMx59ZW(v0<>aikfqhdPmFY*suxwa2D#ho`7nDw;?me{oY(23CY-0hbIaMYy!_ zh6{IF zQi$=2!PwyV@TV>kJHche*E23;9fhp;@Hrl9SWV6esi~c?NNd7$X^zByA^LJ2{S1>{ zgDcrb4C1rSrydWg84+r|Pd({T=>Jqu%`qG(V}%!CeasmwcG5p$&Qrxsk{@%v7jwQD zbE2t1RPM%@b92nOCF;Depz0Z{KYUz^39qVS6{hB4AU@_+D$))Y1kT4meC%JJ%vTF9 ziMj|w@v+a86!pc6DqAf@UVQBDO5#N;Q-u#9B?Z2Mf%w>emK2zaf%w=(B?Z2Qf%w>k zB?Yd)KzxWODR3nQ;zOP(DG)C|6h8Krk^phNr7)*AU>pHNr8Bkq3|J{O9~8O zAU^gx#et?*6}W00zPP5}LRNf;dskiDSvI+>Z-?#vIRpcjCTWsP%;y!*GQ;(7xjLc zy+w!R5TDxKHHZ4t&aT-!Gpu%@aYjh($x|PQNbo=1;E%iJFrV7nH3yG$KkJ$;JXYbd z?!!WxD%kIq>t7M-E0_8vLLGFe@7(HjoeDh%pH&uAA>&zT@&O^`pt#2{-Xy z+{Ax#6aS-Yj_|RQU9%+#$c@|7Dcf9d@_V;KIRoy*;)}R0WVqjBxWAaexMB=pQ7@{TO@bP26;|ei=xTGd ziVP_Meennu&;sQIw29>YMiIn}N7tE%gixB0kd$oXFDE3V{oa6P7~XUlXsHZpp3A^W zWni}kbp{UQd-yV_j;WeK?)p+w&4nrvp5~?UL>M+)E@zmEdggLDLk8Bv&6ydkNxqkw zEU6yw;^QS_xI*6~L5)TyY91oq0lKg68UGCt|7Rlpc>i|*gYn*QF9O~hzK?+ShC47J zGn%*Z65}SZl)qLwGN=!{qPYyo%rs?^#-5m@aXz}BeGKb9!^-Iuu#y9=R2E^yd|cSp zD2v@5tB**tyY->}l0{Q6ZA!Gj%ES`kq@>1QP@>CZ7+J+&uWS*0DYa}y>Liy{{JXLl zsiR!hBz`4g!yd0Hxgt!$Z+RTf6-Z-u#wyL#NJ&brG*k*xY3mkPA9;nWv;xkl-7zoo(cKd#dnA9wCnuyUWt+D;HwK)bu3Bm`5h*yD80#gorljLN zDQBjk-%umQScq*5#6`r&(sN6Wy6Hr8x}@^3zsLIQMwgkE*k?(+YwJP$uK5V@x#lAz z(QRvzyuiBSO7iU%A6ucUn-Fmuy2NIxU>MkC7^=7otP}|s)Nm;rSgQlvOo&n$)Eh2? zES15lTvoaS@`Ibp(j{DJ<>Q2DO)&%JLTh}aGfj3gSIU5Cy28tbL3j-DX4!P%42KMK zsw<(C0n^Z6qvpC3p-asc6W~>I>uef!TSHD3&@hY0R1COKFbN~nmL(1T`Q!sLb`5HU)vKUp7KH( znUKaIon0J4%%1m;3-hS)&7_joFs6AQ!dAw{9*wPxTUj;4#N^RdE-j+VI?QE_PLP*n z9qqD4TmG`FV_nvNJ0)UX*$sx3M8~4irDnN&ktQvDL5CvAAvg6>S`HZ~xD3%*_%BUs zGBpI7RyH!@j#=4ygFEv(IKw1U_{)?THHKTW0Olg|78EDlyhiK)FY=KgvW|?_&!r4B z&XrI~5l{jf7E``0hF3B({$83pQHKWg^_gMa3?~^;C!|~C>6V%6b<3b`Z9Iay9crR( ziCi5qAG?g(r*Cn0FNUIDVnRs+OW?aTAh-(1C@$Gwe|deK9A0`YFtyx${uE`p~$q=JaB%NiXE(Br~s!t|N<41@H>l}i>7hQUotMy83sbSd02>%r!Rl3s@oTGiV( znnlcrY3_=Kz8TU(gBnjh-5s&n^=}C45iUR8y3kZ)y~XgikMM5@=~2j^3$-p}ZH`1n zDXbW@F|0?sB3mI*7Bh^!hTQCBU}H#+bu)mognq!~GQ)WVl!j>Mcnh!YA^o7sMK_1_ zc*DgyBNtl0k}{Y*QZACc%^^L(<9jEpC%V!mAzvkA3E)%NOY@zOe%MQMP*_iPxo}da zDAgY9r8g*~r@9=tP}0+KGDG_Dpq_!LRtW1^hMet@Rl<6XA?JEzYFIyG$ayZQR)+O+ zhFlQT3$dq+4eP~*T;h?#!g{G8U-rngVZAIT6OTKVdvsn{zh;70c;p*l{kj{x%B8W* z^=cEmHmFerSi`Um8ZzG_n}l_NA;T_-%X+=Rjl9tl77FW4F8#Jk<32`jG3@Vnv9rQ@ zn<002rK%U!I}N$ZCCSfWz1xuQxy-l()9)MdLy!C+tpAylNkjT$m&R?H{=@`->ZXLd zEB%=v_j#o_5Y}H9@_@^XXC(SxZoa(eIqtVbRR>q0~R z>5)sr`aF^jxFil%j%7$308EcQ9(Jf9xf_bZlOqhNyc#+ecC;aJ^MK5_yLaLZndnLW zAnYU=jZ`0moRpyBN7ufvljeHu3pp9C7j7DzfQeKl=wN@st+|7*RiR_9&+PT=vG|xT zDaUFaCul}?r2=^WXEz9@Ur zmGU-PAam#r*^BO#edrn4j}bY5rOLZl6*-tSltWo-Ih=Kq_p{z|1iMR)V#DNUHdc;d zljKS+NhSyWk(0U3QS3k;5$!Xk@k8&y> z=NWQ3uY_1tKEdnCIlQrans=0Qc@M-K#NP5*exIDjA4HrWpW}1neEvM*V!4p7lh5-F zh@0dKe3x9rzm$vlQN-hN3I9#L#0%xig2-2dBXfmMzADPd72-Piy67NRitdQF$yMSW zxmrAoI2Fz6o|9|E3dB|N4Y5V87kdytz_$#)kon>#d=2k3VxbI6CEt?eTMUIqPF`M#+vZncS;xm7l8Kh<)VeYNXt!CLlg6_p4{*0ks%$sr*vq$*m-$;0{@c|^BFyjdRAz2x_LIN}I-Tt6m%&~p%Uzs#07YV!o;pw^db*`%G1f`v&olsu?d- zt@w0RJH9$%ZB-|}g}NrbJz^(SH@=Um7e5qnm}(S1MO_y^6Y(k4B>p9p9lr{3t?C@V zP2Cdz0pdrhOZ*{qYy8iMzo~8sLUm6_Mod+=C)7~A66zy1RCgq_P`YpYesH>lOgT@bsewaIs@b;%YHRXuh$q#y6s@+Wq#~xP zohh}{yD1G38>`(ZE!77p-PMOF{Sog{pQYTZ_N6?e_NP3d4y4RgU#2WoU#GmTzDZf5 zzD?Pp4yJsf4y7Dc-=&;ZhkaTd@nxu^z6$D?uYo%5Ym3-H{pjnXe)5e(9HV~r%}~Gi z79%cIzxjgdci%3=_tZ(>x9XJdB;px$IyFI^Nv(ufRTZXYt8=NH5xc1Ksl7F%4#c;R z?$LJYC{0oy(lqrE%~EG-p1MSf)b)rVtx|Vuow^tCbM2)5pyN_cBA(F+{&=0}Pe;5; zC;98@WPfwSn{Qw)5#1V+2b(;SPo$gpzV6 zy)N%RtFKN=(B;#Tb;Yzwx>8yL#OrkBw6?lRS`Wk=T`lcyT|I5Au8}rP*Gzj>*GgNa zYp1=Tv(mQcI%yy2Ytp{a_0oRP*QSGRke;X;rkB@^((CB#^k(|{^iH~IdT)J0`cU00 z{XyM4eY$RuzChoY{;IwyJ)~QvZ`ZBTKh>?%afC@fsoP{Q-8Lgtx67!k+h^3*9Wrjz z9W%PoJ13h)0z(CzMFcNW$?jM+_2Lz_;fr01rU4d8hpukFfcVN99 z9LU#00-LpGD2gv8puz0h5gQbAIoII}AQl|R_|9qUG{KLg?eYKNWLpRDBWYjzzj3Zd z7bU;@-*cLxi_s4LG0qJRexrhp@po{Vqf5{;sXLt(&W&h1N>VpEH>uO0*uvE6PD^Vq z=(sEhIjx-5;KYrB`<$E66&GLIYUsM0KsY5 zsu$8qp!mCJ%oQ;RcG|fH!7Y(Cw#N@-7C7zkz1zr3w@#;QT5Gd&3;y7jKAbK#)V_$y zg%%fZ257AU2Tv$ofaHrK1!!$IU@;_L96$0xX3<+Q!!y_XSKkjpkAf6E1}f;WP)9!q z_4Gr~R8NF9dJ=TklcBGk0>jXMtbXJI%c2LI9Orf{2bRQM?({M?gYW>1N0*HzyZ4K| zPH!}_O@Y5Vcc342EsH#xBop(_;+TDkWA-VI*|#`mpU8Ja;(`2$CQxXB?{un+mg@Ej zr=MqQ1dYJ`xDLcsOLh8J%XbD29Wd*46z^I3qO})*#xEhF584Tv-*y|cOa@TS5NBva z#%N3sErOwXn7o$bbM6U|l^w&*u$+){U#D6@=lx_Ip*IrxS+L0-wn>wRS=io+)J0xmlS)dp6%X zvwnghjLC-RH9p_k-jQzYn3c|TW)dg}{WA=a>TJ)<&*z#m1!U7e`PAmp$fdEPfXqe# zA>G_OBfodLC4*Gjn+o|{ZeF_3m|YIPQEv%hy=w@kCB1ZFp^!%+$>z+0LIxkbY-?L* zM?0s3WZE-L!I!F*ENZ|tY|Cd`JKB*F%Gn3OP1!MSf~jG2hs zUb{CE9No~G>8Q=NEXZZ&ZC^V*lg>5do6@=Law^BDG5F6Ak`P17z+4H)tZI1;4DGVp+tO%jrCSjF z4cdyP$FOMUVSyGwBL?9zmtWFW$j<{&S(sh0P&;L&tv#Eg9&`QANDLG$z+j)l;|}B+ zZ570o=asyC?OijQP=5|N`nZi*Ivt7^5IN_e=G(tTCC7913iVpkw4Sg ztbL`0<6KG$M?p>@pUt*5XQ(n=W-NNFblPL>D-CGmHK%`RI#8K1prJ~nqvMdJ?f_ej>f3qn1N}Ke!7LQF+YUj%8@j zHGUgsh;_Tfn-&-Y}=9u{N(j83;Tfn@)O;z#p z^74f&I8kspEg%n@mlBe6YjeH@0}i7p3zAJ)f@#X34Eyv5v2=atSk{)Q1tCV;Hq{ou zUDej*^68El2xiu*cTkkZOcR%;DhPnDy%S{H5YzwNe6yOdA*MUbxNj_>s;Is3KGE1_}v@H54 zQ)tl&+%&w*7G%6>G%GWUiDR6S#{{2}r|Jw+>K{D(4-%;GK5kuST;Kd;3%GYHxx#oIau{05@C6Y1&vzosk zmjrJVy3x3xJtbPakk5khZc@JYe5R6$KnzJQVqmH7{SCynCGkEPNYp|xe9ZT|{ zXsy^;8e-5)ELq^g+cT}>!A8_|w)(Soa6=RPQ_|plmtw0FeL}Al+xnSO}IE9T-H_^j9a>3v1*2Y@sF7tS4}c zivc^3C)()wGejeRFF^2kjJ*X|HV_@Y`qsctq7k11!>WrM)M>FJAaNH>~M3GPut*wlwey$xs`*ioS+!o5@@1)%iqJirA7nu0Wo zi2A3S(``&&hnA)HWA$gm?a!<5^h|SSQ>M8wO|mKwjYrDU$G9wo*Gs)MT%0a+wjP+y zEx@ANv9RU9Lb|m*-E`n|2DKAeFocCPE;8sYd5EPK&!(%$ri58oJNrS>9o~&H!Nh0@ zsQ-G*0APy;8rt0Tqv8O64jN)a`IhL(q)g5CsT3M?=km$umYRmmCO8IW@i7+z;>LyV zi`>(Do)I&y(O4N_i zj!LETuJXr9te{P?;>7x*$Bp#Eql-#2a0dE`s7xcMHn9%8?=nO>miv!wjHY0g)X`46 zP)o^UKS`YHc$Vb)b7}x39Iv=;iv$0Stb0~zPm{~BtqW+2Iw6+dztGTgCuj=MM<*I1 z5y$^~371f*vh#uZtBW)?s(>z+E?d9G&UxL6cgg>S;!zKm?*6*cW=(K;qUaK1fW+GL z$p37iSt_`uuC(3}mrbZ6^^TXxHMNVfK_!@1Cu{wF>f`*t|eX zBvh=x5VyrA(pp-qd-`Rn^*>!$>f*k-xV+|Yd!o#JVpYHlL&*8DG4t=&;)e)vnXW6} z+Ejt6u?-D0*8+J9n2do4;tF*M_Ym$cDZuJa710AcMnlG~y{0{&s@O|{qLxj3-yfZL z&uAjDjPfg`i9aAXF0D@~MKi=YC8-i_AbDNG7f`j*lEf%mZxYy(;8*WE4N<@L)(Fm! zy0cG|P$Q(sF|h|HPNXGBJjEv=IcDDa#4>8kkSy1iXfbg_NV&r&8e3))#3AbwX~ZcH zNi-n{%v1G|Q-h z132N3Ckv|4(@8+>SDyDLIY9e(2y8);Ne+YLoJJ9YOyJvqQ9a@ zPG@WOkj@O9OeQ*7+6Y>6_;&_}u>i^tdzNrO*a?=EI+wB&yqnAA7eJIeE*pt*Jx)>| zOo1TA1iL|)w;i$i`q03ee2Es7=oOhnXeDxJZ!*x z52#X{;atlIiZA)yxg^@_gT2P!rvpUv1@HB>e1*<;Ar^0+k9vk$nJT6^WkiauyzdjoF5o@@P)?V zyv~jel80(Nq;Twv<6w-OstNk6FNUf|X%LYlm0Hpig!J;G1pYz5Ag;L^qD#wWfaQ&& z_MKl%D4+NW5-Bwv`Py*RpI-4xwWj`2GpQTZjALNyqedFgq)Tvz9? z(L%rV4wO>@?1ypwy(hL&<S~^>*{Yh9Ik~NS}%+1gn#Ce?J zq?fKKP7+nmkV;Hn&F9e0u%3f3Ig&U8M-PMt_4IL4A=^AIy`X`=0LOspQ%oxDf5wV} z0;EBJNIl5adWl5o>0p&Mtl>;-WR*qw1VjgCLO2vEU>Dy-XM$MS@%kh1dii)A*^V4b z<#`eWDr4m`(7G0wA3l;S1g>C8_#ae!3h4EhMqq$VlfhSW@*{+EDDVfP!uLlV?+5hZ z6|a#ONU9bslC-KafuP3XFC?QQb~xTxdbO_|$BRC&#_|p=p2>V98hRo>j(!`C9Y8iBU2(XPojqbT)+@b^h0KU?^OGBBMn5<5i?gGsA}X!Y?v|6JD{)@4|Yt6G>8$zhw3hAFcZuN z;+iS;m1}6uGN%M-`y?DHkqWFs`fwV{*O855(ZFhA>99soK`Z&`PAjAJraz^4eE10? z7Aes%Lx;n7kR=c)RA}gx+0I@Zr zwWnC00_#8nX&1~&L(v>ML4KeO1v=DWctZ~r+c63fI0@|}^0Yvg3xgNi!(qegsUSNk zv52y_QRq1oFhYr~bje04v9)%MR^nhnQb5A0g0&fQi>g;Qd=|yoLm96ceZiFV$j&sn66t=$-IcgbD`woA>px}d)n5@&D zr^FQPYF1({8U$bW)P3RO>vqJBeK6ASi2??`CJUOfP?85L@wo1doD#3;BwLkuUAx+p zc$G&{l*U+6KA@QGN_?rYI+b`{yOt>Nf_5!a#sM4?weoZ4nCP)6t#P28l+@u${7pyu zsS+=0*HKEmq+LH##zB-+?b|_Tb_~FpNmSD%W+>uF>|S0>S(UC@H^Cr1+%AH%a|mMbjtB4wU$Uf`IQ;2XB;3o-}0<}b|4 zrFj`5LGo2US(#Uuzr>6|ZDI@d=KgI0mKioO8$N0{ugxXbL8y&>rGyf_9P?^aa3Y^# z9!SBzR-(7)MXqa==%WY3^-A>9t{au;uU)@YVxXu>?y<2}U#AE~B?gIgl6yE&&}~Yr zE7qZptCd(!tRu}kv1FAwiEzz3%!>%`9wpWnL!@~hT06q?g)Xjnug3bF5*vzYX+8u^ z#|R56JR~~huTQwt)_89zF-GV6 z7bV8&Jl<7eymq~>#02g7P>G4!^;ae8wCfWkCTZ7aO6;j!Uno(pU4K)eLFe(c64S&! zRK9POn66#_P@+-0{;9+ac3Bt$GquZ9VwQHgWeX)P5kn}(a3y|8 z71&dk80+Jey2RTk@hcr|TP3bS*Ns0{qwJu>)fxq>*#kPgU6goOBk!uj@3m``5|3!t zZc03%UArssq;`!};!oN&UWu2rYoZdbXxAhq{;cz;SKCSf-=$?sL z(Zn;+I!zhPI_z|1WVGu{Wz5&Evz4)cMldLN=?G@|sAHXPh#F>KA$cI?q8d76xlA>7 z4xmR`Dpaj?gh>U@wa&5Dqw-&*jD<#)T$d>0U}F)vexZza;fqKmV`GCI`gq!?vEN*kG2O%qfXn@rBs0E3lSA)OCR>oK3cdh$%6aHQq7Z?{h7DOHc zeV)G*lgjm&GA<&dCy*=%P4z@AyKF}{HCMCzS9e%DA^%Ys7ZdW+Sj?wrz3ysel&eF* z9ics|j7tdZd9+QC@vLk+WN+=&!)S{zoUmS0#-)VyCz7uCi6I%TP)TyHDB~A|_!?L@ zlKEBhicCrTSs9nnmp5_H&R?dbNhnWetGqYm>j2fXnd0hXmS%~WwBA<6ywd<6`>-Kp03^L04F_(mC55ys!KECjJ< z(1-7oaWyy=>-*RT+EUntGOob~+X7b|#F$fETTQX-gff0jA0-Imblyi(Xi6Esp->Oa zGoB^(;U=Fx_fp2S%mvwflyMy)_QhD8PGzjl&#w+u{D^z7`zzymLKz5l44c$yk}(hK zY@q>QucM3`L@#L%j?;0y?%7!3!s5=T-mQ8&*Iw5iiV0_LP=3N;vqx!GE8|AvG{@dJ z7Jm-Kr;nQ|<0ksJIryiNwg?c0DdV?d8#41ds>hQ;pg+AZEvg~rMqzl0 z*;^~)W{Oa2hz-Zi$GEF*NiRZwS3{g$I@f4q+1n}O7UMK&?|}KkD_4vc9oLzqE6R?& z6I!!Nvsa^luD!jr9)9c*%HGu;i5|oG77KuefMM>&$NI^Qq8Ouw7ynegT8pVeqy}ZIB&5A@CnG|d=pV~;#h#{&RfIjAW+_WGV+F-pgVVJx z<(;97+l50D%`?1naF+(mdeIbC-O_Fg0@FNfEmjJpYMF-oCjCp1Lb zwX#1C_od3XhcK3tD7w5*-!JcB%D9&>kHAn`lR)1`JyIF>=?WdKjQceqIz|}}Y8QB# z-)k3mnn#JcoCGQsa*0|j@7gEYrw}bU6`eh-9{)7)__;EkFrJk58NQT*@8FaO>s|YF z-YMDOdj4oUC2ep$>xZd#7u>t{IU4^0Wjt*>BkhaOsDvLb>oK9cYhS35FIC3##tRU+ z*q2c$FUQQ~Qu@UN%kvV8QEae0FKZWAo>#OBEYEA&1(xS^?YdqWZ)g`-o;S7Yx5{`+ zyKYg&+svBSMP>X243WJOoBwzzwWJNYBrR>EQa_G`=hD6nRId!$3_4NpksNz9D8H67 z>Lu2-S6S;x`z}oKGI1x;g?62O%09>bxoh8P-%E{fAJHs5D~R3q&sT=ea_trN1BCE9 zL+q*%g0}Tbty9xVgylc!-hW6L?-=ho_U|DF@e53Mq-YZTK^gDSr^k3Vmdmt)$rwo= zo>0d7VyU$Mh&e#DDZRDj+E3~ZeMT7{h?^bzIo!_jX;mngsP4#Dw-Z;Rby?c0v#pf! z3(EKqE4}@aA*v_RNp&@yW>Ch=fdt4D$aC#wWqf74OEF(n#@D(#UsuLA+VzGqz7@S_ z4Sh@5Z_`>pf;4*Zf?~X*jPLZM0qgTm?fO8OSVHORN6NId>tkgmXi9w=pC2WHao7Gt z*Z2!%I%Z1Re+xtvap|~Ye~n2=I{x}Xc{Lc%cGv#W{+2otyv;UcXlK;-B{`1LoVNX) zGF{ViY%oBBnLn)d#opCg9L{;5ItfFW3Wy0XME!9`sTvHbKEKc>IfRl>W-mekThxb8 zx}=a&W^eio{-~Ni(}|9cv~`)}dMUGy=~2Rclv%}_vP55H_7(d$iT)t>0gD_L^ZUtB zt5`*fI!KxQC{qXog7fyEhSdKF$OZb-_aVgi&|x=Kj1CXz;|9tcfWefg2DO5;KiVts zSPIu#Lfu%I0||B0*eX$SN}|bdVsm8oH>W#UM_O^?PuP#-jNGA?~Hj4G9s#ce>%_@0sHmQBxWmf z6Kqiva})c^1o$1zJ+Tm%w}2UyI3NMOM^4;(QbT_ zCpRV6)>Wc8kUuGYy9@4J!l%d7F39xPZ zYJv@U)PC9pdDLv}`h^mI)UL~w7$AD#y1}p>X?c#C;A>3=uS|6BB09TV+_Tim=!`=D z25LKH9w6vR#?&l00l8cp?k28I{8}ci0Sm|6jbB`q_zkfTDXHIq@CoZL?bTQ;#epc|zJvv^j) zKb+mIXN?s$eSGv&V=wGk{C}mfyJj_}XIY`BbIK2lY|j$-ab;aYUOgOyqk1~<%Vbl& zl8^q+SF%TC{I^@JTyrrh-x3vjYB@hU!r% z|C0%3v8RG|KQ@wqUj2_~BI?cimAH_46RU7f=vxnalM2!Eo)S;?Ec5?l!{Q1>>64lLFWws_$VraH!EVNdRcAI4I0Pb+bT zIGtS2DshxJ(n&lY$Dwnx#EVLtsd4_K#L9BM;hX?p7m1?2T9Kc+>dH^!`QfIMf|@ylzLS0wUn^@r2i4~?ZKWk(*I~b_NdZK z;Co&XqrGPpSxZ}Xo98_$=l`!0{5OXWk%sq_xPVp-3-5!FA9^gO>P-jqSWbfyP~sCq zY!+Wo@GMwunqGa?Z@<>JEU3f&70+b3iI2^TWCF*DTg36#@&bsP_{_Wr(Gq`CiLVln z!6otV4eOMSEgcRK2Oi%Th6f<=?5KDs6K}x2RYs#Y&rSRz@lTof4!n9Ck7=t= zCB9EW30Pt8kMk=np2eH8rA1iN1vza0x}{1-M3B8SuQ86BG?TVWLgY8J>&q;BTWN<{ zKC<0GB@@Xc_NyF-D|^Exlw{U5{T@zpY<-hX(v``SA!_4=&n#`i8=nvgfeRsV4g^G` zY>GY2#NdlcdgcWXBDCS|Nk=2SZbQON+PNm7U8MJ+Nu0xu5_Im9?5m93w5z`o|74w> zy*tCO5}nCe-e<4oB%bZ*FokjXPY4Ux>?Oa?7sAAGq2UyyNI5Ycw)+jXDJ>O(daf zRMbVYCt)h--vZl_)2l(D^b z;S_U6?W$A8&f10J$`RT%SsA-(7fvTfYS-S%=%-ycgB+k;P;Kp{T{vazqg}In2PvaUyXGllBaMf{zD=}iz7hrPTBt;ab{(w5N-+eA ztx2d+9$=0kSF19Q)vh*W?xtPP&z)wD!Ja(Xp^}}+#fC^VgOh1Bp@cSjg2OG1A+pvZ#DulRRFDuf*4>0wnLEQ})+H}g$%~Q~Yu#O6FM$Xp9)j#4P2?lYwUR1ggdsW35uHw6s>GY( zJaS#8#9P{Rg%WRT*Of~A1ymg6x=JOlrgHT~KgP<1v}E$vh8Py7WhKi_mxaUo?)xYlwUe zF+P5p6vT`#Y~zXca(ZhBB?^8`@>Z2Bl8nkib2!PB=&#^LrLPl~ye+v}CU3|2RlG{H zLi*xPJ?Q<}G&y+Hs$C`TNZyHANY572s?)QcEF)$J-$COyrKy74by2c=DHoig z?%c!Vhj&lx0VSr2u}<}A-dgjov36w3+?bRczog}X1832!u|p3p zfNaXD^W*v1Je7Pq`4B7L6>LNRe*-Kkh3MWdO+|1U zt@KI~NtPXnV`yU=2%=fxN+y&zLL82C(uD=v;=K#HqrY9reCYvGH40O=l%7FHwIq?X zQ8UEG^)@aYE32U|cwxkQ1HLHQ0dw!)MC|L+o{O!E2~3FcAP9M_{8% z%sb?UN>suHN(r{EH|9pn5CNg!7S(UWwV2VeM#(MZ zR!E)}hji=WGkK#{+`n!g09XUw|bK+EXaGmjtEhqu(^)FU#TrfA1)lh5aWj-)D#I4R^!ckU+4O;|SX)j+&F(WmxHQ+Ow9WbE$d%O3WW4 zW+CS0QQ4wotIT6MXWRYLQMALb;dAKQAxah`rZ-)v!iG&hC8XJTT0Q-jhImegpV8)> za*34KX$)Q?l9Eg1GEGAGMlA9uRZR68S;|8}>*v;a@^JZ6DWN4eBt9|&o{svGm|P`~ zlt+mImozD$n%KYx zvGQDH?5$nrD`P73zvPATVks{IRfq;HKTl7y=MtJdWi*M24U)KeF$D>r;+HFVh5RMt z<`@jVg&H(qMELzMj)ha?;k=-L{YTVml>D{)4WfdYhEeL_zMDCJr$m^86o|ukg z{oakRJ3&D=DtQwH$*D7EP#7(nlj-LcC2yt9=&jezfQKLoxkAa6LAg?M`{e_=Etqc_3%UUUQ$k_< zF-+_3gGxRm!5!=U4}^GnY;tF?jD<%s!Z9`8W6HM6!$qLpAX`Wlk!EJ-s?-+Yk&4X zC0~$#QW7_}FpfLO_o|W$p)Lg1>q`Dvz5!Ct)6>5NG!@;mTD#8zo8lU9%h@VlTPQ+v2}V*tjBw$`QldnN9rE#jk&p&Zk( z&{)WusX6ntXj(nHqac#iI0>u@PLeiJ$Zd@zp4I`!QBH~%5NPGYs8z>|Jj6njhNtkU z7bXNcf$yeMa%)Kp%2@IZ>0p!%3c8zaXW_;Ho*<=ma-BX-KQz13pS4JPxKA6!`bmg7 zP&tE$kVx8!w<0+deqH4ZroyM1AqL7e5fkXFU%IMcw8S3SbDi~^p~~5SIc$u(Oow!; zeGg226K2g;V`IGbRVfvVzU*V)k7jEcTF@*g$<|FAO4&75JF9L{ji?2u4mgcE;? zY_?R+R?gO%KjlJT;KbNl4)RfJm9s5r!qGbd^zPukWJiP2*j_n1Q2KT=o^VBgTtYpw zvvPKEz)7co9<0rS;*6L-_eik|4L6bsK8kNW^tjE@nGU#V&)*YJp3@~2!=&S1xC%C(9@kU&K6>20ZVv? zGf_EpOgi<3!g2OQ=WwCw={~44S&2ii7TDW64NyCErgXXY5zy8c->4bb@ZJZe9ke&k zccy{T#+%pYb3?9?ry_MFB20%U$Gm`CGn6xvhZziYwP&ziIr}PSKi;cPpIYxYb0XE& zyiTqbv)Rlclosnz3j7^a&H{r|?7kRLVul$IeC&r*mNmHI)`AU#YdDD5M)?kvfXvsoOUW0mR%?k#_@Qu!9iIWdy`to7w(l~ zM-V(!+MLD8S;BRpkwuq1on_9U(g9OHB%Xy|6i{iclB=ABxw1chbN0Snt@YVl}hvAyRU=bQ{tkX+_ECpf24!=47oh_9i^kL|AQ4ddanaK7N2 zuADQRGeKtxF-Wo2TRG=A=c2+L^ypyy1Wf0C(7x?bW6U)3A}Z$v%DIpj zl_Ul)9$*He;asd7Tw_NHIdmI6q>62y^9$u%#-gpPZy1!u*cIR}oL_d^E&8+7bAIJq zEuE{-Qfpd7m2-{rYs@Voj{c)5q~cuh8Cx2g@?Gdy>16 z-GQ-E%XNO^+(_+ylOaZ}eXTD&d}OUi--ag&LFwGAoLih*aV!FbbI9bDYoQu%h(@3@ zeq0ItU5e|ha8^kNvhN-LmH5iJjpmg#e@Qbn@@i_vJ7{c_a4Ye;U`zq=F5(93Gbj8kZSctJ}dV7jRE~ess5)=p2~qtUQ=pe^ky>RGZ#D#q;GluJerZ zoOGVW1Ps<9AAv3bfJ!=tJI)J{1U*Y9Yt-w4C z(8DJBG>5C7JYA=xL7Af^rW6ZwAi6~Ibo`QP(mW=mZBrr8OnwlEicS*3Lv5L~SDb@~ z@TSB+o2CP}rZD!p;aXhEqM;D<&K5~`HJ^@_3Q?nF8_9wA(xOk(8F7LYsR3T3fyYES z4gFR6xTpm!Wk4@krqFQXH9EdRpR0r-mn{sW#}ok&3k5G1mjpgn@jDfhnL6HN>eC9Z2KcA1`6ykD*cuSM|4)Viz%!NoRx(rwZB|K>hx5UIgOB~CDfc88fp7Y5(Y|1k5zD9fOxD7e44Ei32~ zy*&W6*kr8;Tn1nnXm@|`^jRhoMj=(Qv4Q=}x3{CsMkbl}op4gi#QAnp@G&X#Aw6 zsMOxhC*bG``7G~;Qqxk?WojQV4!)cN+>lB&k_3q!9K=cm1FI}FqUbYKY8LIMd~=#e zM5gG-&W}4$-+M(8Zff7ubc!)orG65Vwvt^<#DZWeHQjYa)6X-hgH$R_+hn^XU06f` zO)Aw)0T==#as_+E{1*b)wDnT(L?kGedi}%z=pQWaQ3)_J;%oU@S4~y zf6r&qr?%$k%5(~^b6qE{cTy1fZXUgEF`q6Q>bHe*cx4&fS9QliaPPEbrC5h3<5PNK zPXO99-n%jK+o>d`@i}|F-19A8K2=QbLtpS?1}Vn6Dj0$M>|d#pX2?R)CmCv z<6HUA$k0zwyQ$-GP$R6=$trb9>QpWK)1N@bV{%0u1{#j7O94U?C-rlXt?el48IV?{ z&cq|BF?1rfkUq6`#MP?;(HJ71jmXcZZz^re~v}CuA2g?-nwYt2U=D%(2rk+eaP0RW-big%Nb9ua$g}qMYg6h^s#$TNj zcEk3hLS~thdI`IBJmc@AUPjP3yqxQ#UWIE~ww0$B9<=y`BH%P>9j-QRi^AeLZPI<} z!1aAg8K=^0fPit}L^>1J6SRIp@YV)p{;o34BqSUiVagB^z4j5dI_cUF+K0+GOSk!7 zm2ncaISA@zP%Yeso{cB#4_fh!TRxYCz@RlK3q}7-89%2P_eF{1LbrmG!V!|go5t#T z)cI@BWs-gHGXeWKsc+-{*-q*o=$G*OoZ1;O^-mlo&n?ruIGc0@+TS%ud{+*mOe7q; z+UK~Ia&0=rOX5xMh4}&nC6y~FD3NVTH!r1-lyY4PNiD!j_WbfPg(~ItqEMGY7qWRX z3htxaDvnOU%P6Fua`93s5~Pr&cq7LhNQE85XGzdyg{U~D{p&yV!bdd1a@SSvV0S%D zn}WLoI5VOhLckGfQX0JRPD0fNcZhO_2G_qQwvz5>Gsv_%M7m%NH*BJTivuV;;6Q{K z?|j#4AKomRG_&4yH*|4UbH0nSni)P} zAu`xZxtqJ-Eft>)_{&jrKpa$VGCy=bO1e0*86IaM)*@KmbUVj|>b-p9q?<`$&~=Bo zTT^wmF$C@+_b?H^PEA>T?Ob@bW}SQgwJS(wawIbL|e zWx<#7mIg|9=O0nS?HzXnYDh=3Z=7_C>+a-kO|xK>az|6Q55kg*CoytRZpkcZgMFYq zd3;J2_t^OuhOdkv^TF>(&i&@1U_?%L;-qyiuMz)7@9Pv%tZ|2XlFB(ffq^C0D>-FcvC+cHv0XyoVk8E|0(EOuZr*LfFd}7!=7T%i zJLo08pj?q61!bc3D7#}}rb-u#cK@hybAl9H_YilHbn%=??CbRCYX^M=r@lcH7i1gc zR6kD_Aub`rLrW0#Bbl1qMu^J^5sF?LMk)F)!go#baKbza3~GdlgJA|C6WfFe4FL-Eu-Nx3Jxr{E|S z9SXIdmH-R8QGfl3x-$NqI}wL=TbE?ZnrCax`&9P?>7G&2FH~Gw7E23xIw68#A6k+D zz2#MkcNXEDSAxfP?DR9EL3LdBT=xW8hc8s_MefDAzO+^b2<7S<|IYQjv?QBxWDZ4h z20<#WdkGci@{&Q)081tOFieDX8DU*n(h9t)!b%v{rf}M$~H_nZ_cLUrR6AvM4FSx&e)h zh5BaY-s0X0%1Ts%SLXnQ=(i?2(XcAT;D%mNQg~8cr*@%-e1e2rw@8J*4GHF=!e=pl zVU#;~p*TWbMaXyHpfy6CKvv&lvtl*X@a~eHYs@rt#kz~I?kgDw6Y}BnyHSSsQii|7 znjFipYYiSC9Gn*p>4pPtFiPqnLc?wbR z^T|XE6n z2;pTycpa-xF6#1pHv0Y=eSfngfjKxDiV)tQ+F-X9BjI}az;-9XdYiD`D;a_jp^qcJ zONbwKMZ|UuYc<%VD8&z`U>}#%9@|P%Jc)4rN;sdD;$*S5#c@6*oG-C|&xH*zA(Mm2 zqfA?r&=-V+Jzi}3BeOE4`ic<$Uea7MN}B6isvq`#u}g|PEPu-y z7RnQZW|vGr6lz8xn$Vs_D6(Xz5s3*Ouynm7VYnsqN=p{Nar2XvQGO{x?bR)@s82ja z7}y-f+LE#>Ku0r5rVkFg2H^hVfVU0J8+G#7Crsl%&t3UCA<39%&WxFMz z*WyGaLf8F`p`#xhjBvMc7h(UGI32P@5BKZu@URIil?}qo0TzeC8n&0`q|R`>T_6K$ zfB`XFKJ#`}-pJrZKVRj7rBYKdwpnySNTZdv8=WE8Z5R~@NSqG$Lhz#yCO73ksiMV> zE~IIBCWOdtOHc!BrIKv<42}euF9URF@XUbP4%pziI)(Ud7#ku&;0_0JQ$9z}wYGOb z_9b9IF65U$K;B6hFu(>*3x{ub%Ngz3@B{J)l(aXav;>T<^y;KnZ-@g&&lou}Ry1&M z{?*peGiE^0J+kD$lg~n^Li!_Q0lYcqN`b57E3x_$BIAE(<9O4daps{h^n14@sM4DW zfmD=4l)3cwMWe^n9>T0WZY(estv`DpgQ1G~%FGa8y`)@bURnnej;{A47=Bf+-n`7Nn+|2~|Kd zg%~CA0}N93txxX$I;?QupX=qkR_V2%N24N6Np~z%Uf#owE(tS8ka8k#M4JH6g4u$S zmq=CpZ6XBm9@-UEXuGa}!Kc>nuGKT^1% z%Y0lUVhLe!!k?zDcX;Xy>0xUYy?7jcMXK&WlU#KP)^kgncK5uLa~@!SUc@z@4K*zIEo zOHmAQiH0HU6f!*6hcFz-{+th6{Q9-}<%4}G{@&(8XsNTm5bK9_^g~A%KVDv8JeZJT z2m?|KVLpl>j7Kp%OdQS-x3L-Gc z@yZ)Ryz#~muOl(U+ei%YwK2qlYYbu1is3oFA1{%jmU>>D4;NlvD)HkD zztDdXTzH`|^bE<+dqV+iwK3?K2~AH<{Vf6Rxl ztH^$s1Y`Il!@l&eF8iMnPlxq<2Cipg{^#I_O-06oMMZ`%jK~mX5gEd)7(-l5V+fOC z3}La6A*?wvge^ygFf_&xX2uxep_DLx8V`6brNoa%Q%d}JI;F(_P8UDyk#V~3`w(`< z*bl2>3_tSA1yf?||Ja8x8peK@3}g724`CXN{jdkd5Y{Fc!c-(fypY2X2ErJAEB?+9 z4?KnC{0=TW78UwwJmGn$xZl7NQi0#VaXmdG75cGi($i6)KLI~pVWNBtoE$QAe26!X zLOiUi^zu=OU-@{wM7aJLy@A)Kiy!YKh4HZd(u+)?zd!r~V*Y{f4~pTh1OK`)|6usn zi}}}we@M(f6#fmm_&4n0ukPaCD3<@mh`&k9kM-Nw%-EdU1y}mRat}v%O$={K__ylf z-#X^s2H$IAerl&}yZE<@`KcYYkNJ0if5(`AC-`@c`FDYTL>K?AUHl`v_(#S3qY;0% zE`B^27S?wU3=o*B<8~My!<&G36J!25_$S5ud%_Rnb{r3nvoOS!XNI_?%n%o!8RFVA zL%bFf=DQE#{gV+d1u z3}GmbAvar+vu!o(2Pl>iMoiDw7{N(^5_JPL=!B!;k; z#1Ph!7{YcoL)c7W2#ZM!VW^rR>?SdU)g*?nnZytllNiEY5<{2{VhB4y3}F_CA?*GD z_BCM4hy74^LOqxtH9khUpwCp|hx#DnL7k5w^z|4*Bak6f2^m5!kRgozFoek;Kw-e( z4?q=}`w83Y@9@LG4~N6NPZ<8as4}rR4*aGGKTQ3U_+juT^e53-uCtuy_M#ki8p*uy(@`Mr;_uSPetirC|u8Gk}7O$}ogU8HO+u!w_~~01dL( z!VuiUe0+9TaW%P4F`of8+aF@2)Z@t}yrNn#pq_Wjv2D^_&=Fyb#M| zov{Avka=gSuZdSD%Ve?-WzvYhnQP4CWn=+_F9Plde7X4ky#K$M6KFU)?z($!)qp%_c?jz?vFV z_CWll@z;b(uP`$x;DJ#AdqemnU?wbKd?%bPt8X)eNFi!N}{%tz%NgfSzao}4hg5(A8{ z#X5jPjc;Q4m6lsiheN{lneFE{l-j4j`4x~=2mTfZ*)5H-)6Me_WcM93%>Nn7W@uQg zp<%U_mu;Rx*XA+JSUTmJ=g{)SJ&cQc7>arX{*DZadvurLno=Z8M+`Ajv24l}_n5N9 zJ*-=Cd&SZzS6n>%T5j|m$Hk@2JOO_vg`@AduI25A^7a=)%>l7|N_$a{zEjGU_qcB5 zT{o6ax$>TBp5~YLKoC~C1>I}Y^^Y59AJ@9srt=S;cSGr5HaFnpcGQSrulF*4&a@Vh z&C@YGh=TWmk@FpCLc!GsUu|SlZ%+0zOXCY=)@PGV=Od$5WO(o@^NcPt+}tFVU7s+! zKIVBm2N6BYu1}a{jVzk))KoPW&G%}m+@kpbmHZ3m=Op?WiytdNA0&{i z6s6Kw)87J^NudgEwAOeARv!xLThrJ0qGMUAIP={qi zc^Mp5V-~IDHAc}oj7oC^RsBd^_@hxqb1Be{DO$(kqj_{q)%Hc}c-^WehQq=-nY-IM zm23WUO6CjA-5T7Bi1#Dn?^aq56|IMT-y`gM z)b~Bkz9)U(Q|x=j_dUnH7kuAK?0ebwy~@7Vecv1Gd&~Fzg?;b(zW3Spq3`=E`#$k~ zpRw-?-}g86eT`oFwrKqW;GacX)bzK_qHO~ti?##c7H!NA0=)sMigrJM0Y!UI&1!qy z+5PSH?3MPAqP?N--AH>k@x7aA?-ssy_)2@rqP>;x+lGDHvTwD$Jrdh-rM)wejurMU zE9_A^?(RPJSjHaj`zEq)lJBc$-(J3N3j3z|zJ1u&$Ue%Y=2m;qDtjh!>Tl0oVegOp z-d!_ASyryM=vu-**W6+I?Rq`4_|NhgO{>B4E`zU~iiuTU{9x2+#0z6i#Qbhx0->FD}}bU_v&`w$Kv85v#BD*-HBtMH^Ge58X8uidm&Y ztzofHOfU+yq97Cf)+NZs-0K>IIoKu0+^1WRjp^A1%f>YA8gx@QPwkr{=dGycirK5} zRTy8lue9%2ZQn^_;QU+dyI0!xthDbh+7HH19xjja2qq0y0{c-~3GBxa;YqaJQ$_n} zJ)7)jX*Su<$5MK+d`f?ciuOw6d<|Xq_o7WJr@i|M`^}>L7leFYwBN=0hZ*_41|I@o zV*VA`e1iNxE83sK`6UYW6(W9)9{g&h{Y}yShadV+KlB?4O$Z&DF#XWKbExfyVmCl3 zSO$kWeyHo?e#fB-ggw!leyaTM{c3JaRIN-5C?*E_A=nZ6A%k_>B-W?4NeuOU8?tXB zV6TgA!L(0o!UNnoH8u?soBhCdO;U7xkQE{uOXKmkjK$wte?L0@y*BoJJN^Ck6^R`J zGLYCA-~C~e7*Vz-6C-0Nqsya=iJ|OK9%Wn%WkPwBx){oy>WdyRvu+~ z3}r@nlvy=SF|l9G&51dy5(gC%^U>M(v(8$PSV&H5MFK=Hk)ORXaY!+NJ=(2_LjOcZ z|HR_{iKVL&%ZrI4DeTDAiKAzO#lrt96UVGd9A8YFjNp?I{nVO1#l)HPb2j*a#JTiy zKK)!sKNr)_rC>S|mxVhKdnAr~ggFc>`*l#ZxLyntH{fXHMyO}pBsPc8YfHR)y*&;^ zcNHtd7#xGvi&bIe5G0QW?>q3c=Q44RI6~YD`&;+pDER>x*ZCc+ayD}PjSrnwRk~% zCtk$C*Pmdx<7Jfc6{Ej+9S2E&Hin8fjE%*c#unl&9N4@KBO~wQAmsyCW%!&za_zd<#KZoG|3kdW7X5_?IM!Wdl!0id+r!Y@_s*%LSL}{FFIL2kh zP~%s|rp8UiFr#P;H|{WMjC+kOjYo{FAo$$Fc+Qvr1Mm}J+`SIA%j&ECcgv%fJDHkW6?Ch@*dpWDx@F=pdktU2ZgW3D;YNSk{a^USG6 zlR3+1HupE?o6W`obCI#oEErjHnQ^drq_N06-pHA!87<~HMyq+T(Pmz5TxMQn{K~x6 zxYoSMxW!y)+-lxo6wUjM73RanO7n4JmHDi3oB6tNyZMf>+Wg44!~D$L-26)JX(eR6 zRV61|>&v~Y&1HkNot$EgmU~;1vTEKI!`uPm&%Ow zD>>h~PA;@=ms#s!d9d}ITx7i^bJl0F#TK&F?k)575ZPwe$V2QAvfZwe9rg^_Y0s03 z?W|m4x5=gU61mJiLN2$Dm517=$iwWj*+;9+R-- zv5CI&xWtC?#KhL}q{JwBN}^t#nrN3lPn;#sNL((@Ox!5XO57>WPW(ZhlXy{{oA^ea zmmDlFNLI@WlUvA(liSEkk~_&?Bqz$tlGEhn$+_~%$%28_Gp-T3b@z^COs@>O{+-a)+&kDcBx|1KYJO!+%J#`&OAB_DFuk-v8~mVaOAX{G-z*pK>|?m&>P})8(_yIr2HYwD!Dn9bQ4t~pEkE~KQL?E&&_S!ug&e; z@67Ez)7-(6=8m2+ck=q0J9~r7UA*)06Kzs#60xWP({E`H*eg2C5 zxFdu&y0Ray?8htn@ydRj_s*65xLw(gYbix#KVI37SN7xne!j9FPyDC%#$Fsgnjy+*r(Tf8<~^6&CR{MEzJgRTXTxHlexDy%ACsk_c725!QMPL zZ}&zEeT4&Iklw9B$NO;6H3O((d7~36a1n!J2)c456Dx7BuVnxflT`Le2CyoW0o=wi zfY%@akchuaMFvoj0aRoF6&XN922eSZubjz8NB)&F`O2Amc&1;G0sO~h05}xCl1|3| zquc+|oqze$dJ<0XaeaLIzfWcRU)laww*UWi+y6%QVsi$L>}Ns*Fw4Ek+}B-g?&m&X z&UW81=eQr6bKP&upLn*pKaT1TfC%6~Zyoa>9L}d90+EDu<5@&-1M*JP5=A;!T*TLroXc3uWb4&oBqnC zzq0ACZ2Bvk{)!^LzMqq*DB{QOlKj_g`UhffzlL3ZxkLYjm0f>j*I(K7|JUvM55}&4 z5qAAK?D|`<>u<%bKaX918+QGNVAo&3uD>0-{toQ=JF)9uj9vc{Z>YJ{+t^&@ZDB6= zwlWX(wlfdIKL7C8q5p_**FPfM^^XX5{UgF%{|J7Ciw^xqbUXB?n8E)4j&T2fC++{M z`eFa?BG#D7{=c&Suk8OT`~S-Rzq0?Y?EfqK|H}Tqvi~phmc;*^{r_Ox$@fqFw<;#? z`VVRT4`-$S1S$Ow#9n_5Qh-#++5g_S058m+R-^zGDL_RE@LwkdI2uxbpSf3<$GBIU z$KuNVaqbH9c=ulO1ov6$mJZOjiyMC-AikzS# zC#c8?DsqB~oS-5nsK^N_a)OGS;D3*tzzyXDTNe}e)%*vA1p^_XIJagXqygviLjia$ z0Gb2*T!80gxA*Uy&D7Xnh>tkL6dBLwCFZd1Q1=o7R&Fj2c^LlSb z^9FBM^G0uX^CoYi`CFD5v_s0(EVb$IymJhBMj)v@$|9uKHv=>h2x z)Uot{WCnExS@wdB8+;`>0_h9Xu{A=@gCz=zB?{GkYJo(7@*;@>;a?~&!dH5D;DJ!K z@H>(%^d zZr%g)2lslyyx;T82k?~Q@9>7ggSbiY5ac2c;t9ovy(#AJy_x18yo1a~y(Yk{`IvW< z`M7tY`J{KY`A6?Oz)Q@hV1e#w?@ses?;-O!?S>Phe>5#L)MYhu+XF&{-xIoSV*B=qaxv|8hSER#%Z@$C@u1JUDtx(SLHp^Ms zkV_w=R8^6)ROBobIZH*(QjxP%wRi@-dC3LzO#BM%j&ICRv*>Js$z-DF?0__RfQ6ls!-xm z6-r#H0?igx6==4=MY0!Ywy3H=vjr|+vjr}ay^zGEie)b(aj9b23rSq6SoXsCvh1aI zr0s%lf$W9yXKfdRMcOWZ38gUauoUJrNMWjw=AeocrXq!@NMR~cn2HppB890)VJcFX ziWH_Ih56qjg@JhBTDm{;uC}!~7{ZeKY9NqV!{ZyF0Oenl#o(0<$YSuy#@Ug&%l~__ z7~BrR9U(|zp!otR3}i77c0m>cDGX#WeM)38R~W;Du=-VGF%?-%MHchlCX2DwbuYFC zyH{B2xmR23yVqGm+?%bT?n-L|_g-s5_ff0becsy0{lMDTv#d=#&)U@MYi;JOV{Ptj zY;ECfZ4L8ASi`;XR*hE&*kEnxHCtPGIcpnliB;<@2Rzc+);r(Y&b!Ro!MoPl5we&a zyj!iEyhp8_y=SalyjQIe-h0-rkj0FIEM^pBF{2@i*$uLoF_6XVp$1xGSsrs5-`yD$ z%3}tF@|Z#50$&4W5KCrA17;9QW^mI8F4Bs@J0)9cLeONM61FA_Ofn8G$>6gnhwZcwv2Sv`AYa^qrh6F4`6bBkhf7@Re5ZdFWf9pTnS ziES4-cZi%jMb2Gn5_h=8=ak4fHFE9~ zIU6JA%*eTKek6V#!A^7Tw zWS-#L2si}Ye`^w2Tj+OlNBa2*hg>E`)!0RIOZpk5gK4}ZZcDbC;sioP0;7t_B?NaY zCjU=+Umjjntu3CtGiP@`J#7!8%+OM15GpbWGEXvDKm<_+5k)3JKok%JMNmX$5JU|A0~ZxVMMf0_|`)iKprO{2laYp8U6ErK?$pQ+J&+CCI6}<`iI~(sd5(lIS3DgV}6M78{?*CYmyq zqR92^6%Tve&!(?~m@T`I1I*}ZZTD%Wel}CX`}}N{iPvFxYUnh0x@WUFnQQ^%$Q?7q zG$D+Sdig$tHA3ISa4Q?lRuXTVe2laoyO%SG$irH*s zs*hx|4^sVXLoJ%@rJ4A75RxtSFn>17&Sbi-Y;iW*mdUn5XiI=pOZTuHS)>+}km4sj zJ#3esJmO)W!Cze{MEMCc$``lH59})_d9R)y*f#|w?7KC=$ZB$Y4;6w&tO{WET%_A@ z?p#y~!2G$WNOCsYpUDn`Y=?Itm{8b{P|{Hyv!4CrXD8geNp9X)H?Pup=b%B!8{u~> zLFh-}=><>kSbWlNKjF7qLdsg50=i(o0?(A(6rIV>PD5lUOE&vG6L{!6ZbB%BNtg(N z{9y#abN%d3m>mK91z@3{>A4cX)nx2vdai^DY1v?U<;i|_6B;BV-NUUcjx#w~2R~L_ z6*WdpnLxx&h1*yQ78p5wrsLKpXL+Xe;iCKF7V#KKvLuihHBe_;K_Xe!{YFA1ea) zwLG|=RSfsHD&Qxr8hC(JA3tTazyqyL_-U&re#RPr2U)}MVCyA3#Ci=6wPxXA))GA2 zT8*ExvhfIOJ05B6!OvU!@hIyU9&Me)W2~$A1)Jirb_{;WE{w<7rSNz=1y8W6<5%pu zc#7Q^zh*bbQ|&f*y4@MSX?Mdj?4I~7`w9HEJrvKfN8{P{1U$!n9nZJl#tZFvc#-`M zUTlAWm)KkIyY^1J)ZT;Nvrpk=_E~`E@e2DYUWqYYg$e!uJ9sVj;B~k#UXM%R4Y&s0 zh-(9^hyAz}&cf+98}|a(7jME(;}7vD9KhoNPR3jCbi55Oz#rrH0ItNJ0EeBBYZv_8 zi9g4m;=TAYd;mhn@osz?@4@FFCI{~$3V%;(;~zO;FIJcK1HtJ-)J;GO^f4y(UJhm<1@4d{+-?jup$0~Hp3TaTYQmr#(&XA z@Fn^q3 zRNS7<6~g!cN<;{R-l|0$aSR*?VCjR<2l zfHm_!Wj6e8vjB)*<^4R`tnDNdmrhRc^SImAb?Yj60l}1uC?QA@hLVUtRf&TdlStHz zM59*3gW8jL)SY-yUy^_ZlSDLzB%xP`56vKj&_Yretsq6vMp6`QCB@L^q&V6~?mPb?q0i=>OoK&%1 zB2}%|NHuE~sctPHHLTU7rj<=e(@*zMVuG z*!Pfzc6rjou1cEPbx3o&9%*4eKw8@^Nn5)eX=isL?d`6lgWaEWw4WuN?2)9iJ)Wf7 zuaa)|bkg0PNqX2z$)olL($fx*UiNnKn0=V^wvPckLHgKdNk98C>2F^lPhpD;#F9LN z3z0#%7{JoxSzL__#`Vb%+!SDIG8A_t!*DM$9QOzK3>k@slTmm)8I4~B_y!q^XOoxk zd@>HN0=O1d=xj0pe@tG+Uyw=oYcd&D@Tss;Pa`qpO;Ul(AdSddq#JpgJVs`b7swnk zo6IHi$vm;BR%eIT>W{-~_1Cwp)h>N#tuBh9f@`()mszXBOZ75XsbQUtwdc6& zwDl*f)9~VsRXKShPw;cFqctcBV>-^yivcL&f+#;P0cj=uyfj31b0c9xqcfD-iNNq? zTl~C&#{4{0V{TgYThglgc}=i?z1Yv|0Q4D%GielT5-A)J@Va0Q*URD!GI{Q9nl}N{ zo34iG^wFvCXX4as-qdU}f`zLiI^DYE3qwZsbGMpL~R#C7aPmvIV_BwxaQ58+wC$ zj6jpnI@(psxwl;2Dj<_m9#oq{$;EC`#pSMBz{)64A-bjPC z-Us@VO*#el(}2J>8urhbdfm-0T&hRsF64tj1m|IVnN9>U9qZ>A0G2|(YG6h%UwR8EWY*h%otTlbp09ilRA4<{ zvkbvtr*F>zu24|kA?WAp^bqv(^(nCXU537aWZ1j!1^|299ad|dFTC0H0(&?B$sNC$ zkj}GrqIeCAt^tWIC@NX&Tqe)XK#kqB|3vZJoKtR*0ZtcqwmIG4AKu0322}r}W#shT z9Vc;^D)?5I%s$>mI=2pJORZWVB>|T0bmKSu0(qHV5WnDlp(ZfP%Rno-FU|SMa5N?F zHchh4{wUaD--cRDM5VEyZiBE4SAD&f^f3ZJ`53KQ$MG>%wN50f6&(=K>F~O}S7nLV zv>0EEU&MoCY@%11vJXGfM7`1d zCT81jObQ3GMCr6xpYX-{MLEp{o+teRiom0xUX?V|X`pdz;!fmoYp)Bdp(UspZqxD^ zHcS!vFbH(4i-l^;NG(vGTUrvz3biD+yc}AxM!_v9GOS;G!qq)!N$%si z<)n{8%PAj+mWyDuqO{g{Wv;4lXysfL2{mk%sB?GC%0kVmyivhsjR=LPH!9Su!GPPW zEZD4|iqWnrro~xlx)FO-3@L~nAg+RVGf+#PH(0H@X)vGM)*Ze$ss~n*g29v&@lbNM zsF#-DOYp@nLoueS;-Ra2@o65-$LRVxpJykE^2Naz@Ok{AF(`RKg*W}r3y;VxJXCOI zs7x5Z!I}*D?5G1%o1(? zP>|jiqGsw$xlwv*4rcL-$3c|7X8jRQE+g9B>gE>%7oy_1v-#{fC^jGlr6*fiVldo0 ziD6+Xy^O%&u>t@Gz#KYz1eEoQU%ar;)+M*~i?N%;cnh`3u-l{t^=4x0fOsXn;wCXg zPrP-r#A|6JeEG!|=U#rLQ6JqTrh)Opd<4ec48tl7#@6M~*y3H%xNi9@G0O)7v?Xwz z4O~fQclAz_t~jm`X-#@5iI#{S5RSXhMO^db}&%>Dz+W3d~>JW&2fci!b){9<9P zc&+yXz41%YM3`GYa?-KSHnS=kr5(~S@WR7nSJeBf$nzSj|NhA#@} zmEb+k72SF8+38m(uq1DHu0*k96y_Rj!M&_?!k znuZx|isRrqpeSvDlW9v_m$t%R>DFf$3EBg$6don9v67FU`ZRf*K0^l6XUP~km`tQY$ZK>cSpskk9Y*%i;p8BFj{HnVkdt&IIZa1VMMu*F zI))aeV`(M&BCScs(Ry?OZAD+E{QwT66X{U;3dBx^m??A>eU)a>*XUL{m3~ZLr(Xj6 zmQJGw=yZCFzClj{&3QVLUZQU^o6cet=xkPl&S7=wTvm_HW32$Tr}NpvbOGxH=>zCu zHk`i8UZP9cWcnU^lV-3vbU9l=hp`QCeSCOp0=6un*<8_0JeCHy z4@`y>x`o%K+jvX*58i`*!Uxcu{2BT=A49+3GwDD1BKjqNm+s{O`Yqo|f8d|dANfD& z0sad;$S=}E{5m}>6g?y&=n>(iKZ&aJXVHcp69edR@f`hCyhKlkMf8+dPEU(Z=^1gD zo)yRGIdOshDQ?mW(n~MOQUJ@-OR^HZEUN>oO|QuM^qOo$Z^))JN48^@90~YK^o)F! zo{%%?F}a9E$h9m=ZelU=BNivWU>^BB^U6akL7oM8l_e>Q6;XxgPpUNiQKhnysyQpI z+Oe{#3oEC3vhwOFfP+~DHIgN(aV$l>4)86Os^+muYB{T{)&lgis%kTF~R!vv|e$@MWYa*)d?(eNPPOAGUzy~oH!+ZLvi$FtF$;R{)Ier6{;)FKq#Jmi{i!a5K3SbP;EDkL2cYP z?n9l$S@8#4@1>x|;v8_nd=Vnz7jYiqez6;hKg9(IU9>BUi{dXxiLjHzCFmI{ymqv> zEUq9Xs#>SSRdEe*(Z<>>u0!ieF~CX|Hz2PP&soJpGl+{2i_pKsP3VP4u^jy(av&~R ze2TV%|5Fi==rHurDQTftXo>mKhBy!8pK4BW;$>~rT*8?z^2++Ck))EL1o;K3WpqQy z?@_YR4M+Zn?lC#s4>T`9F+p8|v!9>_!P$?iEs}?{MZ#sft1VK7v_&d7U^Oiu;fqVb zde08kTSh>=O&ukCAlj{&jDniE^;rn@F|~j)JVDu`!?MSK?5@ObB1fM?!NqHfn%B_l zkwqel;OnR~r)y&q)lE?I@VQiEc%e+2&#;{0=juAwzKs0oB` zP33$yoR%f!pi;MGgrrc`uX5l+OYHWn2*s-#D2nqkmSF+4xutAi0ckFm9P}NE&o`Y5 z;PAPHv89C;(OeYg{1g{;L&Z1EK|U1s-^`10P@(^{zz1?rQ*`Ib$o$(Q2UUULU|Ip8 z;@8lVk%^IsSj$g>v8Fe2P|GlXF%hD;cm+C@i}yl}RR=``D|~A@=Up^8=^P@gj-u~O z$Xy1xIqD(LUFIIa;mVxI2Dv=xhMT7F1T<;A}Zry z%C?Xtz4`E#3F+E;PtB4^nz!%oygLH#&cOTOf_Te9nm3ORxptu6;9wqnPEm8%aF(8n zN(976w+M}=WXZy<;ZB9lujrS>GEmuoEPhLBiEwI3@EUEC>X&8o1!-N>06ief!mO;T zT}giij&(zvb%*U@4^)ixL?zi{s0{0a?q&T@T{ZwUU{9gO>}k{toC?97KsB_3EovUJ zCs6a?rJ4^(886F2Q{RhXAyffEjS-Q_G6hyVeG}kAxJ(Wz3K-OrW^h>?pe%b8YH9J7 zycJ|93S_pdXl_a#hj>`G(3IdAK}sO({RZa(u({O-1UyCWG-WCTMF#2wJ4IR9LOu=w zvj+yeirEAEW!3Vt7NWA6A{>)MX3s|+I4Cq5W%iX{*2q9HdGUKSeHv^zb25BvElA^D zvQ*Pw&0`}_JbNA$Wus7eHU_1zv8Wn*3EjuWp`L648pvKogV;ng2GS<6S54y??;#ZS z9zs*(eX!8$%a$pq1sFS8Pp81dmI!r^N5f@ZNM+C}1Lgf-25>N&9^)^<1&p9sKxurE z4UgHt!_ZsW-`LR0dSUuEVPVo85Qp$hl;Td3a2In8H3t79SwF1JU7%DgL888_iwMXD zA(d{VRr-NkmDaD2Aod2L>`fHM-U7>KW*)Up4C)5l{D2-bHfoK+h2#!L*(Aijspj7- zm%lq4=fIGe2Sa9l9^SVMN7-ES7JWjz?cYFrxLmV(z!6mN3bm>}ld|-EC4}7Lpw=0d zuVUyhs8vevJnB@N)Qz$QZ1PjyL02INzKiyK@1U~~Y_${I0c(*AR6-+OQcWLF!6J%=@HYnzqR+G2B3yax+i_ zw2C=rIi?Q@;8xK+ZisUaADrZD`6xV(>3(vD!P}?|=)pTMe=LE1S_(s88LG*a!r6(OGhTh~H0E!)`g7a29ul9yCzg@28-=6b-kLJU-r;X<4>i85? z5_J3&==f>S@iU;~XF#nG9P_JJL=_^`bbXe>9r{TE$VZA1yILn)l zUPD=7y?*$1y{>$lUSCIrxNHYXop9Hh&0t2~8`5m6nXB1wWfIoxntDb@aNhuzB%vAI zow(+pCi&@iep7eHf)w~bo~dt+x!{0v*LB+46ux5k*I?#u^q7=;nlRI7LT_d|epLTsNO8yAO0?bXm4d4cmWNxs) z={~`|&hU_KaPSU`-*NX91d>IHWDl8=`50xDF*@eRXWL%gEQsO5A$O4=Pr;b9|P z(twx; z!wZ22MCuIheZz7(zr+t95gz+Qeu?WK5gz-`{1P`nB0TuT{1P)E5gvO@ zeu-I-2oJ86Un2CXga_BoFEId#@Zhxk5;sF4Joegfq8?RSjaq+vYfHn8n}i2He+wzR zG6Vx~SHNzSjv;Ys_#+qK2VC__+bV!FI@3Zi1goy?zN|#|?HOh@CPRI0?aE5N0Mh z18oD3{f+k53Qr_w4c$4hS^lYIyJ!l(WD37x3cuDNoqOov4rv1W#FicMhOIA(S*M+- zs0LJTx6vCqz^>P$dWh&SyKtN?G5ehg# zk|+&vriz7*)RRi_0GXMNF(FY=F|j(^>lkOLcz0IP1WOD7DUd+UHw57CTmT1}=2wX@ zpKJtUy&ROPXR1;KmYSjc(hN(}l(T zz0rX{?sKbV2;kBRR+Jb8!zo6=qLQb7H0cpd%E(=i;!oZ72$F&vdHWi8(MGxLvCNgg zw1-irJhZxEO4n$Pl?*k4l|{Z}$nn3E0!Vp#mvc~G3ap#v`m!PAcf+;2A&p^o!}epB zR_5Ojy5Q|Dg>x6&=!3b9wp?cut`gnWXknj8Yp>gAWRxi-)M$U}4-kW_F$AH$&4*yD zA)p0Tgse3LtUv;{<7iBX;!fS+_8g57chMhi*U=bt7qr~;9gT^&3tIe#t`HB(5c&79 zqx)`yOW~HB9|g`bjdC|?b=RE>gIkY?xpOcao!Xb?SUaGPwVm~VDa)N-ZxaRxWTqh? zRL=$Z5s(g=Kol@TYYv+Yfy?2xDI5rBEmMT6CHWR%^cK51kS{?~Q$&7qd_ch&@J0e2 zZ+3l<1*TF`A69HY55x07mMW2^bW`}0UzIj;IJ3K#xy^Kb0N9Ws3LKDzC5JVUPS^$SUEnENvv}#Ku1TT<4ZZZU-KmxaMX~7)u z4@Sy@IoxgI!-8pz(_?C`H7Yom`WxX2m@xHFSfL5HOAx88p**XW8IA)ChXN){&0(G4 z5F9J{Z~!wHioovVaogIhIb;|PEAU8{w92)b6((({hCY*(40 zsaL@Y6HCzFmdwzr9N>661Bl@e0ADZ}Lhb)I{+htGBSYURWr~;)d;9sf*k90*rVE*-HXP^ zPADkmaorZ0xEC(F9 z_MK_T0izh?8+Rim-*X}P9$;V4yrV&QG6;{kkZgpsW2Tsp+7?(-ZK13;q@lS0BJwU1 zdd!$rP`LBTZ3cuUFolWWGV!+|1zTp-PhT?8`1lk2hyJfD*Qe)jR+XvK(h6=_C(FR`!x4iaQYMh|}LrJ}y4ol4> zznToCih!D`(P=Ik5m3`L`ldn2^?;h8(U}GX<6ph4(b;}A2S&=6fLfr@MJ_rtpx)8w z5*N)1sCUzo;ke^H7Y_u~a-ICXi*5?2l_q($!J*I98lAk>ui)^H77eIOjb^!MiGa%1 z=!XUcvt9*E=FKj%%>lK=;M-hge+1OWntZ#P+Z#}yXcXovU1wiF?b7IH2E_*h>T`|m zHpF0psefwpOBekipy1e*WU0L_{#8JIqm%a;6n1UuJB|Ke3Wm*=+OJU{%$~>H27~Wz9OJb>+EM-bY(#OuF*eS zbag>cHK1mIIpyUFV|#Cqko9ZklRQ21j-kbiBKQ_c{?{QP`RyidZv6QR^L1%=$nSw>FCs;F4O>`caeyC)G06Wl_%NqP*=Dsdi~m z*{&|CfRk%AyPc?R_YgJhfufc@O4PQeh&uLcai5(b>e`v2p8biaZ|@Zi?Vm&=`>bee z-xN)7glLTmi#E7|XoqWy_V_{30e2D|ac|KX4;EeUiy|GrF1q3cq8nZ*y5mja5xiSG zijRt(_=0#0UlF|t5swi^JVAV-4=Dn$r07ekh<>Cxz&hed(o#G{9u`lN;bIUOF9wr0 z#1OJb3?-|@FtSMuC%eQ5vQLaASHuexix+9U7)MKsiL|D8g*Fkd(T?JE+FeYePlz|@ z)8b9~oR~q!inr(_F`Ld4bLe{jmy3DyBQc-u1o)X)2=3~Oz%6|t{Y@;U7exlWA(pdP zv4Rx_SPWnZv69skt5|)2jl^o!S*&3_0rnPa*>hqYdkNqKv7Sv68`!&IBU=q{t?;vK z0k7nUP3%(v&QfAC`$24BN5#kNy7&j2(`@I302URW@Ty`5ZvwEH*vY$#U3?J0A>uPW zNqo-V0Qi>J%@>J1e1rIsZw9zce9iZWZ}`vRTmBcoD`KCB6yJ#w085J>L~XHOv;x>x z{3sq52gC?~qr@RG9h}4G0$c#D;p@duVkf}Q1aJ|@#7S{noCkOjT#~UkEfdARWC?(! z#qYAVI4c_gY$DFdba7tx2G~bjkk5;Yast3f;*y*%F3Yt5H;60pQ*l)u1b9STmlwnh zCB${*h@0vjk)vt=yjNPPm9$l7fa#K`rzKS*0FIJeO_M^s3vii~YMpe{4jG}o1h`j5 ztD`bjosb@NO~yM^dK@7WoFX#ODGRWI^f~v-LQYeFEo2d=hb-#!12{kycgD(loXG%R zlO>&ZWGUx;fU9L0=VMvc`2ye`nd1B`D>`QYo|ly(sH_|j3ou?*jVLXvMbrRTN7jgF zC2K}J46vKLH)4>i9q~NCF)}UU6?tF8d|5YQ8Nd~?e#CCsFyf$W6!9;B=VaqZDw{+` z1B{aoMwXUMBdY+cE?Y%5m8~P&1MDco?3hY&Q%oa(P2`6$=`s*A5a1xWIc9>~5;Gg%Jh?UI z1Gz0`JHVatA2C15?J>UtJSRViwdIc31b{xdD>hYr8hbCmH2HaK8~H`-!vMRB-^7lV-^NY`_?rAKcCP#(c8%O0n+-4^55#^c5612Xcu*dW{X-s!y$bM#{3%Yz z<8g)LuW{u7Cd)H%)#TZ@hVoooJ9$3tVR<30kNhj{S$QdLn7kY}NnVMYBd^A-l-J@m z%jj+tE!}Uc!xQ@p(+<osOQ zuZqM!p^C2Hs}szt)Ls%642s#U_Ds$C*h?GxkFLy09+hr}wXV`6>PDY1>}oY+Hk zNgSviP8_Y$6JJ$b6X&XKi7Qn1#7*jv#LrZZ#Qo~g#FMIL;w9B9iK)kuysCFnY4vzg z4fRCQ1FBEbL#l65FV!z;km{c_Ry~>Yx*CwQKs}YTN)1ffs-8~Ts|F?gq@GQ>s0RD6 zde+C(5MLoR)K?y0iW=@qQ_uODs}a7=YNYQ8fc?}c-yk*GH%g81O;#`XW~mo_i_}ZL zWon%7eKp>^({Z3N|adIX8L#LV39DGo5{92g#ZX)TNM_Eow zrxj8Wb0gQnCHv)rbf6>pgM9cs=oq4jI?D4k#gqmNt&!;0Qd4+pU8C zoi<$mZ_DKkgzmY94#4wr;x%+6@|sl^o;~n3aDE8L;!M9a&Hs^Yi8OLEY1#vYM#i?oN9yJ|RQZrChH51iU zvrtnt8+B51QBO4wJq_{CsReo5g+@3%onBB6sEIw@c}zP3q7i5mglus3eU|TXdV?c- z9O~md4spb|3%QO)I_DGNoPEMM`-F4$4d?6={Blbq!Z*OL3EnwyI+g<8e0!$T&vhU| zwZP3*dkROCjd%K&&2|P1=>7UCz+G>8aN$Boy<-H|-bz4cS4HrV7toYvoxwpK!=OO$ zx<};#cxJlC8IpzPvKZxK#@y^T6L=f6`CTUL2D-L}06MXxKnCMJ# XUSm?h$MN+)Rnvv+XJk238~FYU=hTmF diff --git a/target/classes/plugins.config b/target/classes/plugins.config index fff38ca..3e9ff45 100644 --- a/target/classes/plugins.config +++ b/target/classes/plugins.config @@ -1 +1 @@ -Plugins>ETrack, " Automated Angle and Curvature measurement ", pluginTools.IlastikEllipseFileChooser \ No newline at end of file +Plugins>Wizard_of_OZ, " Local Deformation & Intensity Tracking ", pluginTools.IlastikEllipseFileChooser \ No newline at end of file diff --git a/target/classes/utility/DisplayAuto$1.class b/target/classes/utility/DisplayAuto$1.class index 4577d9f9d15883907d1f4565ecb81dee628cc41d..8bd341b7a14085bcc3fedd453992cab77eb9b034 100644 GIT binary patch delta 22 ecmdldxKD6HH5=y>22O@&4B`w=Hn*@@F#!NsV+SAr delta 22 dcmdldxKD6HH5(@*11rN522O@&n_Jkdm;g*s1}gvn diff --git a/target/classes/utility/DisplayAuto$2$1.class b/target/classes/utility/DisplayAuto$2$1.class index 0e9e92f5ee777624e4e8831dc319ce5e93ea3828..de5cfc7a19cfd77e985e61cb7665d926047a4252 100644 GIT binary patch delta 50 zcmey(`JHpaC1y2C1||k(1_lOxE$yugJX$(i8ThoeGYD)13NbMV14%XpMh1q-f0+LR E03!MddjJ3c delta 54 zcmey)`I~dYC1x#K1||k(1_lOxE$yugJX$(i8ThoeGYD)13NbMV14%9*&A`COz%}^? G^M3$DdJ2yK diff --git a/target/classes/utility/DisplayAuto$2.class b/target/classes/utility/DisplayAuto$2.class index ce4286b4abda3edc2dc3cd2402fcd000b608f562..9bad1852a3ffd159cc9d4b15e939649cb97e52b1 100644 GIT binary patch delta 27 icmeyN{zH9(F&`hJ2m>dh7=t*YID-tM$Yw{r?W_P@TLqB- delta 27 icmeyN{zH9(F&`fTBLgd=2m>dh7=t*Y_-04G?W_P=&IM)w diff --git a/target/classes/utility/LabelRansac.class b/target/classes/utility/LabelRansac.class deleted file mode 100644 index 290cc9f6d32889a6e0caae7af6d402849bfd28bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9332 zcmeHN3v?Xib^h+|YDZd+{E~$&FtJQ*kQQ6Ag=36m8)V4>f&4%#)>sZUqusGQW_L%- z?mD(hz@df&Qu3fF!GzL~k~nRXG^Ca+$7xEMLJMuvmOkh!Y18x(+NLFK+O%kO+GhpTN zR?0v`F!z9U&}z?Exsmqm!w2kC!9Y|ne`<7~n9Eti8Cwt?wLCjl5VZD>W{M+DZqRiz z`S!kC!S<|F!8vFroa|`E-jc~Uqj|g6DRc_Vu2i9DWd>Zw$&LuF?9bVSb~ZE4a7R1& zNW0mtRLaih9iE{{9kepsSC?=`a#o?}@!(r8YSV^_RfWT&c6+XvwLK@*?%7tRy*J}p zg+UH?ZX*3?!7CQ_=P7`GeOD!)_I@X?c)t6*p-Ren$MM3AE|1t)<{ip4k?nJ?I}u*n-grjSYl-cxGg}jexFmwTBG?PGOVa8!Z=Yp%rTSL+vpj8m)hQeSwiF zwfvC{4z(sFbjO=~%SwBm*?|OZssvKGHJ~Oz?fGAMi0TrrZtwx-%Gi`wPFeSi%SxtP zea(dp_G0%{wax0AgQ5&b_+str4H=@+FGRX&hB1T@H?bU73&Jg}$vOyDnlMnSTJ0w4 z5L2zyCh9R;wXQKS7xPr>Z6@aXt!qs*V1XLD&cr2ttJB0HzqQfCV!w60iA()fmx;Id zt!@)<#WJ;bi-~3|RjnIMT!DFlISQt2#q6-{4f>DteEkL`EzeQ?(qN>p-{Di&*stVv zUKBvTaY^MKK9Qw;ufNF_))_$d|AZgW{8aU(ecHtwdXoiE=_h{dVpOyc(20Pz`hV|Y zE}(jd=G`KnetI1S8g&@Jpn*h1yq-9mFW6ZVw}H+o>=qedm7+z*ZQtQBT1pfc`DQz7 zal0U<0(k}vZEbBa4B_ntb~7!|v2W#a6L(;biaZpB3`_iO|QQS=y7*TnL6E-C_ucb9W zgF~IOkVnw~=jx~SbHc?l1khZ*K(I5^n6u{nlAc&B=3_Ndl3lv;|R+rB>)Lg+LE#@);iInGNGAhSVJATQ;r*!Obp0%pE&zSgSHP>|BoKLuq z*@=p$o;C3~{0jAFgqO@%d1c5I`lAP_K4Um#V4TvJj9U^JCuMskCU9D?)VCwsXK&c@ zcDQ**<%YeUn@w7oBF)rj>Fw?9Ywe5S41U$X7Z~g)d!2qY6JNxyk)^y{C~b|<;4g4OX&zbdXv!ill*V{6gUdQki#1)G#oA^Wg5oz+#$P@BzdgOik=t6qc z?4Ou;Ma`NHz3}9un)ovlf37BCStnP@OV#jSn)s@^u;~v|kC>*biN7-O*Zzcci0h{& z{>H>N)I>CE9ik(S;qUMd2L7I2YvvpI*>(9oCccSRmCa`|c}rEEs7QNCeotozI>zkj z>0$CTGhv3%s!(KQ5^m-oYYd%zlipU-nKGFe{t4eQ@XwQvi#9Zs#EdAld>{TrJ(rVv zD}CW>_%{RJu6)7uV`}2x@g1rc%PvKGJ7Kso=)3)@T<3J>Py6^!{Fj06@|l_Wb_v%j zBBhRk98#D9o`wWc2C?^Z5ln=()4Q=2CHiMy5i*xj?cr?n?~!vYpl`HymuOH5fP zi>SSxople=$y5YLlXwj&5f&9&mzlCyE~m9AwpfE>YR2RE-jX%N+Y(u7Nb?0zBwwQ$ z>#&s{*xqN#74lXFL@CC11-qli7XxWF`3zU711ip!n{uV%JZz=YwbDX&=ZmR%J)d=9 zntN23<}PmOnXYZ+NI_N-`sP|`=kDeKEA15X&FYq`P3cg=S593GJGHWgcT~>`SFaU> znwx7?_IjsWXUcjkpla!2*MOZartEaWw_wHQ22(Z)%UDseN!wPp3dCsLe2e@VU}x)) zB6ou+T`I6NJUh*rMG|$N=gOyBIml0#KS(rgDmtJPlL4swqu9}KeAM8{4p*s8~ zQ~Ffk5gi)jNqGjJa*HYbvXxS1kjI*56=M!WF_Y>teJCc|1fr*s3Utw3n=&8? z-nlfyNax~HRaM-m$)%kv{biKTO5UZ6SG`de$;Yn*UD^587IV(sMZ2$up=01erJR9! z!-95Hl{1xPhIVsV%mQ=_c1i}bogcG4WU{D+&_Fvb z3@hR*#?TgDIfhm7#>f~t;*B+9SQBrGj$v)Q$r!`BcvI~d*2kM-W7rUHsvEqdYW9W&8$I$z%KJ_O4-b}85)rep%rFAXkvyLUh>(GStSb|RSwEb-Jab9Mdf&UnR3aEm;%&Y!8&y*Ael_Q|FXUZ9rrBgoWi3n#w0! zMj0q-i*b4Rge5#d;j^`j&o+(EN7>fMzRz8tbbd)Jtjgle?{y;5H(( zoe1q9LbsB$0V0$jLW4x;HX@WHE<1_J?Oe5sBSRXY$9-Qo;!b*hh=I@L!_o$8~ePW4gSLCJ_htL96jOO7J5K8IW9?;OXj z_(`ldiM>zY^L{J+1U~Dx?Bf__YxCSRfA{Y92ltO7a~ga;GVeZvL%Zf5IfZ*T(=m>F z=ifh$WBw{zb>HoG*HlhD#O^qLY62fPfumeqN&d0($RGE~&p>0tG#U?;Si8{PICr+4 z!AEw@I)$H|z(yH4Tvw{mNVfj@Y}C-cXp@1K^wf8h^*ZH52+`ehUN+ieO4 z_Sa0{A1CmyB{e}vc8oPfQuv1S}+pA$JPVZn1^j7#16C`TiA zP9Ha#qD{tx)L&P-s8$`RF1$b*0?oS~e-g7-`b5X2ap$obX{=c}E=`g2)8`&jN6NZt zM@GM3>LP4<+j_@_3=wR%EvzzL<~M2t3B zmg3W|Kd*#+l0qDp&YilgY;~dEN*~7-fByO>(0N8SD@9n#pLP6M&!08?>4=i(K(l8kJ>TxWz91n)t@Se~ucqo*_(NG$Xgbv{Sp?mRx&{Oze=rkS;z0CeA z_)zHUcr09x4~IMOc=!O0hmW)WBpwNW5>JIs;$z{L@l5zNd^~&(Cn8aNDpH3}M`HMl zcK=6|^Nq~%-M>5>dY25+4U0S)IxM$IlK)xf#n5KiNe3U6S3_6G?XrviPv<)rl_BN+ z$!nkNmbW8Hj#tYaY#HRcMfS*E)P_c+tF-PFJTG^Og(x|>M202Br$aryCi~ diff --git a/target/classes/utility/NormalIntersection.class b/target/classes/utility/NormalIntersection.class deleted file mode 100644 index eadde1005147aa25e8412377150a037a73690f67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6119 zcmcIoYhWBz75?sQl1X-F^JuyagT+#9A+L66D6u9bk~S@*4Q*{oV_P6hveRUm&F*#| zfff;gA}T5(AP5RY(SRrgZIh-!1W{4IxA*`>1>X-qK@kd#-<_F#glyCJgQm&eIrp41 z=lPv`_t7JV9ssaZ6sS-Hj!z}6u$9~uTo;WG8sYXx(u^m}-lP?cs3;I9-(d_H!LSkO z3$E|k!3}|;m=QN4T-0>L!l^zhvMCx3CxW)9LCGPr(;AG0%~j#B6-$__tzo50qao7^4}2O_Xab(v~u>DW-^hAnQ<#84g!(t4m%7|HrN`E8@oEJM6&tK zAhx$luS+_uzKD@b#p%xTd0)P~!`cz-OIblT${V9rb4y-}GMwv&jPMNDWIT_m%CvJD zwL)!XlEn~>2RE@aSPfCTIQ|d0+)B#`OYc=2Cs36wyUrxD(YGRHg+pfCi{mj@#ajd_ zvK&%XpBdM10^TZ6oQygm0)guG8YfA10XyVoFHXeURLq}L+|Hhcw_|~T%1EWm_1gtb za)Y1K0L^(-R^cRBpeM_hPJLy~W-qExqoEdc>;)qhGb15^Mfq5#NV~4n?kX&jPA+Dl zR_C!o#VG=nc`DV5AbcuLWxGerWY8My3tK%6LA#tcOIJ4-R=fnK;TtMhqRpc#!5SVXhExn z6A1C)}n*H7$N3!QtH!uTP5E*4ePOi1}CiD>}i5nyCiSa(1}g5B0{0o za9BWh-)*O6Wi%Xipt!k)T0GdSp-XBh?l%(s5*TGHwrJRzj>QV8^jr<+Nt=q|=3sP) z;MomB#RUR&(+4BBBNG~W(96hfh!P4D(e1gL0*k9V^5!Pp8*czg74&)DF|fmn5c*^a z`URHch9L)V^S{(VjUAi`E+)E2r-BhxwzbL1j%bJ?#&HohLRKmfvf+zEPCzgd7Kk1y zShi|)8~@1>a~e__hHxRn-5U)@<3v0nhEoW}g~=eZ6OFSeE$q^;8yEBS2{TC?i4K_d z9y`NzBg-zU<`P`0;(f=gO&3@j+BCc$mkD?|n_QYeu)1UVWIAJkmEF9pt!7)BeCrh& z_TWkZpHvjLS|fdOTCSO1 z+|CPLvd&~(d`iR3xP`^g+R>5wVwO*3j^J5r(zA^iOXM@SO~tM0nPoSJyQyI>ZYLhZ zQY@}zGXtKfSR{@5oQ6B3QAJxfamXj+OTVCD7KDNm<#DV^3a+^^wlb}w&_rsB!|Hfzv~B-qO( z7{k{!JSg`{dESeMCT)E~!^3i3mBh_ND$G8$q`@8<4>ayU zg+^_+l7mJp5i@%&gJ4!@MMCC9OtSsSMfoI#xXB?TECJ2w5HrSk$*!t8N^_H$sF};W zg7AY^ToZVl%VTxMDOQjRWDK<3GLug+nR=zseph|kxXR4Ef^KHC z%$a->*%eFQMrw0lA~W7H2lZ9X#fh4Dp5~mGmN|Q>CooRSX+x@1xGX>0u@WjpPoJr1 zo~TOs%k#_Uo$Jewk#HwWa>UJ;vBeJPK>l`kQ{KlTU0#>BwaIWS&)LhAmrKmOvgD~Q zDemt~8odJ)bT7Hy&YdA%OpQn{hn;E<_8LMinp;c@n8{aXX_jM1ybYTC>OFLH{ zu2mHCZwa4PL*WnLtsUiW-2wRTTXes!1&RXdD7H71dK$}y@ys|Zk^^P7c>|s$j-P{9^<{|#Sz@8P-G5bf zHHw{0<$AfZlN`ZC^?JDsWY*EFs)KlMQ$^Ov$s4ou3Vl|VB<;SusdABCxgQ^p3hmWL z)<+M4(J(Vbf|7XjVRK6ps? z*B`()b^oFRctlD+Dsc0}xa^JsyE~plBiGfK%WoDIq8@cvj3qdQ7xM<3gHw6uX+VOs zE4gjyrj2ya#9C5bw||VLXCW_zBL$3%vaL(Jlg5BaXugu@Ehy0i9wQ&Jk_s z5^HguI2YZb2Zp#9J>m|2-o;De0ffZ;*e;&n&FwktP&^n={0J*mh$yv)Dogm;h@DCc zx|OvUQaaJ8T!7t56qhL1;8NulT&C>970LnZQO0ql@(}5d;cD6GVgbMIVDF|oohu&U z*yobQW1GYQu6Rt7D8beDxhk+%Kj5m+Ui}a;i8EpwAE)$>UGx2#L6Q6%(;`1-Y z$BDp;jDX|qi}cp<@g?eWJbQ^2JN}U7`txu2T}%GMNWIMWGe<{Ir{Y8vLGc<2#bLB6 zucBgtb*ZTkB)OZ@fxnD nu0nn>`8unu(2jL6l-E)0VV5d6LVg7kQjED1*e`_e6qWoBL3+53 diff --git a/target/classes/utility/SuperIntersection.class b/target/classes/utility/SuperIntersection.class deleted file mode 100644 index 7314320cf29a0253a4ad32573f55cf8de3dd481f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6304 zcmcIoYhWBz75?rdo5}9Z=FxN;1}ddMAEq5)9~+9pkd2%@5Z_&`JjQNcF~3MxXO@w+qo3fZRd2i<1(&N=to zd!FC9cOE-*;6VTjgr-6f&{Ih(Y$bODI#V$--X2Ms@r2ovw4xCe#R3&ujqOGtY(#nk ztGc(6Ay5)C;%0<{+KyN_)oVr8Mx)_Gz!o(qx!vrv24Z2eEgZID33Hj1Y^Km+D`F*E z1R83lELGbjP`or6GCe4QSAz;ops1#{%MF1t4K9=jly_JW^X$|>w;5k+bcaoWX&uoX zBiv=gE%}@#7AO0xgupQ!hc%Z%`38f9m$+HQoWFipDiuuGYCNTXIL zXeOBp(Rg4jGlR(xwR7YDkjqcBtgy6R#Zdy)xvc9!s1R4uXogkeyoqAWJQ5rdyiCR-Y z3l;Abs49@D9t7Z3af-mv5i=RE271F*_xymJ&Rx>f)rJ*!<5av)#X^CTr&!e*7U4AR zXjjY{GnDEvL!E}Kd={F&V0B1Mnl&uO=`7@4lNj=j7&#cztl|u^CMYZQ^r8i=8kS%w z!GTM(zSA4oCE94pb|apOY%;>V(YTfD8`u;#A_=2s(;E9HT|7)@59Z@c8NlU~$&X5U zvAm$9DKex1tkAF$9kj&=F`nZ>pP9Eo&O2MfDy*i$32P^7ni$qD$!j!pVy(=GP^dK= zCK#vpwnMWt8V);1++535iqNHDom{1~&q(x1T$H|8uVF*R7fa-#=W6(n)TtzH4n(&T zJ2$~lv00#ga%bdMWI{tXdg$5JQ9@xNx+R|}Ft?_oU~DqA@eZI=L7QhC0XsYhp;v~W zPheiY8}b0R@TLxGY-LMGV`3WVRM4Z!#-PmXh=wR)>=tn&WTg@z8@|}%1Oy{tf#{)v zX{*)*`A>G3Q<2iJ9p}^CJ<)J9PQ)W(IEi4KpA0ZM(Kw6J!VV2PaUr))m`UPDwBNLO ztcSP{XD8h=7N^DNPdy)O1W9Os6d{vzs>tYc~evt{>H~ z3zrLc)zZ+NL<0?MJgTa)AG<*V|6mW&vby&&1 zOk1#jTm1Wfw=9sbM#6CmzI7 zOs+*!1D=UkB$fK2hC8KFB^%bV$tUEdU)C^)yO_W1&+#3dGJh*GeZy9Qq-HO^g0HH$ zhrZ8s4l*=+4fj&k;(5?uqZgPlPPRQw=K}ZP>niT&xhuCohO6NlHa11f3z91KjcZTz z&53PNGcWdHSi^ofvy_N#w(#glzOYru>oSj%pV z;9D9VlDsk=_u`>(tZ!?0MD{Cp+)SjxtW!%0d{@KwB-Q6oqip?Y&i6I^Ade$6lpJa9 z;~Jj84+$2;;j?L(4d_JZ5&THz){h0+4g+9059Og+A!2*`28k6v#m`hcMYm-f5wsGq zu(6{xm5ge58l_YsO(Iycg-pO}LLSKk@$9jlbwG{9PC7dkv9_hm_Mkj7+gjwGljxHz zR&v0IC1PfeWe`hURwQIzz#OxGIj9&nA#Sn<3QIt93e=3TU9+gG4}&e7Owi7syoBhK zCo2mh%Bm`NtjgA!!0e8R7%=`MO6Bs6PB4Qce2?!C&4g7MS6*n00cx6ikeb-2;iMg# z=Mexm9|#$*o>W}kk@8jGX|jOUS)-W1X&j}a?TQ_Lh{~1=mG-5V&nQ=wz17gn>?$+H z--C9NCPNDq6hYM(1=2`yy_!gP-*lTp4^kl1X*3G+EZX--TKLOh+e zz%onr>=_eYOU#u>McinQ5RjLKjYPsssKP^iazIL@SVeYs!EC-PDB=qbmcM*sz)=y$ zDoXk7=F>7L`~aT1VgA+cgYSMmDJbW6nJsxNig{;r<9MzB%y4F-64UrZIeS%t8LBXy zJU?j)XL0S4ZRGM;39}==(8i7<2q391fvo2n|i8qX?`Y#=ItX(L&E4HWw}Q z4r00A7WxKpp5N>D-G;Sf_|l9rUCZb( zgmd(=5q!{AiVwR{FPYhA$#gWfnPuFpYzQ0I4WeR~PxyU0?V^6S+8U=HE|Hpu+4-92 z+J2nZAFjwZilj5_{(*Ao?bdyFP7d*$Jg=Jp=TYXKc|=Kt|5GW zo%49d5WX~my9JuGL9|M3_s9_KZSv~g5e&)kNRv-zYK{6!c4L+tJ?K|=!>{{T2M_DM zhJE;s?rYqKN2T;*0ymG1j$&keeM6&P^_LV6(bLAtUCaA796x z@Mq53gumIc<@DtZ_zV7u3h7C_gfJ@U&(-9IF^yhr#^3QWs_4s7yuxTrr}yULpLi8s z`tT&YhJV3FuT|S4FaFKU*h%fAwU6LGSx>#5_0)f}o_ZtesW<5<#}^0b3&-w*wA-=v zEw1O-_!f0{EF&##+1uRT(diI7T>L0v)?8VwWoLyvB?F^ZX ztVabmag|$`*EcciZnazB0-INt$#T>Qh4H639I>(}qo76<<9e>;!U@8~ymQ&UE`{ 3) + Zselect.add(zText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + else + Zselect.add(zgenText, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + Zselect.add(zslider, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Zselect.add(inputFieldZ, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST, + GridBagConstraints.HORIZONTAL, layoutManager.Setlayout.insets, 0, 0)); + + Zselect.setBorder(zborder); + + return Zselect; + } + + +}