Search in sources :

Example 81 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class Loader method importImage.

/**
 * Import a new image at the given coordinates; does not puts it into any layer, unless it's a stack -in which case importStack is called with the current front layer of the given project as target. If a path is not provided it will be asked for.
 */
public Patch importImage(final Project project, final double x, final double y, String path, final boolean synch_mipmap_generation) {
    if (null == path) {
        final OpenDialog od = new OpenDialog("Import image", "");
        final String name = od.getFileName();
        if (null == name || 0 == name.length())
            return null;
        String dir = od.getDirectory().replace('\\', '/');
        if (!dir.endsWith("/"))
            dir += "/";
        path = dir + name;
    } else {
        path = path.replace('\\', '/');
    }
    // avoid opening trakem2 projects
    if (path.toLowerCase().endsWith(".xml")) {
        Utils.showMessage("Cannot import " + path + " as a stack.");
        return null;
    }
    releaseToFit(new File(path).length() * 3);
    IJ.redirectErrorMessages();
    final ImagePlus imp = openImagePlus(path);
    if (null == imp)
        return null;
    if (imp.getNSlices() > 1) {
        // a stack!
        final Layer layer = Display.getFrontLayer(project);
        if (null == layer)
            return null;
        importStack(layer, x, y, imp, true, path, true);
        return null;
    }
    if (0 == imp.getWidth() || 0 == imp.getHeight()) {
        Utils.showMessage("Can't import image of zero width or height.");
        flush(imp);
        return null;
    }
    final Patch p = new Patch(project, imp.getTitle(), x, y, imp);
    addedPatchFrom(path, p);
    // WARNING may be altered concurrently
    last_opened_path = path;
    if (isMipMapsRegenerationEnabled()) {
        if (synch_mipmap_generation) {
            try {
                // wait
                regenerateMipMaps(p).get();
            } catch (final Exception e) {
                IJError.print(e);
            }
        } else
            // queue for regeneration
            regenerateMipMaps(p);
    }
    return p;
}
Also used : File(java.io.File) ImagePlus(ij.ImagePlus) Layer(ini.trakem2.display.Layer) Patch(ini.trakem2.display.Patch) IOException(java.io.IOException) FormatException(loci.formats.FormatException) OpenDialog(ij.io.OpenDialog)

Example 82 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class Loader method askForXMLTemplate.

/**
 * Ask for the user to provide a template XML file to extract a root TemplateThing.
 */
public TemplateThing askForXMLTemplate(final Project project) {
    // ask for an .xml file or a .dtd file
    // fd.setFilenameFilter(new XMLFileFilter(XMLFileFilter.BOTH));
    final OpenDialog od = new OpenDialog("Select XML Template", OpenDialog.getDefaultDirectory(), null);
    final String filename = od.getFileName();
    if (null == filename || filename.toLowerCase().startsWith("null"))
        return null;
    // if there is a path, read it out if possible
    String dir = od.getDirectory();
    if (null == dir)
        return null;
    if (IJ.isWindows())
        dir = dir.replace('\\', '/');
    if (!dir.endsWith("/"))
        dir += "/";
    TemplateThing[] roots;
    try {
        roots = DTDParser.extractTemplate(dir + filename);
    } catch (final Exception e) {
        IJError.print(e);
        return null;
    }
    if (null == roots || roots.length < 1)
        return null;
    if (roots.length > 1) {
        Utils.showMessage("Found more than one root.\nUsing first root only.");
    }
    return roots[0];
}
Also used : TemplateThing(ini.trakem2.tree.TemplateThing) IOException(java.io.IOException) FormatException(loci.formats.FormatException) OpenDialog(ij.io.OpenDialog)

Example 83 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class Loader method fetchAWTImage.

public final MipMapImage fetchAWTImage(final Patch p, final int level, final int max_level) {
    // Below, the complexity of the synchronized blocks is to provide sufficient granularity. Keep in mind that only one thread at at a time can access a synchronized block for the same object (in this case, the db_lock), and thus calling lock() and unlock() is not enough. One needs to break the statement in as many synch blocks as possible for maximizing the number of threads concurrently accessing different parts of this function.
    // find an equal or larger existing pyramid awt
    final long id = p.getId();
    ImageLoadingLock plock = null;
    synchronized (db_lock) {
        try {
            if (null == mawts) {
                // when lazy repainting after closing a project, the awts is null
                return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
            }
            if (level >= 0 && isMipMapsRegenerationEnabled()) {
                // 1 - check if the exact level is cached
                final Image mawt = mawts.get(id, level);
                if (null != mawt) {
                    // Utils.log2("returning cached exact mawt for level " + level);
                    final double scale = Math.pow(2.0, level);
                    return new MipMapImage(mawt, scale, scale);
                }
                plock = getOrMakeImageLoadingLock(p.getId(), level);
            }
        } catch (final Exception e) {
            IJError.print(e);
        }
    }
    MipMapImage mipMap = null;
    // 2 - check if the exact file is present for the desired level
    if (level >= 0 && isMipMapsRegenerationEnabled()) {
        synchronized (plock) {
            final Image mawt;
            synchronized (db_lock) {
                mawt = mawts.get(id, level);
            }
            if (null != mawt) {
                final double scale = Math.pow(2.0, level);
                // was loaded by a different thread
                return new MipMapImage(mawt, scale, scale);
            }
        }
        final long n_bytes = estimateImageFileSize(p, level);
        // going to load:
        releaseToFit(n_bytes * 8);
        synchronized (plock) {
            try {
                mipMap = fetchMipMapAWT(p, level, n_bytes);
            } catch (final Throwable t) {
                IJError.print(t);
                mipMap = null;
            }
            synchronized (db_lock) {
                try {
                    if (null != mipMap) {
                        // Utils.log2("returning exact mawt from file for level " + level);
                        if (REGENERATING != mipMap.image) {
                            mawts.put(id, mipMap.image, level);
                            Display.repaintSnapshot(p);
                        }
                        return mipMap;
                    }
                    // Check if an appropriate level is cached
                    mipMap = mawts.getClosestAbove(id, level);
                    if (mipMap == null) {
                        // 3 - else, load closest level to it but still giving a larger image
                        // finds the file for the returned level, otherwise returns zero
                        final int lev = getClosestMipMapLevel(p, level, max_level);
                        // Utils.log2("closest mipmap level is " + lev);
                        if (lev > -1) {
                            // overestimating n_bytes
                            mipMap = fetchMipMapAWT(p, lev, n_bytes);
                            if (null != mipMap) {
                                mawts.put(id, mipMap.image, lev);
                                // Utils.log2("from getClosestMipMapLevel: mawt is " + mawt);
                                Display.repaintSnapshot(p);
                                // Utils.log2("returning from getClosestMipMapAWT with level " + lev);
                                return mipMap;
                            }
                        } else if (ERROR_PATH_NOT_FOUND == lev) {
                            mipMap = new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
                        }
                    } else {
                        return mipMap;
                    }
                } catch (final Throwable t) {
                    handleCacheError(t);
                } finally {
                    removeImageLoadingLock(plock);
                }
            }
        }
    }
    synchronized (db_lock) {
        try {
            // 4 - check if any suitable level is cached (whithout mipmaps, it may be the large image)
            mipMap = mawts.getClosestAbove(id, level);
            if (null != mipMap) {
                // Utils.log2("returning from getClosest with level " + level);
                return mipMap;
            }
        } catch (final Exception e) {
            IJError.print(e);
        }
    }
    if (hs_unloadable.contains(p))
        return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
    synchronized (db_lock) {
        try {
            plock = getOrMakeImageLoadingLock(p.getId(), level);
        } catch (final Exception e) {
            // TODO there may be a flaw in the image loading locks: when removing it, if it had been acquired by another thread, then a third thread will create it new. The image loading locks should count the number of threads that have them, and remove themselves when zero.
            if (null != plock)
                removeImageLoadingLock(plock);
            return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
        }
    }
    synchronized (plock) {
        // Check if a previous call made it while waiting:
        mipMap = mawts.getClosestAbove(id, level);
        if (null != mipMap) {
            synchronized (db_lock) {
                removeImageLoadingLock(plock);
            }
            return mipMap;
        }
    }
    Image mawt = null;
    try {
        // Else, create the mawt:
        final Patch.PatchImage pai = p.createTransformedImage();
        synchronized (plock) {
            if (null != pai && null != pai.target) {
                mawt = pai.createImage(p.getMin(), p.getMax());
            }
        }
    } catch (final Exception e) {
        Utils.log2("Could not create an image for Patch " + p);
        mawt = null;
    }
    synchronized (db_lock) {
        try {
            if (null != mawt) {
                mawts.put(id, mawt, 0);
                Display.repaintSnapshot(p);
                // Utils.log2("Created mawt from scratch.");
                return new MipMapImage(mawt, 1.0, 1.0);
            }
        } catch (final Throwable t) {
            handleCacheError(t);
        } finally {
            removeImageLoadingLock(plock);
        }
    }
    return new MipMapImage(NOT_FOUND, p.getWidth() / NOT_FOUND.getWidth(), p.getHeight() / NOT_FOUND.getHeight());
}
Also used : MipMapImage(ini.trakem2.display.MipMapImage) Image(java.awt.Image) BufferedImage(java.awt.image.BufferedImage) MipMapImage(ini.trakem2.display.MipMapImage) Patch(ini.trakem2.display.Patch) IOException(java.io.IOException) FormatException(loci.formats.FormatException)

Example 84 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class ProjectTiler method createRetiledSibling.

/**
 * Take a {@link Project}, a size for the image tiles, and a target directory,
 * and create a new copy of the current project in that folder but with the underlying
 * images converted to tiles with a translation-only transform (saved as zipped TIFFs,
 * with extension ".tif.zip").
 * The new, returned {@link Project} represents the given project but with much
 * simpler transformations (just translation) for the images and a defined size for
 * the latter, which helps a lot regarding storage space of the XML (and parsing and
 * saving time) and performance when browsing layers (keep in mind that, for a 32k x 32k image,
 * at 100% zoom one would have to load a 32k x 32k image and render just a tiny bit
 * of it). The copied Project preserves the ID of the {@link Layer}s of the original
 * {@link Project}, as well as the dimensions; this means the copy is a sibling of
 * the original, and it is possible to send segmentations from one to the other "as is"
 * (directly, without having to transform along with the images which would not be possible).
 *
 * Image files are stored as
 *
 * The non-image objects of the given project are copied into the new project as well.
 *
 * @param srcProject The project to create a sibling of.
 * @param targetDirectory The directory in which to create all the necessary data and mipmap folders for the new Project.
 * @param tileWidth The width of the tiles to create for the data of the new project.
 * @param tileHeight The height of the tiles.
 * @param exportImageType Any of {@link ImagePlus#GRAY8}, {@link ImagePlus#GRAY16} or {@link ImagePlus#COLOR_RGB}, otherwise an {@link IllegalArgumentException} is thrown.
 * @param onlyVisibleImages Whether to consider visible images only.
 * @param nExportThreads Number of layers to export in parallel. Use a small number when original images are huge (such as larger than 4096 x 4096 pixels).
 * @param createMipMaps Whether to generate the mipmaps when done or not.
 *
 * @throws Exception IllegalArgumentException When {@code exportImageType} is not {@link ImagePlus#GRAY16} or {@link ImagePlus#COLOR_RGB}, or when the directory exists and cannot be written to.
 */
public static final Project createRetiledSibling(final Project srcProject, final String targetDirectory, final int tileWidth, final int tileHeight, final int exportImageType, final boolean onlyVisibleImages, final int nExportThreads, final boolean createMipMaps) throws Exception {
    // Validate exportImageType
    switch(exportImageType) {
        case ImagePlus.GRAY8:
        case ImagePlus.GRAY16:
        case ImagePlus.COLOR_RGB:
            break;
        default:
            throw new IllegalArgumentException("Can only accept GRAY8, GRAY16 or COLOR_RGB as values for 'exportImageType'!");
    }
    // Validate targetDirectory
    final File fdir = new File(targetDirectory);
    if (fdir.exists()) {
        if (!fdir.isDirectory() || !fdir.canWrite())
            throw new IllegalArgumentException("Invalid directory: not a directory or cannot write to: " + targetDirectory);
    } else {
        if (!fdir.mkdirs()) {
            throw new IllegalArgumentException("Cannot create directory at: " + targetDirectory);
        }
    }
    final String targetDir = Utils.fixDir(targetDirectory);
    // Create "data" directory
    final String dataDir = new StringBuilder(targetDir).append("data/").toString();
    final File fDataDir = new File(dataDir);
    if (fDataDir.exists() && (!fDataDir.isDirectory() || !fDataDir.canWrite())) {
        throw new IllegalArgumentException("Cannot create or write to 'data' directory in the targetDirectory at: " + targetDir);
    } else {
        fDataDir.mkdir();
    }
    // Create new Project, plain, without any automatic creation of a Layer or a Display
    final Project newProject = Project.newFSProject("blank", null, targetDir, false);
    final LayerSet newLayerSet = newProject.getRootLayerSet();
    newLayerSet.setCalibration(srcProject.getRootLayerSet().getCalibrationCopy());
    if (!createMipMaps) {
        Utils.log("MipMaps are DISABLED:\n --> When done, right-click and choose 'Display - Properties...' and enable mipmaps,\n     and then run 'Project - Regenerate all mipmaps'\n");
        newProject.getLoader().setMipMapsRegeneration(false);
        Utils.log("mipmaps enabled? " + newProject.getLoader().isMipMapsRegenerationEnabled());
    }
    // Copy the Template Tree of types
    newProject.resetRootTemplateThing(srcProject.getRootTemplateThing().clone(newProject, true), null);
    for (final TemplateThing tt : newProject.getRootTemplateThing().getUniqueTypes(new HashMap<String, TemplateThing>()).values()) {
        newProject.addUniqueType(tt);
    }
    // Clone layers with the exact same IDs, so that the two projects are siblings at the layer-level:
    // (Being siblings allows for treelines, arealists, etc. to be transferred from one to another "as is").
    final List<Layer> srcLayers = srcProject.getRootLayerSet().getLayers();
    final List<Layer> newLayers = new ArrayList<Layer>();
    for (final Layer srcLayer : srcLayers) {
        final Layer newLayer = new Layer(newProject, srcLayer.getId(), srcLayer.getZ(), srcLayer.getThickness());
        // to update the ID generator in FSLoader
        newLayer.addToDatabase();
        newLayerSet.add(newLayer);
        newLayers.add(newLayer);
        newProject.getRootLayerThing().addChild(new LayerThing(newProject.getRootLayerThing().getChildTemplate("layer"), newProject, newLayer));
    }
    newProject.getLayerTree().rebuild();
    // Update the LayerSet
    newLayerSet.setDimensions(srcProject.getRootLayerSet().getLayerWidth(), srcProject.getRootLayerSet().getLayerHeight(), LayerSet.NORTHWEST);
    Display.updateLayerScroller(newLayerSet);
    Display.update(newLayerSet);
    // Copy template from the src Project
    // (It's done after creating layers so the IDs will not collide with those of the Layers)
    newProject.resetRootTemplateThing(srcProject.getRootTemplateThing().clone(newProject, false), null);
    // Export tiles as new Patch instances, creating new image files in disk
    final int numThreads = Math.max(1, Math.min(nExportThreads, Runtime.getRuntime().availableProcessors()));
    int i = 0;
    for (final Layer srcLayer : srcLayers) {
        Utils.log("Processing layer " + (i + 1) + "/" + srcLayers.size() + " -- " + new Date());
        final int layerIndex = i++;
        // Create subDirectory
        final String dir = dataDir + "/" + layerIndex + "/";
        new File(dir).mkdir();
        // Create a new Layer with the same Z and thickness
        final Layer newLayer = newLayers.get(layerIndex);
        // Export layer tiles
        final ArrayList<Patch> patches = new ArrayList<Patch>();
        if (ImagePlus.GRAY16 == exportImageType) {
            Process.progressive(ExportUnsignedShort.exportTiles(srcLayer, tileWidth, tileHeight, onlyVisibleImages), new CountingTaskFactory<Callable<ExportedTile>, Patch>() {

                public Patch process(final Callable<ExportedTile> c, final int index) {
                    try {
                        // Create the tile
                        final ExportedTile t = c.call();
                        // Store the file
                        final String title = layerIndex + "-" + index;
                        final String path = dir + title + ".tif.zip";
                        final ImagePlus imp = new ImagePlus(title, t.sp);
                        if (!new FileSaver(imp).saveAsZip(path)) {
                            throw new Exception("Could not save tile: " + path);
                        }
                        // Create a Patch
                        final Patch patch = new Patch(newProject, title, t.x, t.y, imp);
                        patch.setLocked(true);
                        newProject.getLoader().addedPatchFrom(path, patch);
                        return patch;
                    } catch (Exception e) {
                        IJError.print(e);
                        return null;
                    }
                }
            }, patches, numThreads);
        } else {
            // GRAY8 or COLOR_RGB: created from mipmaps
            Process.progressive(tileSequence(srcLayer, tileWidth, tileHeight, onlyVisibleImages), new CountingTaskFactory<Rectangle, Patch>() {

                @Override
                public Patch process(final Rectangle bounds, final int index) {
                    try {
                        // Create the tile
                        final ImagePlus imp = srcLayer.getProject().getLoader().getFlatImage(srcLayer, bounds, 1.0, -1, exportImageType, Patch.class, null, false, Color.black);
                        final String title = layerIndex + "-" + index;
                        imp.setTitle(title);
                        final String path = dir + title + ".tif.zip";
                        if (!new FileSaver(imp).saveAsZip(path)) {
                            throw new Exception("Could not save tile: " + path);
                        }
                        // Create a Patch
                        final Patch patch = new Patch(newProject, title, bounds.x, bounds.y, imp);
                        patch.setLocked(true);
                        newProject.getLoader().addedPatchFrom(path, patch);
                        return patch;
                    } catch (Exception e) {
                        IJError.print(e);
                        return null;
                    }
                }
            }, patches, numThreads);
        }
        // Add all Patches to the new Layer
        for (final Patch p : patches) {
            newLayer.add(p);
        }
    }
    // Copy all segmentations "As is"
    final ProjectThing root = srcProject.getRootProjectThing();
    if (null != root.getChildren() && !root.getChildren().isEmpty()) {
        final ProjectThing source_pt = srcProject.getRootProjectThing().getChildren().get(0);
        // "As is"
        final int transfer_mode = 0;
        final ProjectThing landing_parent = newProject.getRootProjectThing();
        srcProject.getProjectTree().rawSendToSiblingProject(source_pt, transfer_mode, newProject, landing_parent);
    }
    // Copy all floating text labels
    i = 0;
    for (final Layer srcLayer : srcLayers) {
        for (final DLabel srcLabel : srcLayer.getAll(DLabel.class)) {
            newLayers.get(i++).add(srcLabel.clone(newProject, false));
        }
    }
    if (createMipMaps) {
        final LinkedList<Future<?>> fus = new LinkedList<Future<?>>();
        final int batch = Runtime.getRuntime().availableProcessors();
        for (final Layer newLayer : newLayers) {
            for (final Patch p : newLayer.getAll(Patch.class)) {
                fus.add(p.updateMipMaps());
                // Don't build-up too much
                if (fus.size() > batch * 3) {
                    while (fus.size() > batch) {
                        try {
                            fus.removeFirst().get();
                        } catch (Exception e) {
                            IJError.print(e);
                        }
                    }
                }
            }
        }
        Utils.wait(fus);
    }
    // Save:
    newProject.saveAs(targetDir + "exported.xml", false);
    return newProject;
}
Also used : HashMap(java.util.HashMap) ArrayList(java.util.ArrayList) Rectangle(java.awt.Rectangle) Callable(java.util.concurrent.Callable) FileSaver(ij.io.FileSaver) ProjectThing(ini.trakem2.tree.ProjectThing) LayerSet(ini.trakem2.display.LayerSet) LayerThing(ini.trakem2.tree.LayerThing) Layer(ini.trakem2.display.Layer) ImagePlus(ij.ImagePlus) Date(java.util.Date) LinkedList(java.util.LinkedList) Project(ini.trakem2.Project) DLabel(ini.trakem2.display.DLabel) TemplateThing(ini.trakem2.tree.TemplateThing) Future(java.util.concurrent.Future) File(java.io.File) Patch(ini.trakem2.display.Patch) ExportedTile(mpicbg.trakem2.transform.ExportedTile)

Example 85 with Project

use of ini.trakem2.Project in project TrakEM2 by trakem2.

the class StaleFiles method delete.

/**
 * Generic method called by other methods of this class.
 */
public static final boolean delete(final Project project, final String topDir, final Path g) {
    if (null == topDir)
        return true;
    // Collect the set of files to keep
    final HashSet<String> keepers = new HashSet<String>();
    for (final Layer l : project.getRootLayerSet().getLayers()) {
        for (final Patch p : l.getAll(Patch.class)) {
            String path = g.get(p);
            if (null != path)
                keepers.add(path);
        }
    }
    // Iterate all directories, recursively
    final String extension = g.extension();
    final LinkedList<File> subdirs = new LinkedList<File>();
    subdirs.add(new File(topDir));
    final AtomicInteger counter = new AtomicInteger(0);
    final ExecutorService exec = Utils.newFixedThreadPool(Math.max(2, Runtime.getRuntime().availableProcessors()), "Stale-file-remover");
    while (!subdirs.isEmpty()) {
        final File fdir = subdirs.removeFirst();
        final String absPath = fdir.getAbsolutePath();
        for (final String s : fdir.list()) {
            final String path = absPath + "/" + s;
            if (s.endsWith(extension)) {
                if (keepers.contains(path))
                    continue;
                // Else, delete the file, which is by definition stale
                exec.submit(new Runnable() {

                    public void run() {
                        if (!new File(path).delete()) {
                            Utils.log2("Failed to delete: " + path);
                            counter.incrementAndGet();
                        }
                    }
                });
            } else {
                final File f = new File(path);
                if (f.isDirectory()) {
                    subdirs.add(f);
                }
            }
        }
    }
    // Do not accept more tasks, but execute all submitted tasks
    exec.shutdown();
    // Wait maximum for an unreasonable amount of time
    try {
        exec.awaitTermination(1, TimeUnit.DAYS);
    } catch (InterruptedException e) {
        IJError.print(e);
    }
    if (counter.get() > 0) {
        Utils.log("ERROR: failed to delete " + counter.get() + " files.\n        See the stdout log for details.");
    }
    return 0 == counter.get();
}
Also used : AtomicInteger(java.util.concurrent.atomic.AtomicInteger) ExecutorService(java.util.concurrent.ExecutorService) Layer(ini.trakem2.display.Layer) Patch(ini.trakem2.display.Patch) File(java.io.File) LinkedList(java.util.LinkedList) HashSet(java.util.HashSet)

Aggregations

ArrayList (java.util.ArrayList)33 Project (ini.trakem2.Project)26 HashMap (java.util.HashMap)25 Layer (ini.trakem2.display.Layer)21 Displayable (ini.trakem2.display.Displayable)19 Patch (ini.trakem2.display.Patch)18 File (java.io.File)18 HashSet (java.util.HashSet)18 ZDisplayable (ini.trakem2.display.ZDisplayable)17 ImagePlus (ij.ImagePlus)16 ProjectThing (ini.trakem2.tree.ProjectThing)16 Worker (ini.trakem2.utils.Worker)16 TemplateThing (ini.trakem2.tree.TemplateThing)15 Map (java.util.Map)15 LayerSet (ini.trakem2.display.LayerSet)14 ResultSet (java.sql.ResultSet)13 DBObject (ini.trakem2.persistence.DBObject)12 AffineTransform (java.awt.geom.AffineTransform)11 TreeMap (java.util.TreeMap)11 FSLoader (ini.trakem2.persistence.FSLoader)10