tracing.SkeletonPlugin.java Source code

Java tutorial

Introduction

Here is the source code for tracing.SkeletonPlugin.java

Source

/* -*- mode: java; c-basic-offset: 8; indent-tabs-mode: t; tab-width: 8 -*- */

/* Copyright 2016 Tiago Ferreira */

/*
  This file is part of the ImageJ plugin "Simple Neurite Tracer".
    
  The ImageJ plugin "Simple Neurite Tracer" is free software; you
  can redistribute it and/or modify it under the terms of the GNU
  General Public License as published by the Free Software
  Foundation; either version 3 of the License, or (at your option)
  any later version.
    
  The ImageJ plugin "Simple Neurite Tracer" is distributed in the
  hope that it will be useful, but WITHOUT ANY WARRANTY; without
  even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  PARTICULAR PURPOSE.  See the GNU General Public License for more
  details.
    
  In addition, as a special exception, the copyright holders give
  you permission to combine this program with free software programs or
  libraries that are released under the Apache Public License.
    
  You should have received a copy of the GNU General Public License
  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package tracing;

import java.awt.AWTEvent;
import java.awt.Checkbox;
import java.awt.Window;
import java.util.ArrayList;
import java.util.Vector;
import java.util.stream.IntStream;

import org.apache.commons.math3.stat.StatUtils;

import ij.IJ;
import ij.ImagePlus;
import ij.ImageStack;
import ij.WindowManager;
import ij.gui.DialogListener;
import ij.gui.GenericDialog;
import ij.gui.Roi;
import ij.gui.YesNoCancelDialog;
import ij.measure.ResultsTable;
import ij.process.ImageProcessor;
import ij.text.TextWindow;
import sc.fiji.analyzeSkeleton.AnalyzeSkeleton_;
import sc.fiji.analyzeSkeleton.SkeletonResult;
import sc.fiji.skeletonize3D.Skeletonize3D_;

public class SkeletonPlugin implements DialogListener {

    private GenericDialog gd;
    private boolean useOnlySelectedPaths;
    private boolean restrictByRoi;
    private boolean restrictBySWCType;
    private boolean callAnalyzeSkeleton;
    private boolean summarizeSkeleton;
    private ArrayList<Integer> selectedSwcTypes;
    private ArrayList<Path> renderingPaths;

    private final SimpleNeuriteTracer plugin;
    private final PathAndFillManager pafm;
    private final ImagePlus imp;
    private Roi roi;

    public SkeletonPlugin(final SimpleNeuriteTracer plugin) {
        this.plugin = plugin;
        pafm = plugin.pathAndFillManager;
        imp = plugin.getImagePlus();
    }

    public void run() {

        selectedSwcTypes = new ArrayList<>();
        renderingPaths = new ArrayList<>();

        if (!showDialog())
            return;

        if (useOnlySelectedPaths && !pafm.anySelected()) {
            final YesNoCancelDialog ynd = new YesNoCancelDialog(IJ.getInstance(),
                    "Render Unselected Paths Instead?",
                    "No path is currently selected.\n" + "Render unselected paths instead?");
            if (!ynd.yesPressed())
                return;
            useOnlySelectedPaths = false;
        }

        roi = imp.getRoi();
        if (restrictByRoi && (roi == null || !roi.isArea())) {
            final YesNoCancelDialog ynd = new YesNoCancelDialog(IJ.getInstance(), "Proceed Without ROI Filtering?",
                    "ROI filtering requested but image contains no area ROI.\n" + "Proceed without ROI filtering?");
            if (!ynd.yesPressed())
                return;
            restrictByRoi = false;
        }

        if (selectedSwcTypes.isEmpty())
            restrictBySWCType = false;

        for (Path p : pafm.allPaths) {
            if (p.getUseFitted())
                p = p.fitted;
            else if (p.fittedVersionOf != null)
                continue;
            if (useOnlySelectedPaths && !pafm.isSelected(p))
                continue;
            if (restrictBySWCType && !selectedSwcTypes.contains(p.getSWCType()))
                continue;
            renderingPaths.add(p);
        }

        if (renderingPaths.isEmpty()) {
            SNT.error("No paths fullfilled the selected criteria.");
            return;
        }

        final ImagePlus imagePlus = plugin.makePathVolume(renderingPaths);
        if (restrictByRoi && roi != null && roi.isArea()) {
            final ImageStack stack = imagePlus.getStack();
            for (int i = 1; i <= stack.getSize(); i++) {
                final ImageProcessor ip = stack.getProcessor(i);
                ip.setValue(0);
                ip.fillOutside(roi);
            }
            imagePlus.setRoi(roi);
        }

        if (callAnalyzeSkeleton || summarizeSkeleton) {
            final Skeletonize3D_ skeletonizer = new Skeletonize3D_();
            skeletonizer.setup("", imagePlus);
            skeletonizer.run(imagePlus.getProcessor());
            final AnalyzeSkeleton_ analyzer = new AnalyzeSkeleton_();
            analyzer.setup("", imagePlus);
            if (callAnalyzeSkeleton)
                analyzer.run(imagePlus.getProcessor());
            else
                summarizeSkeleton(analyzer.run());
        }

        imagePlus.show();

    }

    private void summarizeSkeleton(final SkeletonResult sr) {
        final String TABLE_TITLE = "Summary of Rendered Paths";
        final ResultsTable rt = getTable(TABLE_TITLE);
        try {
            double sumLength = 0d;
            final int[] branches = sr.getBranches();
            final double[] avgLengths = sr.getAverageBranchLength();
            for (int i = 0; i < sr.getNumOfTrees(); i++)
                sumLength += avgLengths[i] * branches[i];
            rt.incrementCounter();
            rt.addValue("N. Rendered Paths", renderingPaths.size());
            rt.addValue("Unit", imp.getCalibration().getUnits());
            rt.addValue("Total length", sumLength);
            rt.addValue("Mean branch length", StatUtils.mean(avgLengths));
            rt.addValue("Length of longest branch", StatUtils.max(sr.getMaximumBranchLength()));
            rt.addValue("# Branches", IntStream.of(sr.getBranches()).sum());
            rt.addValue("# Junctions", IntStream.of(sr.getJunctions()).sum());
            rt.addValue("# End-points", IntStream.of(sr.getEndPoints()).sum());
            rt.addValue("Fitering", getFilterString());
            if (restrictByRoi && roi != null && roi.isArea())
                rt.addValue("ROI Name", roi.getName() == null ? "Unammed ROI" : roi.getName());

        } catch (final Exception ignored) {
            SNT.error("Some statistics could not be calculated.");
        } finally {
            rt.show(TABLE_TITLE);
        }
    }

    private String getFilterString() {
        final StringBuilder filter = new StringBuilder();
        if (useOnlySelectedPaths)
            filter.append("SelectedPathsOnly");
        if (restrictByRoi && roi != null && roi.isArea())
            filter.append(" OnlyPointsInsideROI");
        if (restrictBySWCType) {
            filter.append(" [ ");
            for (final int type : selectedSwcTypes)
                filter.append(Path.getSWCtypeName(type)).append(" ");
            filter.append("]");
        }
        if (filter.length() == 0)
            filter.append("None");
        return filter.toString();
    }

    private ResultsTable getTable(final String title) {
        ResultsTable rt = null;
        final Window window = WindowManager.getWindow(title);
        if (window != null)
            rt = ((TextWindow) window).getTextPanel().getResultsTable();
        if (rt == null)
            rt = new ResultsTable();
        rt.setPrecision(5);
        rt.setNaNEmptyCells(true);
        rt.showRowNumbers(false);
        return rt;
    }

    private boolean showDialog() {

        gd = new GenericDialog("Render Paths as Topographic Skeletons");
        final String[] pwScopes = { "Render all paths", "Render only selected paths" };
        gd.addRadioButtonGroup("Path selection:", pwScopes, 2, 1, pwScopes[useOnlySelectedPaths ? 1 : 0]);
        final String[] roiScopes = { "None", "Render only segments contained by ROI" };
        gd.addRadioButtonGroup("ROI filtering:", roiScopes, 2, 1, roiScopes[restrictByRoi ? 1 : 0]);

        // Assemble SWC choices
        final ArrayList<String> swcTypeNames = Path.getSWCtypeNames();
        final int nTypes = swcTypeNames.size();
        final String[] typeNames = swcTypeNames.toArray(new String[nTypes]);
        final boolean[] typeChoices = new boolean[nTypes];
        for (int i = 0; i < nTypes; i++)
            typeChoices[i] = typeNames[i].contains("dendrite");
        final String[] swcScopes = { "None", "Include only the following SWC types:" };
        gd.addRadioButtonGroup("SWC filtering:", swcScopes, 2, 1, swcScopes[restrictBySWCType ? 1 : 0]);
        gd.setInsets(0, 40, 0);
        gd.addCheckboxGroup(nTypes / 2, 2, typeNames, typeChoices);

        final String[] analysisScopes = { "None", "Obtain summary", "Run \"Analyze Skeleton\" plugin" };
        gd.addRadioButtonGroup("Analysis of rendered paths:", analysisScopes, 3, 1, analysisScopes[0]);
        gd.addDialogListener(this);
        dialogItemChanged(gd, null);
        gd.showDialog();
        if (gd.wasCanceled())
            return false;
        else if (gd.wasOKed()) {
            return dialogItemChanged(gd, null);
        }
        return false;

    }

    @Override
    public boolean dialogItemChanged(final GenericDialog arg, final AWTEvent event) {

        useOnlySelectedPaths = gd.getNextRadioButton().contains("only");
        restrictByRoi = gd.getNextRadioButton().contains("only");
        restrictBySWCType = gd.getNextRadioButton().contains("only");
        final String analysisChoice = gd.getNextRadioButton();
        summarizeSkeleton = analysisChoice.contains("summary");
        callAnalyzeSkeleton = analysisChoice.contains("Analyze Skeleton");
        if (restrictBySWCType) {
            selectedSwcTypes.clear();
            for (final int type : Path.getSWCtypes()) {
                if (gd.getNextBoolean())
                    selectedSwcTypes.add(type);
            }
        }
        final Vector<?> cbxs = gd.getCheckboxes();
        for (int i = 0; i < cbxs.size(); i++)
            ((Checkbox) cbxs.get(i)).setEnabled(restrictBySWCType);

        return true;
    }

}