Search in sources :

Example 11 with ImageRegion

use of qupath.lib.regions.ImageRegion in project qupath by qupath.

the class SparseImageServer method readTile.

@Override
protected BufferedImage readTile(final TileRequest tileRequest) throws IOException {
    WritableRaster raster = null;
    for (ImageRegion subRegion : manager.getRegions()) {
        if (subRegion.getZ() != tileRequest.getZ() + originZ || subRegion.getT() != tileRequest.getT() + originT)
            continue;
        double downsample = tileRequest.getRegionRequest().getDownsample();
        if (subRegion.intersects(tileRequest.getImageX() + originX, tileRequest.getImageY() + originY, tileRequest.getImageWidth(), tileRequest.getImageHeight())) {
            // If we overlap, request the overlapping portion
            ImageServer<BufferedImage> serverTemp = manager.getServer(subRegion, downsample);
            // Get image coordinates for bounding box of valid region
            int x1 = Math.max(tileRequest.getImageX() + originX, subRegion.getX());
            int y1 = Math.max(tileRequest.getImageY() + originY, subRegion.getY());
            int x2 = Math.min(tileRequest.getImageX() + originX + tileRequest.getImageWidth(), subRegion.getX() + subRegion.getWidth());
            int y2 = Math.min(tileRequest.getImageY() + originY + tileRequest.getImageHeight(), subRegion.getY() + subRegion.getHeight());
            // Determine request coordinates
            // TODO: Test whether sparse images with pyramidal regions work, or images stored as single planes at pre-specified downsamples
            int xr = x1 - subRegion.getX();
            int yr = y1 - subRegion.getY();
            int xr2 = x2 - subRegion.getX();
            int yr2 = y2 - subRegion.getY();
            double requestDownsample = downsample;
            // if (requestDownsample > 1 && serverTemp.nResolutions() == 1) {
            // requestDownsample = serverTemp.getDownsampleForResolution(0);
            // double scale = requestDownsample / downsample;
            // xr = (int)Math.round(xr * scale);
            // yr = (int)Math.round(yr * scale);
            // xr2 = (int)Math.round(xr2 * scale);
            // yr2 = (int)Math.round(yr2 * scale);
            // System.err.println(downsample + ", " + scale + ": " + serverTemp.getPath());
            // }
            RegionRequest requestTemp = RegionRequest.createInstance(serverTemp.getPath(), requestDownsample, xr, yr, xr2 - xr, yr2 - yr, tileRequest.getZ() + originZ, tileRequest.getT() + originT);
            BufferedImage imgTemp = null;
            synchronized (serverTemp) {
                imgTemp = serverTemp.readBufferedImage(requestTemp);
            }
            if (imgTemp == null)
                continue;
            // If we don't have an output image yet, create a compatible one
            if (raster == null) {
                raster = imgTemp.getRaster().createCompatibleWritableRaster(tileRequest.getTileWidth(), tileRequest.getTileHeight());
            }
            int x = (int) Math.round((x1 - tileRequest.getImageX() - originX) / downsample);
            int y = (int) Math.round((y1 - tileRequest.getImageY() - originY) / downsample);
            int w = Math.min(imgTemp.getWidth(), raster.getWidth() - x);
            int h = Math.min(imgTemp.getHeight(), raster.getHeight() - y);
            raster.setDataElements(x, y, w, h, imgTemp.getRaster().getDataElements(0, 0, w, h, null));
        }
    }
    // reusing an existing raster where possible to reduce memory requirements.
    if (raster == null) {
        return getEmptyTile(tileRequest.getTileWidth(), tileRequest.getTileHeight(), true);
    }
    // System.err.println(String.format("%.2f - %.2f", (double)tileRequest.getImageHeight()/raster.getHeight(), tileRequest.getDownsample()));
    return new BufferedImage(colorModel, raster, false, null);
}
Also used : WritableRaster(java.awt.image.WritableRaster) ImageRegion(qupath.lib.regions.ImageRegion) RegionRequest(qupath.lib.regions.RegionRequest) BufferedImage(java.awt.image.BufferedImage)

Example 12 with ImageRegion

use of qupath.lib.regions.ImageRegion in project qupath by qupath.

the class OMEPyramidWriterCommand method run.

@Override
public void run() {
    if (currentTask != null && !currentTask.isDone()) {
        if (!Dialogs.showConfirmDialog("OME Pyramid writer", "Do you want to stop the current export?"))
            // TODO: Delete exporting file?
            return;
        else {
            currentTask.cancel(true);
        }
    }
    QuPathViewer viewer = qupath.getViewer();
    int zPos = viewer.getZPosition();
    int tPos = viewer.getTPosition();
    ImageData<BufferedImage> imageData = viewer.getImageData();
    if (imageData == null) {
        Dialogs.showNoImageError("OME Pyramid writer");
        return;
    }
    ImageServer<BufferedImage> server = imageData.getServer();
    // Region
    PathObject selected = imageData.getHierarchy().getSelectionModel().getSelectedObject();
    ImageRegion region = null;
    int width, height;
    if (selected == null || !selected.hasROI() || !selected.getROI().isArea()) {
        width = server.getWidth();
        height = server.getHeight();
    } else {
        region = ImageRegion.createInstance(selected.getROI());
        width = region.getWidth();
        height = region.getHeight();
    }
    // Set compression - with a sanity check for validity, defaulting to another comparable method if necessary
    CompressionType compression = getDefaultPyramidCompression();
    List<String> compatibleCompression = Arrays.stream(CompressionType.values()).filter(c -> c.supportsImage(server)).map(c -> c.toFriendlyString()).collect(Collectors.toList());
    if (!compatibleCompression.contains(compression.toFriendlyString()))
        compression = CompressionType.DEFAULT;
    var params = new ParameterList().addChoiceParameter("compression", "Compression type", compression.toFriendlyString(), compatibleCompression).addIntParameter("scaledDownsample", "Pyramidal downsample", scaledDownsample.get(), "", 1, 8, "Amount to downsample each consecutive pyramidal level; use 1 to indicate the image should not be pyramidal").addIntParameter("tileSize", "Tile size", getDefaultTileSize(), "px", "Tile size for export (should be between 128 and 8192)").addBooleanParameter("parallelize", "Parallelize export", parallelizeTiling.get(), "Export image tiles in parallel - " + "this should be faster, best keep it on unless you encounter export problems").addBooleanParameter("allZ", "All z-slices", allZ.get(), "Include all z-slices in the stack").addBooleanParameter("allT", "All timepoints", allT.get(), "Include all timepoints in the time-series");
    boolean singleTile = server.getTileRequestManager().getTileRequests(RegionRequest.createInstance(server)).size() == 1;
    params.setHiddenParameters(server.nZSlices() == 1, "allZ");
    params.setHiddenParameters(server.nTimepoints() == 1, "allT");
    params.setHiddenParameters(singleTile, "tileSize", "parallelize");
    if (!Dialogs.showParameterDialog("Export OME-TIFF", params))
        return;
    compression = CompressionType.fromFriendlyString((String) params.getChoiceParameterValue("compression"));
    defaultPyramidCompression.set(compression);
    int downsampleScale = params.getIntParameterValue("scaledDownsample");
    scaledDownsample.set(downsampleScale);
    int tileSize = params.getIntParameterValue("tileSize");
    boolean parallelize = params.getBooleanParameterValue("parallelize");
    if (!singleTile) {
        tileSize = GeneralTools.clipValue(tileSize, 128, 8192);
        defaultTileSize.set(tileSize);
        parallelizeTiling.set(parallelize);
    }
    boolean doAllZ = false;
    boolean doAllT = false;
    if (server.nZSlices() > 1) {
        doAllZ = params.getBooleanParameterValue("allZ");
        allZ.set(doAllZ);
    }
    if (server.nTimepoints() > 1) {
        doAllT = params.getBooleanParameterValue("allT");
        allT.set(doAllT);
    }
    OMEPyramidWriter.Builder builder = new OMEPyramidWriter.Builder(server);
    if (region != null) {
        builder = builder.region(region);
    } else {
        if (server.nZSlices() > 1 && !doAllZ)
            builder.zSlice(zPos);
        if (server.nTimepoints() > 1 && !doAllT)
            builder.timePoint(tPos);
    }
    builder.compression(compression);
    if (downsampleScale <= 1 || Math.max(width, height) / server.getDownsampleForResolution(0) < minSizeForTiling.get())
        builder.downsamples(server.getDownsampleForResolution(0));
    else
        builder.scaledDownsampling(server.getDownsampleForResolution(0), downsampleScale);
    // Set tile size; if we just have one tile, use the image width & height
    if (singleTile)
        builder = builder.tileSize(width, height);
    else
        builder = builder.tileSize(tileSize).parallelize(parallelize);
    if (server.nZSlices() > 1 && doAllZ)
        builder.allZSlices();
    if (server.nTimepoints() > 1 && doAllT)
        builder.allTimePoints();
    // Prompt for file
    File fileOutput = Dialogs.promptToSaveFile("Write pyramid", null, null, "OME TIFF pyramid", ".ome.tif");
    if (fileOutput == null)
        return;
    String name = fileOutput.getName();
    // We can have trouble with only the '.tif' part of the name being included
    if (name.endsWith(".tif") && !name.endsWith(".ome.tif"))
        fileOutput = new File(fileOutput.getParentFile(), name.substring(0, name.length() - 4) + ".ome.tif");
    OMEPyramidSeries writer = builder.build();
    if (pool == null) {
        pool = Executors.newSingleThreadExecutor(ThreadTools.createThreadFactory("ome-pyramid-export", false));
    }
    currentTask = pool.submit(new WriterTask(OMEPyramidWriter.createWriter(writer), fileOutput.getAbsolutePath()));
}
Also used : Arrays(java.util.Arrays) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) IntegerProperty(javafx.beans.property.IntegerProperty) Dialogs(qupath.lib.gui.dialogs.Dialogs) Future(java.util.concurrent.Future) ParameterList(qupath.lib.plugins.parameters.ParameterList) OMEPyramidSeries(qupath.lib.images.writers.ome.OMEPyramidWriter.OMEPyramidSeries) ImageRegion(qupath.lib.regions.ImageRegion) CompressionType(qupath.lib.images.writers.ome.OMEPyramidWriter.CompressionType) ExecutorService(java.util.concurrent.ExecutorService) QuPathGUI(qupath.lib.gui.QuPathGUI) ImageData(qupath.lib.images.ImageData) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) BufferedImage(java.awt.image.BufferedImage) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) Collectors(java.util.stream.Collectors) File(java.io.File) Executors(java.util.concurrent.Executors) PathObject(qupath.lib.objects.PathObject) ClosedByInterruptException(java.nio.channels.ClosedByInterruptException) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) ThreadTools(qupath.lib.common.ThreadTools) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageRegion(qupath.lib.regions.ImageRegion) BufferedImage(java.awt.image.BufferedImage) PathObject(qupath.lib.objects.PathObject) OMEPyramidSeries(qupath.lib.images.writers.ome.OMEPyramidWriter.OMEPyramidSeries) ParameterList(qupath.lib.plugins.parameters.ParameterList) CompressionType(qupath.lib.images.writers.ome.OMEPyramidWriter.CompressionType) File(java.io.File) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer)

Aggregations

ImageRegion (qupath.lib.regions.ImageRegion)12 PathObject (qupath.lib.objects.PathObject)6 BufferedImage (java.awt.image.BufferedImage)4 ArrayList (java.util.ArrayList)3 QuPathViewer (qupath.lib.gui.viewer.QuPathViewer)3 ROI (qupath.lib.roi.interfaces.ROI)3 Graphics2D (java.awt.Graphics2D)2 Rectangle (java.awt.Rectangle)2 HashMap (java.util.HashMap)2 Geometry (org.locationtech.jts.geom.Geometry)2 Logger (org.slf4j.Logger)2 LoggerFactory (org.slf4j.LoggerFactory)2 OverlayOptions (qupath.lib.gui.viewer.OverlayOptions)2 ImageData (qupath.lib.images.ImageData)2 ImageServer (qupath.lib.images.servers.ImageServer)2 PathObjectHierarchy (qupath.lib.objects.hierarchy.PathObjectHierarchy)2 RegionRequest (qupath.lib.regions.RegionRequest)2 Calibration (ij.measure.Calibration)1 AlphaComposite (java.awt.AlphaComposite)1 Composite (java.awt.Composite)1