use of qupath.lib.images.writers.ImageWriter in project qupath by qupath.
the class Commands method promptToExportImageRegion.
/**
* Prompt to export the current image region selected in the viewer.
* @param viewer the viewer containing the image to export
* @param renderedImage if true, export the rendered (RGB) image rather than original pixel values
*/
public static void promptToExportImageRegion(QuPathViewer viewer, boolean renderedImage) {
if (viewer == null || viewer.getServer() == null) {
Dialogs.showErrorMessage("Export image region", "No viewer & image selected!");
return;
}
ImageServer<BufferedImage> server = viewer.getServer();
if (renderedImage)
server = RenderedImageServer.createRenderedServer(viewer);
PathObject pathObject = viewer.getSelectedObject();
ROI roi = pathObject == null ? null : pathObject.getROI();
double regionWidth = roi == null ? server.getWidth() : roi.getBoundsWidth();
double regionHeight = roi == null ? server.getHeight() : roi.getBoundsHeight();
// Create a dialog
GridPane pane = new GridPane();
int row = 0;
pane.add(new Label("Export format"), 0, row);
ComboBox<ImageWriter<BufferedImage>> comboImageType = new ComboBox<>();
Function<ImageWriter<BufferedImage>, String> fun = (ImageWriter<BufferedImage> writer) -> writer.getName();
comboImageType.setCellFactory(p -> GuiTools.createCustomListCell(fun));
comboImageType.setButtonCell(GuiTools.createCustomListCell(fun));
var writers = ImageWriterTools.getCompatibleWriters(server, null);
comboImageType.getItems().setAll(writers);
comboImageType.setTooltip(new Tooltip("Choose export image format"));
if (writers.contains(lastWriter))
comboImageType.getSelectionModel().select(lastWriter);
else
comboImageType.getSelectionModel().selectFirst();
comboImageType.setMaxWidth(Double.MAX_VALUE);
GridPane.setHgrow(comboImageType, Priority.ALWAYS);
pane.add(comboImageType, 1, row++);
TextArea textArea = new TextArea();
textArea.setPrefRowCount(2);
textArea.setEditable(false);
textArea.setWrapText(true);
// textArea.setPadding(new Insets(15, 0, 0, 0));
comboImageType.setOnAction(e -> textArea.setText(((ImageWriter<BufferedImage>) comboImageType.getValue()).getDetails()));
textArea.setText(((ImageWriter<BufferedImage>) comboImageType.getValue()).getDetails());
pane.add(textArea, 0, row++, 2, 1);
var label = new Label("Downsample factor");
pane.add(label, 0, row);
TextField tfDownsample = new TextField();
label.setLabelFor(tfDownsample);
pane.add(tfDownsample, 1, row++);
tfDownsample.setTooltip(new Tooltip("Amount to scale down image - choose 1 to export at full resolution (note: for large images this may not succeed for memory reasons)"));
ObservableDoubleValue downsample = Bindings.createDoubleBinding(() -> {
try {
return Double.parseDouble(tfDownsample.getText());
} catch (NumberFormatException e) {
return Double.NaN;
}
}, tfDownsample.textProperty());
// Define a sensible limit for non-pyramidal images
long maxPixels = 10000 * 10000;
Label labelSize = new Label();
labelSize.setMinWidth(400);
labelSize.setTextAlignment(TextAlignment.CENTER);
labelSize.setContentDisplay(ContentDisplay.CENTER);
labelSize.setAlignment(Pos.CENTER);
labelSize.setMaxWidth(Double.MAX_VALUE);
labelSize.setTooltip(new Tooltip("Estimated size of exported image"));
pane.add(labelSize, 0, row++, 2, 1);
labelSize.textProperty().bind(Bindings.createStringBinding(() -> {
if (!Double.isFinite(downsample.get())) {
labelSize.setStyle("-fx-text-fill: red;");
return "Invalid downsample value! Must be >= 1";
} else {
long w = (long) (regionWidth / downsample.get() + 0.5);
long h = (long) (regionHeight / downsample.get() + 0.5);
String warning = "";
var writer = comboImageType.getSelectionModel().getSelectedItem();
boolean supportsPyramid = writer == null ? false : writer.supportsPyramidal();
if (!supportsPyramid && w * h > maxPixels) {
labelSize.setStyle("-fx-text-fill: red;");
warning = " (too big!)";
} else if (w < 5 || h < 5) {
labelSize.setStyle("-fx-text-fill: red;");
warning = " (too small!)";
} else
labelSize.setStyle(null);
return String.format("Output image size: %d x %d pixels%s", w, h, warning);
}
}, downsample, comboImageType.getSelectionModel().selectedIndexProperty()));
tfDownsample.setText(Double.toString(exportDownsample.get()));
PaneTools.setMaxWidth(Double.MAX_VALUE, labelSize, textArea, tfDownsample, comboImageType);
PaneTools.setHGrowPriority(Priority.ALWAYS, labelSize, textArea, tfDownsample, comboImageType);
pane.setVgap(5);
pane.setHgap(5);
if (!Dialogs.showConfirmDialog("Export image region", pane))
return;
var writer = comboImageType.getSelectionModel().getSelectedItem();
boolean supportsPyramid = writer == null ? false : writer.supportsPyramidal();
int w = (int) (regionWidth / downsample.get() + 0.5);
int h = (int) (regionHeight / downsample.get() + 0.5);
if (!supportsPyramid && w * h > maxPixels) {
Dialogs.showErrorNotification("Export image region", "Requested export region too large - try selecting a smaller region, or applying a higher downsample factor");
return;
}
if (downsample.get() < 1 || !Double.isFinite(downsample.get())) {
Dialogs.showErrorMessage("Export image region", "Downsample factor must be >= 1!");
return;
}
exportDownsample.set(downsample.get());
// Now that we know the output, we can create a new server to ensure it is downsampled as the necessary resolution
if (renderedImage && downsample.get() != server.getDownsampleForResolution(0))
server = new RenderedImageServer.Builder(viewer).downsamples(downsample.get()).build();
// selectedImageType.set(comboImageType.getSelectionModel().getSelectedItem());
// Create RegionRequest
RegionRequest request = null;
if (pathObject != null && pathObject.hasROI())
request = RegionRequest.createInstance(server.getPath(), exportDownsample.get(), roi);
// Create a sensible default file name, and prompt for the actual name
String ext = writer.getDefaultExtension();
String writerName = writer.getName();
String defaultName = GeneralTools.getNameWithoutExtension(new File(ServerTools.getDisplayableImageName(server)));
if (roi != null) {
defaultName = String.format("%s (%s, x=%d, y=%d, w=%d, h=%d)", defaultName, GeneralTools.formatNumber(request.getDownsample(), 2), request.getX(), request.getY(), request.getWidth(), request.getHeight());
}
File fileOutput = Dialogs.promptToSaveFile("Export image region", null, defaultName, writerName, ext);
if (fileOutput == null)
return;
try {
if (request == null) {
if (exportDownsample.get() == 1.0)
writer.writeImage(server, fileOutput.getAbsolutePath());
else
writer.writeImage(ImageServers.pyramidalize(server, exportDownsample.get()), fileOutput.getAbsolutePath());
} else
writer.writeImage(server, request, fileOutput.getAbsolutePath());
lastWriter = writer;
} catch (IOException e) {
Dialogs.showErrorMessage("Export region", e);
}
}
use of qupath.lib.images.writers.ImageWriter in project qupath by qupath.
the class Commands method saveSnapshot.
/**
* Save an image snapshot, prompting the user to select the output file.
* @param qupath the {@link QuPathGUI} instance to snapshot
* @param type the snapshot type
* @return true if a snapshot was saved, false otherwise
*/
public static boolean saveSnapshot(QuPathGUI qupath, GuiTools.SnapshotType type) {
BufferedImage img = GuiTools.makeSnapshot(qupath, type);
String ext = defaultScreenshotExtension.get();
List<ImageWriter<BufferedImage>> compatibleWriters = ImageWriterTools.getCompatibleWriters(BufferedImage.class, ext);
if (compatibleWriters.isEmpty()) {
logger.error("No compatible image writers found for extension: " + ext);
return false;
}
File fileOutput = Dialogs.promptToSaveFile(null, null, null, ext, ext);
if (fileOutput == null)
return false;
// Loop through the writers and stop when we are successful
for (var writer : compatibleWriters) {
try {
writer.writeImage(img, fileOutput.getAbsolutePath());
return true;
} catch (Exception e) {
logger.error("Error saving snapshot " + type + " to " + fileOutput.getAbsolutePath(), e);
}
}
return false;
}
Aggregations