├── src ├── test │ ├── resources │ │ ├── mandersA.tiff │ │ ├── mandersB.tiff │ │ ├── mandersC.tiff │ │ ├── mandersD.tiff │ │ ├── mandersE.tiff │ │ ├── mandersF.tiff │ │ ├── mandersG.tiff │ │ ├── mandersH.tiff │ │ ├── mandersI.tiff │ │ ├── redZstack.tif │ │ ├── greenZstack.tif │ │ ├── syntheticNegCh1.tif │ │ ├── syntheticNegCh2.tif │ │ ├── colocsample1b-green.tif │ │ ├── colocsample1b-mask.tif │ │ ├── colocsample1b-red.tif │ │ ├── high_random_overlap1.tif │ │ ├── high_random_overlap2.tif │ │ ├── random-dots-smoothed.ijm │ │ └── SineXCosY.ijm │ └── java │ │ └── sc │ │ └── fiji │ │ └── coloc │ │ ├── tests │ │ ├── TestInteractively.java │ │ ├── ColocalisationGUITest.java │ │ ├── LiICQTest.java │ │ ├── StatisticsTest.java │ │ ├── AutoThresholdRegressionTest.java │ │ ├── ImprovedNoise.java │ │ ├── CommutativityTest.java │ │ ├── KendallTauTest.java │ │ ├── SpearmanRankTest.java │ │ ├── CostesSignificanceTest.java │ │ ├── ColocalisationTest.java │ │ ├── MandersColocalizationTest.java │ │ └── PearsonsCorrelationTest.java │ │ └── Main.java └── main │ ├── java │ ├── sc │ │ └── fiji │ │ │ └── coloc │ │ │ ├── algorithms │ │ │ ├── IntComparator.java │ │ │ ├── ChannelMapper.java │ │ │ ├── Stepper.java │ │ │ ├── MissingPreconditionException.java │ │ │ ├── SimpleStepper.java │ │ │ ├── Algorithm.java │ │ │ ├── BisectionStepper.java │ │ │ ├── Accumulator.java │ │ │ ├── LiICQ.java │ │ │ ├── IntArraySorter.java │ │ │ ├── LiHistogram2D.java │ │ │ ├── InputCheck.java │ │ │ ├── SpearmanRankCorrelation.java │ │ │ ├── KendallTauRankCorrelation.java │ │ │ └── MandersColocalization.java │ │ │ ├── gadgets │ │ │ ├── ThresholdMode.java │ │ │ ├── Autoscaler.java │ │ │ ├── Statistics.java │ │ │ └── MaskFactory.java │ │ │ ├── results │ │ │ ├── NamedContainer.java │ │ │ ├── Warning.java │ │ │ ├── ValueResult.java │ │ │ ├── ResultHandler.java │ │ │ ├── AnalysisResults.java │ │ │ └── EasyDisplay.java │ │ │ └── ColocImgLibGadgets.java │ └── net │ │ └── imglib2 │ │ ├── predicate │ │ ├── MaskPredicate.java │ │ └── Predicate.java │ │ ├── PairIterator.java │ │ ├── PredicateCursor.java │ │ ├── TwinCursor.java │ │ └── algorithm │ │ └── math │ │ └── ImageStatistics.java │ └── resources │ ├── plugins.config │ └── script_templates │ └── Examples │ └── Colocalisation.groovy ├── .github ├── build.sh ├── setup.sh └── workflows │ └── build.yml ├── .gitignore ├── README.md └── pom.xml /src/test/resources/mandersA.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersA.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersB.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersB.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersC.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersC.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersD.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersD.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersE.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersE.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersF.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersF.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersG.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersG.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersH.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersH.tiff -------------------------------------------------------------------------------- /src/test/resources/mandersI.tiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/mandersI.tiff -------------------------------------------------------------------------------- /src/test/resources/redZstack.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/redZstack.tif -------------------------------------------------------------------------------- /src/test/resources/greenZstack.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/greenZstack.tif -------------------------------------------------------------------------------- /.github/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/ci-build.sh 3 | sh ci-build.sh 4 | -------------------------------------------------------------------------------- /src/test/resources/syntheticNegCh1.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/syntheticNegCh1.tif -------------------------------------------------------------------------------- /src/test/resources/syntheticNegCh2.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/syntheticNegCh2.tif -------------------------------------------------------------------------------- /src/test/resources/colocsample1b-green.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/colocsample1b-green.tif -------------------------------------------------------------------------------- /src/test/resources/colocsample1b-mask.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/colocsample1b-mask.tif -------------------------------------------------------------------------------- /src/test/resources/colocsample1b-red.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/colocsample1b-red.tif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse # 2 | /.classpath 3 | /.project 4 | /.settings/ 5 | 6 | # IntelliJ # 7 | /.idea/ 8 | 9 | # Maven # 10 | /target/ 11 | -------------------------------------------------------------------------------- /src/test/resources/high_random_overlap1.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/high_random_overlap1.tif -------------------------------------------------------------------------------- /src/test/resources/high_random_overlap2.tif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fiji/Colocalisation_Analysis/HEAD/src/test/resources/high_random_overlap2.tif -------------------------------------------------------------------------------- /.github/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | curl -fsLO https://raw.githubusercontent.com/scijava/scijava-scripts/master/ci-setup-github-actions.sh 3 | sh ci-setup-github-actions.sh 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://github.com/fiji/Colocalisation_Analysis/actions/workflows/build-main.yml/badge.svg)](https://github.com/fiji/Colocalisation_Analysis/actions/workflows/build-main.yml) 2 | [![developer chat](https://img.shields.io/badge/zulip-join_chat-brightgreen.svg)](https://imagesc.zulipchat.com/#narrow/stream/327238-Fiji) 3 | 4 | # Colocalisation Analysis 5 | 6 | Coloc 2 is Fiji's plugin for colocalization analysis. It implements 7 | and performs the pixel intensity correlation over space methods of Pearson, 8 | Manders, Costes, Li and more, for scatterplots, analysis, automatic thresholding 9 | and statistical significance testing. 10 | 11 | Also included here are the old no longer supported plugins: 12 | Colocalization Threshold and Colocalization Test 13 | We only use these for comparison with Coloc_2, 14 | and don't suggest you actually use them for real work. 15 | 16 | For more details, see http://imagej.net/Coloc_2 17 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | tags: 8 | - "*-[0-9]+.*" 9 | pull_request: 10 | branches: 11 | - master 12 | 13 | jobs: 14 | build: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Java 20 | uses: actions/setup-java@v3 21 | with: 22 | java-version: '8' 23 | distribution: 'zulu' 24 | cache: 'maven' 25 | - name: Set up CI environment 26 | run: .github/setup.sh 27 | - name: Execute the build 28 | run: .github/build.sh 29 | env: 30 | GPG_KEY_NAME: ${{ secrets.GPG_KEY_NAME }} 31 | GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} 32 | MAVEN_USER: ${{ secrets.MAVEN_USER }} 33 | MAVEN_PASS: ${{ secrets.MAVEN_PASS }} 34 | OSSRH_PASS: ${{ secrets.OSSRH_PASS }} 35 | SIGNING_ASC: ${{ secrets.SIGNING_ASC }} 36 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/IntComparator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | public interface IntComparator { 25 | int compare(int a, int b); 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/gadgets/ThresholdMode.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.gadgets; 23 | 24 | /** 25 | * An enumerator for different modes of threshold handling. 26 | */ 27 | public enum ThresholdMode { 28 | Below, Above, None 29 | } 30 | -------------------------------------------------------------------------------- /src/main/resources/plugins.config: -------------------------------------------------------------------------------- 1 | ### 2 | # #%L 3 | # Fiji's plugin for colocalization analysis. 4 | # %% 5 | # Copyright (C) 2009 - 2024 Fiji developers. 6 | # %% 7 | # This program is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as 9 | # published by the Free Software Foundation, either version 3 of the 10 | # License, or (at your option) any later version. 11 | # 12 | # This program is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public 18 | # License along with this program. If not, see 19 | # . 20 | # #L% 21 | ### 22 | Analyze>Colocalization, "Colocalization Threshold", sc.fiji.coloc.Colocalisation_Threshold 23 | Analyze>Colocalization, "Colocalization Test", sc.fiji.coloc.Colocalisation_Test 24 | Analyze>Colocalization, "Coloc 2", sc.fiji.coloc.Coloc_2 25 | -------------------------------------------------------------------------------- /src/main/java/net/imglib2/predicate/MaskPredicate.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package net.imglib2.predicate; 23 | 24 | import net.imglib2.Cursor; 25 | import net.imglib2.type.logic.BitType; 26 | 27 | public class MaskPredicate implements Predicate { 28 | 29 | @Override 30 | public boolean test(final Cursor cursor) { 31 | return cursor.get().get(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/ChannelMapper.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | /** 25 | * A channel mapper should map an input value to either channel one or 26 | * channel two. 27 | * 28 | * @author Tom Kazimiers 29 | */ 30 | public interface ChannelMapper { 31 | double getCh1Threshold(double t); 32 | double getCh2Threshold(double t); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/net/imglib2/predicate/Predicate.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package net.imglib2.predicate; 23 | 24 | import net.imglib2.Cursor; 25 | import net.imglib2.type.Type; 26 | 27 | /** 28 | * An interface to check a value against arbitrary conditions. 29 | */ 30 | public interface Predicate> { 31 | 32 | // evaluate a predicate check for a given value 33 | boolean test(Cursor value); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/Stepper.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | /** 25 | * A stepper is used to change some value with every update() call. It should 26 | * finish at some point in time. 27 | * 28 | * @author Tom Kazimiers 29 | */ 30 | public abstract class Stepper { 31 | public abstract void update(double value); 32 | public abstract double getValue(); 33 | public abstract boolean isFinished(); 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/results/NamedContainer.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.results; 23 | 24 | /** 25 | * A small container to name objects by overriding 26 | * the toString method. 27 | * 28 | */ 29 | public class NamedContainer { 30 | T object; 31 | String name; 32 | 33 | public NamedContainer(T object, String name) { 34 | this.object = object; 35 | this.name = name; 36 | } 37 | 38 | public T getObject() { 39 | return object; 40 | } 41 | 42 | @Override 43 | public String toString() { 44 | return name; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/test/resources/random-dots-smoothed.ijm: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | // This macro draws randomly positioned dots on the active 23 | // image in the current foreground color, then gaussian smooths them. 24 | 25 | dotSize = 3; 26 | numberOfDots = 5000; 27 | width = getWidth(); 28 | height = getHeight(); 29 | for (i=0; i. 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.results; 23 | 24 | /** 25 | * A class representing a warning, combining a short and 26 | * a long message. Typically Algorithms can produce such 27 | * warnings if they find problems with the input data. 28 | */ 29 | public class Warning 30 | { 31 | private String shortMessage; 32 | private String longMessage; 33 | 34 | public Warning(String shortMessage, String longMessage) 35 | { 36 | this.shortMessage = shortMessage; 37 | this.longMessage = longMessage; 38 | } 39 | 40 | public String getShortMessage() { 41 | return shortMessage; 42 | } 43 | 44 | public String getLongMessage() { 45 | return longMessage; 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/MissingPreconditionException.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | /** 25 | * An exception class for missing preconditions for algorithm execution. 26 | */ 27 | public class MissingPreconditionException extends Exception{ 28 | 29 | private static final long serialVersionUID = 1L; 30 | 31 | public MissingPreconditionException() { 32 | super(); 33 | } 34 | 35 | public MissingPreconditionException(String message, Throwable cause) { 36 | super(message, cause); 37 | } 38 | 39 | public MissingPreconditionException(String message) { 40 | super(message); 41 | } 42 | 43 | public MissingPreconditionException(Throwable cause) { 44 | super(cause); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/results/ValueResult.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.results; 23 | 24 | /** 25 | * A small structure to keep decimal places information 26 | * with numbers along with a name or a simple named text. 27 | */ 28 | public class ValueResult { 29 | public String name; 30 | public double number; 31 | public int decimals; 32 | public String value; 33 | public boolean isNumber; 34 | 35 | public ValueResult( String name, double number, int decimals ) { 36 | this.name = name; 37 | this.number = number; 38 | this.decimals = decimals; 39 | this.isNumber = true; 40 | } 41 | 42 | public ValueResult( String name, String value) { 43 | this.name = name; 44 | this.value = value; 45 | this.isNumber = false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/resources/script_templates/Examples/Colocalisation.groovy: -------------------------------------------------------------------------------- 1 | // @ImagePlus imp1 2 | // @ImagePlus imp2 3 | 4 | // Colocalisation.groovy 5 | // 6 | // This script demonstrates programmatic usage of Fiji's Coloc 2 plugin, 7 | // including how to extract quantitative measurements after execution. 8 | 9 | import sc.fiji.coloc.Coloc_2 10 | coloc2 = new Coloc_2() 11 | 12 | indexMask = 0 13 | indexRegr = 0 14 | autoSavePdf = false 15 | displayImages = false 16 | displayShuffledCostes = false 17 | useLiCh1 = true 18 | useLiCh2 = true 19 | useLiICQ = true 20 | useSpearmanRank = true 21 | useManders = true 22 | useKendallTau = true 23 | useScatterplot = true 24 | useCostes = true 25 | psf = 3 26 | nrCostesRandomisations = 10 27 | 28 | coloc2.initializeSettings( 29 | imp1, 30 | imp2, 31 | indexMask, 32 | indexRegr, 33 | autoSavePdf, 34 | displayImages, 35 | displayShuffledCostes, 36 | useLiCh1, 37 | useLiCh2, 38 | useLiICQ, 39 | useSpearmanRank, 40 | useManders, 41 | useKendallTau, 42 | useScatterplot, 43 | useCostes, 44 | psf, 45 | nrCostesRandomisations) 46 | 47 | img1 = coloc2.img1 48 | img2 = coloc2.img2 49 | box = coloc2.masks[0].roi 50 | mask = coloc2.masks[0].mask 51 | 52 | // NB: Passing a different bounding box and/or mask here 53 | // may work, but is (as of this writing) UNTESTED. 54 | results = coloc2.colocalise(img1, img2, box, mask, null) 55 | for (v in results.values()) { 56 | println(v.name + " = " + (v.isNumber ? v.number : v.value)) 57 | } 58 | println("I also have histograms:") 59 | for (h in results.histograms()) { 60 | println("\t" + h) 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/gadgets/Autoscaler.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.gadgets; 23 | 24 | import ij.process.ImageProcessor; 25 | 26 | /** Utility class for autoscaling image displays. */ 27 | public final class Autoscaler { 28 | 29 | private Autoscaler() { } 30 | 31 | public static void autoscale(ImageProcessor ip) { 32 | // Find the minimum and maximum bounds of an image, 33 | // excluding infinities and NaNs. 34 | float min = Float.POSITIVE_INFINITY; 35 | float max = Float.NEGATIVE_INFINITY; 36 | for (int y = 0; y < ip.getHeight(); y++) { 37 | for (int x = 0; x < ip.getWidth(); x++) { 38 | float pix = ip.getPixelValue(x, y); 39 | if (!Float.isFinite(pix)) continue; 40 | if (pix < min) min = pix; 41 | if (pix > max) max = pix; 42 | } 43 | } 44 | ip.setMinAndMax(min, max); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/net/imglib2/PairIterator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package net.imglib2; 23 | 24 | /** 25 | * An iterator over pairs of types. 26 | * 27 | * @author "Johannes Schindelin" 28 | * 29 | * @param 30 | */ 31 | public interface PairIterator { 32 | 33 | /** 34 | * Returns whether there are pairs left. 35 | * 36 | * @return true if there are pairs left. 37 | */ 38 | boolean hasNext(); 39 | 40 | /** 41 | * Resets the iterator to just before the first element. 42 | */ 43 | void reset(); 44 | 45 | /** 46 | * Go to the next pair. 47 | */ 48 | void fwd(); 49 | 50 | /** 51 | * Return the first value of the pair. 52 | * 53 | * @return the first value of the pair 54 | */ 55 | T getFirst(); 56 | 57 | /** 58 | * Return the second value of the pair. 59 | * 60 | * @return the second value of the pair 61 | */ 62 | T getSecond(); 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/TestInteractively.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | 23 | package sc.fiji.coloc.tests; 24 | 25 | import fiji.Debug; 26 | 27 | public class TestInteractively { 28 | 29 | public static void main(String[] args) { 30 | // dummy call: initialize debug session 31 | Debug.run("Close All", ""); 32 | final String image1 = "colocsample1b-green.tif"; 33 | final String image2 = "colocsample1b-red.tif"; 34 | String testsDataDir = System.getProperty("plugins.dir") + "/../src/test/resources/"; 35 | Debug.run("Open...", "open=" + testsDataDir + image1); 36 | Debug.run("Open...", "open=" + testsDataDir + image2); 37 | Debug.run("Coloc 2", 38 | "channel_1=" + image1 + " channel_2=" + image2 + " roi_or_mask= " 39 | + "li_histogram_channel_1 " 40 | + "li_histogram_channel_2 " 41 | + "li_icq " 42 | + "spearman's_rank_correlation " 43 | + "manders'_correlation " 44 | + "kendall's_tau_rank_correlation " 45 | + "2d_instensity_histogram " 46 | + "costes'_significance_test " 47 | + "psf=3 " 48 | + "costes_randomisations=10" 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/test/resources/SineXCosY.ijm: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | // This macro uses the Process>Math>Macro command 23 | // to create a synthetic image. 24 | // The formula that generates the image comes from 25 | // MathMacroDemo macro from imageJ example macros page 26 | // http://rsbweb.nih.gov/ij/macros/examples/MathMacroDemo.txt 27 | // The image is a sin wave in x and a cos wave in y, 28 | // giving a regular grid of blobs that look a bit like 29 | // diffraction limited images of sub resolution objects 30 | 31 | // get a new image to write into - it is 32 bit for high precision 32 | run("Hyperstack...", "title=sine type=32-bit display=Grayscale width=1000 height=1000 channels=1 slices=1 frames=1"); 33 | 34 | // Macro... is the Process>Math>Macro command 35 | // for making images from expresions. 36 | // Here is the string expression for making the image 37 | eqn = "v = (254/2) + ( (254/2) * (sin(0.4*(x+1)) * sin(0.4*(y+1)) ) )"; 38 | // why cant i just do: 39 | //"code=[Eqn]" 40 | //string concatenation or something i guess? 41 | 42 | //Now run the Process>Math>Macro command with the expression 43 | run("Macro...", "code=["+eqn+"]"); 44 | 45 | // 46 | setMinAndMax(0.0, 255.0); 47 | run("8-bit"); 48 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/results/ResultHandler.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.results; 23 | 24 | import net.imglib2.RandomAccessibleInterval; 25 | import net.imglib2.type.numeric.RealType; 26 | 27 | import sc.fiji.coloc.algorithms.Histogram2D; 28 | 29 | /** 30 | * A result handler offers different methods to process results 31 | * of algorithms. Algorithms get passed such a result handler and 32 | * can let the handler process whatever information they like. 33 | * 34 | * @param The source images value type 35 | */ 36 | public interface ResultHandler> { 37 | 38 | void handleImage(RandomAccessibleInterval image, String name); 39 | 40 | void handleHistogram(Histogram2D histogram, String name); 41 | 42 | void handleWarning(Warning warning); 43 | 44 | void handleValue(String name, String value); 45 | 46 | void handleValue(String name, double value); 47 | 48 | void handleValue(String name, double value, int decimals); 49 | 50 | /** 51 | * The process method should start the processing of the 52 | * previously collected results. E.g. it could show some 53 | * windows or produce a final zip file. 54 | */ 55 | void process(); 56 | } 57 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/ColocalisationGUITest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertFalse; 25 | import static org.junit.Assert.assertTrue; 26 | import static org.junit.Assume.assumeFalse; 27 | 28 | import java.awt.Window; 29 | import java.awt.event.WindowEvent; 30 | 31 | import org.junit.Test; 32 | 33 | import ij.IJ; 34 | import ij.ImagePlus; 35 | import ij.WindowManager; 36 | import ij.plugin.ChannelSplitter; 37 | import sc.fiji.coloc.Colocalisation_Test; 38 | 39 | public class ColocalisationGUITest extends ColocalisationTest { 40 | @Test 41 | public void testThatWindowRefIsCleanedUpIfClosed() { 42 | assumeFalse(java.awt.GraphicsEnvironment.isHeadless()); 43 | 44 | ImagePlus img = IJ.openImage("http://imagej.net/images/FluorescentCells.zip"); 45 | ImagePlus[] splitImg = ChannelSplitter.split(img); 46 | Colocalisation_Test colocalisationTest = new Colocalisation_Test(); 47 | 48 | // run module once, it'll open results textWindow 49 | colocalisationTest.correlate(splitImg[0], splitImg[1], splitImg[2]); 50 | 51 | // check textWindow is open, and close it 52 | Window resultsWindow = WindowManager.getWindow("Results"); 53 | assertTrue(resultsWindow.isVisible()); 54 | resultsWindow.dispatchEvent(new WindowEvent(resultsWindow, WindowEvent.WINDOW_CLOSING)); 55 | assertFalse(resultsWindow.isVisible()); 56 | 57 | // run module again 58 | colocalisationTest.correlate(splitImg[0], splitImg[1], splitImg[2]); 59 | 60 | // check textWindow is open, .correlate didnt crash! 61 | resultsWindow = WindowManager.getWindow("Results"); 62 | assertTrue(resultsWindow.isVisible()); 63 | 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/LiICQTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertTrue; 25 | 26 | import net.imglib2.TwinCursor; 27 | import net.imglib2.type.numeric.integer.UnsignedByteType; 28 | import net.imglib2.view.Views; 29 | 30 | import org.junit.Test; 31 | 32 | import sc.fiji.coloc.algorithms.LiICQ; 33 | 34 | /** 35 | * This class contains JUnit 4 test cases for the calculation of Li's 36 | * ICQ value. 37 | * 38 | * @author Dan White 39 | * @author Tom Kazimiers 40 | */ 41 | public class LiICQTest extends ColocalisationTest { 42 | 43 | /** 44 | * Checks Li's ICQ value for positive correlated images. 45 | */ 46 | @Test 47 | public void liPositiveCorrTest() { 48 | TwinCursor cursor = new TwinCursor( 49 | positiveCorrelationImageCh1.randomAccess(), 50 | positiveCorrelationImageCh2.randomAccess(), 51 | Views.iterable(positiveCorrelationAlwaysTrueMask).localizingCursor()); 52 | // calculate Li's ICQ value 53 | double icq = LiICQ.calculateLisICQ(cursor, positiveCorrelationImageCh1Mean, 54 | positiveCorrelationImageCh2Mean); 55 | assertTrue(icq > 0.34 && icq < 0.35); 56 | } 57 | 58 | /** 59 | * Checks Li's ICQ value for zero correlated images. The ICQ value 60 | * should be about zero. 61 | */ 62 | @Test 63 | public void liZeroCorrTest() { 64 | TwinCursor cursor = new TwinCursor( 65 | zeroCorrelationImageCh1.randomAccess(), 66 | zeroCorrelationImageCh2.randomAccess(), 67 | Views.iterable(zeroCorrelationAlwaysTrueMask).localizingCursor()); 68 | // calculate Li's ICQ value 69 | double icq = LiICQ.calculateLisICQ(cursor, zeroCorrelationImageCh1Mean, 70 | zeroCorrelationImageCh2Mean); 71 | assertTrue(Math.abs(icq) < 0.01); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/SimpleStepper.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | /** 25 | * The simple stepper decrements a start threshold with every update() call. It 26 | * is finished if the update value is not a number, below zero or larger than 27 | * the last value. It also stops if the decremented threshold falls below one. 28 | * 29 | * @author tom 30 | */ 31 | public class SimpleStepper extends Stepper { 32 | double threshold; 33 | double lastThreshold; 34 | double currentValue; 35 | double lastValue; 36 | boolean finished = false; 37 | 38 | /** 39 | * Initialize the simple sequential stepper with a starting threshold. 40 | * 41 | * @param threshold The starting threshold. 42 | */ 43 | public SimpleStepper(double threshold) { 44 | this.threshold = threshold; 45 | this.currentValue = 1.0; 46 | this.lastValue = Double.MAX_VALUE; 47 | } 48 | 49 | /** 50 | * Decrement the threshold if the stepper is not marked as finished. 51 | * Rendering a stepper finished happens if {@code value} is not a number, 52 | * below or equal zero or bigger than the last update value. The same 53 | * thing happens if the internal threshold falls below one. 54 | */ 55 | @Override 56 | public void update(double value) { 57 | if (!finished) { 58 | // Remember current value and store new value 59 | lastValue = this.currentValue; 60 | currentValue = value; 61 | // Decrement threshold 62 | threshold = this.threshold - 1.0; 63 | 64 | // Stop if the threshold was 65 | finished = Double.NaN == value || 66 | threshold < 1 || 67 | value < 0.0001 || 68 | value > lastValue; 69 | } 70 | } 71 | 72 | /** 73 | * Get the current threshold. 74 | */ 75 | @Override 76 | public double getValue() { 77 | return threshold; 78 | } 79 | 80 | /** 81 | * Indicates if the stepper is marked as finished. 82 | */ 83 | @Override 84 | public boolean isFinished() { 85 | return finished; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/Algorithm.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import java.util.ArrayList; 25 | import java.util.List; 26 | 27 | import net.imglib2.type.numeric.RealType; 28 | 29 | import sc.fiji.coloc.gadgets.DataContainer; 30 | import sc.fiji.coloc.results.ResultHandler; 31 | import sc.fiji.coloc.results.Warning; 32 | 33 | /** 34 | * An algorithm is an abstraction of techniques like the 35 | * calculation of the Persons coefficient or Li'S ICQ. It 36 | * allows to separate initialization and execution of 37 | * such an algorithm. 38 | */ 39 | public abstract class Algorithm> { 40 | // a name for the algorithm 41 | protected String name; 42 | /* a list of warnings that can be filled by the 43 | * execute method 44 | */ 45 | List warnings = new ArrayList(); 46 | 47 | public Algorithm(String name) { 48 | this.name = name; 49 | } 50 | 51 | /** 52 | * Executes the previously initialized {@link Algorithm}. 53 | */ 54 | public abstract void execute(DataContainer container) throws MissingPreconditionException; 55 | 56 | public String getName() { 57 | return name; 58 | } 59 | 60 | /** 61 | * A method to give the algorithm the opportunity to let 62 | * its results being processed by the passed handler. 63 | * By default this methods passes the collected warnings to 64 | * the handler and sub-classes should make use of this by 65 | * adding custom behavior and call the super class. 66 | * 67 | * @param handler The ResultHandler to process the results. 68 | */ 69 | public void processResults(ResultHandler handler) { 70 | for (Warning w : warnings) 71 | handler.handleWarning( w ); 72 | } 73 | 74 | /** 75 | * Gets a reference to the warnings. 76 | * 77 | * @return A reference to the warnings list 78 | */ 79 | public List getWarnings() { 80 | return warnings; 81 | } 82 | 83 | /** 84 | * Adds a warning to the list of warnings. 85 | * 86 | * @param shortMsg A short descriptive message 87 | * @param longMsg A long message 88 | */ 89 | protected void addWarning(String shortMsg, String longMsg) { 90 | warnings.add( new Warning(shortMsg, longMsg) ); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/BisectionStepper.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | /** 25 | * Try to converge a threshold based on an update value condition. If the 26 | * update value is larger zero, the threshold is lowered by half the distance 27 | * between the last thresholds. If the update value falls below zero or is not 28 | * a number, the threshold is increased by such a half step. 29 | * 30 | * @author Tom Kazimiers 31 | * 32 | */ 33 | public class BisectionStepper extends Stepper { 34 | protected double threshold1; 35 | protected double threshold2; 36 | protected double thrDiff = Double.NaN; 37 | protected int iterations = 0; 38 | protected int maxIterations = 100; 39 | 40 | /** 41 | * Initialize the bisection stepper with a start threshold and its 42 | * last threshold 43 | * 44 | * @param threshold The current threshold 45 | * @param lastThreshold The last threshold 46 | */ 47 | public BisectionStepper(double threshold, double lastThreshold) { 48 | threshold1 = threshold; 49 | threshold2 = lastThreshold; 50 | thrDiff = Math.abs(threshold1 - threshold2); 51 | } 52 | 53 | /** 54 | * Update threshold by a bisection step. If {@code value} is below zero or 55 | * not a number, the step is made upwards. If it is above zero, the stoep is 56 | * downwards. 57 | */ 58 | @Override 59 | public void update(double value) { 60 | // update working thresholds for next iteration 61 | threshold2 = threshold1; 62 | if (Double.NaN == value || value < 0) { 63 | // we went too far, increase by the absolute half 64 | threshold1 = threshold1 + thrDiff * 0.5; 65 | } else if (value > 0) { 66 | // as long as r > 0 we go half the way down 67 | threshold1 = threshold1 - thrDiff * 0.5; 68 | } 69 | // Update difference to last threshold 70 | thrDiff = Math.abs(threshold1 - threshold2); 71 | // Update update counter 72 | iterations++; 73 | } 74 | 75 | /** 76 | * Get current threshold. 77 | */ 78 | @Override 79 | public double getValue() { 80 | return threshold1; 81 | } 82 | 83 | /** 84 | * If the difference between both thresholds is < 1, we consider 85 | * that as reasonable close to abort the regression. 86 | */ 87 | @Override 88 | public boolean isFinished() { 89 | return iterations > maxIterations || thrDiff < 1.0; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/Accumulator.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import net.imglib2.TwinCursor; 25 | import net.imglib2.type.numeric.RealType; 26 | 27 | /** 28 | * A class allowing an easy accumulation of values visited by a 29 | * TwinCursor. After instantiation the sum of channel one, 30 | * channel two, products with them self and a product of both of 31 | * them will be available. It additionally provides the possibility 32 | * to subtract values from the data before the adding them to the 33 | * sum. 34 | * 35 | * @author Johannes Schindelin and Tom Kazimiers 36 | */ 37 | public abstract class Accumulator> { 38 | protected double x, y, xx, xy, yy; 39 | protected int count; 40 | 41 | /** 42 | * The two values x and y from each cursor iteration to get 43 | * summed up as single values and their combinations. 44 | */ 45 | public Accumulator(final TwinCursor cursor) { 46 | this(cursor, false, 0.0d, 0.0d); 47 | } 48 | 49 | /** 50 | * The two values (x - xDiff) and (y - yDiff) from each cursor 51 | * iteration to get summed up as single values and their combinations. 52 | */ 53 | public Accumulator(final TwinCursor cursor, double xDiff, double yDiff) { 54 | this(cursor, true, xDiff, yDiff); 55 | } 56 | 57 | protected Accumulator(final TwinCursor cursor, boolean substract, double xDiff, double yDiff) { 58 | while (cursor.hasNext()) { 59 | cursor.fwd(); 60 | 61 | T type1 = cursor.getFirst(); 62 | T type2 = cursor.getSecond(); 63 | 64 | if (!accept(type1, type2)) 65 | continue; 66 | 67 | double value1 = type1.getRealDouble(); 68 | double value2 = type2.getRealDouble(); 69 | 70 | if (substract) { 71 | value1 -= xDiff; 72 | value2 -= yDiff; 73 | } 74 | 75 | x += value1; 76 | y += value2; 77 | xx += value1 * value1; 78 | xy += value1 * value2; 79 | yy += value2 * value2; 80 | count++; 81 | } 82 | } 83 | 84 | public abstract boolean accept(T type1, T type2); 85 | 86 | public double getX() { 87 | return x; 88 | } 89 | 90 | public double getY() { 91 | return y; 92 | } 93 | 94 | public double getXX() { 95 | return xx; 96 | } 97 | 98 | public double getXY() { 99 | return xy; 100 | } 101 | 102 | public double getYY() { 103 | return yy; 104 | } 105 | 106 | public int getCount() { 107 | return count; 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/Main.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc; 23 | 24 | import ij.IJ; 25 | import ij.ImageJ; 26 | import ij.ImagePlus; 27 | import ij.plugin.ChannelSplitter; 28 | 29 | /** 30 | * Test class for Coloc 2 functionality. 31 | * 32 | * @author Ellen T Arena 33 | */ 34 | public class Main { 35 | 36 | 37 | /** 38 | * Main method for debugging. 39 | * 40 | * For debugging, it is convenient to have a method that starts ImageJ, loads an 41 | * image and calls the plugin, e.g. after setting breakpoints. 42 | * 43 | * @param args unused 44 | */ 45 | public static void main(String[] args) { 46 | // start ImageJ 47 | new ImageJ(); 48 | 49 | // open the FluorescentCells sample (to test single slice images) 50 | ImagePlus fluorCellImage = IJ.openImage("http://imagej.net/images/FluorescentCells.zip"); 51 | ImagePlus[] fluorCellchannels = ChannelSplitter.split(fluorCellImage); 52 | fluorCellchannels[0].show(); 53 | fluorCellchannels[1].show(); 54 | // run the plugin, Coloc 2 55 | IJ.runPlugIn(Coloc_2.class.getName(),"channel_1=C1-FluorescentCells.tif channel_2=C2-FluorescentCells.tif roi_or_mask= threshold_regression=Costes display_images_in_result li_histogram_channel_1 li_histogram_channel_2 li_icq spearman's_rank_correlation manders'_correlation kendall's_tau_rank_correlation 2d_instensity_histogram costes'_significance_test psf=3 costes_randomisations=10"); 56 | 57 | // // open the Confocal Series sample (to test z-stacks) 58 | // ImagePlus confocalImage = IJ.openImage("http://imagej.net/images/confocal-series.zip"); 59 | // ImagePlus[] confocalchannels = ChannelSplitter.split(confocalImage); 60 | // confocalchannels[0].show(); 61 | // confocalchannels[1].show(); 62 | // // run the plugin, Coloc 2 63 | // IJ.runPlugIn(Coloc_2.class.getName(), "channel_1=C1-confocal-series.tif channel_2=C2-confocal-series.tif roi_or_mask= threshold_regression=Costes display_images_in_result li_histogram_channel_1 li_histogram_channel_2 li_icq spearman's_rank_correlation manders'_correlation kendall's_tau_rank_correlation 2d_instensity_histogram costes'_significance_test psf=3 costes_randomisations=10"); 64 | 65 | // // testing RGB image samples... 66 | // ImagePlus fluorCellImage = IJ.openImage("http://imagej.net/images/hela-cells.zip"); 67 | // ImagePlus[] fluorCellchannels = ChannelSplitter.split(fluorCellImage); 68 | // fluorCellchannels[0].show(); 69 | // fluorCellchannels[1].show(); 70 | // IJ.run("RGB Color"); 71 | // // run the plugin, Coloc 2 72 | // IJ.runPlugIn(Coloc_2.class.getName(),"channel_1=C1-hela-cells.tif channel_2=C2-hela-cells.tif roi_or_mask= threshold_regression=Costes display_images_in_result li_histogram_channel_1 li_histogram_channel_2 li_icq spearman's_rank_correlation manders'_correlation kendall's_tau_rank_correlation 2d_instensity_histogram costes'_significance_test psf=3 costes_randomisations=10"); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/StatisticsTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertTrue; 25 | 26 | import java.util.Arrays; 27 | import java.util.List; 28 | 29 | import org.junit.Test; 30 | 31 | import sc.fiji.coloc.gadgets.Statistics; 32 | 33 | /** 34 | * This class contains JUnit 4 test cases for the implementation 35 | * of different statistics methods. 36 | * 37 | * @author Tom Kazimiers 38 | */ 39 | public class StatisticsTest { 40 | /** 41 | * Test the error function. Test values have been taken from: 42 | * http://en.wikipedia.org/wiki/Error_function 43 | */ 44 | @Test 45 | public void erfTest() { 46 | // erf(0) = 0 47 | double erf = Statistics.erf(0.0); 48 | assertTrue( Math.abs(erf) < 0.0001); 49 | // erf(0.5) = 0.5204999 50 | erf = Statistics.erf(0.5); 51 | assertTrue( Math.abs(erf - 0.5204999) < 0.0001); 52 | // erf(1.0) = 0.8427008 53 | erf = Statistics.erf(1.0); 54 | assertTrue( Math.abs(erf - 0.8427008) < 0.0001); 55 | // erf(-0.5) = -0.5204999 56 | erf = Statistics.erf(-0.5); 57 | assertTrue( Math.abs(erf + 0.5204999) < 0.0001); 58 | // erf(-1.0) = -0.8427008 59 | erf = Statistics.erf(-1.0); 60 | assertTrue( Math.abs(erf + 0.8427008) < 0.0001); 61 | } 62 | 63 | /** 64 | * Test the cumulative distribution function (phi) for the 65 | * standard normal distribution. 66 | */ 67 | @Test 68 | public void phiTest() { 69 | // phi(0) = 0.5 70 | double phi = Statistics.phi(0.0); 71 | assertTrue( Math.abs(phi - 0.5) < 0.0001 ); 72 | // phi(-1) = 0.158655 73 | phi = Statistics.phi(-1.0); 74 | assertTrue( Math.abs(phi - 0.158655) < 0.0001 ); 75 | // phi(0.5) = 0.691462 76 | phi = Statistics.phi(0.5); 77 | assertTrue( Math.abs(phi - 0.691462) < 0.0001 ); 78 | // phi(1.960) = 0.975002 79 | phi = Statistics.phi(1.960); 80 | assertTrue( Math.abs(phi - 0.975002) < 0.0001 ); 81 | } 82 | 83 | /** 84 | * Test the cumulative distribution function (phi) for the 85 | * normal distribution (based on mean and standard derivation). 86 | */ 87 | @Test 88 | public void phiDifferentDistributionTest() { 89 | // phi(0.5, 0, 1) = 0.691462 90 | double phi = Statistics.phi(0.5, 0.0, 1.0); 91 | assertTrue( Math.abs(phi - 0.691462) < 0.0001 ); 92 | // phi(0.5, 20, 12) = 0.052081 93 | phi = Statistics.phi(0.5, 20, 12); 94 | assertTrue( Math.abs(phi - 0.052081) < 0.0001 ); 95 | // phi(-1, 42, 33) = 0.096282 96 | phi = Statistics.phi(-1, 42, 33); 97 | assertTrue( Math.abs(phi - 0.096282) < 0.0001 ); 98 | } 99 | 100 | /** 101 | * Tests the calculation of the standard deviation of a list 102 | * of values. 103 | */ 104 | @Test 105 | public void stdDeviationTest() { 106 | /* the standard deviation of the list 107 | * [1, 3, 4, 6, 9, 19] is 6.48074069 108 | */ 109 | List values = Arrays.asList( new Double[] {1.0, 3.0, 4.0, 6.0, 9.0, 19.0} ); 110 | double sd = Statistics.stdDeviation(values); 111 | assertTrue( Math.abs( sd - 6.48074069 ) < 0.0001); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/AutoThresholdRegressionTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | 26 | import net.imglib2.RandomAccessibleInterval; 27 | import net.imglib2.type.numeric.integer.UnsignedByteType; 28 | 29 | import org.junit.Test; 30 | 31 | import sc.fiji.coloc.algorithms.AutoThresholdRegression; 32 | import sc.fiji.coloc.algorithms.AutoThresholdRegression.Implementation; 33 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 34 | import sc.fiji.coloc.algorithms.PearsonsCorrelation; 35 | import sc.fiji.coloc.gadgets.DataContainer; 36 | 37 | 38 | public class AutoThresholdRegressionTest extends ColocalisationTest { 39 | 40 | @Test 41 | public void clampHelperTest() throws MissingPreconditionException { 42 | assertEquals(4, AutoThresholdRegression.clamp(5, 1, 4), 0.00001); 43 | assertEquals(1, AutoThresholdRegression.clamp(-2, 1, 4), 0.00001); 44 | assertEquals(1, AutoThresholdRegression.clamp(5, 1, 1), 0.00001); 45 | assertEquals(2, AutoThresholdRegression.clamp(2, -1, 3), 0.00001); 46 | } 47 | 48 | /** 49 | * This test makes sure the test images A and B lead to the same thresholds, 50 | * regardless whether they are added in the order A, B or B, A to the data 51 | * container. 52 | * 53 | * @throws MissingPreconditionException 54 | */ 55 | @Test 56 | public void cummutativityTest() throws MissingPreconditionException { 57 | _cummutativityTest(Implementation.Costes); 58 | _cummutativityTest(Implementation.Bisection); 59 | } 60 | 61 | protected void _cummutativityTest(Implementation atrImplementation) 62 | throws MissingPreconditionException { 63 | PearsonsCorrelation pc1 = 64 | new PearsonsCorrelation( 65 | PearsonsCorrelation.Implementation.Fast); 66 | PearsonsCorrelation pc2 = 67 | new PearsonsCorrelation( 68 | PearsonsCorrelation.Implementation.Fast); 69 | 70 | AutoThresholdRegression atr1 = 71 | new AutoThresholdRegression(pc1, atrImplementation); 72 | AutoThresholdRegression atr2 = 73 | new AutoThresholdRegression(pc2, atrImplementation); 74 | 75 | RandomAccessibleInterval img1 = syntheticNegativeCorrelationImageCh1; 76 | RandomAccessibleInterval img2 = syntheticNegativeCorrelationImageCh2; 77 | 78 | DataContainer container1 = 79 | new DataContainer(img1, img2, 1, 1, 80 | "Channel 1", "Channel 2"); 81 | 82 | DataContainer container2 = 83 | new DataContainer(img2, img1, 1, 1, 84 | "Channel 2", "Channel 1"); 85 | 86 | atr1.execute(container1); 87 | atr2.execute(container2); 88 | 89 | assertEquals(atr1.getCh1MinThreshold(), atr2.getCh2MinThreshold()); 90 | assertEquals(atr1.getCh1MaxThreshold(), atr2.getCh2MaxThreshold()); 91 | assertEquals(atr1.getCh2MinThreshold(), atr2.getCh1MinThreshold()); 92 | assertEquals(atr1.getCh2MaxThreshold(), atr2.getCh1MaxThreshold()); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/LiICQ.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import net.imglib2.RandomAccessible; 25 | import net.imglib2.RandomAccessibleInterval; 26 | import net.imglib2.TwinCursor; 27 | import net.imglib2.type.logic.BitType; 28 | import net.imglib2.type.numeric.RealType; 29 | import net.imglib2.view.Views; 30 | 31 | import sc.fiji.coloc.gadgets.DataContainer; 32 | import sc.fiji.coloc.results.ResultHandler; 33 | 34 | /** 35 | * This algorithm calculates Li et al.'s ICQ (intensity 36 | * correlation quotient). 37 | * 38 | * @param 39 | */ 40 | public class LiICQ> extends Algorithm { 41 | // the resulting ICQ value 42 | double icqValue; 43 | 44 | public LiICQ() { 45 | super("Li ICQ calculation"); 46 | } 47 | 48 | @Override 49 | public void execute(DataContainer container) 50 | throws MissingPreconditionException { 51 | double mean1 = container.getMeanCh1(); 52 | double mean2 = container.getMeanCh2(); 53 | 54 | // get the 2 images for the calculation of Li's ICQ 55 | RandomAccessible img1 = container.getSourceImage1(); 56 | RandomAccessible img2 = container.getSourceImage2(); 57 | RandomAccessibleInterval mask = container.getMask(); 58 | 59 | TwinCursor cursor = new TwinCursor(img1.randomAccess(), 60 | img2.randomAccess(), Views.iterable(mask).localizingCursor()); 61 | // calculate ICQ value 62 | icqValue = calculateLisICQ(cursor, mean1, mean2); 63 | } 64 | 65 | /** 66 | * Calculates Li et al.'s intensity correlation quotient (ICQ) for 67 | * two images. 68 | * 69 | * @param cursor A TwinCursor that iterates over two images 70 | * @param mean1 The first images mean 71 | * @param mean2 The second images mean 72 | * @return Li et al.'s ICQ value 73 | */ 74 | public static > double calculateLisICQ(TwinCursor cursor, double mean1, double mean2) { 75 | /* variables to count the positive and negative results 76 | * of Li's product of the difference of means. 77 | */ 78 | long numPositiveProducts = 0; 79 | long numNegativeProducts = 0; 80 | // iterate over image 81 | while (cursor.hasNext()) { 82 | cursor.fwd(); 83 | T type1 = cursor.getFirst(); 84 | T type2 = cursor.getSecond(); 85 | double ch1 = type1.getRealDouble(); 86 | double ch2 = type2.getRealDouble(); 87 | 88 | double productOfDifferenceOfMeans = (mean1 - ch1) * (mean2 - ch2); 89 | 90 | // check for positive and negative values 91 | if (productOfDifferenceOfMeans < 0.0 ) 92 | ++numNegativeProducts; 93 | else 94 | ++numPositiveProducts; 95 | } 96 | 97 | /* calculate Li's ICQ value by dividing the amount of "positive pixels" to the 98 | * total number of pixels. Then shift it in the -0.5,0.5 range. 99 | */ 100 | return ( (double) numPositiveProducts / (double) (numNegativeProducts + numPositiveProducts) ) - 0.5; 101 | } 102 | 103 | @Override 104 | public void processResults(ResultHandler handler) { 105 | super.processResults(handler); 106 | handler.handleValue("Li's ICQ value", icqValue); 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/gadgets/Statistics.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.gadgets; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * This class provides some basic statistics methods. 28 | * 29 | * @author Tom Kazimiers 30 | * 31 | */ 32 | public class Statistics { 33 | 34 | // have the inverted square root of two ready to use 35 | static final double invSqrtTwo = 1.0 / Math.sqrt(2); 36 | 37 | /** 38 | * Calculates an estimate of the upper tail cumulative normal distribution 39 | * (which is simply the complementary error function with linear scalings 40 | * of x and y axis). 41 | * 42 | * Fractional error in math formula less than 1.2 * 10 ^ -7. 43 | * although subject to catastrophic cancellation when z in very close to 0 44 | * 45 | * Code from (thanks to Bob Dougherty): 46 | * w 47 | * 48 | * Original algorithm from Section 6.2 of Numerical Recipes 49 | */ 50 | public static double erf(double z) { 51 | double t = 1.0 / (1.0 + 0.5 * Math.abs(z)); 52 | 53 | // use Horner's method 54 | double ans = 1 - t * Math.exp( -z*z - 1.26551223 + 55 | t * ( 1.00002368 + 56 | t * ( 0.37409196 + 57 | t * ( 0.09678418 + 58 | t * (-0.18628806 + 59 | t * ( 0.27886807 + 60 | t * (-1.13520398 + 61 | t * ( 1.48851587 + 62 | t * (-0.82215223 + 63 | t * ( 0.17087277)))))))))); 64 | if (z >= 0) 65 | return ans; 66 | else 67 | return -ans; 68 | } 69 | 70 | /** 71 | * Calculates phi, which is the area of the Gaussian standard 72 | * distribution from minus infinity to the query value in units 73 | * of standard derivation. 74 | * The formula is: 75 | * 76 | * 1 + erf( z / sqrt(2) ) 77 | * Phi(z) = ---------------------- 78 | * 2 79 | * @param z The point of interest 80 | * @return phi 81 | */ 82 | public static double phi(double z) { 83 | return 0.5 * (1.0 + erf( z * invSqrtTwo ) ); 84 | } 85 | 86 | /** 87 | * Calculates phi, but with a Gaussian distribution defined by 88 | * its mean and its standard derivation. This is a quantile. 89 | * 90 | * 1 + erf( (z - mean) / (sqrt(2) * stdDev) ) 91 | * Phi(z,mean,stdDev) = ------------------------------------------ 92 | * 2 * 93 | * @param z The point of interest 94 | * @param mean The mean of the distribution 95 | * @param sd The standard derivation of the distribution 96 | * @return phi 97 | */ 98 | public static double phi(double z, double mean, double sd) { 99 | return phi( (z - mean) / sd); 100 | } 101 | 102 | /** 103 | * Calculates the standard deviation of a list of values. 104 | * 105 | * @param values The list of values. 106 | * @return The standard deviation. 107 | */ 108 | public static double stdDeviation(List values) { 109 | int count = values.size(); 110 | // calculate mean 111 | double sum = 0; 112 | for( Double val : values ) { 113 | sum += val; 114 | } 115 | double mean = sum / count; 116 | 117 | // calculate deviates 118 | sum = 0; 119 | for( Double val : values ) { 120 | double diff = val - mean; 121 | double sqDiff = diff * diff; 122 | sum += sqDiff; 123 | } 124 | double stdDeviation = Math.sqrt( sum / (count - 1) ); 125 | 126 | return stdDeviation; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/ImprovedNoise.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | /** 25 | * JAVA REFERENCE IMPLEMENTATION OF IMPROVED NOISE - COPYRIGHT 2002 KEN PERLIN. 26 | * From: http://mrl.nyu.edu/~perlin/noise/ 27 | */ 28 | public final class ImprovedNoise { 29 | static public double noise(double x, double y, double z) { 30 | int X = (int)Math.floor(x) & 255, // FIND UNIT CUBE THAT 31 | Y = (int)Math.floor(y) & 255, // CONTAINS POINT. 32 | Z = (int)Math.floor(z) & 255; 33 | x -= Math.floor(x); // FIND RELATIVE X,Y,Z 34 | y -= Math.floor(y); // OF POINT IN CUBE. 35 | z -= Math.floor(z); 36 | double u = fade(x), // COMPUTE FADE CURVES 37 | v = fade(y), // FOR EACH OF X,Y,Z. 38 | w = fade(z); 39 | int A = p[X ]+Y, AA = p[A]+Z, AB = p[A+1]+Z, // HASH COORDINATES OF 40 | B = p[X+1]+Y, BA = p[B]+Z, BB = p[B+1]+Z; // THE 8 CUBE CORNERS, 41 | 42 | return lerp(w, lerp(v, lerp(u, grad(p[AA ], x , y , z ), // AND ADD 43 | grad(p[BA ], x-1, y , z )), // BLENDED 44 | lerp(u, grad(p[AB ], x , y-1, z ), // RESULTS 45 | grad(p[BB ], x-1, y-1, z ))),// FROM 8 46 | lerp(v, lerp(u, grad(p[AA+1], x , y , z-1 ), // CORNERS 47 | grad(p[BA+1], x-1, y , z-1 )), // OF CUBE 48 | lerp(u, grad(p[AB+1], x , y-1, z-1 ), 49 | grad(p[BB+1], x-1, y-1, z-1 )))); 50 | } 51 | static double fade(double t) { return t * t * t * (t * (t * 6 - 15) + 10); } 52 | static double lerp(double t, double a, double b) { return a + t * (b - a); } 53 | static double grad(int hash, double x, double y, double z) { 54 | int h = hash & 15; // CONVERT LO 4 BITS OF HASH CODE 55 | double u = h<8 ? x : y, // INTO 12 GRADIENT DIRECTIONS. 56 | v = h<4 ? y : h==12||h==14 ? x : z; 57 | return ((h&1) == 0 ? u : -u) + ((h&2) == 0 ? v : -v); 58 | } 59 | static final int p[] = new int[512], permutation[] = { 151,160,137,91,90,15, 60 | 131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,8,99,37,240,21,10,23, 61 | 190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,57,177,33, 62 | 88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,134,139,48,27,166, 63 | 77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,55,46,245,40,244, 64 | 102,143,54, 65,25,63,161, 1,216,80,73,209,76,132,187,208, 89,18,169,200,196, 65 | 135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,250,124,123, 66 | 5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,189,28,42, 67 | 223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167, 43,172,9, 68 | 129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,97,228, 69 | 251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,107, 70 | 49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254, 71 | 138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 72 | }; 73 | static { for (int i=0; i < 256 ; i++) p[256+i] = p[i] = permutation[i]; } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/results/AnalysisResults.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | 23 | package sc.fiji.coloc.results; 24 | 25 | import java.util.ArrayList; 26 | import java.util.HashMap; 27 | import java.util.List; 28 | import java.util.Map; 29 | 30 | import net.imglib2.RandomAccessibleInterval; 31 | import net.imglib2.type.numeric.RealType; 32 | import net.imglib2.type.numeric.integer.LongType; 33 | 34 | import sc.fiji.coloc.algorithms.Histogram2D; 35 | 36 | /** 37 | * Data structure housing all colocalisation results. Intended for programmatic 38 | * access via API calls. 39 | * 40 | * @author Curtis Rueden 41 | */ 42 | public class AnalysisResults> implements 43 | ResultHandler 44 | { 45 | 46 | /** Result images, no matter what specific kinds. */ 47 | private final List>>> listOfImages = 48 | new ArrayList<>(); 49 | 50 | /** Histogram results. */ 51 | private final Map, Histogram2D> mapOf2DHistograms = 52 | new HashMap<>(); 53 | 54 | /** Warnings produced during analysis. */ 55 | private final List warnings = new ArrayList<>(); 56 | 57 | /** Named values, collected from algorithms. */ 58 | private final List valueResults = new ArrayList<>(); 59 | 60 | /** 61 | * Images and corresponding LUTs. When an image is not in there no LUT should 62 | * be applied. 63 | */ 64 | private final Map listOfLUTs = new HashMap<>(); 65 | 66 | // -- AllTheData methods -- 67 | 68 | public List>>> 69 | images() 70 | { 71 | return listOfImages; 72 | } 73 | 74 | public Map, Histogram2D> histograms() { 75 | return mapOf2DHistograms; 76 | } 77 | 78 | public List warnings() { 79 | return warnings; 80 | } 81 | 82 | public List values() { 83 | return valueResults; 84 | } 85 | 86 | // -- ResultHandler methods -- 87 | 88 | @Override 89 | public void handleImage(final RandomAccessibleInterval image, 90 | final String name) 91 | { 92 | listOfImages.add( 93 | new NamedContainer>>(image, 94 | name)); 95 | } 96 | 97 | @Override 98 | public void handleHistogram(final Histogram2D histogram, 99 | final String name) 100 | { 101 | listOfImages.add( 102 | new NamedContainer>>( 103 | histogram.getPlotImage(), name)); 104 | mapOf2DHistograms.put(histogram.getPlotImage(), histogram); 105 | // link the histogram to a LUT 106 | listOfLUTs.put(histogram.getPlotImage(), "Fire"); 107 | } 108 | 109 | @Override 110 | public void handleWarning(final Warning warning) { 111 | warnings.add(warning); 112 | } 113 | 114 | @Override 115 | public void handleValue(final String name, final String value) { 116 | valueResults.add(new ValueResult(name, value)); 117 | } 118 | 119 | @Override 120 | public void handleValue(final String name, final double value) { 121 | handleValue(name, value, 3); 122 | } 123 | 124 | @Override 125 | public void handleValue(final String name, final double value, 126 | final int decimals) 127 | { 128 | valueResults.add(new ValueResult(name, value, decimals)); 129 | } 130 | 131 | @Override 132 | public void process() { 133 | // NB: No action needed. 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/IntArraySorter.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | /** 25 | * Sorts and int[] according to a custom comparator. 26 | *

27 | * This is an implementation of introsort, i.e. it is stable because 28 | * it tries the quicksort algorithm first and falls back to the heap 29 | * sort when it detects an unfavorable execution path. 30 | *

31 | * 32 | * @author Johannes Schindelin 33 | */ 34 | public class IntArraySorter { 35 | 36 | private final static int SORT_SIZE_THRESHOLD = 16; 37 | 38 | public static void sort(int[] array, IntComparator comparator) { 39 | introSort(array, comparator, 0, array.length, array.length); 40 | insertionSort(array, comparator); 41 | } 42 | 43 | private static void introSort(int[] array, IntComparator comparator, int begin, int end, int limit) 44 | { 45 | while (end - begin > SORT_SIZE_THRESHOLD) { 46 | if (limit == 0) { 47 | heapSort(array, comparator, begin, end); 48 | return; 49 | } 50 | limit >>= 1; 51 | 52 | // median of three 53 | int a = array[begin]; 54 | int b = array[begin + (end - begin) / 2 + 1]; 55 | int c = array[end - 1]; 56 | int median; 57 | if (comparator.compare(a, b) < 0) { 58 | median = comparator.compare(b, c) < 0 ? 59 | b : (comparator.compare(a, c) < 0 ? c : a); 60 | } else { 61 | median = comparator.compare(b, c) > 0 ? 62 | b : (comparator.compare(a, c) > 0 ? c : a); 63 | } 64 | 65 | // partition 66 | int pivot, i = begin, j = end; 67 | for (;;) { 68 | while (comparator.compare(array[i], median) < 0) { 69 | ++i; 70 | } 71 | --j; 72 | while (comparator.compare(median, array[j]) < 0) { 73 | --j; 74 | } 75 | if (i >= j) { 76 | pivot = i; 77 | break; 78 | } 79 | int swap = array[i]; 80 | array[i] = array[j]; 81 | array[j] = swap; 82 | ++i; 83 | } 84 | 85 | introSort(array, comparator, pivot, end, limit); 86 | end = pivot; 87 | } 88 | } 89 | 90 | private static void heapSort(int[] array, IntComparator comparator, 91 | int begin, int end) 92 | { 93 | int count = end - begin; 94 | for (int i = count / 2 - 1; i >= 0; --i) { 95 | siftDown(array, comparator, i, count, begin); 96 | } 97 | for (int i = count - 1; i > 0; --i) { 98 | // swap begin and begin + i 99 | int swap = array[begin + i]; 100 | array[begin + i] = array[begin]; 101 | array[begin] = swap; 102 | 103 | siftDown(array, comparator, 0, i, begin); 104 | } 105 | } 106 | 107 | private static void siftDown(int[] array, IntComparator comparator, 108 | int i, int count, int offset) 109 | { 110 | int value = array[offset + i]; 111 | while (i < count / 2) { 112 | int child = 2 * i + 1; 113 | if (child + 1 < count && 114 | comparator.compare(array[child], array[child + 1]) < 0) { 115 | ++child; 116 | } 117 | if (comparator.compare(value, array[child]) >= 0) { 118 | break; 119 | } 120 | array[offset + i] = array[offset + child]; 121 | i = child; 122 | } 123 | array[offset + i] = value; 124 | } 125 | 126 | private static void insertionSort(int[] array, 127 | IntComparator comparator) 128 | { 129 | for (int j = 1; j < array.length; ++j) { 130 | int t = array[j]; 131 | int i = j - 1; 132 | while (i >= 0 && comparator.compare(array[i], t) > 0) { 133 | array[i + 1] = array[i]; 134 | i = i - 1; 135 | } 136 | array[i + 1] = t; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/results/EasyDisplay.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.results; 23 | 24 | import ij.IJ; 25 | import ij.ImagePlus; 26 | import ij.text.TextWindow; 27 | 28 | import net.imglib2.RandomAccessibleInterval; 29 | import net.imglib2.algorithm.math.ImageStatistics; 30 | import net.imglib2.img.display.imagej.ImageJFunctions; 31 | import net.imglib2.type.numeric.RealType; 32 | 33 | import sc.fiji.coloc.algorithms.Histogram2D; 34 | import sc.fiji.coloc.gadgets.DataContainer; 35 | 36 | public class EasyDisplay> implements ResultHandler { 37 | // the text window to present value and text results 38 | protected static TextWindow textWindow; 39 | /* the data container with general information about the 40 | * source images that were processed by the algorithms. 41 | */ 42 | protected DataContainer container; 43 | 44 | public EasyDisplay(DataContainer container) { 45 | final int twWidth = 170; 46 | final int twHeight = 250; 47 | //test if the results windows is already there, if so use it. 48 | if (textWindow == null || !textWindow.isVisible()) 49 | textWindow = new TextWindow("Results", 50 | "Result\tValue\n", "", twWidth, twHeight); 51 | else { 52 | // set dimensions 53 | textWindow.setSize(twWidth, twHeight); 54 | } 55 | // deactivate the window for now 56 | textWindow.setVisible(false); 57 | // save a reference to the data container 58 | this.container = container; 59 | } 60 | 61 | @Override 62 | public void handleImage(RandomAccessibleInterval image, String name) { 63 | ImagePlus imp = ImageJFunctions.wrapFloat( image, name ); 64 | double max = ImageStatistics.getImageMax( image ).getRealDouble(); 65 | showImage( imp, max ); 66 | } 67 | 68 | @Override 69 | public void handleHistogram(Histogram2D histogram, String name) { 70 | ImagePlus imp = ImageJFunctions.wrapFloat( histogram.getPlotImage(), name ); 71 | double max = ImageStatistics.getImageMax( histogram.getPlotImage() ).getRealDouble(); 72 | showImage( imp, max ); 73 | } 74 | 75 | protected void showImage(ImagePlus imp, double max) { 76 | // set the display range 77 | imp.setDisplayRange(0.0, max); 78 | imp.show(); 79 | } 80 | 81 | @Override 82 | public void handleWarning(Warning warning) { 83 | // no warnings are shown in easy display 84 | } 85 | 86 | @Override 87 | public void handleValue(String name, String value) { 88 | textWindow.getTextPanel().appendLine(name + "\t" 89 | + value + "\n"); 90 | } 91 | 92 | @Override 93 | public void handleValue(String name, double value) { 94 | handleValue(name, value, 3); 95 | } 96 | 97 | @Override 98 | public void handleValue(String name, double value, int decimals) { 99 | handleValue(name, IJ.d2s(value, decimals)); 100 | } 101 | 102 | protected void printTextStatistics(DataContainer container){ 103 | textWindow.getTextPanel().appendLine("Ch1 Mean\t" + container.getMeanCh1() + "\n"); 104 | textWindow.getTextPanel().appendLine("Ch2 Mean\t" + container.getMeanCh2() + "\n"); 105 | textWindow.getTextPanel().appendLine("Ch1 Min\t" + container.getMinCh1() + "\n"); 106 | textWindow.getTextPanel().appendLine("Ch2 Min\t" + container.getMinCh2() + "\n"); 107 | textWindow.getTextPanel().appendLine("Ch1 Max\t" + container.getMaxCh1() + "\n"); 108 | textWindow.getTextPanel().appendLine("Ch2 Max\t" + container.getMaxCh2() + "\n"); 109 | } 110 | 111 | @Override 112 | public void process() { 113 | // print some general information about images 114 | printTextStatistics(container); 115 | // show the results 116 | textWindow.setVisible(true); 117 | IJ.selectWindow("Results"); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/CommutativityTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | 26 | import net.imglib2.RandomAccessibleInterval; 27 | import net.imglib2.TwinCursor; 28 | import net.imglib2.type.logic.BitType; 29 | import net.imglib2.type.numeric.RealType; 30 | import net.imglib2.view.Views; 31 | 32 | import org.junit.Test; 33 | 34 | import sc.fiji.coloc.algorithms.LiICQ; 35 | import sc.fiji.coloc.algorithms.MandersColocalization; 36 | import sc.fiji.coloc.algorithms.MandersColocalization.MandersResults; 37 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 38 | import sc.fiji.coloc.algorithms.PearsonsCorrelation; 39 | import sc.fiji.coloc.algorithms.SpearmanRankCorrelation; 40 | 41 | public class CommutativityTest extends ColocalisationTest { 42 | 43 | /** 44 | * This test makes sure the test images A and B lead to the same results, 45 | * regardless whether they are added in the order A, B or B, A to the data 46 | * container. 47 | * 48 | * @throws MissingPreconditionException 49 | */ 50 | @Test 51 | public void cummutativityTest() throws MissingPreconditionException { 52 | assertCommutativity(zeroCorrelationImageCh1, zeroCorrelationImageCh2, 53 | zeroCorrelationAlwaysTrueMask, zeroCorrelationImageCh1Mean, 54 | zeroCorrelationImageCh2Mean); 55 | assertCommutativity(positiveCorrelationImageCh1, positiveCorrelationImageCh1, 56 | positiveCorrelationAlwaysTrueMask, positiveCorrelationImageCh1Mean, 57 | positiveCorrelationImageCh2Mean); 58 | assertCommutativity(syntheticNegativeCorrelationImageCh1, 59 | syntheticNegativeCorrelationImageCh2, 60 | syntheticNegativeCorrelationAlwaysTrueMask, 61 | syntheticNegativeCorrelationImageCh1Mean, 62 | syntheticNegativeCorrelationImageCh2Mean); 63 | } 64 | 65 | protected static > void assertCommutativity( 66 | RandomAccessibleInterval ch1, RandomAccessibleInterval ch2, 67 | RandomAccessibleInterval mask, double mean1, double mean2) 68 | throws MissingPreconditionException 69 | { 70 | // create a twin value range cursor that iterates over all pixels of the input data 71 | TwinCursor cursor1 = new TwinCursor(ch1.randomAccess(), 72 | ch2.randomAccess(), Views.iterable(mask).localizingCursor()); 73 | TwinCursor cursor2 = new TwinCursor(ch1.randomAccess(), 74 | ch2.randomAccess(), Views.iterable(mask).localizingCursor()); 75 | 76 | // get the Pearson's values 77 | double pearsonsR1 = PearsonsCorrelation.fastPearsons(cursor1); 78 | double pearsonsR2 = PearsonsCorrelation.fastPearsons(cursor2); 79 | // check Pearsons R is the same 80 | assertEquals(pearsonsR1, pearsonsR2, 0.0001); 81 | 82 | // get Li's ICQ values 83 | double icq1 = LiICQ.calculateLisICQ(cursor1, mean1, mean2); 84 | double icq2 = LiICQ.calculateLisICQ(cursor2, mean2, mean1); 85 | // check Li's ICQ is the same 86 | assertEquals(icq1, icq2, 0.0001); 87 | 88 | // get Manders values 89 | MandersColocalization mc = new MandersColocalization(); 90 | MandersResults manders1 = mc.calculateMandersCorrelation(cursor1, 91 | ch1.randomAccess().get().createVariable()); 92 | MandersResults manders2 = mc.calculateMandersCorrelation(cursor2, 93 | ch2.randomAccess().get().createVariable()); 94 | // check Manders m1 and m2 values are the same 95 | assertEquals(manders1.m1, manders2.m2, 0.0001); 96 | assertEquals(manders1.m2, manders2.m1, 0.0001); 97 | 98 | // calculate Spearman's Rank rho values 99 | SpearmanRankCorrelation src = new SpearmanRankCorrelation(); 100 | double rho1 = src.calculateSpearmanRank(cursor1); 101 | double rho2 = src.calculateSpearmanRank(cursor2); 102 | // make sure both ranks are the same 103 | assertEquals(rho1, rho2, 0.0001); 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/KendallTauTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertTrue; 26 | import static org.junit.Assume.assumeTrue; 27 | 28 | import net.imglib2.PairIterator; 29 | import net.imglib2.type.numeric.real.DoubleType; 30 | 31 | import org.junit.Test; 32 | 33 | import sc.fiji.coloc.algorithms.KendallTauRankCorrelation; 34 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 35 | 36 | /** 37 | * Tests the Kendall Tau implementation. 38 | */ 39 | public class KendallTauTest { 40 | 41 | private boolean exhaustive = false; 42 | 43 | @Test 44 | public void testSimple() throws MissingPreconditionException { 45 | assumeTrue(!exhaustive); 46 | // From Armitage P, Berry G. Statistical Methods in Medical Research (3rd edition). Blackwell 1994, p. 466. 47 | assertTau(23.0 / 45.0, new int[] { 4, 10, 3, 1, 9, 2, 6, 7, 8, 5 }, new int[] { 5, 8, 6, 2, 10, 3, 9, 4, 7, 1 }); 48 | } 49 | 50 | @Test 51 | public void testPathological() throws MissingPreconditionException { 52 | assumeTrue(!exhaustive); 53 | assertTau(Double.NaN, new int[] { 1, 1, 1, 1 }, new int[] { 2, 2, 2, 2 }); 54 | } 55 | 56 | @Test 57 | public void testSomeDuplicates() throws MissingPreconditionException { 58 | assumeTrue(!exhaustive); 59 | // for pairs (1, 3), (1, 2), (2, 1), (3, 1), 60 | // n = 4, 61 | // n0 = n * (n - 1) / 2 = 4 * 3 / 2 = 6 62 | // n1 = 1 + 0 + 0 + 0 = 1 63 | // n2 = 1 + 0 + 0 + 0 = 1 64 | // nc = #{ } = 0 65 | // nd = #{ (1, 3)x(2, 1), (1, 3)x(3, 1), (1, 2)x(2, 1), (1, 2)x(3, 1) } = 4 66 | // therefore Tau_b = -4 / sqrt(5 * 5) = -0.8 67 | assertTau(-0.8, new int[] { 1, 1, 2, 3 }, new int[] { 3, 2, 1, 1 }); 68 | } 69 | 70 | private PairIterator pairIterator(final int[] values1, final int[] values2) { 71 | assertEquals(values1.length, values2.length); 72 | return new PairIterator() { 73 | private int i = -1; 74 | private DoubleType ch1 = new DoubleType(); 75 | private DoubleType ch2 = new DoubleType(); 76 | 77 | @Override 78 | public boolean hasNext() { 79 | return i + 1 < values1.length; 80 | } 81 | 82 | @Override 83 | public void reset() { 84 | i = -1; 85 | } 86 | 87 | @Override 88 | public void fwd() { 89 | i++; 90 | } 91 | 92 | @Override 93 | public DoubleType getFirst() { 94 | ch1.set(values1[i]); 95 | return ch1; 96 | } 97 | 98 | @Override 99 | public DoubleType getSecond() { 100 | ch2.set(values2[i]); 101 | return ch2; 102 | } 103 | }; 104 | } 105 | 106 | private void assertTau(final double expected, final int[] values1, final int[] values2) throws MissingPreconditionException { 107 | final PairIterator iter = pairIterator(values1, values2); 108 | assertEquals(expected, KendallTauRankCorrelation.calculateMergeSort(iter), 1e-10); 109 | } 110 | 111 | private int seed; 112 | 113 | private int pseudoRandom() 114 | { 115 | return seed = 3170425 * seed + 132102; 116 | } 117 | 118 | @Test 119 | public void exhaustiveTesting() throws Exception { 120 | assumeTrue(exhaustive); 121 | final int n = 5, m = 10; 122 | final int[] values1 = new int[n], values2 = new int[n]; 123 | for (int i = 0; i < 100; i++) { 124 | for (int j = 0; j < n; j++) { 125 | values1[j] = Math.abs(pseudoRandom()) % m; 126 | values2[j] = Math.abs(pseudoRandom()) % m; 127 | } 128 | final PairIterator iter = pairIterator(values1, values2); 129 | double value1 = KendallTauRankCorrelation.calculateNaive(iter); 130 | iter.reset(); 131 | double value2 = KendallTauRankCorrelation.calculateMergeSort(iter); 132 | if (Double.isNaN(value1)) { 133 | assertTrue("i: " + i + ", value2: " + value2, Double.isInfinite(value2) || Double.isNaN(value2)); 134 | } else { 135 | assertEquals("i: " + i, value1, value2, 1e-10); 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/net/imglib2/PredicateCursor.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package net.imglib2; 23 | 24 | import java.util.NoSuchElementException; 25 | 26 | import net.imglib2.predicate.Predicate; 27 | import net.imglib2.type.Type; 28 | 29 | /** 30 | * The PredicateCursor traverses a whole image but only returns 31 | * those pixels for which the Predicate returns true. There is 32 | * little sense to make this less than a LocalizableCursor 33 | */ 34 | public class PredicateCursor> implements Cursor { 35 | // the condition on which a position is valid 36 | final protected Predicate predicate; 37 | // the cursor driven by the evaluation of the predicate 38 | final protected Cursor cursor; 39 | // indicate if the next element has already been looked up 40 | protected boolean lookedForNext = false; 41 | // true if a next element was found after a look-up 42 | protected boolean hasNext = false; 43 | 44 | public PredicateCursor(final Cursor cursor, 45 | final Predicate predicate) { 46 | this.cursor = cursor; 47 | this.predicate = predicate; 48 | } 49 | 50 | /** 51 | * Walks to the next valid elements and stores them in member 52 | * variables cachedType1 and cachedType2. 53 | * 54 | * @return true if a next element was found, false otherwise 55 | */ 56 | protected boolean findNext() { 57 | boolean found = false; 58 | while( cursor.hasNext() ) { 59 | cursor.fwd(); 60 | if ( predicate.test(cursor) ) { 61 | found = true; 62 | break; 63 | } 64 | } 65 | hasNext = found; 66 | return found; 67 | } 68 | 69 | @Override 70 | public boolean hasNext() { 71 | // did we already check for a next element without doing a fwd()? 72 | if ( lookedForNext ) 73 | return hasNext; 74 | 75 | // indicate that we will already move the cursor to the next element 76 | lookedForNext = true; 77 | 78 | return findNext(); 79 | } 80 | 81 | @Override 82 | public void fwd() { 83 | /* If we did not check for a next valid element before, 84 | * walk to the next element now (if there is any). 85 | */ 86 | if ( ! lookedForNext ) 87 | findNext(); 88 | 89 | /* Since we have manually forwarded the cursor, the cached 90 | * information is not valid any more 91 | */ 92 | lookedForNext = false; 93 | } 94 | 95 | @Override 96 | public void jumpFwd(long num) { 97 | while (num > 0) { 98 | fwd(); 99 | } 100 | } 101 | 102 | @Override 103 | public T next() { 104 | if ( hasNext() ) { 105 | fwd(); 106 | return get(); 107 | } else { 108 | throw new NoSuchElementException(); 109 | } 110 | } 111 | 112 | @Override 113 | public void remove() { 114 | cursor.remove(); 115 | } 116 | 117 | @Override 118 | public void reset() { 119 | cursor.reset(); 120 | lookedForNext = false; 121 | } 122 | 123 | @Override 124 | public double getDoublePosition(int arg0) { 125 | return cursor.getDoublePosition(arg0); 126 | } 127 | 128 | @Override 129 | public float getFloatPosition(int arg0) { 130 | return cursor.getFloatPosition(arg0); 131 | } 132 | 133 | @Override 134 | public void localize(float[] arg0) { 135 | cursor.localize(arg0); 136 | } 137 | 138 | @Override 139 | public void localize(double[] arg0) { 140 | cursor.localize(arg0); 141 | } 142 | 143 | @Override 144 | public int numDimensions() { 145 | return cursor.numDimensions(); 146 | } 147 | 148 | @Override 149 | public T get() { 150 | return cursor.get(); 151 | } 152 | 153 | @Override 154 | public int getIntPosition(int arg0) { 155 | return cursor.getIntPosition(arg0); 156 | } 157 | 158 | @Override 159 | public long getLongPosition(int arg0) { 160 | return cursor.getLongPosition(arg0); 161 | } 162 | 163 | @Override 164 | public void localize(int[] arg0) { 165 | cursor.localize(arg0); 166 | } 167 | 168 | @Override 169 | public void localize(long[] arg0) { 170 | cursor.localize(arg0); 171 | } 172 | 173 | @Override 174 | public Cursor copy() { 175 | return new PredicateCursor( cursor.copyCursor(), predicate ); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/main/java/net/imglib2/TwinCursor.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package net.imglib2; 23 | 24 | import net.imglib2.predicate.MaskPredicate; 25 | import net.imglib2.predicate.Predicate; 26 | import net.imglib2.type.Type; 27 | import net.imglib2.type.logic.BitType; 28 | 29 | /** 30 | * The TwinCursor moves over two images with respect to a mask. The mask 31 | * has to be of the same dimensionality as the images. Position information 32 | * obtained from this class comes from the mask. 33 | * 34 | * @author Johannes Schindelin and Tom Kazimiers 35 | */ 36 | public class TwinCursor> implements Cursor, PairIterator { 37 | final protected PredicateCursor mask; 38 | final protected RandomAccess channel1; 39 | final protected RandomAccess channel2; 40 | /* 41 | * For performance, we keep one position array (to avoid 42 | * having to create a new array in every single step). 43 | */ 44 | final protected long[] position; 45 | /* To avoid calling next() too often */ 46 | protected boolean gotNext; 47 | 48 | public TwinCursor(final RandomAccess channel1, 49 | final RandomAccess channel2, 50 | final Cursor mask) { 51 | final Predicate predicate = new MaskPredicate(); 52 | this.mask = new PredicateCursor(mask, predicate); 53 | this.channel1 = channel1; 54 | this.channel2 = channel2; 55 | position = new long[mask.numDimensions()]; 56 | mask.localize(position); 57 | } 58 | 59 | @Override 60 | final public boolean hasNext() { 61 | gotNext = false; 62 | return mask.hasNext(); 63 | } 64 | 65 | final public void getNext() { 66 | if (gotNext) 67 | return; 68 | mask.next(); 69 | mask.localize(position); 70 | channel1.setPosition(position); 71 | channel2.setPosition(position); 72 | gotNext = true; 73 | } 74 | 75 | @Override 76 | final public T getFirst() { 77 | getNext(); 78 | return channel1.get(); 79 | } 80 | 81 | @Override 82 | final public T getSecond() { 83 | getNext(); 84 | return channel2.get(); 85 | } 86 | 87 | @Override 88 | public void reset() { 89 | gotNext = false; 90 | mask.reset(); 91 | } 92 | 93 | @Override 94 | public void fwd() { 95 | if (hasNext()) 96 | getNext(); 97 | } 98 | 99 | @Override 100 | public void jumpFwd(long arg0) { 101 | throw new UnsupportedOperationException("This method has not been implemented, yet."); 102 | } 103 | 104 | @Override 105 | public T next() { 106 | throw new UnsupportedOperationException("This method has not been implemented, yet."); 107 | } 108 | 109 | @Override 110 | public void remove() { 111 | throw new UnsupportedOperationException("This method has not been implemented, yet."); 112 | } 113 | 114 | @Override 115 | public double getDoublePosition(int arg0) { 116 | return mask.getDoublePosition(arg0); 117 | } 118 | 119 | @Override 120 | public float getFloatPosition(int arg0) { 121 | return mask.getFloatPosition(arg0); 122 | } 123 | 124 | @Override 125 | public void localize(float[] arg0) { 126 | mask.localize(arg0); 127 | } 128 | 129 | @Override 130 | public void localize(double[] arg0) { 131 | mask.localize(arg0); 132 | } 133 | 134 | @Override 135 | public int numDimensions() { 136 | return mask.numDimensions(); 137 | } 138 | 139 | @Override 140 | public T get() { 141 | throw new UnsupportedOperationException("This method has not been implemented, yet."); 142 | } 143 | 144 | @Override 145 | public int getIntPosition(int arg0) { 146 | return mask.getIntPosition(arg0); 147 | } 148 | 149 | @Override 150 | public long getLongPosition(int arg0) { 151 | return mask.getLongPosition(arg0); 152 | } 153 | 154 | @Override 155 | public void localize(int[] arg0) { 156 | mask.localize(arg0); 157 | } 158 | 159 | @Override 160 | public void localize(long[] arg0) { 161 | mask.localize(arg0); 162 | } 163 | 164 | @Override 165 | public Cursor copy() { 166 | throw new UnsupportedOperationException("This method has not been implemented, yet."); 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/SpearmanRankTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | import static org.junit.Assert.assertTrue; 26 | 27 | import net.imglib2.TwinCursor; 28 | import net.imglib2.type.numeric.integer.UnsignedByteType; 29 | import net.imglib2.view.Views; 30 | 31 | import org.junit.Test; 32 | 33 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 34 | import sc.fiji.coloc.algorithms.SpearmanRankCorrelation; 35 | 36 | /** 37 | * This class contains JUnit 4 test cases for the calculation of 38 | * Spearman's Rank Correlation (rho). 39 | * 40 | * @author Leonardo Guizzetti 41 | */ 42 | public class SpearmanRankTest extends ColocalisationTest { 43 | 44 | /** 45 | * Checks Spearman's Rank Correlation rho for positive correlated images. 46 | */ 47 | @Test 48 | public void spearmanPositiveCorrTest() throws MissingPreconditionException { 49 | TwinCursor cursor = new TwinCursor( 50 | positiveCorrelationImageCh1.randomAccess(), 51 | positiveCorrelationImageCh2.randomAccess(), 52 | Views.iterable(positiveCorrelationAlwaysTrueMask).localizingCursor()); 53 | // calculate Spearman's Rank rho value 54 | double rho = new SpearmanRankCorrelation().calculateSpearmanRank(cursor); 55 | // Rho value = 0.5463... 56 | assertTrue(rho > 0.546 && rho < 0.547); 57 | } 58 | 59 | /** 60 | * Checks Spearman's Rank Correlation value for zero correlated images. The rho value 61 | * should be about zero. 62 | */ 63 | @Test 64 | public void spearmanZeroCorrTest() throws MissingPreconditionException { 65 | TwinCursor cursor = new TwinCursor( 66 | zeroCorrelationImageCh1.randomAccess(), 67 | zeroCorrelationImageCh2.randomAccess(), 68 | Views.iterable(zeroCorrelationAlwaysTrueMask).localizingCursor()); 69 | // calculate Spearman's Rank rho value 70 | double rho = new SpearmanRankCorrelation().calculateSpearmanRank(cursor); 71 | // Rho value = -0.11... 72 | assertTrue(Math.abs(rho) < 0.012); 73 | } 74 | 75 | /** 76 | * Checks Spearman's Rank Correlation value for slightly negative correlated synthetic data. 77 | * 78 | */ 79 | @Test 80 | public void statisticsTest() throws MissingPreconditionException { 81 | 82 | double[][] data = new double[][] { 83 | {1,113}, 84 | {2,43}, 85 | {3,11}, 86 | {6,86}, 87 | {5,59}, 88 | {8,47}, 89 | {4,92}, 90 | {0,152}, 91 | {6,23}, 92 | {4,9}, 93 | {7,33}, 94 | {3,69}, 95 | {2,75}, 96 | {9,135}, 97 | {3,30} 98 | }; 99 | int n = data.length; 100 | 101 | final SpearmanRankCorrelation src = new SpearmanRankCorrelation(); 102 | 103 | /* 104 | * Check the arithmetic for the rho calculation. 105 | * Rho is exactly -0.1743 (to 4 decimal points) using the 106 | * exact calculation for Spearman's rho as implemented here. 107 | */ 108 | double rho = src.calculateSpearmanRank(data); 109 | assertEquals(-0.1743, rho, 0.001); 110 | 111 | // check the degrees of freedom calculation ( df = n - 2 ) 112 | int df = 0; 113 | df = src.getSpearmanDF(n); 114 | assertEquals(df, n - 2); 115 | 116 | // check the t-statistic calculation ( t = rho * sqrt( df / (1-rho^2) ) ) 117 | // The t-stat = -0.6382 118 | double tstat = 0.0; 119 | tstat = src.getTStatistic(rho, n); 120 | assertEquals(-0.6382, tstat, 0.001); 121 | } 122 | 123 | /** 124 | * Checks Spearman's Rank Correlation value for synthetic test image. 125 | * This tests the same dataset as the statisticsTest() but tests reading in image 126 | * data, the rank transform, and the calling of the statistics calculation methods. 127 | */ 128 | @Test 129 | public void spearmanSyntheticNegCorrTest() throws MissingPreconditionException { 130 | TwinCursor cursor = new TwinCursor( 131 | syntheticNegativeCorrelationImageCh1.randomAccess(), 132 | syntheticNegativeCorrelationImageCh2.randomAccess(), 133 | Views.iterable(syntheticNegativeCorrelationAlwaysTrueMask).localizingCursor()); 134 | 135 | // calculate Spearman's Rank rho value 136 | double rho = new SpearmanRankCorrelation().calculateSpearmanRank(cursor); 137 | assertTrue((rho > -0.178) && (rho < -0.173)); 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/CostesSignificanceTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertTrue; 25 | 26 | import net.imglib2.RandomAccessibleInterval; 27 | import net.imglib2.type.numeric.real.FloatType; 28 | 29 | import org.junit.Test; 30 | 31 | import sc.fiji.coloc.algorithms.AutoThresholdRegression; 32 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 33 | import sc.fiji.coloc.algorithms.PearsonsCorrelation; 34 | import sc.fiji.coloc.gadgets.DataContainer; 35 | 36 | /** 37 | * This class contains JUnit 4 test cases for the Costes 38 | * statistical significance test. 39 | * 40 | * @author Dan White 41 | * @author Tom Kazimiers 42 | */ 43 | public class CostesSignificanceTest extends ColocalisationTest { 44 | 45 | /** 46 | * This test checks the Costes statistical significance test implementation 47 | * by artificially disturbing known colocalisation. It simulates what Costes 48 | * describes in figure 3 of section "Simulated data". An image representing 49 | * colocalised data is generated. This is put onto two random Perlin noise 50 | * images. A smoothing step is applied after the combination. With the two 51 | * resulting images the Costes calculation and shuffling is done. These steps 52 | * are done multiple times, every time with an increasing percentage of 53 | * colocalised data in the images. As stated by Costes, colocalisation data 54 | * percentages above three percent should be detected (P value > 0.95. This 55 | * is the assertion of this test and checked with every iteration. Percentages 56 | * to test are calculated as percentage = 10^x. Five iterations are done, 57 | * increasing "x" in steps of 0.5, starting at 0. The test uses circles with 58 | * a diameter of 7 as objects (similar to Costes' paper, he uses 7x7 squares). 59 | */ 60 | @Test 61 | public void backgroundNoiseTest() throws MissingPreconditionException { 62 | final int width = 512; 63 | final int height = 512; 64 | final double z = 2.178; 65 | final double scale = 0.1; 66 | final int psf = 3; 67 | final int objectSize = 7; 68 | final double[] sigma = new double[] {3.0,3.0}; 69 | 70 | for (double exp=0; exp < 2.5; exp=exp+0.5) { 71 | double colocPercentage = Math.pow(10, exp); 72 | RandomAccessibleInterval ch1 = TestImageAccessor.producePerlinNoiseImage( 73 | new FloatType(), width, height, z, scale); 74 | RandomAccessibleInterval ch2 = TestImageAccessor.producePerlinNoiseImage( 75 | new FloatType(), width, height, z, scale); 76 | /* calculate the number of colocalised pixels, based on the percentage and the 77 | * space one noise point will take (here 9, because we use 3x3 dots) 78 | */ 79 | int nrColocPixels = (int) ( ( (width * height / 100.0) * colocPercentage ) / (objectSize * objectSize) ); 80 | // create non-smoothed coloc image. add it to the noise images and smooth them 81 | RandomAccessibleInterval colocImg = TestImageAccessor.produceNoiseImage( 82 | width, height, objectSize, nrColocPixels); 83 | TestImageAccessor.combineImages(ch1, colocImg); 84 | ch1 = TestImageAccessor.gaussianSmooth(ch1, sigma); 85 | TestImageAccessor.combineImages(ch2, colocImg); 86 | ch2 = TestImageAccessor.gaussianSmooth(ch2, sigma); 87 | 88 | DataContainer container 89 | = new DataContainer(ch1, ch2, 1, 1, "Channel 1", "Channel 2"); 90 | 91 | PearsonsCorrelation pc 92 | = new PearsonsCorrelation(PearsonsCorrelation.Implementation.Fast); 93 | AutoThresholdRegression atr 94 | = new AutoThresholdRegression(pc); 95 | container.setAutoThreshold(atr); 96 | atr.execute(container); 97 | try { 98 | pc.execute(container); 99 | } 100 | catch (MissingPreconditionException e) { 101 | /* this can happen for random noise data in seldom cases, 102 | * but we are not after this here. The cases that are 103 | * important for Costes work well, but are again sanity 104 | * checked here. 105 | */ 106 | if (pc.getPearsonsCorrelationValue() == Double.NaN) 107 | throw e; 108 | } 109 | 110 | sc.fiji.coloc.algorithms.CostesSignificanceTest costes 111 | = new sc.fiji.coloc.algorithms.CostesSignificanceTest(pc, psf, 10, false); 112 | costes.execute(container); 113 | 114 | // check if we can expect a high P 115 | if (colocPercentage > 3.0) { 116 | double pVal = costes.getCostesPValue(); 117 | assertTrue("Costes P value was " + pVal, pVal > 0.95); 118 | } 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | 6 | org.scijava 7 | pom-scijava 8 | 38.0.1 9 | 10 | 11 | 12 | sc.fiji 13 | Colocalisation_Analysis 14 | 3.1.1-SNAPSHOT 15 | 16 | Coloc 2 17 | Fiji's plugin for colocalization analysis. 18 | https://imagej.net/Coloc_2 19 | 2009 20 | 21 | Fiji 22 | http://fiji.sc/ 23 | 24 | 25 | 26 | GNU General Public License v3+ 27 | http://www.gnu.org/licenses/gpl.html 28 | repo 29 | 30 | 31 | 32 | 33 | 34 | chalkie666 35 | Daniel James White 36 | https://imagej.net/User:White 37 | 38 | lead 39 | reviewer 40 | support 41 | maintainer 42 | 43 | 44 | 45 | tomka 46 | Tom Kazimiers 47 | https://imagej.net/User:Kazimiers 48 | 49 | lead 50 | debugger 51 | reviewer 52 | 53 | 54 | 55 | etarena 56 | Ellen Arena 57 | https://imagej.net/User:Etarena 58 | 59 | developer 60 | debugger 61 | reviewer 62 | support 63 | 64 | 65 | 66 | ctrueden 67 | Curtis Rueden 68 | https://imagej.net/User:Rueden 69 | 70 | maintainer 71 | 72 | 73 | 74 | 75 | 76 | Johannes Schindelin 77 | https://imagej.net/User:Schindelin 78 | founder 79 | dscho 80 | 81 | 82 | Jan Eglinger 83 | https://imagej.net/User:Eglinger 84 | imagejan 85 | 86 | 87 | Leonardo Guizzetti 88 | leonardicus 89 | 90 | 91 | Mark Hiner 92 | https://imagej.net/User:Hinerm 93 | hinerm 94 | 95 | 96 | Jean-Yves Tinevez 97 | https://imagej.net/User:JeanYvesTinevez 98 | tinevez 99 | 100 | 101 | 102 | 103 | 104 | Image.sc Forum 105 | https://forum.image.sc/tag/fiji 106 | 107 | 108 | 109 | 110 | scm:git:https://github.com/fiji/Colocalisation_Analysis 111 | scm:git:git@github.com:fiji/Colocalisation_Analysis 112 | HEAD 113 | https://github.com/fiji/Colocalisation_Analysis 114 | 115 | 116 | github.com 117 | https://github.com/fiji/Colocalisation_Analysis 118 | 119 | 120 | GitHub Actions 121 | https://github.com/fiji/Colocalisation_Analysis/actions 122 | 123 | 124 | 125 | gpl_v3 126 | Fiji developers. 127 | **/script_templates/** 128 | 129 | 130 | sign,deploy-to-scijava 131 | 132 | 133 | 134 | 135 | scijava.public 136 | https://maven.scijava.org/content/groups/public 137 | 138 | 139 | 140 | 141 | 142 | 143 | sc.fiji 144 | fiji-lib 145 | 146 | 147 | 148 | 149 | net.imagej 150 | ij 151 | 152 | 153 | 154 | 155 | net.imglib2 156 | imglib2 157 | 158 | 159 | net.imglib2 160 | imglib2-algorithm 161 | 162 | 163 | net.imglib2 164 | imglib2-ij 165 | 166 | 167 | 168 | 169 | com.itextpdf 170 | itextpdf 171 | 172 | 173 | 174 | junit 175 | junit 176 | test 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/ColocImgLibGadgets.java: -------------------------------------------------------------------------------- 1 | /* 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc; 23 | 24 | import ij.IJ; 25 | import ij.ImagePlus; 26 | import ij.plugin.PlugIn; 27 | 28 | import java.util.ArrayList; 29 | import java.util.Collections; 30 | import java.util.List; 31 | import java.util.Random; 32 | 33 | import net.imglib2.Cursor; 34 | import net.imglib2.algorithm.math.ImageStatistics; 35 | import net.imglib2.img.ImagePlusAdapter; 36 | import net.imglib2.img.Img; 37 | import net.imglib2.img.ImgFactory; 38 | import net.imglib2.img.array.ArrayImgFactory; 39 | import net.imglib2.type.NativeType; 40 | import net.imglib2.type.numeric.RealType; 41 | 42 | public class ColocImgLibGadgets & NativeType> implements PlugIn { 43 | 44 | protected Img img1, img2; 45 | 46 | @Override 47 | public void run(String arg) { 48 | ImagePlus imp1 = IJ.openImage("/Users/dan/Documents/Dresden/ipf/colocPluginDesign/red.tif"); 49 | img1 = ImagePlusAdapter.wrap(imp1); 50 | ImagePlus imp2 = IJ.openImage("/Users/dan/Documents/Dresden/ipf/colocPluginDesign/green.tif"); 51 | img2 = ImagePlusAdapter.wrap(imp2); 52 | 53 | double pearson = calculatePearson(); 54 | 55 | Img ranImg = generateRandomImageStack(img1, new int[] {2,2,1}); 56 | } 57 | 58 | /** 59 | * To randomize blockwise we enumerate the blocks, shuffle that list and 60 | * write the data to their new position based on the shuffled list. 61 | */ 62 | protected Img generateRandomImageStack(Img img, int[] blockDimensions) { 63 | int numberOfDimensions = Math.min(img.numDimensions(), blockDimensions.length); 64 | int numberOfBlocks = 0; 65 | long[] numberOfBlocksPerDimension = new long[numberOfDimensions]; 66 | 67 | for (int i = 0 ; i allTheBlocks = new ArrayList(numberOfBlocks); 76 | for (int i = 0; i cursor = img.cursor(); 81 | 82 | // create factories for new image stack 83 | //ContainerFactory containerFactory = new ImagePlusContainerFactory(); 84 | ImgFactory imgFactory = new ArrayImgFactory(); 85 | //new ImageFactory(cursor.getType(), containerFactory); 86 | 87 | // create a new stack for the random images 88 | final long[] dim = new long[ img.numDimensions() ]; 89 | img.dimensions(dim); 90 | Img randomStack = imgFactory.create(dim, img.firstElement().createVariable()); 91 | 92 | // iterate over image data 93 | while (cursor.hasNext()) { 94 | cursor.fwd(); 95 | T type = cursor.get(); 96 | // type.getRealDouble(); 97 | } 98 | 99 | return randomStack; 100 | } 101 | 102 | protected double calculatePearson() { 103 | Cursor cursor1 = img1.cursor(); 104 | Cursor cursor2 = img2.cursor(); 105 | 106 | double mean1 = getImageMean(img1); 107 | double mean2 = getImageMean(img2); 108 | 109 | // Do some rather simple performance testing 110 | long startTime = System.currentTimeMillis(); 111 | 112 | double pearson = calculatePearson(cursor1, mean1, cursor2, mean2); 113 | 114 | // End performance testing 115 | long finishTime = System.currentTimeMillis(); 116 | long elapsed = finishTime - startTime; 117 | 118 | // print some output to IJ log 119 | IJ.log("mean of ch1: " + mean1 + " " + "mean of ch2: " + mean2); 120 | IJ.log("Pearson's Coefficient " + pearson); 121 | IJ.log("That took: " + elapsed + " ms"); 122 | 123 | return pearson; 124 | } 125 | 126 | protected double calculatePearson(Cursor cursor1, double mean1, Cursor cursor2, double mean2) { 127 | double pearsonDenominator = 0; 128 | double ch1diffSquaredSum = 0; 129 | double ch2diffSquaredSum = 0; 130 | while (cursor1.hasNext() && cursor2.hasNext()) { 131 | cursor1.fwd(); 132 | cursor2.fwd(); 133 | T type1 = cursor1.get(); 134 | double ch1diff = type1.getRealDouble() - mean1; 135 | T type2 = cursor2.get(); 136 | double ch2diff = type2.getRealDouble() - mean2; 137 | pearsonDenominator += ch1diff*ch2diff; 138 | ch1diffSquaredSum += (ch1diff*ch1diff); 139 | ch2diffSquaredSum += (ch2diff*ch2diff); 140 | } 141 | double pearsonNumerator = Math.sqrt(ch1diffSquaredSum * ch2diffSquaredSum); 142 | return pearsonDenominator / pearsonNumerator; 143 | } 144 | 145 | protected double getImageMean(Img img) { 146 | double sum = 0; 147 | Cursor cursor = img.cursor(); 148 | while (cursor.hasNext()) { 149 | cursor.fwd(); 150 | T type = cursor.get(); 151 | sum += type.getRealDouble(); 152 | } 153 | return sum / ImageStatistics.getNumPixels(img); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/LiHistogram2D.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import java.util.EnumSet; 25 | 26 | import net.imglib2.RandomAccessibleInterval; 27 | import net.imglib2.TwinCursor; 28 | import net.imglib2.type.logic.BitType; 29 | import net.imglib2.type.numeric.RealType; 30 | import net.imglib2.view.Views; 31 | 32 | import sc.fiji.coloc.gadgets.DataContainer; 33 | 34 | /** 35 | * Represents the creation of a 2D histogram between two images. 36 | * Channel 1 is set out in x direction, while channel 2 in y direction. 37 | * The value calculation is done after Li. 38 | * @param 39 | */ 40 | public class LiHistogram2D> extends Histogram2D { 41 | // On execution these variables hold the images means 42 | double ch1Mean, ch2Mean; 43 | 44 | // On execution these variables hold the images min and max 45 | double ch1Min, ch1Max, ch2Min, ch2Max; 46 | 47 | // On execution these variables hold the Li's value min and max and their difference 48 | double liMin, liMax, liDiff; 49 | 50 | // On execution these variables hold the scaling factors 51 | double ch1Scaling, ch2Scaling; 52 | 53 | // boolean to test which channel we are using for eg. Li 2D histogram y axis 54 | boolean useCh1 = true; 55 | 56 | public LiHistogram2D(boolean useCh1) { 57 | this("Histogram 2D (Li)", useCh1); 58 | } 59 | 60 | public LiHistogram2D(String title, boolean useCh1) { 61 | this(title, false, useCh1); 62 | } 63 | 64 | public LiHistogram2D(String title, boolean swapChannels, boolean useCh1) { 65 | this(title, swapChannels, useCh1, EnumSet.of(DrawingFlags.Plot)); 66 | } 67 | 68 | public LiHistogram2D(String title, boolean swapChannels, boolean useCh1, EnumSet drawingSettings) { 69 | super(title, swapChannels, drawingSettings); 70 | this.useCh1 = useCh1; 71 | } 72 | 73 | @Override 74 | public void execute(DataContainer container) throws MissingPreconditionException { 75 | ch1Mean = swapChannels ? container.getMeanCh2() : container.getMeanCh1(); 76 | ch2Mean = swapChannels ? container.getMeanCh1() : container.getMeanCh2(); 77 | 78 | ch1Min = getMinCh1(container); 79 | ch1Max = getMaxCh1(container); 80 | 81 | ch2Min = getMinCh2(container); 82 | ch2Max = getMaxCh2(container); 83 | 84 | /* A scaling to the x bins has to be made: 85 | * For that to work we need the min and the 86 | * max value that could occur. 87 | */ 88 | 89 | // get the 2 images and the mask 90 | final RandomAccessibleInterval img1 = getImageCh1(container); 91 | final RandomAccessibleInterval img2 = getImageCh2(container); 92 | final RandomAccessibleInterval mask = container.getMask(); 93 | 94 | // get the cursors for iterating through pixels in images 95 | TwinCursor cursor = new TwinCursor(img1.randomAccess(), 96 | img2.randomAccess(), Views.iterable(mask).localizingCursor()); 97 | 98 | // give liMin and liMax appropriate starting values at the top and bottom of the range 99 | liMin = Double.MAX_VALUE; 100 | liMax = Double.MIN_VALUE; 101 | 102 | // iterate over images 103 | while (cursor.hasNext()) { 104 | cursor.fwd(); 105 | double ch1 = cursor.getFirst().getRealDouble(); 106 | double ch2 = cursor.getSecond().getRealDouble(); 107 | 108 | double productOfDifferenceOfMeans = (ch1Mean - ch1) * (ch2Mean - ch2); 109 | 110 | if (productOfDifferenceOfMeans < liMin) 111 | liMin = productOfDifferenceOfMeans; 112 | if (productOfDifferenceOfMeans > liMax) 113 | liMax = productOfDifferenceOfMeans; 114 | } 115 | liDiff = Math.abs(liMax - liMin); 116 | 117 | generateHistogramData(container); 118 | } 119 | 120 | @Override 121 | protected double getXBinWidth(DataContainer container) { 122 | return (double) xBins / (double)(liDiff + 1); 123 | } 124 | 125 | @Override 126 | protected double getYBinWidth(DataContainer container) { 127 | double max; 128 | if (useCh1) { 129 | max = getMaxCh1(container); 130 | } 131 | else { 132 | max = getMaxCh2(container); 133 | } 134 | return (double) yBins / (double)(max + 1); 135 | } 136 | 137 | @Override 138 | protected int getXValue(double ch1Val, double ch1BinWidth, double ch2Val, double ch2BinWidth) { 139 | /* We want the values to be scaled and shifted by and 140 | * offset in a way that the smallest (possibly negative) 141 | * value is in first bin and highest value in largest bin. 142 | */ 143 | return (int)( (( (ch1Mean - ch1Val) * (ch2Mean - ch2Val)) - liMin) * ch1BinWidth); 144 | } 145 | 146 | @Override 147 | protected int getYValue(double ch1Val, double ch1BinWidth, double ch2Val, double ch2BinWidth) { 148 | if (useCh1) 149 | return (yBins - 1) - (int)(ch1Val * ch2BinWidth); 150 | else 151 | return (yBins - 1) - (int)(ch2Val * ch2BinWidth); 152 | } 153 | 154 | @Override 155 | protected double getXMin(DataContainer container) { 156 | return swapChannels ? (useCh1 ? container.getMinCh1(): container.getMinCh2()) : liMin; 157 | } 158 | 159 | @Override 160 | protected double getXMax(DataContainer container) { 161 | return swapChannels ? (useCh1 ? container.getMaxCh1(): container.getMaxCh2()) : liMax; 162 | } 163 | 164 | @Override 165 | protected double getYMin(DataContainer container) { 166 | return swapChannels ? liMin : (useCh1 ? container.getMinCh1(): container.getMinCh2()); 167 | } 168 | 169 | @Override 170 | protected double getYMax(DataContainer container) { 171 | return swapChannels ? liMax : (useCh1 ? container.getMaxCh1(): container.getMaxCh2()); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/test/java/sc/fiji/coloc/tests/ColocalisationTest.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import net.imglib2.RandomAccessibleInterval; 25 | import net.imglib2.algorithm.math.ImageStatistics; 26 | import net.imglib2.img.Img; 27 | import net.imglib2.type.logic.BitType; 28 | import net.imglib2.type.numeric.RealType; 29 | import net.imglib2.type.numeric.integer.UnsignedByteType; 30 | 31 | import org.junit.After; 32 | import org.junit.Before; 33 | 34 | import sc.fiji.coloc.gadgets.MaskFactory; 35 | 36 | 37 | public abstract class ColocalisationTest { 38 | 39 | // images and meta data for zero correlation 40 | RandomAccessibleInterval zeroCorrelationImageCh1; 41 | RandomAccessibleInterval zeroCorrelationImageCh2; 42 | RandomAccessibleInterval zeroCorrelationAlwaysTrueMask; 43 | double zeroCorrelationImageCh1Mean; 44 | double zeroCorrelationImageCh2Mean; 45 | 46 | // images and meta data for positive correlation test 47 | // and real noisy image Manders' coeff with mask test 48 | RandomAccessibleInterval positiveCorrelationImageCh1; 49 | RandomAccessibleInterval positiveCorrelationImageCh2; 50 | // open mask image as a bit type cursor 51 | Img positiveCorrelationMaskImage; 52 | RandomAccessibleInterval positiveCorrelationAlwaysTrueMask; 53 | double positiveCorrelationImageCh1Mean; 54 | double positiveCorrelationImageCh2Mean; 55 | 56 | // images and meta data for a synthetic negative correlation dataset 57 | RandomAccessibleInterval syntheticNegativeCorrelationImageCh1; 58 | RandomAccessibleInterval syntheticNegativeCorrelationImageCh2; 59 | RandomAccessibleInterval syntheticNegativeCorrelationAlwaysTrueMask; 60 | double syntheticNegativeCorrelationImageCh1Mean; 61 | double syntheticNegativeCorrelationImageCh2Mean; 62 | 63 | // images like in the Manders paper 64 | RandomAccessibleInterval mandersA, mandersB, mandersC, mandersD, 65 | mandersE, mandersF, mandersG, mandersH, mandersI; 66 | RandomAccessibleInterval mandersAlwaysTrueMask; 67 | 68 | /** 69 | * This method is run before every single test is run and is meant to set up 70 | * the images and meta data needed for testing image colocalisation. 71 | */ 72 | @Before 73 | public void setup() { 74 | zeroCorrelationImageCh1 = TestImageAccessor.loadTiffFromJar("/greenZstack.tif"); 75 | zeroCorrelationImageCh1Mean = ImageStatistics.getImageMean(zeroCorrelationImageCh1); 76 | 77 | zeroCorrelationImageCh2 = TestImageAccessor.loadTiffFromJar("/redZstack.tif"); 78 | zeroCorrelationImageCh2Mean = ImageStatistics.getImageMean(zeroCorrelationImageCh2); 79 | 80 | final long[] dimZeroCorrCh1 = new long[ zeroCorrelationImageCh1.numDimensions() ]; 81 | zeroCorrelationImageCh1.dimensions(dimZeroCorrCh1); 82 | zeroCorrelationAlwaysTrueMask = MaskFactory.createMask(dimZeroCorrCh1, true); 83 | 84 | positiveCorrelationImageCh1 = TestImageAccessor.loadTiffFromJar("/colocsample1b-green.tif"); 85 | positiveCorrelationImageCh1Mean = ImageStatistics.getImageMean(positiveCorrelationImageCh1); 86 | 87 | positiveCorrelationImageCh2 = TestImageAccessor.loadTiffFromJar("/colocsample1b-red.tif"); 88 | positiveCorrelationImageCh2Mean = ImageStatistics.getImageMean(positiveCorrelationImageCh2); 89 | 90 | positiveCorrelationMaskImage = TestImageAccessor.loadTiffFromJarAsImg("/colocsample1b-mask.tif"); 91 | 92 | final long[] dimPosCorrCh1 = new long[ positiveCorrelationImageCh1.numDimensions() ]; 93 | positiveCorrelationImageCh1.dimensions(dimPosCorrCh1); 94 | positiveCorrelationAlwaysTrueMask = MaskFactory.createMask(dimPosCorrCh1, true); 95 | 96 | syntheticNegativeCorrelationImageCh1 = TestImageAccessor.loadTiffFromJar("/syntheticNegCh1.tif"); 97 | syntheticNegativeCorrelationImageCh1Mean = ImageStatistics.getImageMean(syntheticNegativeCorrelationImageCh1); 98 | 99 | syntheticNegativeCorrelationImageCh2 = TestImageAccessor.loadTiffFromJar("/syntheticNegCh2.tif"); 100 | syntheticNegativeCorrelationImageCh2Mean = ImageStatistics.getImageMean(syntheticNegativeCorrelationImageCh2); 101 | 102 | final long[] dimSynthNegCorrCh1 = new long[ syntheticNegativeCorrelationImageCh1.numDimensions() ]; 103 | syntheticNegativeCorrelationImageCh1.dimensions(dimSynthNegCorrCh1); 104 | syntheticNegativeCorrelationAlwaysTrueMask = MaskFactory.createMask(dimSynthNegCorrCh1, true); 105 | 106 | mandersA = TestImageAccessor.loadTiffFromJar("/mandersA.tiff"); 107 | mandersB = TestImageAccessor.loadTiffFromJar("/mandersB.tiff"); 108 | mandersC = TestImageAccessor.loadTiffFromJar("/mandersC.tiff"); 109 | mandersD = TestImageAccessor.loadTiffFromJar("/mandersD.tiff"); 110 | mandersE = TestImageAccessor.loadTiffFromJar("/mandersE.tiff"); 111 | mandersF = TestImageAccessor.loadTiffFromJar("/mandersF.tiff"); 112 | mandersG = TestImageAccessor.loadTiffFromJar("/mandersG.tiff"); 113 | mandersH = TestImageAccessor.loadTiffFromJar("/mandersH.tiff"); 114 | mandersI = TestImageAccessor.loadTiffFromJar("/mandersI.tiff"); 115 | 116 | final long[] dimMandersA = new long[ mandersA.numDimensions() ]; 117 | mandersA.dimensions(dimMandersA); 118 | mandersAlwaysTrueMask = MaskFactory.createMask(dimMandersA, true); 119 | } 120 | 121 | /** 122 | * This method is run after every single test and is meant to clean up. 123 | */ 124 | @After 125 | public void cleanup() { 126 | // nothing to do 127 | } 128 | 129 | /** 130 | * Creates a ROI offset array with a distance of 1/4 to the origin 131 | * in each dimension. 132 | */ 133 | protected > long[] createRoiOffset(RandomAccessibleInterval img) { 134 | final long[] offset = new long[ img.numDimensions() ]; 135 | img.dimensions(offset); 136 | for (int i=0; i> long[] createRoiSize(RandomAccessibleInterval img) { 147 | final long[] size = new long[ img.numDimensions() ]; 148 | img.dimensions(size); 149 | for (int i=0; i. 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | 26 | import net.imglib2.Cursor; 27 | import net.imglib2.TwinCursor; 28 | import net.imglib2.converter.Converter; 29 | import net.imglib2.converter.Converters; 30 | import net.imglib2.type.logic.BitType; 31 | import net.imglib2.type.numeric.integer.UnsignedByteType; 32 | import net.imglib2.view.Views; 33 | 34 | import org.junit.Test; 35 | 36 | import sc.fiji.coloc.algorithms.MandersColocalization; 37 | import sc.fiji.coloc.algorithms.MandersColocalization.MandersResults; 38 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 39 | import sc.fiji.coloc.gadgets.ThresholdMode; 40 | 41 | /** 42 | * This class contains JUnit 4 test cases for the calculation 43 | * of Manders' split colocalization coefficients 44 | * 45 | * @author Dan White 46 | * @author Tom Kazimiers 47 | */ 48 | public class MandersColocalizationTest extends ColocalisationTest { 49 | 50 | /** 51 | * This method tests artificial test images as detailed in 52 | * the Manders et al. paper, using above zero threshold (none). 53 | * Note: It is not sensitive to choosing the wrong channel to test for 54 | * above threshold, because threshold is same for both channels: above zero, 55 | * and also that the blobs overlap perfectly or not at all. 56 | */ 57 | @Test 58 | public void mandersPaperImagesTest() throws MissingPreconditionException { 59 | MandersColocalization mc = 60 | new MandersColocalization(); 61 | 62 | TwinCursor cursor; 63 | MandersResults r; 64 | // test A-A combination 65 | cursor = new TwinCursor( 66 | mandersA.randomAccess(), 67 | mandersA.randomAccess(), 68 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 69 | 70 | r = mc.calculateMandersCorrelation(cursor, 71 | mandersA.randomAccess().get().createVariable()); 72 | 73 | assertEquals(1.0d, r.m1, 0.0001); 74 | assertEquals(1.0d, r.m2, 0.0001); 75 | 76 | // test A-B combination 77 | cursor = new TwinCursor( 78 | mandersA.randomAccess(), 79 | mandersB.randomAccess(), 80 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 81 | 82 | r = mc.calculateMandersCorrelation(cursor, 83 | mandersA.randomAccess().get()); 84 | 85 | assertEquals(0.75d, r.m1, 0.0001); 86 | assertEquals(0.75d, r.m2, 0.0001); 87 | 88 | // test A-C combination 89 | cursor = new TwinCursor( 90 | mandersA.randomAccess(), 91 | mandersC.randomAccess(), 92 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 93 | 94 | r = mc.calculateMandersCorrelation(cursor, 95 | mandersA.randomAccess().get()); 96 | 97 | assertEquals(0.5d, r.m1, 0.0001); 98 | assertEquals(0.5d, r.m2, 0.0001); 99 | 100 | // test A-D combination 101 | cursor = new TwinCursor( 102 | mandersA.randomAccess(), 103 | mandersD.randomAccess(), 104 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 105 | 106 | r = mc.calculateMandersCorrelation(cursor, 107 | mandersA.randomAccess().get()); 108 | 109 | assertEquals(0.25d, r.m1, 0.0001); 110 | assertEquals(0.25d, r.m2, 0.0001); 111 | 112 | // test A-E combination 113 | cursor = new TwinCursor( 114 | mandersA.randomAccess(), 115 | mandersE.randomAccess(), 116 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 117 | 118 | r = mc.calculateMandersCorrelation(cursor, 119 | mandersA.randomAccess().get()); 120 | 121 | assertEquals(0.0d, r.m1, 0.0001); 122 | assertEquals(0.0d, r.m2, 0.0001); 123 | 124 | // test A-F combination 125 | cursor = new TwinCursor( 126 | mandersA.randomAccess(), 127 | mandersF.randomAccess(), 128 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 129 | 130 | r = mc.calculateMandersCorrelation(cursor, 131 | mandersA.randomAccess().get()); 132 | 133 | assertEquals(0.25d, r.m1, 0.0001); 134 | assertEquals(0.3333d, r.m2, 0.0001); 135 | 136 | // test A-G combination.firstElement( 137 | cursor = new TwinCursor( 138 | mandersA.randomAccess(), 139 | mandersG.randomAccess(), 140 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 141 | 142 | r = mc.calculateMandersCorrelation(cursor, 143 | mandersA.randomAccess().get()); 144 | 145 | assertEquals(0.25d, r.m1, 0.0001); 146 | assertEquals(0.50d, r.m2, 0.0001); 147 | 148 | // test A-H combination 149 | cursor = new TwinCursor( 150 | mandersA.randomAccess(), 151 | mandersH.randomAccess(), 152 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 153 | 154 | r = mc.calculateMandersCorrelation(cursor, 155 | mandersA.randomAccess().get()); 156 | 157 | assertEquals(0.25d, r.m1, 0.0001); 158 | assertEquals(1.00d, r.m2, 0.0001); 159 | 160 | // test A-I combination 161 | cursor = new TwinCursor( 162 | mandersA.randomAccess(), 163 | mandersI.randomAccess(), 164 | Views.iterable(mandersAlwaysTrueMask).localizingCursor()); 165 | 166 | r = mc.calculateMandersCorrelation(cursor, 167 | mandersA.randomAccess().get()); 168 | 169 | assertEquals(0.083d, r.m1, 0.001); 170 | assertEquals(0.75d, r.m2, 0.0001); 171 | } 172 | 173 | /** 174 | * This method tests real experimental noisy but 175 | * biologically perfectly colocalized test images, 176 | * using previously calculated autothresholds (.above mode) 177 | * Amongst other things, hopefully it is sensitive to 178 | * choosing the wrong channel to test for above threshold 179 | */ 180 | @Test 181 | public void mandersRealNoisyImagesTest() throws MissingPreconditionException { 182 | 183 | MandersColocalization mrnc = 184 | new MandersColocalization(); 185 | 186 | // test biologically perfect but noisy image coloc combination 187 | // this cast is bad, so use Views.iterable instead. 188 | //Cursor mask = Converters.convert((IterableInterval) positiveCorrelationMaskImage, 189 | Cursor mask = Converters.convert(Views.iterable(positiveCorrelationMaskImage), 190 | new Converter() { 191 | 192 | @Override 193 | public void convert(UnsignedByteType arg0, BitType arg1) { 194 | arg1.set(arg0.get() > 0); 195 | } 196 | }, new BitType()).cursor(); 197 | 198 | TwinCursor twinCursor; 199 | MandersResults r; 200 | // Manually set the thresholds for ch1 and ch2 with the results from a 201 | // Costes Autothreshold using bisection implementation of regression, of the images used 202 | UnsignedByteType thresholdCh1 = new UnsignedByteType(); 203 | thresholdCh1.setInteger(70); 204 | UnsignedByteType thresholdCh2 = new UnsignedByteType(); 205 | thresholdCh2.setInteger(53); 206 | //Set the threshold mode 207 | ThresholdMode tMode; 208 | tMode = ThresholdMode.Above; 209 | // Set the TwinCursor to have the mask image channel, and 2 images. 210 | twinCursor = new TwinCursor( 211 | positiveCorrelationImageCh1.randomAccess(), 212 | positiveCorrelationImageCh2.randomAccess(), 213 | mask); 214 | 215 | // Use the constructor that takes ch1 and ch2 autothresholds and threshold mode. 216 | r = mrnc.calculateMandersCorrelation(twinCursor, thresholdCh1, thresholdCh2, tMode); 217 | 218 | assertEquals(0.705665d, r.m1, 0.000001); 219 | assertEquals(0.724752d, r.m2, 0.000001); 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/gadgets/MaskFactory.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.gadgets; 23 | 24 | import java.util.Arrays; 25 | 26 | import net.imglib2.Cursor; 27 | import net.imglib2.RandomAccess; 28 | import net.imglib2.RandomAccessibleInterval; 29 | import net.imglib2.img.ImgFactory; 30 | import net.imglib2.img.array.ArrayImgFactory; 31 | import net.imglib2.type.logic.BitType; 32 | import net.imglib2.type.numeric.RealType; 33 | import net.imglib2.view.Views; 34 | 35 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 36 | 37 | public class MaskFactory { 38 | 39 | public enum CombinationMode { 40 | AND, OR, NONE 41 | } 42 | 43 | /** 44 | * Create a new mask image without any specific content, but with 45 | * a defined size. 46 | */ 47 | public static RandomAccessibleInterval createMask(long[] dim) { 48 | ImgFactory< BitType > imgFactory = new ArrayImgFactory< BitType >(); 49 | return imgFactory.create(dim, new BitType()); 50 | } 51 | 52 | /** 53 | * Create a new mask image with a defined size and preset content. 54 | */ 55 | public static RandomAccessibleInterval createMask(long[] dim, boolean val) { 56 | RandomAccessibleInterval mask = createMask(dim); 57 | 58 | for (BitType t : Views.iterable(mask)) 59 | t.set(val); 60 | 61 | return mask; 62 | } 63 | 64 | /** 65 | * Create a new mask image with a defined size and preset content. 66 | * @throws MissingPreconditionException 67 | */ 68 | public static RandomAccessibleInterval createMask(long[] dim, long[] roiOffset, long[] roiDim) 69 | throws MissingPreconditionException { 70 | if (dim.length != roiOffset.length || dim.length != roiDim.length) { 71 | throw new MissingPreconditionException("The dimensions of the mask as well as the ROIs and his offset must be the same."); 72 | } 73 | 74 | final RandomAccessibleInterval mask = createMask(dim); 75 | final int dims = mask.numDimensions(); 76 | final long[] pos = new long[dims]; 77 | 78 | 79 | // create an array with the max corner of the ROI 80 | final long[] roiOffsetMax = new long[dims]; 81 | for (int i=0; i cursor = Views.iterable(mask).localizingCursor(); 85 | while ( cursor.hasNext() ) { 86 | cursor.fwd(); 87 | cursor.localize(pos); 88 | boolean valid = true; 89 | // test if the current position is contained in the ROI 90 | for(int i=0; i= roiOffset[i] && pos[i] < roiOffsetMax[i]; 92 | cursor.get().set(valid); 93 | } 94 | 95 | return mask; 96 | } 97 | 98 | /** 99 | * Create a new mask based on a threshold condition for two images. 100 | */ 101 | public static> RandomAccessibleInterval createMask( 102 | RandomAccessibleInterval ch1, RandomAccessibleInterval ch2, 103 | T threshold1, T threshold2, ThresholdMode tMode, CombinationMode cMode) { 104 | 105 | final long[] dims = new long[ ch1.numDimensions() ]; 106 | ch1.dimensions(dims); 107 | RandomAccessibleInterval mask = createMask(dims); 108 | Cursor cursor1 = Views.iterable(ch1).cursor(); 109 | Cursor cursor2 = Views.iterable(ch2).cursor(); 110 | Cursor maskCursor = Views.iterable(mask).cursor(); 111 | 112 | while (cursor1.hasNext() && cursor2.hasNext() && maskCursor.hasNext()) { 113 | cursor1.fwd(); 114 | cursor2.fwd(); 115 | maskCursor.fwd(); 116 | 117 | boolean ch1Valid, ch2Valid; 118 | 119 | T data1 = cursor1.get(); 120 | T data2 = cursor2.get(); 121 | 122 | // get relation to threshold 123 | if (tMode == ThresholdMode.Above) { 124 | ch1Valid = data1.compareTo(threshold1) > 0; 125 | ch2Valid = data2.compareTo(threshold2) > 0; 126 | } else if (tMode == ThresholdMode.Below) { 127 | ch1Valid = data1.compareTo(threshold1) < 0; 128 | ch2Valid = data2.compareTo(threshold2) < 0; 129 | } else { 130 | throw new UnsupportedOperationException(); 131 | } 132 | 133 | BitType maskData = maskCursor.get(); 134 | 135 | // combine the results into mask 136 | if (cMode == CombinationMode.AND) { 137 | maskData.set( ch1Valid && ch2Valid ); 138 | } else if (cMode == CombinationMode.OR) { 139 | maskData.set( ch1Valid || ch2Valid ); 140 | } else if (cMode == CombinationMode.NONE) { 141 | maskData.set( !(ch1Valid || ch2Valid) ); 142 | } else { 143 | throw new UnsupportedOperationException(); 144 | } 145 | } 146 | 147 | return mask; 148 | } 149 | 150 | /** 151 | * Creates a new mask of the given dimensions, based on the image data 152 | * in the passed image. If the requested dimensionality is higher than 153 | * what is available in the data, the data gets repeated in the higher 154 | * dimensions. 155 | * 156 | * @param dim The dimensions of the new mask image 157 | * @param origMask The image from which the mask should be created from 158 | */ 159 | public static> RandomAccessibleInterval createMask( 160 | final long[] dim, final RandomAccessibleInterval origMask) { 161 | final RandomAccessibleInterval mask = createMask(dim); 162 | final long[] origDim = new long[ origMask.numDimensions() ]; 163 | origMask.dimensions(origDim); 164 | 165 | // test if original mask and new mask have same dimensions 166 | if (Arrays.equals(dim, origDim)) { 167 | // copy the input image to the mask output image 168 | Cursor origCursor = Views.iterable(origMask).localizingCursor(); 169 | RandomAccess maskCursor = mask.randomAccess(); 170 | while (origCursor.hasNext()) { 171 | origCursor.fwd(); 172 | maskCursor.setPosition(origCursor); 173 | boolean value = origCursor.get().getRealDouble() > 0.001; 174 | maskCursor.get().set(value); 175 | } 176 | } else if (dim.length > origDim.length) { 177 | // sanity check 178 | for (int i=0; i origCursor = Views.iterable(origMask).localizingCursor(); 185 | RandomAccess maskCursor = mask.randomAccess(); 186 | final long[] pos = new long[ origMask.numDimensions() ]; 187 | // iterate over the original mask 188 | while (origCursor.hasNext()) { 189 | origCursor.fwd(); 190 | origCursor.localize(pos); 191 | boolean value = origCursor.get().getRealDouble() > 0.001; 192 | // set available (lower dimensional) position information 193 | for (int i=0; i. 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.tests; 23 | 24 | import static org.junit.Assert.assertEquals; 25 | 26 | import net.imglib2.RandomAccessibleInterval; 27 | import net.imglib2.TwinCursor; 28 | import net.imglib2.type.logic.BitType; 29 | import net.imglib2.type.numeric.integer.UnsignedByteType; 30 | import net.imglib2.type.numeric.real.FloatType; 31 | import net.imglib2.view.Views; 32 | 33 | import org.junit.Test; 34 | 35 | import sc.fiji.coloc.algorithms.MissingPreconditionException; 36 | import sc.fiji.coloc.algorithms.PearsonsCorrelation; 37 | import sc.fiji.coloc.algorithms.PearsonsCorrelation.Implementation; 38 | import sc.fiji.coloc.gadgets.MaskFactory; 39 | 40 | /** 41 | * This class contains JUnit 4 test cases for the Pearson's correlation 42 | * implementation. 43 | * 44 | * @author Dan White 45 | * @author Tom Kazimiers 46 | */ 47 | public class PearsonsCorrelationTest extends ColocalisationTest { 48 | 49 | /** 50 | * Tests if the fast implementation of Pearson's correlation with two 51 | * zero correlated images produce a Pearson's R value of about zero. 52 | */ 53 | @Test 54 | public void fastPearsonsZeroCorrTest() throws MissingPreconditionException { 55 | // create a twin value range cursor that iterates over all pixels of the input data 56 | TwinCursor cursor = new TwinCursor( 57 | zeroCorrelationImageCh1.randomAccess(), 58 | zeroCorrelationImageCh2.randomAccess(), 59 | Views.iterable(zeroCorrelationAlwaysTrueMask).localizingCursor()); 60 | // get the Pearson's value 61 | double pearsonsR = PearsonsCorrelation.fastPearsons(cursor); 62 | // check Pearsons R is close to zero 63 | assertEquals(0.0, pearsonsR, 0.05); 64 | } 65 | 66 | /** 67 | * Tests if the fast implementation of Pearson's correlation with two 68 | * positive correlated images produce a Pearson's R value of about 0.75. 69 | */ 70 | @Test 71 | public void fastPearsonsPositiveCorrTest() throws MissingPreconditionException { 72 | // create a twin value range cursor that iterates over all pixels of the input data 73 | TwinCursor cursor = new TwinCursor( 74 | positiveCorrelationImageCh1.randomAccess(), 75 | positiveCorrelationImageCh2.randomAccess(), 76 | Views.iterable(positiveCorrelationAlwaysTrueMask).localizingCursor()); 77 | // get the Pearson's value 78 | double pearsonsR = PearsonsCorrelation.fastPearsons(cursor); 79 | // check Pearsons R is close to 0.75 80 | assertEquals(0.75, pearsonsR, 0.01); 81 | } 82 | 83 | /** 84 | * Tests if the classic implementation of Pearson's correlation with two 85 | * zero correlated images produce a Pearson's R value of about zero. 86 | */ 87 | @Test 88 | public void classicPearsonsZeroCorrTest() throws MissingPreconditionException { 89 | // create a twin value range cursor that iterates over all pixels of the input data 90 | TwinCursor cursor = new TwinCursor( 91 | zeroCorrelationImageCh1.randomAccess(), 92 | zeroCorrelationImageCh2.randomAccess(), 93 | Views.iterable(zeroCorrelationAlwaysTrueMask).localizingCursor()); 94 | // get the Pearson's value 95 | double pearsonsR = PearsonsCorrelation 96 | .classicPearsons(cursor, zeroCorrelationImageCh1Mean, zeroCorrelationImageCh2Mean); 97 | // check Pearsons R is close to zero 98 | assertEquals(0.0, pearsonsR, 0.05); 99 | } 100 | 101 | /** 102 | * Tests if the classic implementation of Pearson's correlation with two 103 | * positive correlated images produce a Pearson's R value of about 0.75. 104 | */ 105 | @Test 106 | public void classicPearsonsPositiveCorrTest() throws MissingPreconditionException { 107 | // create a twin value range cursor that iterates over all pixels of the input data 108 | TwinCursor cursor = new TwinCursor( 109 | positiveCorrelationImageCh1.randomAccess(), 110 | positiveCorrelationImageCh2.randomAccess(), 111 | Views.iterable(positiveCorrelationAlwaysTrueMask).localizingCursor()); 112 | // get the Pearson's value 113 | double pearsonsR = PearsonsCorrelation 114 | .classicPearsons(cursor, positiveCorrelationImageCh1Mean, positiveCorrelationImageCh2Mean); 115 | // check Pearsons R is close to 0.75 116 | assertEquals(0.75, pearsonsR, 0.01); 117 | } 118 | 119 | /** 120 | * Tests Pearson's correlation stays close to zero for image pairs with the same mean and spread 121 | * of randomized pixel values around that mean. 122 | */ 123 | @Test 124 | public void differentMeansTest() throws MissingPreconditionException { 125 | final double initialMean = 0.2; 126 | final double spread = 0.1; 127 | final double[] sigma = new double[] {3.0, 3.0}; 128 | 129 | RandomAccessibleInterval mask = MaskFactory.createMask(new long[] {512, 512}, true); 130 | 131 | for (double mean = initialMean; mean < 1; mean += spread) { 132 | RandomAccessibleInterval ch1 = TestImageAccessor.produceMeanBasedNoiseImage(new FloatType(), 133 | 512, 512, mean, spread, sigma, 0x01234567); 134 | RandomAccessibleInterval ch2 = TestImageAccessor.produceMeanBasedNoiseImage(new FloatType(), 135 | 512, 512, mean, spread, sigma, 0x98765432); 136 | 137 | // create a twin value range cursor that iterates over all pixels of the input data 138 | TwinCursor cursor = new TwinCursor(ch1.randomAccess(), 139 | ch2.randomAccess(), Views.iterable(mask).localizingCursor()); 140 | double resultFast = PearsonsCorrelation.fastPearsons(cursor); 141 | assertEquals(0.0, resultFast, 0.1); 142 | 143 | /* This test will throw Missing PreconsitionException, as the means are the same 144 | * which causes a numerical problem in the classic implementation of Pearson's 145 | * double resultClassic = PearsonsCorrelation.classicPearsons(cursor, mean, mean); 146 | * assertTrue(Math.abs(resultClassic) < 0.1); 147 | */ 148 | } 149 | } 150 | 151 | /** 152 | * The 1993 paper of Manders et. al about colocalization presents an own 153 | * method and testing data for it. For that testing data there are 154 | * Pearson colocalization numbers, too, and these get tested in this test. 155 | * @throws MissingPreconditionException 156 | */ 157 | @Test 158 | public void mandersPaperImagesTest() throws MissingPreconditionException { 159 | PearsonsCorrelation pc = 160 | new PearsonsCorrelation(Implementation.Classic); 161 | double r; 162 | 163 | // test A-A combination 164 | r = pc.calculatePearsons(mandersA, mandersA, mandersAlwaysTrueMask); 165 | assertEquals(1.0d, r, 0.01); 166 | 167 | // test A-B combination 168 | r = pc.calculatePearsons(mandersA, mandersB, mandersAlwaysTrueMask); 169 | assertEquals(0.72d, r, 0.01); 170 | 171 | // test A-C combination 172 | r = pc.calculatePearsons(mandersA, mandersC, mandersAlwaysTrueMask); 173 | assertEquals(0.44d, r, 0.01); 174 | 175 | // test A-D combination 176 | r = pc.calculatePearsons(mandersA, mandersD, mandersAlwaysTrueMask); 177 | assertEquals(0.16d, r, 0.01); 178 | 179 | // test A-E combination 180 | r = pc.calculatePearsons(mandersA, mandersE, mandersAlwaysTrueMask); 181 | assertEquals(-0.12d, r, 0.01); 182 | 183 | // test A-F combination 184 | r = pc.calculatePearsons(mandersA, mandersF, mandersAlwaysTrueMask); 185 | assertEquals(0.22d, r, 0.01); 186 | 187 | // test A-G combination 188 | r = pc.calculatePearsons(mandersA, mandersG, mandersAlwaysTrueMask); 189 | assertEquals(0.30d, r, 0.01); 190 | 191 | // test A-H combination 192 | r = pc.calculatePearsons(mandersA, mandersH, mandersAlwaysTrueMask); 193 | assertEquals(0.48d, r, 0.01); 194 | 195 | // test A-I combination 196 | r = pc.calculatePearsons(mandersA, mandersI, mandersAlwaysTrueMask); 197 | assertEquals(0.23d, r, 0.01); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/InputCheck.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import ij.IJ; 25 | 26 | import net.imglib2.RandomAccessibleInterval; 27 | import net.imglib2.TwinCursor; 28 | import net.imglib2.type.logic.BitType; 29 | import net.imglib2.type.numeric.RealType; 30 | import net.imglib2.view.Views; 31 | 32 | import sc.fiji.coloc.gadgets.DataContainer; 33 | import sc.fiji.coloc.gadgets.DataContainer.MaskType; 34 | import sc.fiji.coloc.results.ResultHandler; 35 | 36 | /** 37 | * This class implements some basic checks for the input image data. For 38 | * instance: Is the percentage of zero-zero or saturated pixels too high? Also, 39 | * we get basic image properties / stats from imglib2, and also the 40 | * colocalization job name from the DataContainer and allow implementations of 41 | * ResultHandler to report them. 42 | */ 43 | public class InputCheck> extends Algorithm { 44 | /* the maximum allowed ratio between zero-zero and 45 | * normal pixels 46 | */ 47 | protected final double maxZeroZeroRatio = 0.1f; 48 | /* the maximum allowed ratio between saturated and 49 | * normal pixels within a channel 50 | */ 51 | protected final double maxSaturatedRatio = 0.1f; 52 | // the zero-zero pixel ratio 53 | double zeroZeroPixelRatio; 54 | // the saturated pixel ratio of channel 1 55 | double saturatedRatioCh1; 56 | // the saturated pixel ratio of channel 2 57 | double saturatedRatioCh2; 58 | 59 | // the coloc job name 60 | String colocJobName; 61 | 62 | // general image stats/parameters/values 63 | double ch1Max; 64 | double ch2Max; 65 | double ch1Min; 66 | double ch2Min; 67 | double ch1Mean; 68 | double ch2Mean; 69 | double ch1Integral; 70 | double ch2Integral; 71 | 72 | // Mask infos 73 | MaskType maskType; 74 | double maskID; 75 | 76 | public InputCheck() { 77 | super("input data check"); 78 | } 79 | 80 | @Override 81 | public void execute(DataContainer container) 82 | throws MissingPreconditionException { 83 | // get the 2 images and the mask 84 | final RandomAccessibleInterval img1 = container.getSourceImage1(); 85 | final RandomAccessibleInterval img2 = container.getSourceImage2(); 86 | final RandomAccessibleInterval mask = container.getMask(); 87 | 88 | // get the cursors for iterating through pixels in images 89 | TwinCursor cursor = new TwinCursor(img1.randomAccess(), 90 | img2.randomAccess(), Views.iterable(mask).cursor()); 91 | 92 | // get various general image properties/stats/values from the DataContainer 93 | ch1Max = container.getMaxCh1(); 94 | ch2Max = container.getMaxCh2(); 95 | ch1Min = container.getMinCh1(); 96 | ch2Min = container.getMinCh2(); 97 | ch1Mean = container.getMeanCh1(); 98 | ch2Mean = container.getMeanCh2(); 99 | ch1Integral = container.getIntegralCh1(); 100 | ch2Integral = container.getIntegralCh2(); 101 | 102 | // get the info about the mask/ROI being used or not. 103 | maskType = container.getMaskType(); 104 | maskID = (double)container.getMaskID(); 105 | 106 | // the total amount of pixels that have been taken into consideration 107 | long N = 0; 108 | // the number of pixels that are zero in both channels 109 | long Nzero = 0; 110 | // the number of ch1 pixels with the maximum ch1 value; 111 | long NsaturatedCh1 = 0; 112 | // the number of ch2 pixels with the maximum ch2 value; 113 | long NsaturatedCh2 = 0; 114 | 115 | while (cursor.hasNext()) { 116 | cursor.fwd(); 117 | double ch1 = cursor.getFirst().getRealDouble(); 118 | double ch2 = cursor.getSecond().getRealDouble(); 119 | 120 | // is the current pixels combination a zero-zero pixel? 121 | if (Math.abs(ch1 + ch2) < 0.00001) 122 | Nzero++; 123 | 124 | // is the current pixel of channel one saturated? 125 | if (Math.abs(ch1Max - ch1) < 0.00001) 126 | NsaturatedCh1++; 127 | 128 | // is the current pixel of channel one saturated? 129 | if (Math.abs(ch2Max - ch2) < 0.00001) 130 | NsaturatedCh2++; 131 | 132 | N++; 133 | } 134 | 135 | // calculate results 136 | double zeroZeroRatio = (double)Nzero / (double)N; 137 | // for channel wise ratios we have to use half of the total pixel amount 138 | double ch1SaturatedRatio = (double)NsaturatedCh1 / ( (double)N *0.5); 139 | double ch2SaturatedRatio = (double)NsaturatedCh2 / ( (double)N * 0.5); 140 | 141 | /* save results 142 | * Percentage results need to be multiplied by 100 143 | */ 144 | zeroZeroPixelRatio = zeroZeroRatio * 100.0; 145 | saturatedRatioCh1 = ch1SaturatedRatio * 100.0; 146 | saturatedRatioCh2 = ch2SaturatedRatio * 100.0; 147 | 148 | // get job name so the ResultsHandler implementation can have it. 149 | colocJobName = container.getJobName(); 150 | 151 | // add warnings if images contain negative values 152 | if (ch1Min < 0 || ch2Min < 0) { 153 | addWarning("Negative minimum pixel value found.", 154 | "The minimum pixel value in at least one of the channels is negative. Negative values might break the logic of some analysis methods by breaking a basic basic assumption: The pixel value is assumed to be proportional to the number of photons detected in a pixel. Negative photon counts make no physical sense. Set negative pixel values to zero, or shift pixel intensities higher so there are no negative pixel values."); 155 | } 156 | 157 | // add warnings if values are not in tolerance range 158 | if ( Math.abs(zeroZeroRatio) > maxZeroZeroRatio ) { 159 | 160 | addWarning("Zero-zero ratio too high", 161 | "The ratio between zero-zero pixels and other pixels is large: " 162 | + IJ.d2s(zeroZeroRatio, 2) + ". Maybe you should use a ROI."); 163 | } 164 | if ( Math.abs(ch1SaturatedRatio) > maxSaturatedRatio ) { 165 | addWarning("Saturated ch1 ratio too high", 166 | "The ratio between saturated pixels and other pixels in channel one is large: " 167 | + IJ.d2s(maxSaturatedRatio, 2) + ". Maybe you should use a ROI."); 168 | } 169 | if ( Math.abs(ch1SaturatedRatio) > maxSaturatedRatio ) { 170 | addWarning("Saturated ch2 ratio too high", 171 | "The ratio between saturated pixels and other pixels in channel two is large: " 172 | + IJ.d2s(maxSaturatedRatio, 2) + ". Maybe you should use a ROI."); 173 | } 174 | } 175 | 176 | @Override 177 | public void processResults(ResultHandler handler) { 178 | super.processResults(handler); 179 | 180 | // Let us have a ValueResult for the colocalisation analysis job name: 181 | // A ValueResult can be two Strings (or a string and a numerical value) 182 | // We want to keep the jobName close to all the value results 183 | // so they get shown together by whatever implementation of ResultsHandler. 184 | handler.handleValue("Coloc_Job_Name", colocJobName); 185 | 186 | // Make the ResultsHander implementation deal with the input check results. 187 | handler.handleValue("% zero-zero pixels", zeroZeroPixelRatio, 2); 188 | handler.handleValue("% saturated ch1 pixels", saturatedRatioCh1, 2); 189 | handler.handleValue("% saturated ch2 pixels", saturatedRatioCh2, 2); 190 | 191 | // Make the ResultsHander implementation deal with the images' 192 | // stats/parameters/values 193 | handler.handleValue("Channel 1 Max", ch1Max, 3); 194 | handler.handleValue("Channel 2 Max", ch2Max, 3); 195 | handler.handleValue("Channel 1 Min", ch1Min, 3); 196 | handler.handleValue("Channel 2 Min", ch2Min, 3); 197 | handler.handleValue("Channel 1 Mean", ch1Mean, 3); 198 | handler.handleValue("Channel 2 Mean", ch2Mean, 3); 199 | handler.handleValue("Channel 1 Integrated (Sum) Intensity", ch1Integral, 3); 200 | handler.handleValue("Channel 2 Integrated (Sum) Intensity", ch2Integral, 3); 201 | 202 | // Make the ResultsHandler implementation deal with the images' 203 | // ROI or mask or lack thereof, so the user knows what they used. 204 | handler.handleValue("Mask Type Used", maskType.label()); 205 | handler.handleValue("Mask ID Used", maskID, 0); 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/main/java/net/imglib2/algorithm/math/ImageStatistics.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | 23 | /* 24 | * Copyright (c) 2009--2010, Stephan Preibisch & Stephan Saalfeld 25 | * All rights reserved. 26 | * 27 | * Redistribution and use in source and binary forms, with or without 28 | * modification, are permitted provided that the following conditions are met: 29 | * 30 | * Redistributions of source code must retain the above copyright notice, this 31 | * list of conditions and the following disclaimer. Redistributions in binary 32 | * form must reproduce the above copyright notice, this list of conditions and 33 | * the following disclaimer in the documentation and/or other materials 34 | * provided with the distribution. Neither the name of the Fiji project nor 35 | * the names of its contributors may be used to endorse or promote products 36 | * derived from this software without specific prior written permission. 37 | * 38 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 39 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 40 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 41 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 42 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 43 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 44 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 45 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 46 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 47 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 48 | * POSSIBILITY OF SUCH DAMAGE. 49 | */ 50 | 51 | package net.imglib2.algorithm.math; 52 | 53 | import net.imglib2.Cursor; 54 | import net.imglib2.RandomAccessibleInterval; 55 | import net.imglib2.TwinCursor; 56 | import net.imglib2.type.Type; 57 | import net.imglib2.type.logic.BitType; 58 | import net.imglib2.type.numeric.RealType; 59 | import net.imglib2.util.RealSum; 60 | import net.imglib2.view.Views; 61 | 62 | /** 63 | * This class contains some basic image statistics 64 | * calculations. 65 | * 66 | * @author Dan White 67 | * @author Tom Kazimiers 68 | */ 69 | public class ImageStatistics { 70 | /** 71 | * Calculates the number of pixels in the image. 72 | * 73 | * @param img The image to calculate the mean of 74 | * @return The mean of the image passed 75 | */ 76 | final public static > long getNumPixels( 77 | final RandomAccessibleInterval img ) 78 | { 79 | long numPixels = 1; 80 | for (int d=0; d> double getImageMean( 94 | final RandomAccessibleInterval img, 95 | final RandomAccessibleInterval mask ) 96 | { 97 | final RealSum sum = new RealSum(); 98 | long numPixels = 0; 99 | // create cursor to walk an image with respect to a mask 100 | final TwinCursor cursor = new TwinCursor( 101 | img.randomAccess(), 102 | img.randomAccess(), 103 | Views.iterable(mask).localizingCursor()); 104 | while (cursor.hasNext()) { 105 | sum.add(cursor.getFirst().getRealDouble()); 106 | ++numPixels; 107 | } 108 | 109 | return sum.getSum() / numPixels; 110 | } 111 | 112 | /** 113 | * Calculates the mean of an image. 114 | * 115 | * @param img The image to calculate the mean of 116 | * @return The mean of the image passed 117 | */ 118 | final public static > double getImageMean( 119 | final RandomAccessibleInterval img ) 120 | { 121 | // Count all values using the RealSum class. 122 | // It prevents numerical instabilities when adding up millions of pixels 123 | RealSum realSum = new RealSum(); 124 | long count = 0; 125 | 126 | for ( final T type : Views.iterable(img) ) 127 | { 128 | realSum.add( type.getRealDouble() ); 129 | ++count; 130 | } 131 | 132 | return realSum.getSum() / count; 133 | } 134 | 135 | /** 136 | * Calculates the integral of the pixel values of an image. 137 | * 138 | * @param img The image to calculate the integral of 139 | * @return The pixel values integral of the image passed 140 | */ 141 | final public static > double getImageIntegral( 142 | final RandomAccessibleInterval img ) 143 | { 144 | final RealSum sum = new RealSum(); 145 | 146 | for ( final T type : Views.iterable(img) ) 147 | sum.add( type.getRealDouble() ); 148 | 149 | return sum.getSum(); 150 | } 151 | 152 | /** 153 | * Calculates the integral of the pixel values of an image. 154 | * 155 | * @param img The image to calculate the integral of 156 | * @return The pixel values integral of the image passed 157 | */ 158 | final public static > double getImageIntegral( 159 | final RandomAccessibleInterval img, 160 | final RandomAccessibleInterval mask ) 161 | { 162 | final RealSum sum = new RealSum(); 163 | // create cursor to walk an image with respect to a mask 164 | final TwinCursor cursor = new TwinCursor( 165 | img.randomAccess(), 166 | img.randomAccess(), 167 | Views.iterable(mask).cursor()); 168 | while (cursor.hasNext()) 169 | sum.add( cursor.getFirst().getRealDouble() ); 170 | 171 | return sum.getSum(); 172 | } 173 | 174 | /** 175 | * Calculates the min of an image. 176 | * 177 | * @param img The image to calculate the min of 178 | * @return The min of the image passed 179 | */ 180 | final public static & Comparable> T getImageMin( 181 | final RandomAccessibleInterval img ) 182 | { 183 | final Cursor cursor = Views.iterable(img).cursor(); 184 | cursor.fwd(); 185 | // copy first element as current maximum 186 | final T min = cursor.get().copy(); 187 | 188 | while ( cursor.hasNext() ) 189 | { 190 | cursor.fwd(); 191 | 192 | final T currValue = cursor.get(); 193 | 194 | if ( currValue.compareTo( min ) < 0 ) 195 | min.set( currValue ); 196 | } 197 | 198 | return min; 199 | } 200 | 201 | /** 202 | * Calculates the min of an image with respect to a mask. 203 | * 204 | * @param img The image to calculate the min of 205 | * @param mask The mask to respect 206 | * @return The min of the image passed 207 | */ 208 | final public static & Comparable> T getImageMin( 209 | final RandomAccessibleInterval img, 210 | final RandomAccessibleInterval mask ) 211 | { 212 | // create cursor to walk an image with respect to a mask 213 | final TwinCursor cursor = new TwinCursor( 214 | img.randomAccess(), 215 | img.randomAccess(), 216 | Views.iterable(mask).localizingCursor()); 217 | // forward one step to get the first value 218 | cursor.fwd(); 219 | // copy first element as current minimum 220 | final T min = cursor.getFirst().copy(); 221 | 222 | while ( cursor.hasNext() ) { 223 | cursor.fwd(); 224 | 225 | final T currValue = cursor.getFirst(); 226 | 227 | if ( currValue.compareTo( min ) < 0 ) 228 | min.set( currValue ); 229 | } 230 | 231 | return min; 232 | } 233 | 234 | /** 235 | * Calculates the max of an image. 236 | * 237 | * @param img The image to calculate the max of 238 | * @return The max of the image passed 239 | */ 240 | final public static & Comparable> T getImageMax( 241 | final RandomAccessibleInterval img ) { 242 | 243 | final Cursor cursor = Views.iterable(img).localizingCursor(); 244 | cursor.fwd(); 245 | // copy first element as current maximum 246 | final T max = cursor.get().copy(); 247 | 248 | while ( cursor.hasNext() ) 249 | { 250 | cursor.fwd(); 251 | 252 | final T currValue = cursor.get(); 253 | 254 | if ( currValue.compareTo( max ) > 0 ) 255 | max.set( currValue ); 256 | } 257 | 258 | return max; 259 | } 260 | /** 261 | * Calculates the max of an image with respect to a mask. 262 | * 263 | * @param img The image to calculate the min of 264 | * @param mask The mask to respect 265 | * @return The min of the image passed 266 | */ 267 | final public static & Comparable> T getImageMax( 268 | final RandomAccessibleInterval img, 269 | final RandomAccessibleInterval mask ) 270 | { 271 | // create cursor to walk an image with respect to a mask 272 | final TwinCursor cursor = new TwinCursor( 273 | img.randomAccess(), 274 | img.randomAccess(), 275 | Views.iterable(mask).localizingCursor()); 276 | // forward one step to get the first value 277 | cursor.fwd(); 278 | final T max = cursor.getFirst().copy(); 279 | 280 | while ( cursor.hasNext() ) { 281 | cursor.fwd(); 282 | 283 | final T currValue = cursor.getFirst(); 284 | 285 | if ( currValue.compareTo( max ) > 0 ) 286 | max.set( currValue ); 287 | } 288 | 289 | return max; 290 | } 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/SpearmanRankCorrelation.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import java.util.Arrays; 25 | import java.util.Comparator; 26 | 27 | import net.imglib2.RandomAccessibleInterval; 28 | import net.imglib2.TwinCursor; 29 | import net.imglib2.type.logic.BitType; 30 | import net.imglib2.type.numeric.RealType; 31 | import net.imglib2.view.Views; 32 | 33 | import sc.fiji.coloc.gadgets.DataContainer; 34 | import sc.fiji.coloc.results.ResultHandler; 35 | 36 | /* 37 | * This code has been heavily adapted from Numerical Recipces: The Art of Scientific Computing. 38 | * 3rd ed., 2007. Other formulations have been gathered from Wolfram's MathWorld: 39 | * http://mathworld.wolfram.com/SpearmanRankCorrelationCoefficient.html 40 | * 41 | * Adapted from code written by Dan White and Tom Kazimiers 42 | * 43 | * @author Leonardo Guizzetti 44 | */ 45 | 46 | 47 | /** 48 | * This algorithm calculates Spearman's rank correlation coefficient (Spearman's rho) 49 | * 50 | * @param 51 | */ 52 | public class SpearmanRankCorrelation> extends Algorithm { 53 | // the resulting Spearman rho value 54 | double rhoValue; 55 | double tStatisticSpearman; 56 | int dfSpearman; 57 | 58 | // create two paired arrays: one with raw pixel values and one for the corresponding ranks 59 | double[][] data; 60 | double[] ch1raw; 61 | double[] ch2raw; 62 | double[] ch1ranks; 63 | double[] ch2ranks; 64 | 65 | public SpearmanRankCorrelation() { 66 | super("Spearman's Rank Corelation calculation"); 67 | } 68 | 69 | @Override 70 | public void execute(DataContainer container) 71 | throws MissingPreconditionException { 72 | 73 | // get the 2 images for the calculation of Spearman's rho 74 | RandomAccessibleInterval img1 = container.getSourceImage1(); 75 | RandomAccessibleInterval img2 = container.getSourceImage2(); 76 | RandomAccessibleInterval mask = container.getMask(); 77 | 78 | TwinCursor cursor = new TwinCursor(img1.randomAccess(), 79 | img2.randomAccess(), Views.iterable(mask).localizingCursor()); 80 | // calculate Spearman's rho value 81 | rhoValue = calculateSpearmanRank(cursor); 82 | } 83 | 84 | /** 85 | * Calculates Spearman's Rank Correlation Coefficient (Spearman's rho) for 86 | * two images. 87 | * 88 | * @param cursor A TwinCursor that iterates over two images 89 | * @return Spearman's rank correlation coefficient (rho) value 90 | */ 91 | public > double calculateSpearmanRank(TwinCursor cursor) { 92 | 93 | // Step 0: Count the pixels first. 94 | int n = 0; 95 | while (cursor.hasNext()) { 96 | n++; 97 | cursor.fwd(); 98 | } 99 | cursor.reset(); 100 | 101 | data = new double[n][2]; 102 | 103 | for (int i = 0; i < n; i++) { 104 | cursor.fwd(); 105 | T type1 = cursor.getFirst(); 106 | T type2 = cursor.getSecond(); 107 | data[i][0] = type1.getRealDouble(); 108 | data[i][1] = type2.getRealDouble(); 109 | } 110 | 111 | return calculateSpearmanRank(data); 112 | } 113 | 114 | /** 115 | * Calculates Spearman's Rank Correlation Coefficient (Spearman's rho) for 116 | * two images. 117 | * 118 | * @param data A 2D array containing the data to be ranked 119 | * @return Spearman's rank correlation coefficient (rho) value 120 | */ 121 | public double calculateSpearmanRank(double[][] data) { 122 | final int n = data.length; 123 | ch1raw = new double[n]; 124 | ch2raw = new double[n]; 125 | ch1ranks = new double[n]; 126 | ch2ranks = new double[n]; 127 | 128 | /** 129 | * Here's the concept. Rank-transform the data, then run 130 | * the Pearson correlation on the transformed data. 131 | * 132 | * 1) We will sort the dataset by one column, extract the 133 | * column values and rank them, and replace the data by 134 | * the ranks. 135 | * 2) Repeat the process now with the remaining column. 136 | * 3) Calculate the coefficient from the individual rank 137 | * columns, the t-statistic and the df's of the test. 138 | */ 139 | 140 | // Step 1: Sort the raw data, by column #2 (arbitrary choice). 141 | Arrays.sort(data, new Comparator() { 142 | @Override 143 | public int compare(double[] row1, double[] row2) { 144 | return Double.compare(row1[1], row2[1]); 145 | } 146 | }); 147 | 148 | for (int i = 0; i < n; i++) { 149 | ch2raw[i] = data[i][1]; 150 | } 151 | 152 | // Rank the data then replace them into the dataset. 153 | ch2ranks = rankValues(ch2raw); 154 | for (int i = 0; i < n; i++) { 155 | data[i][1] = ch2ranks[i]; 156 | } 157 | 158 | // Step 2: Repeat step 1 with the other data column. 159 | Arrays.sort(data, new Comparator() { 160 | @Override 161 | public int compare(double[] row1, double[] row2) { 162 | return Double.compare(row1[0], row2[0]); 163 | } 164 | }); 165 | 166 | for (int i = 0; i < n; i++) { 167 | ch1raw[i] = data[i][0]; 168 | } 169 | 170 | ch1ranks = rankValues(ch1raw); 171 | for (int i = 0; i < n; i++) { 172 | data[i][0] = ch1ranks[i]; 173 | ch2ranks[i] = data[i][1]; 174 | } 175 | 176 | // Step 3: Compute statistics. 177 | rhoValue = calculateRho(ch1ranks, ch2ranks); 178 | tStatisticSpearman = getTStatistic(rhoValue, n); 179 | dfSpearman = getSpearmanDF(n); 180 | 181 | return rhoValue; 182 | } 183 | 184 | /** 185 | * Returns degrees of freedom for Spearman's rank correlation. 186 | * 187 | * @param n - N (number of data pairs) 188 | * @return Spearman's rank degrees of freedom. 189 | */ 190 | public int getSpearmanDF(int n) { 191 | return n - 2; 192 | } 193 | 194 | /** 195 | * Returns associated T-Statistic for Spearman's rank correlation. 196 | * 197 | * @param rho - Spearman's rho 198 | * @param n - N (number of data pairs) 199 | * @return Spearman's rank correlation t-statistic 200 | */ 201 | public double getTStatistic(double rho, int n) { 202 | double rho_squared = rho * rho; 203 | return rho * Math.sqrt( (n - 2) / (1 - rho_squared) ); 204 | } 205 | 206 | /** 207 | * Returns sorted rankings for a list of sorted values. 208 | * 209 | * @param sortedVals - The sorted absolute values 210 | * @return ranked sorted list of values 211 | */ 212 | public double[] rankValues(double[] sortedVals) { 213 | 214 | int len = sortedVals.length; 215 | int start = 0; 216 | int end = 0; 217 | double[] newranks = new double[len]; 218 | double avg = 0, ranksum = 0; 219 | boolean ties_found = false; 220 | 221 | // first assign ranks, ascending from 1 222 | for (int i=0; i handler) { 326 | super.processResults(handler); 327 | handler.handleValue("Spearman's rank correlation value", rhoValue, 8); 328 | handler.handleValue("Spearman's correlation t-statistic", tStatisticSpearman, 4); 329 | handler.handleValue("t-statistic degrees of freedom", dfSpearman); 330 | } 331 | } 332 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/KendallTauRankCorrelation.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import ij.IJ; 25 | 26 | import java.util.Arrays; 27 | 28 | import net.imglib2.PairIterator; 29 | import net.imglib2.RandomAccessible; 30 | import net.imglib2.RandomAccessibleInterval; 31 | import net.imglib2.TwinCursor; 32 | import net.imglib2.type.logic.BitType; 33 | import net.imglib2.type.numeric.RealType; 34 | import net.imglib2.view.Views; 35 | 36 | import sc.fiji.coloc.gadgets.DataContainer; 37 | import sc.fiji.coloc.results.ResultHandler; 38 | 39 | /** 40 | * This algorithm calculates Kendall's Tau-b rank correlation coefficient 41 | *

42 | * According to this 44 | * article, Tau-b (appropriate if multiple pairs share the same first, or 45 | * second, value), the rank correlation of a set of pairs (x_1, y_1), ..., 46 | * (x_n, y_n): 47 | *

48 | * 49 | *
 50 |  * Tau_B = (n_c - n_d) / sqrt( (n_0 - n_1) (n_0 - n_2) )
 51 |  * 
52 | * 53 | * where 54 | * 55 | *
 56 |  * n_0 = n (n - 1) / 2
 57 |  * n_1 = sum_i t_i (t_i - 1) / 2
 58 |  * n_2 = sum_j u_j (u_j - 1) / 2
 59 |  * n_c = #{ i, j; i != j && (x_i - x_j) * (y_i - y_j) > 0 },
 60 |  *   i.e. the number of pairs of pairs agreeing on the order of x and y, respectively
 61 |  * n_d = #{ i, j: i != j && (x_i - x_j) * (y_i - y_j) < 0 },
 62 |  *   i.e. the number of pairs of pairs where x and y are ordered opposite of each other
 63 |  * t_i = number of tied values in the i-th group of ties for the first quantity
 64 |  * u_j = number of tied values in the j-th group of ties for the second quantity
 65 |  * 
66 | * 67 | * @author Johannes Schindelin 68 | * @param 69 | */ 70 | public class KendallTauRankCorrelation> extends Algorithm { 71 | 72 | public KendallTauRankCorrelation() { 73 | super("Kendall's Tau-b Rank Correlation"); 74 | } 75 | 76 | private double tau; 77 | 78 | @Override 79 | public void execute(DataContainer container) 80 | throws MissingPreconditionException 81 | { 82 | RandomAccessible img1 = container.getSourceImage1(); 83 | RandomAccessible img2 = container.getSourceImage2(); 84 | RandomAccessibleInterval mask = container.getMask(); 85 | 86 | TwinCursor cursor = new TwinCursor(img1.randomAccess(), 87 | img2.randomAccess(), Views.iterable(mask).localizingCursor()); 88 | 89 | tau = calculateMergeSort(cursor); 90 | } 91 | 92 | public static> double calculateNaive(final PairIterator iterator) { 93 | if (!iterator.hasNext()) { 94 | return Double.NaN; 95 | } 96 | 97 | // See http://en.wikipedia.org/wiki/Kendall_tau_rank_correlation_coefficient 98 | int n = 0, max1 = 0, max2 = 0, max = 255; 99 | int[][] histogram = new int[max + 1][max + 1]; 100 | while (iterator.hasNext()) { 101 | iterator.fwd(); 102 | T type1 = iterator.getFirst(); 103 | T type2 = iterator.getSecond(); 104 | double ch1 = type1.getRealDouble(); 105 | double ch2 = type2.getRealDouble(); 106 | if (ch1 < 0 || ch2 < 0 || ch1 > max || ch2 > max) { 107 | IJ.log("Error: The current Kendall Tau implementation is limited to 8-bit data"); 108 | return Double.NaN; 109 | } 110 | n++; 111 | int ch1Int = (int)Math.round(ch1); 112 | int ch2Int = (int)Math.round(ch2); 113 | histogram[ch1Int][ch2Int]++; 114 | if (max1 < ch1Int) { 115 | max1 = ch1Int; 116 | } 117 | if (max2 < ch2Int) { 118 | max2 = ch2Int; 119 | } 120 | } 121 | long n0 = n * (long)(n - 1) / 2, n1 = 0, n2 = 0, nc = 0, nd = 0; 122 | for (int i1 = 0; i1 <= max1; i1++) { 123 | IJ.log("" + i1 + "/" + max1); 124 | int ch1 = 0; 125 | for (int i2 = 0; i2 <= max2; i2++) { 126 | ch1 += histogram[i1][i2]; 127 | 128 | int count = histogram[i1][i2]; 129 | for (int j1 = 0; j1 < i1; j1++) { 130 | for (int j2 = 0; j2 < i2; j2++) { 131 | nc += count * histogram[j1][j2]; 132 | } 133 | } 134 | for (int j1 = 0; j1 < i1; j1++) { 135 | for (int j2 = i2 + 1; j2 <= max2; j2++) { 136 | nd += count * histogram[j1][j2]; 137 | } 138 | } 139 | } 140 | n1 += ch1 * (long)(ch1 - 1) / 2; 141 | } 142 | for (int i2 = 0; i2 <= max2; i2++) { 143 | int ch2 = 0; 144 | for (int i1 = 0; i1 <= max1; i1++) { 145 | ch2 += histogram[i1][i2]; 146 | } 147 | n2 += ch2 * (long)(ch2 - 1) / 2; 148 | } 149 | 150 | return (nc - nd) / Math.sqrt((n0 - n1) * (double)(n0 - n2)); 151 | } 152 | 153 | private static> double[][] getPairs(final PairIterator iterator) { 154 | // TODO: it is ridiculous that this has to be counted all the time (i.e. in most if not all measurements!). 155 | // We only need an upper bound to begin with, so even the number of pixels in the first channel would be enough! 156 | int capacity = 0; 157 | while (iterator.hasNext()) { 158 | iterator.fwd(); 159 | capacity++; 160 | } 161 | 162 | double[] values1 = new double[capacity]; 163 | double[] values2 = new double[capacity]; 164 | iterator.reset(); 165 | int count = 0; 166 | while (iterator.hasNext()) { 167 | iterator.fwd(); 168 | values1[count] = iterator.getFirst().getRealDouble(); 169 | values2[count] = iterator.getSecond().getRealDouble(); 170 | count++; 171 | } 172 | 173 | if (count < capacity) { 174 | values1 = Arrays.copyOf(values1, count); 175 | values2 = Arrays.copyOf(values2, count); 176 | } 177 | return new double[][] { values1, values2 }; 178 | } 179 | 180 | /** 181 | * Calculate Tau-b efficiently. 182 | *

183 | * This implementation is based on this 185 | * description of the merge sort based way to calculate Tau-b. This is 186 | * supposed to be the method described in: 187 | *

188 | *
Knight, W. (1966). "A Computer Method for Calculating 189 | * Kendall's Tau with Ungrouped Data". Journal of the American Statistical 190 | * Association 61 (314): 436–439. doi:10.2307/2282833.
191 | *

192 | * but since that article is not available as Open Access, it is 193 | * unnecessarily hard to verify. 194 | *

195 | * 196 | * @param iterator the iterator of the pairs 197 | * @return Tau-b 198 | */ 199 | public static> double calculateMergeSort(final PairIterator iterator) { 200 | final double[][] pairs = getPairs(iterator); 201 | final double[] x = pairs[0]; 202 | final double[] y = pairs[1]; 203 | final int n = x.length; 204 | 205 | int[] index = new int[n]; 206 | for (int i = 0; i < n; i++) { 207 | index[i] = i; 208 | } 209 | 210 | // First sort by x as primary key, y as secondary one. 211 | // We use IntroSort here because it is fast and in-place. 212 | IntArraySorter.sort(index, new IntComparator() { 213 | 214 | @Override 215 | public int compare(int a, int b) { 216 | double xa = x[a], ya = y[a]; 217 | double xb = x[b], yb = y[b]; 218 | int result = Double.compare(xa, xb); 219 | return result != 0 ? result : Double.compare(ya, yb); 220 | } 221 | 222 | }); 223 | 224 | // The trick is to count the ties of x (n1) and the joint ties of x and y (n3) now, while 225 | // index is sorted with regards to x. 226 | long n0 = n * (long)(n - 1) / 2; 227 | long n1 = 0, n3 = 0; 228 | 229 | for (int i = 1; i < n; i++) { 230 | double x0 = x[index[i - 1]]; 231 | if (x[index[i]] != x0) { 232 | continue; 233 | } 234 | double y0 = y[index[i - 1]]; 235 | int i1 = i; 236 | do { 237 | double y1 = y[index[i1++]]; 238 | if (y1 == y0) { 239 | int i2 = i1; 240 | while (i1 < n && x[index[i1]] == x0 && y[index[i1]] == y0) { 241 | i1++; 242 | } 243 | n3 += (i1 - i2 + 2) * (long)(i1 - i2 + 1) / 2; 244 | } 245 | y0 = y1; 246 | } while (i1 < n && x[index[i1]] == x0); 247 | n1 += (i1 - i + 1) * (long)(i1 - i) / 2; 248 | i = i1; 249 | } 250 | 251 | // Now, let's perform that merge sort that also counts S, the number of 252 | // swaps a Bubble Sort would require (and which therefore is half the number 253 | // by which we have to adjust n_0 - n_1 - n_2 + n_3 to obtain n_c - n_d) 254 | final MergeSort mergeSort = new MergeSort(index, new IntComparator() { 255 | 256 | @Override 257 | public int compare(int a, int b) { 258 | double ya = y[a]; 259 | double yb = y[b]; 260 | return Double.compare(ya, yb); 261 | } 262 | }); 263 | long S = mergeSort.sort(); 264 | index = mergeSort.getSorted(); 265 | long n2 = 0; 266 | 267 | for (int i = 1; i < n; i++) { 268 | double y0 = y[index[i - 1]]; 269 | if (y[index[i]] != y0) { 270 | continue; 271 | } 272 | int i1 = i + 1; 273 | while (i1 < n && y[index[i1]] == y0) { 274 | i1++; 275 | } 276 | n2 += (i1 - i + 1) * (long)(i1 - i) / 2; 277 | i = i1; 278 | } 279 | 280 | return (n0 - n1 - n2 + n3 - 2 * S) / Math.sqrt((n0 - n1) * (double)(n0 - n2)); 281 | } 282 | 283 | private final static class MergeSort { 284 | 285 | private int[] index; 286 | private final IntComparator comparator; 287 | 288 | public MergeSort(int[] index, IntComparator comparator) { 289 | this.index = index; 290 | this.comparator = comparator; 291 | } 292 | 293 | public int[] getSorted() { 294 | return index; 295 | } 296 | 297 | /** 298 | * Sorts the {@link #index} array. 299 | *

300 | * This implements a non-recursive merge sort. 301 | *

302 | * @param begin 303 | * @param end 304 | * @return the equivalent number of BubbleSort swaps 305 | */ 306 | public long sort() { 307 | long swaps = 0; 308 | int n = index.length; 309 | // There are merge sorts which perform in-place, but their runtime is worse than O(n log n) 310 | int[] index2 = new int[n]; 311 | for (int step = 1; step < n; step <<= 1) { 312 | int begin = 0, k = 0; 313 | for (;;) { 314 | int begin2 = begin + step, end = begin2 + step; 315 | if (end >= n) { 316 | if (begin2 >= n) { 317 | break; 318 | } 319 | end = n; 320 | } 321 | 322 | // calculate the equivalent number of BubbleSort swaps 323 | // and perform merge, too 324 | int i = begin, j = begin2; 325 | while (i < begin2 && j < end) { 326 | int compare = comparator.compare(index[i], index[j]); 327 | if (compare > 0) { 328 | swaps += (begin2 - i); 329 | index2[k++] = index[j++]; 330 | } else { 331 | index2[k++] = index[i++]; 332 | } 333 | } 334 | if (i < begin2) { 335 | do { 336 | index2[k++] = index[i++]; 337 | } while (i < begin2); 338 | } else { 339 | while (j < end) { 340 | index2[k++] = index[j++]; 341 | } 342 | } 343 | begin = end; 344 | } 345 | if (k < n) { 346 | System.arraycopy(index, k, index2, k, n - k); 347 | } 348 | int[] swapIndex = index2; 349 | index2 = index; 350 | index = swapIndex; 351 | } 352 | 353 | return swaps; 354 | } 355 | 356 | } 357 | 358 | @Override 359 | public void processResults(ResultHandler handler) { 360 | super.processResults(handler); 361 | handler.handleValue("Kendall's Tau-b rank correlation value", tau, 4); 362 | } 363 | } 364 | -------------------------------------------------------------------------------- /src/main/java/sc/fiji/coloc/algorithms/MandersColocalization.java: -------------------------------------------------------------------------------- 1 | /*- 2 | * #%L 3 | * Fiji's plugin for colocalization analysis. 4 | * %% 5 | * Copyright (C) 2009 - 2024 Fiji developers. 6 | * %% 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as 9 | * published by the Free Software Foundation, either version 3 of the 10 | * License, or (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public 18 | * License along with this program. If not, see 19 | * . 20 | * #L% 21 | */ 22 | package sc.fiji.coloc.algorithms; 23 | 24 | import net.imglib2.RandomAccessible; 25 | import net.imglib2.RandomAccessibleInterval; 26 | import net.imglib2.TwinCursor; 27 | import net.imglib2.type.logic.BitType; 28 | import net.imglib2.type.numeric.RealType; 29 | import net.imglib2.view.Views; 30 | 31 | import sc.fiji.coloc.gadgets.DataContainer; 32 | import sc.fiji.coloc.gadgets.ThresholdMode; 33 | import sc.fiji.coloc.results.ResultHandler; 34 | 35 | /** 36 | *

This algorithm calculates Manders et al.'s split colocalization 37 | * coefficients, M1 and M2. These are independent of signal intensities, 38 | * but are directly proportional to the amount of 39 | * fluorescence in the colocalized objects in each colour channel of the 40 | * image, relative to the total amount of fluorescence in that channel. 41 | * See "Manders, Verbeek, Aten - Measurement of colocalization 42 | * of objects in dual-colour confocal images. J. Microscopy, vol. 169 43 | * pt 3, March 1993, pp 375-382".

44 | * 45 | *

M1 = sum of Channel 1 intensity in pixels over the channel 2 threshold / total Channel 1 intensity. 46 | * M2 is vice versa. 47 | * The threshold may be everything > 0 in the other channel, which we call M1 and M2: without thresholds 48 | * or everything above some thresholds in the opposite channels 1 or 2, called tM1 and tM2: with thresholds 49 | * The result is a fraction (range 0-1, but often misrepresented as a %. We wont do that here.

50 | * 51 | *

TODO: Further, it could/should/will calculate other split colocalization coefficients, 52 | * such as fraction of pixels (voxels) colocalized, 53 | * or fraction of intensity colocalized, as described at: 54 | * WCIF 55 | * copy pasted here - credits to Tony Collins.

56 | * 57 | *

Number of colocalised voxels - Ncoloc 58 | * This is the number of voxels which have both channel 1 and channel 2 intensities above threshold 59 | * (i.e., the number of pixels in the yellow area of the scatterplot).

60 | * 61 | *

%Image volume colocalised - %Volume 62 | * This is the percentage of voxels which have both channel 1 and channel 2 intensities above threshold, 63 | * expressed as a percentage of the total number of pixels in the image (including zero-zero pixels); 64 | * in other words, the number of pixels in the scatterplot's yellow area / total number of pixels in the scatter plot (the Red + Green + Blue + Yellow areas).

65 | * 66 | *

%Voxels Colocalised - %Ch1 Vol; %Ch2 Vol 67 | * This generates a value for each channel. This is the number of voxels for each channel 68 | * which have both channel 1 and channel 2 intensities above threshold, 69 | * expressed as a percentage of the total number of voxels for each channel 70 | * above their respective thresholds; in other words, for channel 1 (along the x-axis), 71 | * this equals the (the number of pixels in the Yellow area) / (the number of pixels in the Blue + Yellow areas). 72 | * For channel 2 this is calculated as follows: 73 | * (the number of pixels in the Yellow area) / (the number of pixels in the Red + Yellow areas).

74 | * 75 | *

%Intensity Colocalised - %Ch1 Int; %Ch2 Int 76 | * This generates a value for each channel. For channel 1, this value is equal to 77 | * the sum of the pixel intensities, with intensities above both channel 1 and channel 2 thresholds 78 | * expressed as a percentage of the sum of all channel 1 intensities; 79 | * in other words, it is calculated as follows: 80 | * (the sum of channel 1 pixel intensities in the Yellow area) / (the sum of channel 1 pixels intensities in the Red + Green + Blue + Yellow areas).

81 | * 82 | *

%Intensities above threshold colocalised - %Ch1 Int > thresh; %Ch2 Int > thresh 83 | * This generates a value for each channel. For channel 1, 84 | * this value is equal to the sum of the pixel intensities 85 | * with intensities above both channel 1 and channel 2 thresholds 86 | * expressed as a percentage of the sum of all channel 1 intensities above the threshold for channel 1. 87 | * In other words, it is calculated as follows: 88 | * (the sum of channel 1 pixel intensities in the Yellow area) / (sum of channel 1 pixels intensities in the Blue + Yellow area)

89 | * 90 | *

The results are often represented as % values, but to make them consistent with Manders' 91 | * split coefficients, we will also report them as fractions (range 0-1). 92 | * Perhaps this helps prevent the confusion in comparisons of results 93 | * caused by one person's %coloc being a totally 94 | * different measurement than another person's %coloc.

95 | * 96 | * @param 97 | */ 98 | public class MandersColocalization> extends Algorithm { 99 | // Manders' split coefficients, M1 and M2: without thresholds 100 | // fraction of intensity of a channel, in pixels above zero in the other channel. 101 | private double mandersM1, mandersM2; 102 | 103 | 104 | // thresholded Manders M1 and M2 values, 105 | // Manders' split coefficients, tM1 and tM2: with thresholds 106 | // fraction of intensity of a channel, in pixels above threshold in the other channel. 107 | private double mandersThresholdedM1, mandersThresholdedM2; 108 | 109 | // Number of colocalized voxels (pixels) - Ncoloc 110 | private long numberOfPixelsAboveBothThresholds; 111 | 112 | // Fraction of Image volume colocalized - Fraction of Volume 113 | private double fractionOfPixelsAboveBothThresholds; 114 | 115 | // Fraction Voxels (pixels) Colocalized - Fraction of Ch1 Vol; Fraction of Ch2 Vol 116 | private double fractionOfColocCh1Pixels, fractionOfColocCh2Pixels; 117 | 118 | // Fraction Intensity Colocalized - Fraction of Ch1 Int; Fraction of Ch2 Int 119 | private double fractionOfColocCh1Intensity, fractionOfColocCh2Intensity; 120 | 121 | // Fraction of Intensities above threshold, colocalized - 122 | // Fraction of Ch1 Int > thresh; Fraction of Ch2 Int > thresh 123 | private double fractionOfColocCh1IntensityAboveCh1Thresh, fractionOfColocCh2IntensityAboveCh2Thresh; 124 | 125 | /** 126 | * A result container for Manders' calculations. 127 | */ 128 | public static class MandersResults { 129 | public double m1; 130 | public double m2; 131 | } 132 | 133 | public MandersColocalization() { 134 | super("Manders correlation"); 135 | } 136 | 137 | @Override 138 | public void execute(DataContainer container) 139 | throws MissingPreconditionException { 140 | // get the two images for the calculation of Manders' split coefficients 141 | RandomAccessible img1 = container.getSourceImage1(); 142 | RandomAccessible img2 = container.getSourceImage2(); 143 | RandomAccessibleInterval mask = container.getMask(); 144 | 145 | TwinCursor cursor = new TwinCursor(img1.randomAccess(), 146 | img2.randomAccess(), Views.iterable(mask).localizingCursor()); 147 | 148 | // calculate Manders' split coefficients without threshold, M1 and M2. 149 | MandersResults results = calculateMandersCorrelation(cursor, 150 | img1.randomAccess().get().createVariable()); 151 | 152 | // save the results 153 | mandersM1 = results.m1; 154 | mandersM2 = results.m2; 155 | 156 | // calculate the thresholded Manders' split coefficients, tM1 and tM2, if possible 157 | AutoThresholdRegression autoThreshold = container.getAutoThreshold(); 158 | if (autoThreshold != null ) { 159 | // thresholded Manders' split coefficients, tM1 and tM2 160 | cursor.reset(); 161 | results = calculateMandersCorrelation(cursor, autoThreshold.getCh1MaxThreshold(), 162 | autoThreshold.getCh2MaxThreshold(), ThresholdMode.Above); 163 | 164 | // save the results 165 | mandersThresholdedM1 = results.m1; 166 | mandersThresholdedM2 = results.m2; 167 | } 168 | } 169 | 170 | /** 171 | * Calculates Manders' split coefficients, M1 and M2: without thresholds 172 | * 173 | * @param cursor A TwinCursor that walks over two images 174 | * @param type A type instance, its value is not relevant 175 | * @return Both Manders' split coefficients, M1 and M2. 176 | */ 177 | public MandersResults calculateMandersCorrelation(TwinCursor cursor, T type) { 178 | return calculateMandersCorrelation(cursor, type, type, ThresholdMode.None); 179 | } 180 | 181 | /** 182 | * Calculates Manders' split coefficients, tM1 and tM2: with thresholds 183 | * 184 | * @param cursor A TwinCursor that walks over two images 185 | * @param thresholdCh1 type T 186 | * @param thresholdCh2 type T 187 | * @param tMode A ThresholdMode the threshold mode 188 | * @return Both thresholded Manders' split coefficients, tM1 and tM2. 189 | */ 190 | public MandersResults calculateMandersCorrelation(TwinCursor cursor, 191 | final T thresholdCh1, final T thresholdCh2, ThresholdMode tMode) { 192 | SplitCoeffAccumulator mandersAccum; 193 | // create a zero-values variable to compare to later on 194 | final T zero = thresholdCh1.createVariable(); 195 | zero.setZero(); 196 | 197 | // iterate over images - set the boolean value for if a pixel is thresholded 198 | 199 | // without thresholds: M1 and M1 200 | if (tMode == ThresholdMode.None) { 201 | mandersAccum = new SplitCoeffAccumulator(cursor) { 202 | @Override 203 | final boolean acceptMandersCh1(T type1, T type2) { 204 | return (type2.compareTo(zero) > 0); 205 | } 206 | @Override 207 | final boolean acceptMandersCh2(T type1, T type2) { 208 | return (type1.compareTo(zero) > 0); 209 | } 210 | }; 211 | // with thresholds - below thresholds 212 | } else if (tMode == ThresholdMode.Below) { 213 | mandersAccum = new SplitCoeffAccumulator(cursor) { 214 | @Override 215 | final boolean acceptMandersCh1(T type1, T type2) { 216 | return (type2.compareTo(zero) > 0) && 217 | (type2.compareTo(thresholdCh2) <= 0); 218 | } 219 | @Override 220 | final boolean acceptMandersCh2(T type1, T type2) { 221 | return (type1.compareTo(zero) > 0) && 222 | (type1.compareTo(thresholdCh1) <= 0); 223 | } 224 | }; 225 | // with thresholds - above thresholds: tM1 and tM2 226 | } else if (tMode == ThresholdMode.Above) { 227 | mandersAccum = new SplitCoeffAccumulator(cursor) { 228 | @Override 229 | final boolean acceptMandersCh1(T type1, T type2) { 230 | return (type2.compareTo(zero) > 0) && 231 | (type2.compareTo(thresholdCh2) >= 0); 232 | } 233 | @Override 234 | final boolean acceptMandersCh2(T type1, T type2) { 235 | return (type1.compareTo(zero) > 0) && 236 | (type1.compareTo(thresholdCh1) >= 0); 237 | } 238 | }; 239 | } else { 240 | throw new UnsupportedOperationException(); 241 | } 242 | 243 | MandersResults results = new MandersResults(); 244 | // calculate the results, see description above, as a fraction. 245 | results.m1 = mandersAccum.mandersSumCh1 / mandersAccum.sumCh1; 246 | results.m2 = mandersAccum.mandersSumCh2 / mandersAccum.sumCh2; 247 | 248 | return results; 249 | } 250 | 251 | @Override 252 | public void processResults(ResultHandler handler) { 253 | super.processResults(handler); 254 | handler.handleValue( "Manders' M1 (Above zero intensity of Ch2)", mandersM1 ); 255 | handler.handleValue( "Manders' M2 (Above zero intensity of Ch1)", mandersM2 ); 256 | handler.handleValue( "Manders' tM1 (Above autothreshold of Ch2)", mandersThresholdedM1 ); 257 | handler.handleValue( "Manders' tM2 (Above autothreshold of Ch1)", mandersThresholdedM2 ); 258 | } 259 | 260 | /** 261 | * A class similar to the Accumulator class, but more specific 262 | * to the Manders' split and other split channel coefficient calculations. 263 | */ 264 | protected abstract class SplitCoeffAccumulator { 265 | double sumCh1, sumCh2, mandersSumCh1, mandersSumCh2; 266 | 267 | public SplitCoeffAccumulator(TwinCursor cursor) { 268 | while (cursor.hasNext()) { 269 | cursor.fwd(); 270 | T type1 = cursor.getFirst(); 271 | T type2 = cursor.getSecond(); 272 | double ch1 = type1.getRealDouble(); 273 | double ch2 = type2.getRealDouble(); 274 | 275 | // boolean logics for adding or not adding to the different value counters for a pixel. 276 | if (acceptMandersCh1(type1, type2)) 277 | mandersSumCh1 += ch1; 278 | if (acceptMandersCh2(type1, type2)) 279 | mandersSumCh2 += ch2; 280 | 281 | // add this pixel's two intensity values to the ch1 and ch2 sum counters 282 | sumCh1 += ch1; 283 | sumCh2 += ch2; 284 | } 285 | } 286 | abstract boolean acceptMandersCh1(T type1, T type2); 287 | abstract boolean acceptMandersCh2(T type1, T type2); 288 | } 289 | } 290 | --------------------------------------------------------------------------------