Search in sources :

Example 11 with Worker

use of ini.trakem2.utils.Worker in project TrakEM2 by trakem2.

the class Loader method makeFlatImage.

/**
 * If the srcRect is null, makes a flat 8-bit or RGB image of the entire layer. Otherwise just of the srcRect. Checks first for enough memory and frees some if feasible.
 */
public Bureaucrat makeFlatImage(final Layer[] layer, final Rectangle srcRect, final double scale, final int c_alphas, final int type, final boolean force_to_file, final String format, final boolean quality, final Color background) {
    if (null == layer || 0 == layer.length) {
        Utils.log2("makeFlatImage: null or empty list of layers to process.");
        return null;
    }
    final Worker worker = new Worker("making flat images") {

        @Override
        public void run() {
            try {
                // 
                startedWorking();
                Rectangle srcRect_ = srcRect;
                if (null == srcRect_)
                    srcRect_ = layer[0].getParent().get2DBounds();
                ImagePlus imp = null;
                String target_dir = null;
                boolean choose_dir = force_to_file;
                // if not saving to a file:
                if (!force_to_file) {
                    final long size = (long) Math.ceil((srcRect_.width * scale) * (srcRect_.height * scale) * (ImagePlus.GRAY8 == type ? 1 : 4) * layer.length);
                    if (size > IJ.maxMemory() * 0.9) {
                        final YesNoCancelDialog yn = new YesNoCancelDialog(IJ.getInstance(), "WARNING", "The resulting stack of flat images is too large to fit in memory.\nChoose a directory to save the slices as an image sequence?");
                        if (yn.yesPressed()) {
                            choose_dir = true;
                        } else if (yn.cancelPressed()) {
                            finishedWorking();
                            return;
                        } else {
                            // your own risk
                            choose_dir = false;
                        }
                    }
                }
                if (choose_dir) {
                    final DirectoryChooser dc = new DirectoryChooser("Target directory");
                    target_dir = dc.getDirectory();
                    if (null == target_dir || target_dir.toLowerCase().startsWith("null")) {
                        finishedWorking();
                        return;
                    }
                    if (IJ.isWindows())
                        target_dir = target_dir.replace('\\', '/');
                    if (!target_dir.endsWith("/"))
                        target_dir += "/";
                }
                if (layer.length > 1) {
                    // Get all slices
                    ImageStack stack = null;
                    Utils.showProgress(0);
                    for (int i = 0; i < layer.length; i++) {
                        if (Thread.currentThread().isInterrupted())
                            return;
                        /* free memory */
                        releaseAll();
                        Utils.showProgress(i / (float) layer.length);
                        final ImagePlus slice = getFlatImage(layer[i], srcRect_, scale, c_alphas, type, Displayable.class, null, quality, background);
                        if (null == slice) {
                            Utils.log("Could not retrieve flat image for " + layer[i].toString());
                            continue;
                        }
                        if (null != target_dir) {
                            saveToPath(slice, target_dir, layer[i].getPrintableTitle(), ".tif");
                        } else {
                            if (null == stack)
                                stack = new ImageStack(slice.getWidth(), slice.getHeight());
                            stack.addSlice(layer[i].getProject().findLayerThing(layer[i]).toString(), slice.getProcessor());
                        }
                    }
                    Utils.showProgress(1);
                    if (null != stack) {
                        imp = new ImagePlus("z=" + layer[0].getZ() + " to z=" + layer[layer.length - 1].getZ(), stack);
                        final Calibration impCalibration = layer[0].getParent().getCalibrationCopy();
                        impCalibration.pixelWidth /= scale;
                        impCalibration.pixelHeight /= scale;
                        imp.setCalibration(impCalibration);
                    }
                } else {
                    imp = getFlatImage(layer[0], srcRect_, scale, c_alphas, type, Displayable.class, null, quality, background);
                    if (null != target_dir) {
                        saveToPath(imp, target_dir, layer[0].getPrintableTitle(), format);
                        // to prevent showing it
                        imp = null;
                    }
                }
                if (null != imp)
                    imp.show();
            } catch (final Throwable e) {
                IJError.print(e);
            }
            finishedWorking();
        }
    };
    // I miss my lisp macros, you have no idea
    return Bureaucrat.createAndStart(worker, layer[0].getProject());
}
Also used : ZDisplayable(ini.trakem2.display.ZDisplayable) Displayable(ini.trakem2.display.Displayable) ImageStack(ij.ImageStack) Rectangle(java.awt.Rectangle) Calibration(ij.measure.Calibration) ImagePlus(ij.ImagePlus) Worker(ini.trakem2.utils.Worker) YesNoCancelDialog(ij.gui.YesNoCancelDialog) DirectoryChooser(ij.io.DirectoryChooser)

Example 12 with Worker

use of ini.trakem2.utils.Worker in project TrakEM2 by trakem2.

the class Loader method importImage.

/**
 * Import an image into the given layer, in a separate task thread.
 */
public Bureaucrat importImage(final Layer layer, final double x, final double y, final String path, final boolean synch_mipmap_generation) {
    final Worker worker = new Worker("Importing image") {

        @Override
        public void run() {
            startedWorking();
            try {
                // //
                if (null == layer) {
                    Utils.log("Can't import. No layer found.");
                    finishedWorking();
                    return;
                }
                final Patch p = importImage(layer.getProject(), x, y, path, synch_mipmap_generation);
                if (null != p) {
                    synchronized (layer) {
                        layer.add(p);
                    }
                    layer.getParent().enlargeToFit(p, LayerSet.NORTHWEST);
                }
            // //
            } catch (final Exception e) {
                IJError.print(e);
            }
            finishedWorking();
        }
    };
    return Bureaucrat.createAndStart(worker, layer.getProject());
}
Also used : Worker(ini.trakem2.utils.Worker) Patch(ini.trakem2.display.Patch) IOException(java.io.IOException) FormatException(loci.formats.FormatException)

Example 13 with Worker

use of ini.trakem2.utils.Worker in project TrakEM2 by trakem2.

the class Loader method importImages.

/**
 * <p>Import images from the given text file, which is expected to contain 4 columns or optionally 9 columns:</p>
 * <ul>
 * <li>column 1: image file path (if base_dir is not null, it will be prepended)</li>
 * <li>column 2: x coord [px]</li>
 * <li>column 3: y coord [px]</li>
 * <li>column 4: z coord [px] (layer_thickness will be multiplied to it if not zero)</li>
 * </ul>
 * <p>optional columns, if a property is not known, it can be set to "-" which makes TrakEM2 open the file and find out by itself</p>
 * <ul>
 * <li>column 5: width [px]</li>
 * <li>column 6: height [px]</li>
 * <li>column 7: min intensity [double] (for screen display)</li>
 * <li>column 8: max intensity [double] (for screen display)</li>
 * <li>column 9: type [integer] (pixel types according to ImagepPlus types: 0=8bit int gray, 1=16bit int gray, 2=32bit float gray, 3=8bit indexed color, 4=32-bit RGB color</li>
 * </ul>
 *
 * <p>This function implements the "Import from text file" command.</p>
 *
 * <p>Layers will be automatically created as needed inside the LayerSet to which the given ref_layer belongs.</p>
 * <p>
 * The text file can contain comments that start with the # sign.
 * </p>
 * <p>
 * Images will be imported in parallel, using as many cores as your machine has.
 * </p>
 * @param calibration_ transforms the read coordinates into pixel coordinates, including x,y,z, and layer thickness.
 * @param scale_ Between 0 and 1. When lower than 1, a preprocessor script is created for the imported images, to scale them down.
 */
public Bureaucrat importImages(Layer ref_layer, String abs_text_file_path_, String column_separator_, double layer_thickness_, double calibration_, boolean homogenize_contrast_, float scale_, int border_width_) {
    // check parameters: ask for good ones if necessary
    if (null == abs_text_file_path_) {
        final String[] file = Utils.selectFile("Select text file");
        // user canceled dialog
        if (null == file)
            return null;
        abs_text_file_path_ = file[0] + file[1];
    }
    if (null == column_separator_ || 0 == column_separator_.length() || Double.isNaN(layer_thickness_) || layer_thickness_ <= 0 || Double.isNaN(calibration_) || calibration_ <= 0) {
        final Calibration cal = ref_layer.getParent().getCalibrationCopy();
        final GenericDialog gdd = new GenericDialog("Options");
        final String[] separators = new String[] { "tab", "space", "comma (,)" };
        gdd.addMessage("Choose a layer to act as the zero for the Z coordinates:");
        Utils.addLayerChoice("Base layer", ref_layer, gdd);
        gdd.addChoice("Column separator: ", separators, separators[0]);
        // default: 60 nm
        gdd.addNumericField("Layer thickness: ", cal.pixelDepth, 2);
        gdd.addNumericField("Calibration (data to pixels): ", 1, 2);
        gdd.addCheckbox("Homogenize contrast layer-wise", homogenize_contrast_);
        gdd.addSlider("Scale:", 0, 100, 100);
        gdd.addNumericField("Hide border with alpha mask", 0, 0, 6, "pixels");
        gdd.showDialog();
        if (gdd.wasCanceled())
            return null;
        layer_thickness_ = gdd.getNextNumber();
        if (layer_thickness_ < 0 || Double.isNaN(layer_thickness_)) {
            Utils.log("Improper layer thickness value.");
            return null;
        }
        calibration_ = gdd.getNextNumber();
        if (0 == calibration_ || Double.isNaN(calibration_)) {
            Utils.log("Improper calibration value.");
            return null;
        }
        // not pixelDepth!
        layer_thickness_ /= cal.pixelWidth;
        ref_layer = ref_layer.getParent().getLayer(gdd.getNextChoiceIndex());
        column_separator_ = "\t";
        switch(gdd.getNextChoiceIndex()) {
            case 1:
                column_separator_ = " ";
                break;
            case 2:
                column_separator_ = ",";
                break;
            default:
                break;
        }
        homogenize_contrast_ = gdd.getNextBoolean();
        final double sc = gdd.getNextNumber();
        if (Double.isNaN(sc))
            scale_ = 1.0f;
        else
            scale_ = ((float) sc) / 100.0f;
        final int border = (int) gdd.getNextNumber();
        if (border < 0) {
            Utils.log("Nonsensical border value: " + border);
            return null;
        }
        border_width_ = border;
    }
    if (Float.isNaN(scale_) || scale_ < 0 || scale_ > 1) {
        Utils.log("Non-sensical scale: " + scale_ + "\nUsing scale of 1 instead.");
        scale_ = 1;
    }
    // make vars accessible from inner threads:
    final Layer base_layer = ref_layer;
    final String abs_text_file_path = abs_text_file_path_;
    final String column_separator = column_separator_;
    final double layer_thickness = layer_thickness_;
    final double calibration = calibration_;
    final boolean homogenize_contrast = homogenize_contrast_;
    final float scale = (float) scale_;
    final int border_width = border_width_;
    return Bureaucrat.createAndStart(new Worker.Task("Importing images", true) {

        @Override
        public void exec() {
            try {
                // 1 - read text file
                final String[] lines = Utils.openTextFileLines(abs_text_file_path);
                if (null == lines || 0 == lines.length) {
                    Utils.log2("No images to import from " + abs_text_file_path);
                    return;
                }
                ContrastEnhancerWrapper cew = null;
                if (homogenize_contrast) {
                    cew = new ContrastEnhancerWrapper();
                    cew.showDialog();
                }
                final String sep2 = column_separator + column_separator;
                // 2 - set a base dir path if necessary
                String base_dir = null;
                // to wait on mipmap regeneration
                final Vector<Future<?>> fus = new Vector<Future<?>>();
                final LayerSet layer_set = base_layer.getParent();
                final double z_zero = base_layer.getZ();
                final AtomicInteger n_imported = new AtomicInteger(0);
                final Set<Layer> touched_layers = new HashSet<Layer>();
                final int NP = Runtime.getRuntime().availableProcessors();
                int np = NP;
                switch(np) {
                    case 1:
                    case 2:
                        break;
                    default:
                        np = np / 2;
                        break;
                }
                final ExecutorService ex = Utils.newFixedThreadPool(np, "import-images");
                final List<Future<?>> imported = new ArrayList<Future<?>>();
                final Worker wo = this;
                final String script_path;
                // If scale is at least 1/100 lower than 1, then:
                if (Math.abs(scale - (int) scale) > 0.01) {
                    // Assume source and target sigma of 0.5
                    final double sigma = Math.sqrt(Math.pow(1 / scale, 2) - 0.25);
                    final String script = new StringBuilder().append("import ij.ImagePlus;\n").append("import ij.process.ImageProcessor;\n").append("import ij.plugin.filter.GaussianBlur;\n").append("GaussianBlur blur = new GaussianBlur();\n").append(// as in ij.plugin.filter.GaussianBlur
                    "double accuracy = (imp.getType() == ImagePlus.GRAY8 || imp.getType() == ImagePlus.COLOR_RGB) ? 0.002 : 0.0002;\n").append("imp.getProcessor().setInterpolationMethod(ImageProcessor.NONE);\n").append("blur.blurGaussian(imp.getProcessor(),").append(sigma).append(',').append(sigma).append(",accuracy);\n").append("imp.setProcessor(imp.getTitle(), imp.getProcessor().resize((int)(imp.getWidth() * ").append(scale).append("), (int)(imp.getHeight() * ").append(scale).append(")));").toString();
                    File f = new File(getStorageFolder() + "resize-" + scale + ".bsh");
                    int v = 1;
                    while (f.exists()) {
                        f = new File(getStorageFolder() + "resize-" + scale + "." + v + ".bsh");
                        v++;
                    }
                    script_path = Utils.saveToFile(f, script) ? f.getAbsolutePath() : null;
                    if (null == script_path) {
                        Utils.log("Could NOT save a preprocessor script for image scaling\nat path " + f.getAbsolutePath());
                    }
                } else {
                    script_path = null;
                }
                Utils.log("Scaling script path is " + script_path);
                final AtomicReference<Triple<Integer, Integer, ByteProcessor>> last_mask = new AtomicReference<Triple<Integer, Integer, ByteProcessor>>();
                // 3 - parse each line
                for (int i = 0; i < lines.length; i++) {
                    if (Thread.currentThread().isInterrupted() || hasQuitted()) {
                        this.quit();
                        return;
                    }
                    // process line
                    // first thing is the backslash removal, before they get processed at all
                    String line = lines[i].replace('\\', '/').trim();
                    final int ic = line.indexOf('#');
                    // remove comment at end of line if any
                    if (-1 != ic)
                        line = line.substring(0, ic);
                    if (0 == line.length() || '#' == line.charAt(0))
                        continue;
                    // reduce line, so that separators are really unique
                    while (-1 != line.indexOf(sep2)) {
                        line = line.replaceAll(sep2, column_separator);
                    }
                    final String[] column = line.split(column_separator);
                    if (column.length < 4) {
                        Utils.log("Less than 4 columns: can't import from line " + i + " : " + line);
                        continue;
                    }
                    // obtain coordinates
                    double x = 0, y = 0, z = 0;
                    try {
                        x = Double.parseDouble(column[1].trim());
                        y = Double.parseDouble(column[2].trim());
                        z = Double.parseDouble(column[3].trim());
                    } catch (final NumberFormatException nfe) {
                        Utils.log("Non-numeric value in a numeric column at line " + i + " : " + line);
                        continue;
                    }
                    x *= calibration;
                    y *= calibration;
                    z = z * calibration + z_zero;
                    // obtain path
                    String path = column[0].trim();
                    if (0 == path.length())
                        continue;
                    // check if path is relative
                    if ((!IJ.isWindows() && '/' != path.charAt(0)) || (IJ.isWindows() && 1 != path.indexOf(":/"))) {
                        // path is relative.
                        if (null == base_dir) {
                            // may not be null if another thread that got the lock first set it to non-null
                            // Ask for source directory
                            final DirectoryChooser dc = new DirectoryChooser("Choose source directory");
                            final String dir = dc.getDirectory();
                            if (null == dir) {
                                // quit all threads
                                return;
                            }
                            base_dir = Utils.fixDir(dir);
                        }
                    }
                    if (null != base_dir)
                        path = base_dir + path;
                    final File f = new File(path);
                    if (!f.exists()) {
                        Utils.log("No file found for path " + path);
                        continue;
                    }
                    // will create a new Layer if necessary
                    final Layer layer = layer_set.getLayer(z, layer_thickness, true);
                    touched_layers.add(layer);
                    final String imagefilepath = path;
                    final double xx = x * scale;
                    final double yy = y * scale;
                    final Callable<Patch> creator;
                    if (column.length >= 9) {
                        creator = new Callable<Patch>() {

                            private final int parseInt(final String t) {
                                if (t.equals("-"))
                                    return -1;
                                return Integer.parseInt(t);
                            }

                            private final double parseDouble(final String t) {
                                if (t.equals("-"))
                                    return Double.NaN;
                                return Double.parseDouble(t);
                            }

                            @Override
                            public Patch call() throws Exception {
                                int o_width = parseInt(column[4].trim());
                                int o_height = parseInt(column[5].trim());
                                double min = parseDouble(column[6].trim());
                                double max = parseDouble(column[7].trim());
                                int type = parseInt(column[8].trim());
                                if (-1 == type || -1 == o_width || -1 == o_height) {
                                    // Read them from the file header
                                    final ImageFileHeader ifh = new ImageFileHeader(imagefilepath);
                                    o_width = ifh.width;
                                    o_height = ifh.height;
                                    type = ifh.type;
                                    if (!ifh.isSupportedType()) {
                                        Utils.log("Incompatible image type: " + imagefilepath);
                                        return null;
                                    }
                                }
                                ImagePlus imp = null;
                                if (Double.isNaN(min) || Double.isNaN(max)) {
                                    imp = openImagePlus(imagefilepath);
                                    min = imp.getProcessor().getMin();
                                    max = imp.getProcessor().getMax();
                                }
                                final Patch patch = new Patch(layer.getProject(), new File(imagefilepath).getName(), o_width, o_height, o_width, o_height, type, 1.0f, Color.yellow, false, min, max, new AffineTransform(1, 0, 0, 1, xx, yy), imagefilepath);
                                if (null != script_path && null != imp) {
                                    // For use in setting the preprocessor script
                                    cacheImagePlus(patch.getId(), imp);
                                }
                                return patch;
                            }
                        };
                    } else {
                        creator = new Callable<Patch>() {

                            @Override
                            public Patch call() throws Exception {
                                IJ.redirectErrorMessages();
                                final ImageFileHeader ifh = new ImageFileHeader(imagefilepath);
                                final int o_width = ifh.width;
                                final int o_height = ifh.height;
                                final int type = ifh.type;
                                if (!ifh.isSupportedType()) {
                                    Utils.log("Incompatible image type: " + imagefilepath);
                                    return null;
                                }
                                double min = 0;
                                double max = 255;
                                switch(type) {
                                    case ImagePlus.GRAY16:
                                    case ImagePlus.GRAY32:
                                        // Determine suitable min and max
                                        // TODO Stream through the image, do not load it!
                                        final ImagePlus imp = openImagePlus(imagefilepath);
                                        if (null == imp) {
                                            Utils.log("Ignoring unopenable image from " + imagefilepath);
                                            return null;
                                        }
                                        min = imp.getProcessor().getMin();
                                        max = imp.getProcessor().getMax();
                                        break;
                                }
                                // add Patch
                                final Patch patch = new Patch(layer.getProject(), new File(imagefilepath).getName(), o_width, o_height, o_width, o_height, type, 1.0f, Color.yellow, false, min, max, new AffineTransform(1, 0, 0, 1, xx, yy), imagefilepath);
                                return patch;
                            }
                        };
                    }
                    // Otherwise, images would end up loaded twice for no reason
                    if (0 == (i % (NP + NP))) {
                        final ArrayList<Future<?>> a = new ArrayList<Future<?>>(NP + NP);
                        synchronized (fus) {
                            // .add is also synchronized, fus is a Vector
                            int k = 0;
                            while (!fus.isEmpty() && k < NP) {
                                a.add(fus.remove(0));
                                k++;
                            }
                        }
                        for (final Future<?> fu : a) {
                            try {
                                if (wo.hasQuitted())
                                    return;
                                fu.get();
                            } catch (final Throwable t) {
                                t.printStackTrace();
                            }
                        }
                    }
                    imported.add(ex.submit(new Runnable() {

                        @Override
                        public void run() {
                            if (wo.hasQuitted())
                                return;
                            /* */
                            IJ.redirectErrorMessages();
                            Patch patch;
                            try {
                                patch = creator.call();
                            } catch (final Exception e) {
                                e.printStackTrace();
                                Utils.log("Could not load patch from " + imagefilepath);
                                return;
                            }
                            // Set the script if any
                            if (null != script_path) {
                                try {
                                    patch.setPreprocessorScriptPath(script_path);
                                } catch (final Throwable t) {
                                    Utils.log("FAILED to set a scaling preprocessor script to patch " + patch);
                                    IJError.print(t);
                                }
                            }
                            // Set an alpha mask to crop away the borders
                            if (border_width > 0) {
                                final Triple<Integer, Integer, ByteProcessor> m = last_mask.get();
                                if (null != m && m.a == patch.getOWidth() && m.b == patch.getOHeight()) {
                                    // Reuse
                                    patch.setAlphaMask(m.c);
                                } else {
                                    // Create new mask
                                    final ByteProcessor mask = new ByteProcessor(patch.getOWidth(), patch.getOHeight());
                                    mask.setValue(255);
                                    mask.setRoi(new Roi(border_width, border_width, mask.getWidth() - 2 * border_width, mask.getHeight() - 2 * border_width));
                                    mask.fill();
                                    patch.setAlphaMask(mask);
                                    // Store as last
                                    last_mask.set(new Triple<Integer, Integer, ByteProcessor>(mask.getWidth(), mask.getHeight(), mask));
                                }
                            }
                            if (!homogenize_contrast) {
                                fus.add(regenerateMipMaps(patch));
                            }
                            synchronized (layer) {
                                layer.add(patch, true);
                            }
                            wo.setTaskName("Imported " + (n_imported.incrementAndGet() + 1) + "/" + lines.length);
                        }
                    }));
                }
                Utils.wait(imported);
                ex.shutdown();
                if (0 == n_imported.get()) {
                    Utils.log("No images imported.");
                    return;
                }
                base_layer.getParent().setMinimumDimensions();
                Display.repaint(base_layer.getParent());
                recreateBuckets(touched_layers);
                if (homogenize_contrast) {
                    setTaskName("Enhance contrast");
                    // layer-wise (layer order is irrelevant):
                    cew.applyLayerWise(touched_layers);
                    cew.shutdown();
                }
                Utils.wait(fus);
            } catch (final Exception e) {
                IJError.print(e);
            }
        }
    }, base_layer.getProject());
}
Also used : ByteProcessor(ij.process.ByteProcessor) Set(java.util.Set) HashSet(java.util.HashSet) LayerSet(ini.trakem2.display.LayerSet) ArrayList(java.util.ArrayList) Callable(java.util.concurrent.Callable) GenericDialog(ij.gui.GenericDialog) Worker(ini.trakem2.utils.Worker) AreaList(ini.trakem2.display.AreaList) ArrayList(java.util.ArrayList) List(java.util.List) Vector(java.util.Vector) LayerSet(ini.trakem2.display.LayerSet) AtomicReference(java.util.concurrent.atomic.AtomicReference) ImageFileHeader(ini.trakem2.io.ImageFileHeader) Calibration(ij.measure.Calibration) Layer(ini.trakem2.display.Layer) ImagePlus(ij.ImagePlus) Roi(ij.gui.Roi) IOException(java.io.IOException) FormatException(loci.formats.FormatException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Triple(mpicbg.trakem2.util.Triple) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ExecutorService(java.util.concurrent.ExecutorService) Future(java.util.concurrent.Future) AffineTransform(java.awt.geom.AffineTransform) ContrastEnhancerWrapper(ini.trakem2.imaging.ContrastEnhancerWrapper) File(java.io.File) Patch(ini.trakem2.display.Patch) DirectoryChooser(ij.io.DirectoryChooser)

Example 14 with Worker

use of ini.trakem2.utils.Worker in project TrakEM2 by trakem2.

the class DistortionCorrectionTask method run.

public static final void run(final CorrectDistortionFromSelectionParam p, final List<Patch> patches, final Displayable active, final Layer layer, final Worker worker) {
    /* no multiple inheritance, so p cannot be an Align.ParamOptimize, working around legacy by copying data into one ... */
    final Align.ParamOptimize ap = new Align.ParamOptimize();
    ap.sift.set(p.sift);
    ap.desiredModelIndex = p.desiredModelIndex;
    ap.expectedModelIndex = p.expectedModelIndex;
    ap.maxEpsilon = p.maxEpsilon;
    ap.minInlierRatio = p.minInlierRatio;
    ap.rod = p.rod;
    ap.identityTolerance = p.identityTolerance;
    ap.lambda = p.lambdaRegularize;
    ap.maxIterations = p.maxIterationsOptimize;
    ap.maxPlateauwidth = p.maxPlateauwidthOptimize;
    ap.minNumInliers = p.minNumInliers;
    ap.regularize = p.regularize;
    ap.regularizerModelIndex = p.regularizerIndex;
    ap.rejectIdentity = p.rejectIdentity;
    /**
     * Get all patches that will be affected.
     */
    final List<Patch> allPatches = new ArrayList<Patch>();
    for (final Layer l : layer.getParent().getLayers().subList(p.firstLayerIndex, p.lastLayerIndex + 1)) for (final Displayable d : l.getDisplayables(Patch.class)) allPatches.add((Patch) d);
    /**
     * Unset the coordinate transforms of all patches if desired.
     */
    if (p.clearTransform) {
        if (worker != null)
            worker.setTaskName("Clearing present transforms");
        setCoordinateTransform(allPatches, null, Runtime.getRuntime().availableProcessors());
        Display.repaint();
    }
    if (worker != null)
        worker.setTaskName("Establishing SIFT correspondences");
    final List<AbstractAffineTile2D<?>> tiles = new ArrayList<AbstractAffineTile2D<?>>();
    final List<AbstractAffineTile2D<?>> fixedTiles = new ArrayList<AbstractAffineTile2D<?>>();
    final List<Patch> fixedPatches = new ArrayList<Patch>();
    if (active != null && active instanceof Patch)
        fixedPatches.add((Patch) active);
    Align.tilesFromPatches(ap, patches, fixedPatches, tiles, fixedTiles);
    final List<AbstractAffineTile2D<?>[]> tilePairs = new ArrayList<AbstractAffineTile2D<?>[]>();
    if (p.tilesAreInPlace)
        AbstractAffineTile2D.pairOverlappingTiles(tiles, tilePairs);
    else
        AbstractAffineTile2D.pairTiles(tiles, tilePairs);
    AbstractAffineTile2D<?> fixedTile = null;
    if (fixedTiles.size() > 0)
        fixedTile = fixedTiles.get(0);
    else
        fixedTile = tiles.get(0);
    Align.connectTilePairs(ap, tiles, tilePairs, p.maxNumThreadsSift, p.multipleHypotheses);
    /**
     * Shift all local coordinates into the original image frame
     */
    for (final AbstractAffineTile2D<?> tile : tiles) {
        final Rectangle box = tile.getPatch().getCoordinateTransformBoundingBox();
        for (final PointMatch m : tile.getMatches()) {
            final double[] l = m.getP1().getL();
            final double[] w = m.getP1().getW();
            l[0] += box.x;
            l[1] += box.y;
            w[0] = l[0];
            w[1] = l[1];
        }
    }
    if (Thread.currentThread().isInterrupted())
        return;
    final List<Set<Tile<?>>> graphs = AbstractAffineTile2D.identifyConnectedGraphs(tiles);
    if (graphs.size() > 1)
        Utils.log("Could not interconnect all images with correspondences.  ");
    final List<AbstractAffineTile2D<?>> interestingTiles;
    /**
     * Find largest graph.
     */
    Set<Tile<?>> largestGraph = null;
    for (final Set<Tile<?>> graph : graphs) if (largestGraph == null || largestGraph.size() < graph.size())
        largestGraph = graph;
    interestingTiles = new ArrayList<AbstractAffineTile2D<?>>();
    for (final Tile<?> t : largestGraph) interestingTiles.add((AbstractAffineTile2D<?>) t);
    if (Thread.currentThread().isInterrupted())
        return;
    Utils.log("Estimating lens model:");
    /* initialize with pure affine */
    Align.optimizeTileConfiguration(ap, interestingTiles, fixedTiles);
    /* measure the current error */
    double e = 0;
    int n = 0;
    for (final AbstractAffineTile2D<?> t : interestingTiles) for (final PointMatch pm : t.getMatches()) {
        e += pm.getDistance();
        ++n;
    }
    e /= n;
    double dEpsilon_i = 0;
    double epsilon_i = e;
    double dEpsilon_0 = 0;
    NonLinearTransform lensModel = null;
    Utils.log("0: epsilon = " + e);
    /* Store original point locations */
    final HashMap<Point, Point> originalPoints = new HashMap<Point, Point>();
    for (final AbstractAffineTile2D<?> t : interestingTiles) for (final PointMatch pm : t.getMatches()) originalPoints.put(pm.getP1(), pm.getP1().clone());
    /* ad hoc conditions to terminate iteration:
		 * small improvement ( 1/1000) relative to first iteration
		 * less than 20 iterations
		 * at least 2 iterations */
    for (int i = 1; i < 20 && (i < 2 || dEpsilon_i <= dEpsilon_0 / 1000); ++i) {
        if (Thread.currentThread().isInterrupted())
            return;
        /* Some data shuffling for the lens correction interface */
        final List<PointMatchCollectionAndAffine> matches = new ArrayList<PointMatchCollectionAndAffine>();
        for (final AbstractAffineTile2D<?>[] tilePair : tilePairs) {
            final AffineTransform a = tilePair[0].createAffine();
            a.preConcatenate(tilePair[1].getModel().createInverseAffine());
            final Collection<PointMatch> commonMatches = new ArrayList<PointMatch>();
            tilePair[0].commonPointMatches(tilePair[1], commonMatches);
            final Collection<PointMatch> originalCommonMatches = new ArrayList<PointMatch>();
            for (final PointMatch pm : commonMatches) originalCommonMatches.add(new PointMatch(originalPoints.get(pm.getP1()), originalPoints.get(pm.getP2())));
            matches.add(new PointMatchCollectionAndAffine(a, originalCommonMatches));
        }
        if (worker != null)
            worker.setTaskName("Estimating lens distortion correction");
        lensModel = Distortion_Correction.createInverseDistortionModel(matches, p.dimension, p.lambda, (int) fixedTile.getWidth(), (int) fixedTile.getHeight());
        /* update local points */
        for (final AbstractAffineTile2D<?> t : interestingTiles) for (final PointMatch pm : t.getMatches()) {
            final Point currentPoint = pm.getP1();
            final Point originalPoint = originalPoints.get(currentPoint);
            final double[] l = currentPoint.getL();
            final double[] lo = originalPoint.getL();
            l[0] = lo[0];
            l[1] = lo[1];
            lensModel.applyInPlace(l);
        }
        /* re-optimize */
        Align.optimizeTileConfiguration(ap, interestingTiles, fixedTiles);
        /* measure the current error */
        e = 0;
        n = 0;
        for (final AbstractAffineTile2D<?> t : interestingTiles) for (final PointMatch pm : t.getMatches()) {
            e += pm.getDistance();
            ++n;
        }
        e /= n;
        dEpsilon_i = e - epsilon_i;
        epsilon_i = e;
        if (i == 1)
            dEpsilon_0 = dEpsilon_i;
        Utils.log(i + ": epsilon = " + e);
        Utils.log(i + ": delta epsilon = " + dEpsilon_i);
    }
    if (lensModel != null) {
        if (p.visualize) {
            if (Thread.currentThread().isInterrupted())
                return;
            if (worker != null)
                worker.setTaskName("Visualizing lens distortion correction");
            lensModel.visualizeSmall(p.lambda);
        }
        if (worker != null)
            worker.setTaskName("Applying lens distortion correction");
        appendCoordinateTransform(allPatches, lensModel, Runtime.getRuntime().availableProcessors());
        Utils.log("Done.");
    } else
        Utils.log("No lens model found.");
}
Also used : Set(java.util.Set) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) PointMatchCollectionAndAffine(lenscorrection.Distortion_Correction.PointMatchCollectionAndAffine) Displayable(ini.trakem2.display.Displayable) Align(mpicbg.trakem2.align.Align) AbstractAffineTile2D(mpicbg.trakem2.align.AbstractAffineTile2D) Tile(mpicbg.models.Tile) Point(mpicbg.models.Point) Layer(ini.trakem2.display.Layer) Point(mpicbg.models.Point) PointMatch(mpicbg.models.PointMatch) AffineTransform(java.awt.geom.AffineTransform) Patch(ini.trakem2.display.Patch)

Example 15 with Worker

use of ini.trakem2.utils.Worker in project TrakEM2 by trakem2.

the class Compare method variabilityAnalysis.

/**
 * @param reference_project If null, then the first one found in the Project.getProjects() lists is used.
 * @param regex A String (can be null) to filter objects by, to limit what gets processed.
 *              If regex is not null, then only ProjectThing nodes with the matching regex are analyzed (shallow: none of their children are questioned, but pipes will be built from them all).
 * @param generate_plots Whether to generate the variability plots at all.
 * @param show_plots If generate_plots, whether to show the plots in a stack image window or to save them.
 * @param show_3D Whether to show any 3D data.
 * @param show_condensed_3D If show_3D, whether to show the condensed vector strings, i.e. the "average" pipes.
 * @param show_sources_3D If show_3D, whether to show the source pipes from which the condensed vector string was generated.
 * @param sources_color_table Which colors to give to the pipes of which Project.
 * @param show_envelope_3D If show_3D, whether to generate the variability envelope.
 * @param envelope_alpha If show_envelope_3D, the envelope takes an alpha value between 0 (total transparency) and 1 (total opacity)
 * @param delta_envelope The delta to resample the envelope to. When smaller than or equal to 1, no envelope resampling occurs.
 * @param show_axes_3D If show_3D, whether to display the reference axes as well.
 * @param heat_map If show_3D, whether to color the variability with a Fire LUT.
 *                 If not show_condensed_3D, then the variability is shown in color-coded 3D spheres placed at the entry point to the neuropile.
 * @param map_condensed If not null, all VectorString3D are put into this map.
 * @param projects The projects to use.
 */
public static Bureaucrat variabilityAnalysis(final Project reference_project, final String regex, final String[] ignore, final boolean show_cata_dialog, final boolean generate_plots, final boolean show_plots, final String plot_dir_, final boolean show_3D, final boolean show_condensed_3D, final boolean show_sources_3D, final Map<Project, Color> sources_color_table, final boolean show_envelope_3D, final float envelope_alpha, final double delta_envelope, final int envelope_type, final boolean show_axes_3D, final boolean heat_map, final Map<String, VectorString3D> map_condensed, final Project[] projects) {
    // gather all open projects
    final Project[] p = null == projects ? Project.getProjects().toArray(new Project[0]) : projects;
    // make the reference_project be the first in the array
    if (null != reference_project && reference_project != p[0]) {
        for (int i = 0; i < p.length; i++) {
            if (reference_project == p[i]) {
                p[i] = p[0];
                p[0] = reference_project;
                break;
            }
        }
    }
    final Worker worker = new Worker("Comparing all to all") {

        @Override
        public void run() {
            startedWorking();
            try {
                Utils.log2("Asking for CATAParameters...");
                final CATAParameters cp = new CATAParameters();
                cp.regex = regex;
                cp.delta_envelope = delta_envelope;
                cp.envelope_type = envelope_type;
                if (show_cata_dialog && !cp.setup(false, regex, true, true)) {
                    finishedWorking();
                    return;
                }
                // so source points are stored in VectorString3D for each resampled and interpolated point
                cp.with_source = true;
                // Store a series of results, depending on options
                final HashMap<String, Display3D> results = new HashMap<String, Display3D>();
                String plot_dir = plot_dir_;
                if (generate_plots && !show_plots) {
                    // Save plots
                    if (null == plot_dir) {
                        final DirectoryChooser dc = new DirectoryChooser("Choose plots directory");
                        plot_dir = dc.getDirectory();
                        if (null == plot_dir) {
                            finishedWorking();
                            return;
                        }
                    }
                    if (IJ.isWindows())
                        plot_dir = plot_dir.replace('\\', '/');
                    if (!plot_dir.endsWith("/"))
                        plot_dir += "/";
                }
                Utils.log2("Gathering chains...");
                // Gather chains that do not match the ignore regexes
                // will transform them as well to the reference found in the first project in the p array
                Object[] ob = gatherChains(p, cp, ignore);
                ArrayList<Chain> chains = (ArrayList<Chain>) ob[0];
                // to keep track of each project's chains
                final ArrayList[] p_chains = (ArrayList[]) ob[1];
                ob = null;
                if (null == chains) {
                    finishedWorking();
                    return;
                }
                Utils.log2("Collecting bundles...");
                final HashMap<Project, HashMap<String, VectorString3D>> axes = new HashMap<Project, HashMap<String, VectorString3D>>();
                // Sort out into groups by unique names of lineage bundles
                final HashMap<String, ArrayList<Chain>> bundles = new HashMap<String, ArrayList<Chain>>();
                for (final Chain chain : chains) {
                    String title = chain.getCellTitle();
                    final String t = title.toLowerCase();
                    // unnamed
                    if (0 == t.indexOf('[') || 0 == t.indexOf('#'))
                        continue;
                    Utils.log("Accepting " + title);
                    title = title.substring(0, title.indexOf(' '));
                    // lineage bundle instance chains
                    ArrayList<Chain> bc = bundles.get(title);
                    if (null == bc) {
                        bc = new ArrayList<Chain>();
                        bundles.put(title, bc);
                    }
                    bc.add(chain);
                }
                Utils.log2("Found " + bundles.size() + " bundles.");
                chains = null;
                if (null != cp.regex && show_axes_3D && axes.size() < 3) {
                    // Must find the Mushroom Body lobes separately
                    final String cp_regex = cp.regex;
                    cp.regex = "mb";
                    final Object[] o = gatherChains(p, cp, ignore);
                    final ArrayList<Chain> lobes = (ArrayList<Chain>) o[0];
                    Utils.logAll("Found " + lobes.size() + " chains for lobes");
                    for (final Chain chain : lobes) {
                        final String t = chain.getCellTitle().toLowerCase();
                        if (-1 != t.indexOf("peduncle") || -1 != t.indexOf("medial lobe") || -1 != t.indexOf("dorsal lobe")) {
                            Utils.logAll("adding " + t);
                            final Project pr = chain.pipes.get(0).getProject();
                            HashMap<String, VectorString3D> m = axes.get(pr);
                            if (null == m) {
                                m = new HashMap<String, VectorString3D>();
                                axes.put(pr, m);
                            }
                            m.put(t, chain.vs);
                            continue;
                        }
                    }
                    cp.regex = cp_regex;
                } else {
                    Utils.logAll("Not: cp.regex = " + cp.regex + "  show_axes_3D = " + show_axes_3D + "  axes.size() = " + axes.size());
                }
                final HashMap<String, VectorString3D> condensed = new HashMap<String, VectorString3D>();
                Utils.log2("Condensing each bundle...");
                // Condense each into a single VectorString3D
                for (final Map.Entry<String, ArrayList<Chain>> entry : bundles.entrySet()) {
                    final ArrayList<Chain> bc = entry.getValue();
                    if (bc.size() < 2) {
                        Utils.log2("Skipping single: " + entry.getKey());
                        continue;
                    }
                    final VectorString3D[] vs = new VectorString3D[bc.size()];
                    for (int i = 0; i < vs.length; i++) vs[i] = bc.get(i).vs;
                    final VectorString3D c = condense(cp, vs, this);
                    c.setCalibration(p[0].getRootLayerSet().getCalibrationCopy());
                    condensed.put(entry.getKey(), c);
                    if (this.hasQuitted())
                        return;
                }
                // Store:
                if (null != map_condensed) {
                    map_condensed.putAll(condensed);
                }
                if (generate_plots) {
                    Utils.log2("Plotting stdDev for each condensed bundle...");
                    // Y axis: the stdDev at each point, computed from the group of points that contribute to each
                    for (final Map.Entry<String, VectorString3D> e : condensed.entrySet()) {
                        final String name = e.getKey();
                        final VectorString3D c = e.getValue();
                        final Plot plot = makePlot(cp, name, c);
                        // FAILS//plot.addLabel(10, cp.plot_height-5, name); // must be added after setting size
                        if (show_plots)
                            plot.show();
                        else if (null != plot_dir)
                            new FileSaver(plot.getImagePlus()).saveAsPng(plot_dir + name.replace('/', '-') + ".png");
                    }
                }
                if (show_3D) {
                    final HashMap<String, Color> heat_table = new HashMap<String, Color>();
                    if (heat_map || show_envelope_3D) {
                        // Create a Fire LUT
                        final ImagePlus lutimp = new ImagePlus("lut", new ByteProcessor(4, 4));
                        IJ.run(lutimp, "Fire", "");
                        final IndexColorModel icm = (IndexColorModel) lutimp.getProcessor().getColorModel();
                        final byte[] reds = new byte[256];
                        final byte[] greens = new byte[256];
                        final byte[] blues = new byte[256];
                        icm.getReds(reds);
                        icm.getGreens(greens);
                        icm.getBlues(blues);
                        final List<String> names = new ArrayList<String>(bundles.keySet());
                        Collections.sort(names);
                        // find max stdDev
                        double max = 0;
                        final HashMap<String, Double> heats = new HashMap<String, Double>();
                        for (final String name : names) {
                            final VectorString3D vs_merged = condensed.get(name);
                            if (null == vs_merged) {
                                Utils.logAll("WARNING could not find a condensed pipe for " + name);
                                continue;
                            }
                            final double[] stdDev = vs_merged.getStdDevAtEachPoint();
                            // double avg = 0;
                            // for (int i=0; i<stdDev.length; i++) avg += stdDev[i];
                            // avg = avg/stdDev.length;
                            Arrays.sort(stdDev);
                            // median is more representative than average
                            final double median = stdDev[stdDev.length / 2];
                            if (max < median)
                                max = median;
                            heats.put(name, median);
                        }
                        for (final Map.Entry<String, Double> e : heats.entrySet()) {
                            final String name = e.getKey();
                            final double median = e.getValue();
                            // scale between 0 and max to get a Fire LUT color:
                            int index = (int) ((median / max) * 255);
                            if (index > 255)
                                index = 255;
                            final Color color = new Color(0xff & reds[index], 0xff & greens[index], 0xff & blues[index]);
                            Utils.log2(new StringBuilder(name).append('\t').append(median).append('\t').append(reds[index]).append('\t').append(greens[index]).append('\t').append(blues[index]).toString());
                            heat_table.put(name, color);
                        }
                    }
                    final LayerSet common_ls = new LayerSet(p[0], -1, "Common", 10, 10, 0, 0, 0, 512, 512, false, 2, new AffineTransform());
                    final Display3D d3d = Display3D.get(common_ls);
                    float env_alpha = envelope_alpha;
                    if (env_alpha < 0) {
                        Utils.log2("WARNING envelope_alpha is invalid: " + envelope_alpha + "\n    Using 0.4f instead");
                        env_alpha = 0.4f;
                    } else if (env_alpha > 1)
                        env_alpha = 1.0f;
                    for (final String name : bundles.keySet()) {
                        final ArrayList<Chain> bc = bundles.get(name);
                        final VectorString3D vs_merged = condensed.get(name);
                        if (null == vs_merged) {
                            Utils.logAll("WARNING: could not find a condensed vs for " + name);
                            continue;
                        }
                        if (show_sources_3D) {
                            if (null != sources_color_table) {
                                final HashSet<String> titles = new HashSet<String>();
                                for (final Chain chain : bc) {
                                    final Color c = sources_color_table.get(chain.getRoot().getProject());
                                    final String title = chain.getCellTitle();
                                    String t = title;
                                    int i = 2;
                                    while (titles.contains(t)) {
                                        t = title + "-" + i;
                                        i += 1;
                                    }
                                    titles.add(t);
                                    Display3D.addMesh(common_ls, chain.vs, t, null != c ? c : Color.gray);
                                }
                            } else {
                                for (final Chain chain : bc) Display3D.addMesh(common_ls, chain.vs, chain.getCellTitle(), Color.gray);
                            }
                        }
                        if (show_condensed_3D) {
                            Display3D.addMesh(common_ls, vs_merged, name + "-condensed", heat_map ? heat_table.get(name) : Color.red);
                        }
                        if (show_envelope_3D) {
                            double[] widths = makeEnvelope(cp, vs_merged);
                            if (cp.delta_envelope > 1) {
                                vs_merged.addDependent(widths);
                                vs_merged.resample(cp.delta_envelope);
                                widths = vs_merged.getDependent(0);
                            }
                            Display3D.addMesh(common_ls, vs_merged, name + "-envelope", heat_map ? heat_table.get(name) : Color.red, widths, env_alpha);
                        } else if (heat_map) {
                            // Show spheres in place of envelopes, at the starting tip (neuropile entry point)
                            final double x = vs_merged.getPoints(0)[0];
                            final double y = vs_merged.getPoints(1)[0];
                            final double z = vs_merged.getPoints(2)[0];
                            final double r = 10;
                            final Color color = heat_table.get(name);
                            if (null == color) {
                                Utils.logAll("WARNING: heat table does not have a color for " + name);
                                continue;
                            }
                            final Content sphere = d3d.getUniverse().addMesh(ij3d.Mesh_Maker.createSphere(x, y, z, r), new Color3f(heat_table.get(name)), name + "-sphere", 1);
                        }
                    }
                    if (show_axes_3D) {
                        for (int i = 0; i < p.length; i++) {
                            final Map<String, VectorString3D> m = axes.get(p[i]);
                            if (null == m) {
                                Utils.log2("No axes found for project " + p[i]);
                                continue;
                            }
                            for (final Map.Entry<String, VectorString3D> e : m.entrySet()) {
                                Display3D.addMesh(common_ls, e.getValue(), e.getKey() + "-" + i, Color.gray);
                            }
                        }
                    }
                    results.put("d3d", Display3D.get(common_ls));
                }
                this.result = results;
                Utils.log2("Done.");
            } catch (final Exception e) {
                IJError.print(e);
            } finally {
                finishedWorking();
            }
        }
    };
    return Bureaucrat.createAndStart(worker, p[0]);
}
Also used : CATAParameters(ini.trakem2.analysis.Compare.CATAParameters) ByteProcessor(ij.process.ByteProcessor) HashMap(java.util.HashMap) Color3f(org.scijava.vecmath.Color3f) ArrayList(java.util.ArrayList) FileSaver(ij.io.FileSaver) Worker(ini.trakem2.utils.Worker) IndexColorModel(java.awt.image.IndexColorModel) HashSet(java.util.HashSet) LayerSet(ini.trakem2.display.LayerSet) Display3D(ini.trakem2.display.Display3D) Plot(ij.gui.Plot) Color(java.awt.Color) ImagePlus(ij.ImagePlus) Project(ini.trakem2.Project) VectorString3D(ini.trakem2.vector.VectorString3D) Content(ij3d.Content) AffineTransform(java.awt.geom.AffineTransform) Map(java.util.Map) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) DirectoryChooser(ij.io.DirectoryChooser)

Aggregations

Worker (ini.trakem2.utils.Worker)20 ArrayList (java.util.ArrayList)10 ImagePlus (ij.ImagePlus)9 Patch (ini.trakem2.display.Patch)8 File (java.io.File)8 GenericDialog (ij.gui.GenericDialog)7 Displayable (ini.trakem2.display.Displayable)7 Rectangle (java.awt.Rectangle)7 HashSet (java.util.HashSet)7 IOException (java.io.IOException)6 Future (java.util.concurrent.Future)6 FormatException (loci.formats.FormatException)6 DirectoryChooser (ij.io.DirectoryChooser)5 Project (ini.trakem2.Project)5 Layer (ini.trakem2.display.Layer)5 VectorString3D (ini.trakem2.vector.VectorString3D)5 HashMap (java.util.HashMap)5 AtomicInteger (java.util.concurrent.atomic.AtomicInteger)5 Calibration (ij.measure.Calibration)4 ZDisplayable (ini.trakem2.display.ZDisplayable)4