Search in sources :

Example 6 with Display

use of ini.trakem2.display.Display in project TrakEM2 by trakem2.

the class Display3D method show.

/**
 * Scan the {@link ProjectThing} children and assign the renderable ones to an existing {@link Display3D} for their {@link LayerSet}, or open a new one. If {@code true == wait && -1 != resample}, then the method returns only when the mesh/es have been added.
 */
public static Future<Vector<Future<Content>>> show(final ProjectThing pt, final boolean wait, final int resample) {
    if (null == pt)
        return null;
    final Future<Vector<Future<Content>>> fu = launchers.submit(new Callable<Vector<Future<Content>>>() {

        @Override
        public Vector<Future<Content>> call() {
            // Scan the given ProjectThing for 3D-viewable items
            // So: find arealist, pipe, ball, and profile_list types
            final HashSet<ProjectThing> hs = pt.findBasicTypeChildren();
            if (null == hs || 0 == hs.size()) {
                Utils.logAll("Node " + pt + " does not contain any 3D-displayable children");
                return null;
            }
            // Remove profile if it lives under a profile_list
            for (final Iterator<ProjectThing> it = hs.iterator(); it.hasNext(); ) {
                final ProjectThing pt = it.next();
                if (null != pt.getObject() && pt.getObject().getClass() == Profile.class && pt.getParent().getType().equals("profile_list")) {
                    it.remove();
                }
            }
            setWaitingCursor();
            // Start new scheduler to publish/add meshes to the 3D Viewer every 5 seconds and when done.
            final Hashtable<Display3D, Vector<Content>> contents = new Hashtable<Display3D, Vector<Content>>();
            final ScheduledExecutorService updater = Executors.newScheduledThreadPool(1);
            final AtomicInteger counter = new AtomicInteger();
            updater.scheduleWithFixedDelay(new Runnable() {

                @Override
                public void run() {
                    // Obtain a copy of the contents queue
                    final HashMap<Display3D, Vector<Content>> m = new HashMap<Display3D, Vector<Content>>();
                    synchronized (contents) {
                        m.putAll(contents);
                        contents.clear();
                    }
                    if (m.isEmpty())
                        return;
                    // Add all to the corresponding Display3D
                    for (final Map.Entry<Display3D, Vector<Content>> e : m.entrySet()) {
                        e.getKey().universe.addContentLater(e.getValue());
                        counter.getAndAdd(e.getValue().size());
                    }
                    Utils.showStatus(new StringBuilder("Rendered ").append(counter.get()).append('/').append(hs.size()).toString());
                }
            }, 100, 4000, TimeUnit.MILLISECONDS);
            // A list of all generated Content objects
            final Vector<Future<Content>> list = new Vector<Future<Content>>();
            for (final Iterator<ProjectThing> it = hs.iterator(); it.hasNext(); ) {
                // obtain the Displayable object under the node
                final ProjectThing child = it.next();
                final Object obc = child.getObject();
                final Displayable displ = obc.getClass().equals(String.class) ? null : (Displayable) obc;
                if (null != displ) {
                    if (displ.getClass().equals(Profile.class)) {
                        // handled by profile_list Thing
                        continue;
                    }
                    if (!displ.isVisible()) {
                        Utils.log("Skipping non-visible node " + displ);
                        continue;
                    }
                }
                // obtain the containing LayerSet
                final Display3D d3d;
                if (null != displ)
                    d3d = Display3D.get(displ.getLayerSet());
                else if (child.getType().equals("profile_list")) {
                    final ArrayList<ProjectThing> al_children = child.getChildren();
                    if (null == al_children || 0 == al_children.size())
                        continue;
                    // else, get the first Profile and get its LayerSet
                    d3d = Display3D.get(((Displayable) ((ProjectThing) al_children.get(0)).getObject()).getLayerSet());
                } else {
                    Utils.log("Don't know what to do with node " + child);
                    d3d = null;
                }
                if (null == d3d) {
                    Utils.log("Could not get a proper 3D display for node " + displ);
                    // java3D not installed most likely
                    return null;
                }
                boolean already;
                synchronized (d3d.ht_pt_meshes) {
                    already = d3d.ht_pt_meshes.containsKey(child);
                }
                if (already) {
                    if (child.getObject() instanceof ZDisplayable) {
                        Utils.log("Updating 3D view of " + child.getObject());
                    } else {
                        Utils.log("Updating 3D view of " + child);
                    }
                }
                list.add(d3d.executors.submit(new Callable<Content>() {

                    @Override
                    public Content call() {
                        Content c = null;
                        try {
                            c = d3d.createMesh(child, displ, resample).call();
                            Vector<Content> vc;
                            synchronized (contents) {
                                vc = contents.get(d3d);
                                if (null == vc)
                                    vc = new Vector<Content>();
                                contents.put(d3d, vc);
                            }
                            vc.add(c);
                        } catch (final Exception e) {
                            IJError.print(e);
                        }
                        return c;
                    }
                }));
                // If it's the last one:
                if (!it.hasNext()) {
                    // Add the concluding task, that waits on all and shuts down the scheduler
                    d3d.executors.submit(new Runnable() {

                        @Override
                        public void run() {
                            // Wait until all are done
                            for (final Future<Content> c : list) {
                                try {
                                    c.get();
                                } catch (final Throwable t) {
                                    IJError.print(t);
                                }
                            }
                            try {
                                // Shutdown scheduler and execute remaining tasks
                                for (final Runnable r : updater.shutdownNow()) {
                                    r.run();
                                }
                            } catch (final Throwable e) {
                                IJError.print(e);
                            }
                            // Reset cursor
                            doneWaiting();
                            Utils.showStatus(new StringBuilder("Done rendering ").append(counter.get()).append('/').append(hs.size()).toString());
                        }
                    });
                }
            }
            return list;
        }
    });
    if (wait && -1 != resample) {
        try {
            fu.get();
        } catch (final Throwable t) {
            IJError.print(t);
        }
    }
    return fu;
}
Also used : HashMap(java.util.HashMap) Iterator(java.util.Iterator) Vector(java.util.Vector) ProjectThing(ini.trakem2.tree.ProjectThing) HashSet(java.util.HashSet) ScheduledExecutorService(java.util.concurrent.ScheduledExecutorService) Hashtable(java.util.Hashtable) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) Content(ij3d.Content) Future(java.util.concurrent.Future) HashMap(java.util.HashMap) Map(java.util.Map)

Example 7 with Display

use of ini.trakem2.display.Display in project TrakEM2 by trakem2.

the class Display3D method removeFrom3D.

/**
 * Remove all basic type children contained in {@code pt} and its children, recursively.
 *
 * @param pt
 */
public static void removeFrom3D(final ProjectThing pt) {
    final HashSet<ProjectThing> hs = pt.findBasicTypeChildren();
    if (null == hs || 0 == hs.size()) {
        Utils.logAll("Nothing to remove from 3D.");
        return;
    }
    // Ignore Profile instances ("profile_list" takes care of them)
    for (final ProjectThing child : hs) {
        if (child.getByType().equals("profile"))
            continue;
        // Find the LayerSet
        LayerSet lset = null;
        if (child.getType().equals("profile_list")) {
            if (!child.hasChildren())
                continue;
            for (final ProjectThing p : child.getChildren()) {
                if (null != p.getObject() && p.getObject() instanceof Profile) {
                    lset = ((Displayable) p.getObject()).getLayerSet();
                    break;
                }
            }
            if (null == lset)
                continue;
        } else if (child.getType().equals("profile")) {
            // Taken care of by "profile list"
            continue;
        } else {
            final Displayable d = (Displayable) child.getObject();
            if (null == d) {
                Utils.log("Null object for ProjectThing " + child);
                continue;
            }
            lset = d.getLayerSet();
        }
        if (null == lset) {
            Utils.log("No LayerSet found for " + child);
            continue;
        }
        final Display3D d3d = getDisplay(lset);
        if (null == d3d) {
            Utils.log("No Display 3D found for " + child);
            // no Display3D window open
            continue;
        }
        final String oldTitle = d3d.ht_pt_meshes.remove(child);
        if (null == oldTitle) {
            Utils.log("Could not find a title for " + child);
            continue;
        }
        Utils.log("Removed from 3D view: " + oldTitle);
        d3d.getUniverse().removeContent(oldTitle);
    }
}
Also used : ProjectThing(ini.trakem2.tree.ProjectThing)

Example 8 with Display

use of ini.trakem2.display.Display in project TrakEM2 by trakem2.

the class ImageJCommandListener method commandExecuting.

// I know, I could create a hashtable and then map methods of this class to each command key ... this is just easier, and performance-wise nobody cares
// Or even a hastable with String command keys and then a number as value, and use a gigantic switch block. So much pain to write. WHAT I REALLY WANT is a switch that takes a String and is fast because it has its own hash setup.
public String commandExecuting(final String command) {
    // Utils.log2("Command: " + command);
    // 1 - check source
    ImagePlus current = WindowManager.getCurrentImage();
    // not a trakem2 display: continue happily
    if (!(current instanceof FakeImagePlus))
        return command;
    // 2 - identify project
    final FakeImagePlus fimp = (FakeImagePlus) current;
    final Display display = fimp.getDisplay();
    final LayerSet layer_set = display.getLayer().getParent();
    final Project project = display.getProject();
    final ProjectTree ptree = project.getProjectTree();
    final Displayable active = display.getActive();
    final Selection selection = display.getSelection();
    // FILE menu
    if (command.equals("Save")) {
        project.save();
        return null;
    } else // EDIT menu
    if (command.equals("Undo")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Cut")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Copy")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Copy to System")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Paste")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Clear")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Clear Outside")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Fill")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Draw")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Invert")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else // EDIT - SELECTION menu
    if (command.equals("Select All")) {
        if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
            selection.selectAll();
            return null;
        }
        return command;
    } else if (command.equals("Select None")) {
        if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
            display.select(null);
            return null;
        }
        return command;
    } else if (command.equals("Restore Selection")) {
        if (ProjectToolbar.SELECT == Toolbar.getToolId()) {
            selection.restore();
            return null;
        }
        return command;
    } else // IMAGE - TYPE menu
    if (command.equals("8-bit")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("16-bit")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("32-bit")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("8-bit Color")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("RGB Color")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("RGB Stack") || command.equals("HSB Stack")) {
        Utils.showMessage("Can't convert to " + command);
        return null;
    } else // IMAGE - ADJUST menu
    if (command.equals("Brightness/Contrast...")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Window/Level...")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Color Balance...")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Threshold...")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Size...")) {
        if (null != active)
            selection.specify();
        return null;
    } else if (command.equals("Canvas Size...")) {
        display.resizeCanvas();
        return null;
    } else // IMAGE menu
    if (command.equals("Show Info...")) {
        // TODO perhaps it should show only for images ...
        if (null == active) {
            ptree.showInfo(project.getRootProjectThing());
        } else {
            ProjectThing pt = project.findProjectThing(active);
            if (null != pt)
                ptree.showInfo(pt);
        }
        return null;
    } else // IMAGE - COLOR menu
    if (in(command, new String[] { "RGB Split", "RGB Merge...", "Stack to RGB", "Make Composite" })) {
        notAvailable(command);
        return null;
    } else if (command.equals("Show LUT")) {
        return setTempCurrentImage(command, active);
    } else if (command.equals("Edit LUT...")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("Average Color")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("RGB to CIELAB")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else if (command.equals("RGB to Luminance")) {
        // TODO forward to the active image, if any
        niy(command);
        return null;
    } else // IMAGE STACK menu
    if (in(command, new String[] { "Add Slice", "Delete Slice" })) {
        Utils.showMessage("Go to the Layer Tree and right-click to add/delete a layer.");
        return null;
    } else if (command.equals("Next Slice [>]")) {
        display.nextLayer(IJ.shiftKeyDown() ? Event.SHIFT_MASK : 0);
        return null;
    } else if (command.equals("Previous Slice [<]")) {
        display.previousLayer(IJ.shiftKeyDown() ? Event.SHIFT_MASK : 0);
        return null;
    } else if (in(command, new String[] { "Set Slice", "Images to Stack", "Stack to Images", "Make Montage..." })) {
        notAvailable(command);
        return null;
    } else if (command.equals("Reslice [/]...")) {
        // TODO
        niy(command);
        return null;
    } else if (command.equals("Z Project...")) {
        // TODO
        niy(command);
        return null;
    } else if (command.equals("3D Project...")) {
        // TODO
        niy(command);
        return null;
    } else if (command.equals("Plot Z-axis Profile")) {
        // TODO
        niy(command);
        return null;
    } else if (command.equals("Start Animation [\\]")) {
        // TODO
        niy(command);
        return null;
    } else if (command.equals("Stop Animation")) {
        // TODO
        niy(command);
        return null;
    } else // IMAGE menu again
    if (command.equals("Crop")) {
        notAvailable(command);
        return null;
    } else if (in(command, new String[] { "Translate...", "Scale..." })) {
        if (null != active)
            selection.specify();
        return null;
    } else if (command.equals("Duplicate...")) {
        if (null != active && active.getClass().equals(Patch.class)) {
            // TODO stacks?
            // 2.5 security factor: for awt in non-1.6.0 machines
            project.getLoader().releaseToFit((long) (project.getLoader().estimateImageFileSize((Patch) active, 0) * 2.5));
            new ImagePlus(active.getTitle(), ((Patch) active).getImageProcessor().duplicate()).show();
        }
        return null;
    } else if (command.equals("Rename...")) {
        if (null != active) {
            active.adjustProperties();
            Display.updateSelection();
        }
        return null;
    } else // IMAGE ROTATE menu
    if (command.equals("Flip Horizontally")) {
        selection.apply(2, new double[] { -1, 1 });
        return null;
    } else if (command.equals("Flip Vertically")) {
        selection.apply(2, new double[] { 1, -1 });
        return null;
    } else if (command.equals("Rotate 90 Degrees Right")) {
        selection.apply(1, new double[] { 90 });
        return null;
    } else if (command.equals("Rotate 90 Degrees Left")) {
        selection.apply(1, new double[] { -90 });
        return null;
    } else if (command.equals("Arbitrarily...")) {
        if (null != active)
            selection.specify();
        return null;
    } else // IMAGE ZOOM menu
    if (command.equals("To Selection")) {
        Roi roi = fimp.getRoi();
        if (null != roi) {
            Rectangle b = roi.getBounds();
            b.x -= b.width / 2;
            b.y -= b.height / 2;
            b.width *= 2;
            b.height *= 2;
            display.getCanvas().showCentered(b);
        }
        return null;
    } else if (command.equals("View 100%")) {
        // TODO
        niy(command);
        return null;
    } else // ANALYZE menu
    if (command.equals("Measure")) {
        // Minimal measurement: area of closed ROIs, length of unclosed ROIs, calibrated.
        Roi roi = fimp.getRoi();
        if (null != roi) {
            Calibration cal = fimp.getCalibration();
            AffineTransform caff = new AffineTransform();
            caff.scale(cal.pixelWidth, cal.pixelHeight);
            if (M.isAreaROI(roi)) {
                Area area = M.getArea(roi);
                area = area.createTransformedArea(caff);
                ResultsTable rt = Utils.createResultsTable("ROI area", new String[] { "area", "perimeter" });
                rt.incrementCounter();
                rt.addLabel("units", cal.getUnit());
                rt.addValue(0, Math.abs(AreaCalculations.area(area.getPathIterator(null))));
                rt.addValue(1, roi.getLength());
                rt.show("ROI area");
            } else {
                ResultsTable rt = Utils.createResultsTable("ROI length", new String[] { "length" });
                rt.incrementCounter();
                rt.addLabel("units", cal.getUnit());
                rt.addValue(0, roi.getLength());
                rt.show("ROI length");
            }
            return null;
        } else if (null != active) {
            // Measure the active displayable
            if (active.getClass() == Patch.class) {
                // measure like 'm' would in ImageJ for an image
                ImagePlus imp = ((Patch) active).getImagePlus();
                imp.setCalibration(active.getLayer().getParent().getCalibrationCopy());
                IJ.run(imp, "Measure", "");
            } else {
                // Call measure like ProjectThing does
                ProjectThing pt = active.getProject().findProjectThing(active);
                if (active instanceof Profile)
                    ((ProjectThing) pt.getParent()).measure();
                else
                    pt.measure();
            }
            return null;
        }
        Utils.log("Draw a ROI or select an object!");
        return null;
    } else if (in(command, new String[] { "Analyze Particles...", "Histogram", "Plot Profile", "Surface Plot...", "Color Inspector 3D", "3D Surface Plot", "Color Histogram" })) {
        return setTempCurrentImage(command, active);
    } else if (command.equals("Label")) {
        notAvailable(command);
        return null;
    } else // PLUGINS menu
    if (command.equals("Volume Viewer")) {
        return runOnVirtualLayerSet(command, layer_set, display);
    } else if (command.equals("3D Viewer")) {
        // it's virtual and non-caching, will appear as a regular ImageJ stack
        layer_set.createLayerStack(Displayable.class, ImagePlus.COLOR_RGB, display.getDisplayChannelAlphas()).getImagePlus().show();
        return command;
    } else // PROCESS menu and submenus
    if (in(command, new String[] { "FFT", "Fast FFT (2D/3D)" })) {
        return setTempCurrentImage(command, active);
    } else if (in(command, new String[] { "Bandpass Filter...", "Custom Filter...", "FD Math...", "Swap Quadrants", "Convolve...", "Gaussian Blur...", "Median...", "Mean...", "Minimum...", "Maximum...", "Unsharp Mask...", "Variance...", "Show Circular Masks...", "Subtract Background..." })) {
        return duplicate(command, active);
    } else if (in(command, new String[] { "Smooth", "Sharpen", "Find Edges", "Enhance Contrast", "Add Noise", "Add Specified Noise...", "Salt and Pepper", "Despeckle", "Remove Outliers...", "North", "Northeast", "East", "Southeast", "South", "Southwest", "West", "Northwest", "Make Binary", "Convert to Mask", "Find Maxima...", "Erode", "Dilate", "Open ", "Close-", "Outline", "Fill Holes", "Skeletonize", "Distance Map", "Ultimate Points", "Watershed", "Add...", "Subtract...", "Multiply...", "Divide...", "AND...", "OR...", "XOR...", "Min...", "Max...", "Gamma...", "Log", "Exp", "Square", "Square Root", "Reciprocal", "NaN Background", "Abs" })) {
        return duplicate(command, active);
    }
    /*else {
			// continue happily
			//Utils.log2("Skipping " + command);
		}*/
    // If it's part of "Save As", ignore it
    Menu menu = Menus.getSaveAsMenu();
    for (int i = menu.getItemCount() - 1; i > -1; i--) {
        if (command.equals(menu.getItem(i).getActionCommand())) {
            notAvailable(command);
            return null;
        }
    }
    // Give it back to ImageJ
    return command;
}
Also used : Rectangle(java.awt.Rectangle) Calibration(ij.measure.Calibration) ImagePlus(ij.ImagePlus) Roi(ij.gui.Roi) Project(ini.trakem2.Project) Area(java.awt.geom.Area) ProjectTree(ini.trakem2.tree.ProjectTree) AffineTransform(java.awt.geom.AffineTransform) ResultsTable(ij.measure.ResultsTable) Menu(java.awt.Menu) ProjectThing(ini.trakem2.tree.ProjectThing)

Example 9 with Display

use of ini.trakem2.display.Display in project TrakEM2 by trakem2.

the class Loader method makePrescaledTiles.

/**
 * Generate 256x256 tiles, as many as necessary, to cover the given srcRect, starting at max_scale. Designed to be slow but memory-capable.
 *
 * filename = z + "/" + row + "_" + column + "_" + s + ".jpg";
 *
 * row and column run from 0 to n stepsize 1
 * that is, row = y / ( 256 * 2^s ) and column = x / ( 256 * 2^s )
 *
 * z : z-level (slice)
 * x,y: the row and column
 * s: scale, which is 1 / (2^s), in integers: 0, 1, 2 ...
 *
 *  var MAX_S = Math.floor( Math.log( MAX_Y + 1 ) / Math.LN2 ) - Math.floor( Math.log( Y_TILE_SIZE ) / Math.LN2 ) - 1;
 *
 * The module should not be more than 5
 * At al levels, there should be an even number of rows and columns, except for the coarsest level.
 * The coarsest level should be at least 5x5 tiles.
 *
 * Best results obtained when the srcRect approaches or is a square. Black space will pad the right and bottom edges when the srcRect is not exactly a square.
 * Only the area within the srcRect is ever included, even if actual data exists beyond.
 *
 * @return The watcher thread, for joining purposes, or null if the dialog is canceled or preconditions are not passed.
 * @throws IllegalArgumentException if the type is not ImagePlus.GRAY8 or Imageplus.COLOR_RGB.
 */
public Bureaucrat makePrescaledTiles(final Layer[] layers, final Class<?> clazz, final Rectangle srcRect, double max_scale_, final int c_alphas, final int type, String target_dir, final boolean from_original_images, final Saver saver, final int tileSide) {
    if (null == layers || 0 == layers.length)
        return null;
    switch(type) {
        case ImagePlus.GRAY8:
        case ImagePlus.COLOR_RGB:
            break;
        default:
            throw new IllegalArgumentException("Can only export for web with 8-bit or RGB");
    }
    // choose target directory
    if (null == target_dir) {
        final DirectoryChooser dc = new DirectoryChooser("Choose target directory");
        target_dir = dc.getDirectory();
        if (null == target_dir)
            return null;
    }
    if (IJ.isWindows())
        target_dir = target_dir.replace('\\', '/');
    if (!target_dir.endsWith("/"))
        target_dir += "/";
    if (max_scale_ > 1) {
        Utils.log("Prescaled Tiles: using max scale of 1.0");
        // no point
        max_scale_ = 1;
    }
    final String dir = target_dir;
    final double max_scale = max_scale_;
    final Worker worker = new Worker("Creating prescaled tiles") {

        private void cleanUp() {
            finishedWorking();
        }

        @Override
        public void run() {
            startedWorking();
            try {
                // project name
                // String pname = layer[0].getProject().getTitle();
                // create 'z' directories if they don't exist: check and ask!
                // start with the highest scale level
                final int[] best = determineClosestPowerOfTwo(srcRect.width > srcRect.height ? srcRect.width : srcRect.height);
                final int edge_length = best[0];
                final int n_edge_tiles = edge_length / tileSide;
                Utils.log2("srcRect: " + srcRect);
                Utils.log2("edge_length, n_edge_tiles, best[1] " + best[0] + ", " + n_edge_tiles + ", " + best[1]);
                // thumbnail dimensions
                // LayerSet ls = layer[0].getParent();
                final double ratio = srcRect.width / (double) srcRect.height;
                double thumb_scale = 1.0;
                if (ratio >= 1) {
                    // width is larger or equal than height
                    thumb_scale = 192.0 / srcRect.width;
                } else {
                    thumb_scale = 192.0 / srcRect.height;
                }
                // Figure out layer indices, given that layers are not necessarily evenly spaced
                final TreeMap<Integer, Layer> indices = new TreeMap<Integer, Layer>();
                final ArrayList<Integer> missingIndices = new ArrayList<Integer>();
                final double resolution_z_px;
                final int smallestIndex, largestIndex;
                if (1 == layers.length) {
                    indices.put(0, layers[0]);
                    resolution_z_px = layers[0].getZ();
                    smallestIndex = 0;
                    largestIndex = 0;
                } else {
                    // Ensure layers are sorted by Z index and are unique pointers and unique in Z coordinate:
                    final TreeMap<Double, Layer> t = new TreeMap<Double, Layer>();
                    for (final Layer l1 : new HashSet<Layer>(Arrays.asList(layers))) {
                        final Layer l2 = t.get(l1.getZ());
                        if (null == l2) {
                            t.put(l1.getZ(), l1);
                        } else {
                            // Ignore the layer with less objects
                            if (l1.getDisplayables().size() > l2.getDisplayables().size()) {
                                t.put(l1.getZ(), l1);
                                Utils.log("Ignoring duplicate layer: " + l2);
                            }
                        }
                    }
                    // What is the mode thickness, measured by Z(i-1) - Z(i)?
                    // (Distance between the Z of two consecutive layers)
                    final HashMap<Double, Integer> counts = new HashMap<Double, Integer>();
                    final Layer prev = t.get(t.firstKey());
                    double modeThickness = 0;
                    int modeThicknessCount = 0;
                    for (final Layer la : t.tailMap(prev.getZ(), false).values()) {
                        // Thickness with 3-decimal precision only
                        final double d = ((int) ((la.getZ() - prev.getZ()) * 1000 + 0.5)) / 1000.0;
                        Integer c = counts.get(d);
                        // 
                        if (null == c)
                            c = 0;
                        ++c;
                        counts.put(d, c);
                        // 
                        if (c > modeThicknessCount) {
                            modeThicknessCount = c;
                            modeThickness = d;
                        }
                    }
                    // Not pixelDepth
                    resolution_z_px = modeThickness * prev.getParent().getCalibration().pixelWidth;
                    // Assign an index to each layer, approximating each layer at modeThickness intervals
                    for (final Layer la : t.values()) {
                        indices.put((int) (la.getZ() / modeThickness + 0.5), la);
                    }
                    // First and last
                    smallestIndex = indices.firstKey();
                    largestIndex = indices.lastKey();
                    Utils.logAll("indices: " + smallestIndex + ", " + largestIndex);
                    // Which indices are missing?
                    for (int i = smallestIndex + 1; i < largestIndex; ++i) {
                        if (!indices.containsKey(i)) {
                            missingIndices.add(i);
                        }
                    }
                }
                // JSON metadata for CATMAID
                {
                    final StringBuilder sb = new StringBuilder("{");
                    final LayerSet ls = layers[0].getParent();
                    final Calibration cal = ls.getCalibration();
                    sb.append("\"volume_width_px\": ").append(srcRect.width).append(',').append('\n').append("\"volume_height_px\": ").append(srcRect.height).append(',').append('\n').append("\"volume_sections\": ").append(largestIndex - smallestIndex + 1).append(',').append('\n').append("\"extension\": \"").append(saver.getExtension()).append('\"').append(',').append('\n').append("\"resolution_x\": ").append(cal.pixelWidth).append(',').append('\n').append("\"resolution_y\": ").append(cal.pixelHeight).append(',').append('\n').append("\"resolution_z\": ").append(resolution_z_px).append(',').append('\n').append("\"units\": \"").append(cal.getUnit()).append('"').append(',').append('\n').append("\"offset_x_px\": 0,\n").append("\"offset_y_px\": 0,\n").append("\"offset_z_px\": ").append(indices.get(indices.firstKey()).getZ() * cal.pixelWidth / cal.pixelDepth).append(',').append('\n').append("\"missing_layers\": [");
                    for (final Integer i : missingIndices) sb.append(i - smallestIndex).append(',');
                    // remove last comma
                    sb.setLength(sb.length() - 1);
                    sb.append("]}");
                    if (!Utils.saveToFile(new File(dir + "metadata.json"), sb.toString())) {
                        Utils.logAll("WARNING: could not save " + dir + "metadata.json\nThe contents was:\n" + sb.toString());
                    }
                }
                for (final Map.Entry<Integer, Layer> entry : indices.entrySet()) {
                    if (this.quit) {
                        cleanUp();
                        return;
                    }
                    final int index = entry.getKey() - smallestIndex;
                    final Layer layer = entry.getValue();
                    // 1 - create a directory 'z' named as the layer's index
                    String tile_dir = dir + index;
                    File fdir = new File(tile_dir);
                    final int tag = 1;
                    // Ensure there is a usable directory:
                    while (fdir.exists() && !fdir.isDirectory()) {
                        fdir = new File(tile_dir + "_" + tag);
                    }
                    if (!fdir.exists()) {
                        fdir.mkdir();
                        Utils.log("Created directory " + fdir);
                    }
                    // if the directory exists already just reuse it, overwritting its files if so.
                    final String tmp = fdir.getAbsolutePath().replace('\\', '/');
                    if (!tile_dir.equals(tmp))
                        Utils.log("\tWARNING: directory will not be in the standard location.");
                    // debug:
                    Utils.log2("tile_dir: " + tile_dir + "\ntmp: " + tmp);
                    tile_dir = tmp;
                    if (!tile_dir.endsWith("/"))
                        tile_dir += "/";
                    // 2 - create layer thumbnail, max 192x192
                    ImagePlus thumb = getFlatImage(layer, srcRect, thumb_scale, c_alphas, type, clazz, true);
                    saver.save(thumb, tile_dir + "small");
                    // ImageSaver.saveAsJpeg(thumb.getProcessor(), tile_dir + "small.jpg", jpeg_quality, ImagePlus.COLOR_RGB != type);
                    flush(thumb);
                    thumb = null;
                    // 3 - fill directory with tiles
                    if (edge_length < tileSide) {
                        // edge_length is the largest length of the tileSide x tileSide tile map that covers an area equal or larger than the desired srcRect (because all tiles have to be tileSide x tileSide in size)
                        // create single tile per layer
                        makeTile(layer, srcRect, max_scale, c_alphas, type, clazz, tile_dir + "0_0_0", saver);
                    } else {
                        // create pyramid of tiles
                        if (from_original_images) {
                            Utils.log("Exporting from web using original images");
                            // Create a giant 8-bit image of the whole layer from original images
                            double scale = 1;
                            Utils.log("Export srcRect: " + srcRect);
                            // WARNING: the snapshot will most likely be smaller than the virtual square image being chopped into tiles
                            ImageProcessor snapshot = null;
                            if (ImagePlus.COLOR_RGB == type) {
                                Utils.log("WARNING: ignoring alpha masks for 'use original images' and 'RGB color' options");
                                snapshot = Patch.makeFlatImage(type, layer, srcRect, scale, (ArrayList<Patch>) (List) layer.getDisplayables(Patch.class, true), Color.black, true);
                            } else if (ImagePlus.GRAY8 == type) {
                                // Respect alpha masks and display range:
                                Utils.log("WARNING: ignoring scale for 'use original images' and '8-bit' options");
                                snapshot = ExportUnsignedShort.makeFlatImage((ArrayList<Patch>) (List) layer.getDisplayables(Patch.class, true), srcRect, 0).convertToByte(true);
                            } else {
                                Utils.log("ERROR: don't know how to generate mipmaps for type '" + type + "'");
                                cleanUp();
                                return;
                            }
                            int scale_pow = 0;
                            int n_et = n_edge_tiles;
                            final ExecutorService exe = Utils.newFixedThreadPool("export-for-web");
                            final ArrayList<Future<?>> fus = new ArrayList<Future<?>>();
                            try {
                                while (n_et >= best[1]) {
                                    final int snapWidth = snapshot.getWidth();
                                    final int snapHeight = snapshot.getHeight();
                                    final ImageProcessor source = snapshot;
                                    for (int row = 0; row < n_et; row++) {
                                        for (int col = 0; col < n_et; col++) {
                                            final String path = new StringBuilder(tile_dir).append(row).append('_').append(col).append('_').append(scale_pow).toString();
                                            final int tileXStart = col * tileSide;
                                            final int tileYStart = row * tileSide;
                                            final int pixelOffset = tileYStart * snapWidth + tileXStart;
                                            fus.add(exe.submit(new Callable<Boolean>() {

                                                @Override
                                                public Boolean call() {
                                                    if (ImagePlus.GRAY8 == type) {
                                                        final byte[] pixels = (byte[]) source.getPixels();
                                                        final byte[] p = new byte[tileSide * tileSide];
                                                        for (int y = 0, sourceIndex = pixelOffset; y < tileSide && tileYStart + y < snapHeight; sourceIndex = pixelOffset + y * snapWidth, y++) {
                                                            final int offsetL = y * tileSide;
                                                            for (int x = 0; x < tileSide && tileXStart + x < snapWidth; sourceIndex++, x++) {
                                                                p[offsetL + x] = pixels[sourceIndex];
                                                            }
                                                        }
                                                        return saver.save(new ImagePlus(path, new ByteProcessor(tileSide, tileSide, p, GRAY_LUT)), path);
                                                    } else {
                                                        final int[] pixels = (int[]) source.getPixels();
                                                        final int[] p = new int[tileSide * tileSide];
                                                        for (int y = 0, sourceIndex = pixelOffset; y < tileSide && tileYStart + y < snapHeight; sourceIndex = pixelOffset + y * snapWidth, y++) {
                                                            final int offsetL = y * tileSide;
                                                            for (int x = 0; x < tileSide && tileXStart + x < snapWidth; sourceIndex++, x++) {
                                                                p[offsetL + x] = pixels[sourceIndex];
                                                            }
                                                        }
                                                        return saver.save(new ImagePlus(path, new ColorProcessor(tileSide, tileSide, p)), path);
                                                    }
                                                }
                                            }));
                                        }
                                    }
                                    // 
                                    scale_pow++;
                                    // works as magnification
                                    scale = 1 / Math.pow(2, scale_pow);
                                    n_et /= 2;
                                    // 
                                    Utils.wait(fus);
                                    fus.clear();
                                    // Scale snapshot in half with area averaging
                                    final ImageProcessor nextSnapshot;
                                    if (ImagePlus.GRAY8 == type) {
                                        nextSnapshot = new ByteProcessor((int) (srcRect.width * scale), (int) (srcRect.height * scale));
                                        final byte[] p1 = (byte[]) snapshot.getPixels();
                                        final byte[] p2 = (byte[]) nextSnapshot.getPixels();
                                        final int width1 = snapshot.getWidth();
                                        final int width2 = nextSnapshot.getWidth();
                                        final int height2 = nextSnapshot.getHeight();
                                        int i = 0;
                                        for (int y1 = 0, y2 = 0; y2 < height2; y1 += 2, y2++) {
                                            final int offset1a = y1 * width1;
                                            final int offset1b = (y1 + 1) * width1;
                                            for (int x1 = 0, x2 = 0; x2 < width2; x1 += 2, x2++) {
                                                p2[i++] = (byte) (((p1[offset1a + x1] & 0xff) + (p1[offset1a + x1 + 1] & 0xff) + (p1[offset1b + x1] & 0xff) + (p1[offset1b + x1 + 1] & 0xff)) / 4);
                                            }
                                        }
                                    } else {
                                        nextSnapshot = new ColorProcessor((int) (srcRect.width * scale), (int) (srcRect.height * scale));
                                        final int[] p1 = (int[]) snapshot.getPixels();
                                        final int[] p2 = (int[]) nextSnapshot.getPixels();
                                        final int width1 = snapshot.getWidth();
                                        final int width2 = nextSnapshot.getWidth();
                                        final int height2 = nextSnapshot.getHeight();
                                        int i = 0;
                                        for (int y1 = 0, y2 = 0; y2 < height2; y1 += 2, y2++) {
                                            final int offset1a = y1 * width1;
                                            final int offset1b = (y1 + 1) * width1;
                                            for (int x1 = 0, x2 = 0; x2 < width2; x1 += 2, x2++) {
                                                final int ka = p1[offset1a + x1], kb = p1[offset1a + x1 + 1], kc = p1[offset1b + x1], kd = p1[offset1b + x1 + 1];
                                                // Average each channel independently
                                                p2[i++] = (((// red
                                                ((ka >> 16) & 0xff) + ((kb >> 16) & 0xff) + ((kc >> 16) & 0xff) + ((kd >> 16) & 0xff)) / 4) << 16) + (((// green
                                                ((ka >> 8) & 0xff) + ((kb >> 8) & 0xff) + ((kc >> 8) & 0xff) + ((kd >> 8) & 0xff)) / 4) << 8) + (// blue
                                                (ka & 0xff) + (kb & 0xff) + (kc & 0xff) + (kd & 0xff)) / 4;
                                            }
                                        }
                                    }
                                    // Assign for next iteration
                                    snapshot = nextSnapshot;
                                // Scale snapshot with a TransformMesh
                                /*
							AffineModel2D aff = new AffineModel2D();
							aff.set(0.5f, 0, 0, 0.5f, 0, 0);
							ImageProcessor scaledSnapshot = new ByteProcessor((int)(snapshot.getWidth() * scale), (int)(snapshot.getHeight() * scale));
							final CoordinateTransformMesh mesh = new CoordinateTransformMesh( aff, 32, snapshot.getWidth(), snapshot.getHeight() );
							final mpicbg.ij.TransformMeshMapping<CoordinateTransformMesh> mapping = new mpicbg.ij.TransformMeshMapping<CoordinateTransformMesh>( mesh );
							mapping.mapInterpolated(snapshot, scaledSnapshot, Runtime.getRuntime().availableProcessors());
							// Assign for next iteration
							snapshot = scaledSnapshot;
							snapshotPixels = (byte[]) scaledSnapshot.getPixels();
							*/
                                }
                            } catch (final Throwable t) {
                                IJError.print(t);
                            } finally {
                                exe.shutdown();
                            }
                        } else {
                            // max_scale; // WARNING if scale is different than 1, it will FAIL to set the next scale properly.
                            double scale = 1;
                            int scale_pow = 0;
                            // cached for local modifications in the loop, works as loop controler
                            int n_et = n_edge_tiles;
                            while (n_et >= best[1]) {
                                // best[1] is the minimal root found, i.e. 1,2,3,4,5 from which then powers of two were taken to make up for the edge_length
                                // 0 < scale <= 1, so no precision lost
                                final int tile_side = (int) (256 / scale);
                                for (int row = 0; row < n_et; row++) {
                                    for (int col = 0; col < n_et; col++) {
                                        final int i_tile = row * n_et + col;
                                        Utils.showProgress(i_tile / (double) (n_et * n_et));
                                        if (0 == i_tile % 100) {
                                            // RGB int[] images
                                            releaseToFit(tile_side * tile_side * 4 * 2);
                                        }
                                        if (this.quit) {
                                            cleanUp();
                                            return;
                                        }
                                        final Rectangle tile_src = new // TODO row and col are inverted
                                        Rectangle(// TODO row and col are inverted
                                        srcRect.x + tile_side * row, srcRect.y + tile_side * col, tile_side, // in absolute coords, magnification later.
                                        tile_side);
                                        // crop bounds
                                        if (tile_src.x + tile_src.width > srcRect.x + srcRect.width)
                                            tile_src.width = srcRect.x + srcRect.width - tile_src.x;
                                        if (tile_src.y + tile_src.height > srcRect.y + srcRect.height)
                                            tile_src.height = srcRect.y + srcRect.height - tile_src.y;
                                        // negative tile sizes will be made into black tiles
                                        // (negative dimensions occur for tiles beyond the edges of srcRect, since the grid of tiles has to be of equal number of rows and cols)
                                        // should be row_col_scale, but results in transposed tiles in googlebrains, so I reversed the order.
                                        makeTile(layer, tile_src, scale, c_alphas, type, clazz, new StringBuilder(tile_dir).append(col).append('_').append(row).append('_').append(scale_pow).toString(), saver);
                                    }
                                }
                                scale_pow++;
                                // works as magnification
                                scale = 1 / Math.pow(2, scale_pow);
                                n_et /= 2;
                            }
                        }
                    }
                }
            } catch (final Exception e) {
                IJError.print(e);
            } finally {
                Utils.showProgress(1);
            }
            cleanUp();
            finishedWorking();
        }
    };
    // watcher thread
    return Bureaucrat.createAndStart(worker, layers[0].getProject());
}
Also used : ByteProcessor(ij.process.ByteProcessor) HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) Callable(java.util.concurrent.Callable) ImageProcessor(ij.process.ImageProcessor) ColorProcessor(ij.process.ColorProcessor) Worker(ini.trakem2.utils.Worker) HashSet(java.util.HashSet) LayerSet(ini.trakem2.display.LayerSet) Calibration(ij.measure.Calibration) TreeMap(java.util.TreeMap) Layer(ini.trakem2.display.Layer) ImagePlus(ij.ImagePlus) IOException(java.io.IOException) FormatException(loci.formats.FormatException) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ExecutorService(java.util.concurrent.ExecutorService) Future(java.util.concurrent.Future) File(java.io.File) Map(java.util.Map) TreeMap(java.util.TreeMap) HashMap(java.util.HashMap) DirectoryChooser(ij.io.DirectoryChooser)

Example 10 with Display

use of ini.trakem2.display.Display 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)

Aggregations

ArrayList (java.util.ArrayList)11 Layer (ini.trakem2.display.Layer)10 Worker (ini.trakem2.utils.Worker)10 LayerSet (ini.trakem2.display.LayerSet)9 ZDisplayable (ini.trakem2.display.ZDisplayable)9 DBObject (ini.trakem2.persistence.DBObject)9 ImagePlus (ij.ImagePlus)8 Display (ini.trakem2.display.Display)8 Displayable (ini.trakem2.display.Displayable)8 HashSet (java.util.HashSet)8 Project (ini.trakem2.Project)7 GenericDialog (ij.gui.GenericDialog)6 Roi (ij.gui.Roi)6 ProjectThing (ini.trakem2.tree.ProjectThing)6 Rectangle (java.awt.Rectangle)6 File (java.io.File)6 DirectoryChooser (ij.io.DirectoryChooser)5 Calibration (ij.measure.Calibration)5 Future (java.util.concurrent.Future)5 JPopupMenu (javax.swing.JPopupMenu)5