use of qupath.lib.display.ChannelDisplayInfo in project qupath by qupath.
the class ProjectImportImagesCommand method getThumbnailRGB.
// /**
// * Add a single ImageServer to a project, without considering sub-images.
// * <p>
// * This includes an optional attempt to request a thumbnail; if this fails, the image will not be added.
// *
// * @param project the project to which the entry should be added
// * @param server the server to add
// * @param type the ImageType that should be set for each entry being added
// * @return
// */
// public static ProjectImageEntry<BufferedImage> addSingleImageToProject(Project<BufferedImage> project, ImageServer<BufferedImage> server, ImageType type) {
// ProjectImageEntry<BufferedImage> entry = null;
// try {
// var img = getThumbnailRGB(server, null);
// entry = project.addImage(server);
// if (entry != null) {
// // Write a thumbnail if we can
// entry.setThumbnail(img);
// // Initialize an ImageData object with a type, if required
// if (type != null) {
// var imageData = new ImageData<>(server, type);
// entry.saveImageData(imageData);
// }
// }
// } catch (IOException e) {
// logger.warn("Error attempting to add " + server, e);
// }
// return entry;
// }
public static BufferedImage getThumbnailRGB(ImageServer<BufferedImage> server, ImageDisplay imageDisplay) throws IOException {
var img2 = server.getDefaultThumbnail(server.nZSlices() / 2, 0);
// Try to write RGB images directly
boolean success = false;
if (imageDisplay == null && (server.isRGB() || img2.getType() == BufferedImage.TYPE_BYTE_GRAY)) {
return resizeForThumbnail(img2);
}
if (!success) {
// Try with display transforms
if (imageDisplay == null) {
// By wrapping the thumbnail, we avoid slow z-stack/time series requests & determine brightness & contrast just from one plane
var wrappedServer = new WrappedBufferedImageServer("Dummy", img2, server.getMetadata().getChannels());
imageDisplay = new ImageDisplay(new ImageData<>(wrappedServer));
// imageDisplay = new ImageDisplay(new ImageData<>(server));
}
for (ChannelDisplayInfo info : imageDisplay.selectedChannels()) {
imageDisplay.autoSetDisplayRange(info);
}
img2 = imageDisplay.applyTransforms(img2, null);
return resizeForThumbnail(img2);
}
return img2;
}
use of qupath.lib.display.ChannelDisplayInfo in project qupath by qupath.
the class MiniViewers method createDialog.
static Stage createDialog(QuPathViewer viewer, boolean channelViewer) {
final Stage dialog = new Stage();
dialog.initOwner(viewer.getView().getScene().getWindow());
ObservableList<ChannelDisplayInfo> channels = viewer.getImageDisplay().availableChannels();
MiniViewerManager manager = new MiniViewerManager(viewer, channelViewer ? channels.size() : 0);
manager.getPane().styleProperty().bind(style);
if (channelViewer) {
dialog.setTitle("Channel viewer");
Scene scene = new Scene(manager.getPane(), 400, 400);
ListChangeListener<ChannelDisplayInfo> listChangeListener = new ListChangeListener<ChannelDisplayInfo>() {
@Override
public void onChanged(Change<? extends ChannelDisplayInfo> c) {
if (c.getList().size() != manager.nChannels()) {
manager.setChannels(channels.size());
scene.setRoot(manager.getPane());
}
}
};
channels.addListener(listChangeListener);
dialog.setOnHiding(e -> {
channels.removeListener(listChangeListener);
manager.close();
manager.getPane().styleProperty().unbind();
});
dialog.setScene(scene);
} else {
dialog.setTitle("Mini viewer");
Scene scene = new Scene(manager.getPane(), 400, 400);
dialog.setScene(scene);
dialog.setOnHiding(e -> {
manager.close();
manager.getPane().styleProperty().unbind();
});
}
createPopup(manager);
return dialog;
}
use of qupath.lib.display.ChannelDisplayInfo in project qupath by qupath.
the class BrightnessContrastCommand method createDialog.
protected Stage createDialog() {
if (!isInitialized())
initializeSliders();
initializePopup();
changed(null, null, qupath.getImageData());
BorderPane pane = new BorderPane();
GridPane box = new GridPane();
String blank = " ";
Label labelMin = new Label("Min display");
// labelMin.setTooltip(new Tooltip("Set minimum lookup table value - double-click to edit manually"));
Label labelMinValue = new Label(blank);
labelMinValue.setTooltip(new Tooltip("Set minimum lookup table value - double-click to edit manually"));
labelMinValue.textProperty().bind(Bindings.createStringBinding(() -> {
// If value < 10, return 2 dp, otherwise 1 dp. Should be more useful for 16-/32-bit images.
return sliderMin.getValue() < 10 ? String.format("%.2f", sliderMin.getValue()) : String.format("%.1f", sliderMin.getValue());
}, table.getSelectionModel().selectedItemProperty(), sliderMin.valueProperty()));
box.add(labelMin, 0, 0);
box.add(sliderMin, 1, 0);
box.add(labelMinValue, 2, 0);
Label labelMax = new Label("Max display");
labelMax.setTooltip(new Tooltip("Set maximum lookup table value - double-click to edit manually"));
Label labelMaxValue = new Label(blank);
labelMaxValue.setTooltip(new Tooltip("Set maximum lookup table value - double-click to edit manually"));
labelMaxValue.textProperty().bind(Bindings.createStringBinding(() -> {
// If value < 10, return 2 dp, otherwise 1 dp. Should be more useful for 16-/32-bit images.
return sliderMax.getValue() < 10 ? String.format("%.2f", sliderMax.getValue()) : String.format("%.1f", sliderMax.getValue());
}, table.getSelectionModel().selectedItemProperty(), sliderMax.valueProperty()));
box.add(labelMax, 0, 1);
box.add(sliderMax, 1, 1);
box.add(labelMaxValue, 2, 1);
box.setVgap(5);
GridPane.setFillWidth(sliderMin, Boolean.TRUE);
GridPane.setFillWidth(sliderMax, Boolean.TRUE);
box.prefWidthProperty().bind(pane.widthProperty());
box.setPadding(new Insets(5, 0, 5, 0));
GridPane.setHgrow(sliderMin, Priority.ALWAYS);
GridPane.setHgrow(sliderMax, Priority.ALWAYS);
// In the absence of a better way, make it possible to enter display range values
// manually by double-clicking on the corresponding label
labelMinValue.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
ChannelDisplayInfo infoVisible = getCurrentInfo();
if (infoVisible == null)
return;
Double value = Dialogs.showInputDialog("Display range", "Set display range minimum", (double) infoVisible.getMinDisplay());
if (value != null && !Double.isNaN(value)) {
sliderMin.setValue(value);
// Update display directly if out of slider range
if (value < sliderMin.getMin() || value > sliderMin.getMax()) {
imageDisplay.setMinMaxDisplay(infoVisible, (float) value.floatValue(), (float) infoVisible.getMaxDisplay());
// infoVisible.setMinDisplay(value.floatValue());
// viewer.updateThumbnail();
updateSliders();
viewer.repaintEntireImage();
}
}
}
});
labelMaxValue.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
ChannelDisplayInfo infoVisible = getCurrentInfo();
if (infoVisible == null)
return;
Double value = Dialogs.showInputDialog("Display range", "Set display range maximum", (double) infoVisible.getMaxDisplay());
if (value != null && !Double.isNaN(value)) {
sliderMax.setValue(value);
// Update display directly if out of slider range
if (value < sliderMax.getMin() || value > sliderMax.getMax()) {
imageDisplay.setMinMaxDisplay(infoVisible, (float) infoVisible.getMinDisplay(), (float) value.floatValue());
// infoVisible.setMaxDisplay(value.floatValue());
// viewer.updateThumbnail();
updateSliders();
viewer.repaintEntireImage();
}
}
}
});
Button btnAuto = new Button("Auto");
btnAuto.setOnAction(e -> {
if (imageDisplay == null)
return;
// if (histogram == null)
// return;
// // setSliders((float)histogram.getEdgeMin(), (float)histogram.getEdgeMax());
ChannelDisplayInfo info = getCurrentInfo();
double saturation = PathPrefs.autoBrightnessContrastSaturationPercentProperty().get() / 100.0;
imageDisplay.autoSetDisplayRange(info, saturation);
for (ChannelDisplayInfo info2 : table.getSelectionModel().getSelectedItems()) {
imageDisplay.autoSetDisplayRange(info2, saturation);
}
updateSliders();
handleSliderChange();
});
Button btnReset = new Button("Reset");
btnReset.setOnAction(e -> {
for (ChannelDisplayInfo info : table.getSelectionModel().getSelectedItems()) {
imageDisplay.setMinMaxDisplay(info, info.getMinAllowed(), info.getMaxAllowed());
}
sliderMin.setValue(sliderMin.getMin());
sliderMax.setValue(sliderMax.getMax());
});
Stage dialog = new Stage();
dialog.initOwner(qupath.getStage());
dialog.setTitle("Brightness & contrast");
// Create color/channel display table
table = new TableView<>(imageDisplay == null ? FXCollections.observableArrayList() : imageDisplay.availableChannels());
table.setPlaceholder(new Text("No channels available"));
table.addEventHandler(KeyEvent.KEY_PRESSED, new CopyTableListener());
table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);
table.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> {
updateHistogram();
updateSliders();
});
TableColumn<ChannelDisplayInfo, ChannelDisplayInfo> col1 = new TableColumn<>("Color");
col1.setCellValueFactory(new Callback<CellDataFeatures<ChannelDisplayInfo, ChannelDisplayInfo>, ObservableValue<ChannelDisplayInfo>>() {
@Override
public ObservableValue<ChannelDisplayInfo> call(CellDataFeatures<ChannelDisplayInfo, ChannelDisplayInfo> p) {
return new SimpleObjectProperty<ChannelDisplayInfo>(p.getValue());
}
});
col1.setCellFactory(column -> {
return new TableCell<ChannelDisplayInfo, ChannelDisplayInfo>() {
@Override
protected void updateItem(ChannelDisplayInfo item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
return;
}
setText(item.getName());
Rectangle square = new Rectangle(0, 0, 10, 10);
Integer rgb = item.getColor();
if (rgb == null)
square.setFill(Color.TRANSPARENT);
else
square.setFill(ColorToolsFX.getCachedColor(rgb));
setGraphic(square);
}
};
});
col1.setSortable(false);
TableColumn<ChannelDisplayInfo, Boolean> col2 = new TableColumn<>("Selected");
col2.setCellValueFactory(new Callback<CellDataFeatures<ChannelDisplayInfo, Boolean>, ObservableValue<Boolean>>() {
@Override
public ObservableValue<Boolean> call(CellDataFeatures<ChannelDisplayInfo, Boolean> item) {
SimpleBooleanProperty property = new SimpleBooleanProperty(imageDisplay.selectedChannels().contains(item.getValue()));
// Remove repaint code here - now handled by table selection changes
property.addListener((v, o, n) -> {
imageDisplay.setChannelSelected(item.getValue(), n);
table.refresh();
Platform.runLater(() -> viewer.repaintEntireImage());
});
return property;
}
});
col2.setCellFactory(column -> {
CheckBoxTableCell<ChannelDisplayInfo, Boolean> cell = new CheckBoxTableCell<>();
// Select cells when clicked - means a click anywhere within the row forces selection.
// Previously, clicking within the checkbox didn't select the row.
cell.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> {
if (e.isPopupTrigger())
return;
int ind = cell.getIndex();
var tableView = cell.getTableView();
if (ind < tableView.getItems().size()) {
if (e.isShiftDown())
tableView.getSelectionModel().select(ind);
else
tableView.getSelectionModel().clearAndSelect(ind);
var channel = cell.getTableRow().getItem();
// Handle clicks within the cell but outside the checkbox
if (e.getTarget() == cell && channel != null && imageDisplay != null) {
updateDisplay(channel, !imageDisplay.selectedChannels().contains(channel));
}
e.consume();
}
});
return cell;
});
col2.setSortable(false);
col2.setEditable(true);
col2.setResizable(false);
// Handle color change requests when an appropriate row is double-clicked
table.setRowFactory(tableView -> {
TableRow<ChannelDisplayInfo> row = new TableRow<>();
row.setOnMouseClicked(e -> {
if (e.getClickCount() == 2) {
ChannelDisplayInfo info = row.getItem();
var imageData = viewer.getImageData();
if (info instanceof DirectServerChannelInfo && imageData != null) {
DirectServerChannelInfo multiInfo = (DirectServerChannelInfo) info;
int c = multiInfo.getChannel();
var channel = imageData.getServer().getMetadata().getChannel(c);
Color color = ColorToolsFX.getCachedColor(multiInfo.getColor());
picker.setValue(color);
Dialog<ButtonType> colorDialog = new Dialog<>();
colorDialog.setTitle("Channel properties");
colorDialog.getDialogPane().getButtonTypes().setAll(ButtonType.APPLY, ButtonType.CANCEL);
var paneColor = new GridPane();
int r = 0;
var labelName = new Label("Channel name");
var tfName = new TextField(channel.getName());
labelName.setLabelFor(tfName);
PaneTools.addGridRow(paneColor, r++, 0, "Enter a name for the current channel", labelName, tfName);
var labelColor = new Label("Channel color");
labelColor.setLabelFor(picker);
PaneTools.addGridRow(paneColor, r++, 0, "Choose the color for the current channel", labelColor, picker);
paneColor.setVgap(5.0);
paneColor.setHgap(5.0);
colorDialog.getDialogPane().setContent(paneColor);
Optional<ButtonType> result = colorDialog.showAndWait();
if (result.orElse(ButtonType.CANCEL) == ButtonType.APPLY) {
// if (!DisplayHelpers.showMessageDialog("Choose channel color", picker))
// return;
String name = tfName.getText().trim();
if (name.isEmpty()) {
Dialogs.showErrorMessage("Set channel name", "The channel name must not be empty!");
return;
}
Color color2 = picker.getValue();
if (color == color2 && name.equals(channel.getName()))
return;
// Update the server metadata
int colorUpdated = ColorToolsFX.getRGB(color2);
if (imageData != null) {
var server = imageData.getServer();
var metadata = server.getMetadata();
var channels = new ArrayList<>(metadata.getChannels());
channels.set(c, ImageChannel.getInstance(name, colorUpdated));
var metadata2 = new ImageServerMetadata.Builder(metadata).channels(channels).build();
imageData.updateServerMetadata(metadata2);
}
// Update the display
multiInfo.setLUTColor((int) (color2.getRed() * 255), (int) (color2.getGreen() * 255), (int) (color2.getBlue() * 255));
// Add color property
imageDisplay.saveChannelColorProperties();
viewer.repaintEntireImage();
updateHistogram();
table.refresh();
}
}
}
});
return row;
});
table.getColumns().add(col1);
table.getColumns().add(col2);
table.setEditable(true);
table.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
// Hack... space for a scrollbar
col1.prefWidthProperty().bind(table.widthProperty().subtract(col2.widthProperty()).subtract(25));
// col2.setResizable(false);
// col2.setMaxWidth(100);
// col2.setPrefWidth(50);
// col2.setResizable(false);
// col1.prefWidthProperty().bind(table.widthProperty().multiply(0.7));
// col2.prefWidthProperty().bind(table.widthProperty().multiply(0.2));
// table.getColumnModel().getColumn(0).setCellRenderer(new ChannelCellRenderer());
// table.getColumnModel().getColumn(1).setMaxWidth(table.getColumnModel().getColumn(1).getPreferredWidth());
BorderPane panelColor = new BorderPane();
// panelColor.setBorder(BorderFactory.createTitledBorder("Color display"));
BorderPane paneTableAndFilter = new BorderPane(table);
TextField tfFilter = new TextField("");
tfFilter.textProperty().bindBidirectional(filterText);
tfFilter.setTooltip(new Tooltip("Enter text to find specific channels by name"));
tfFilter.setPromptText("Filter channels by name");
paneTableAndFilter.setBottom(tfFilter);
predicate.addListener((v, o, n) -> updatePredicate());
panelColor.setCenter(paneTableAndFilter);
CheckBox cbShowGrayscale = new CheckBox("Show grayscale");
cbShowGrayscale.selectedProperty().bindBidirectional(showGrayscale);
cbShowGrayscale.setTooltip(new Tooltip("Show single channel with grayscale lookup table"));
if (imageDisplay != null)
cbShowGrayscale.setSelected(!imageDisplay.useColorLUTs());
showGrayscale.addListener(o -> {
if (imageDisplay == null)
return;
Platform.runLater(() -> viewer.repaintEntireImage());
table.refresh();
});
CheckBox cbKeepDisplaySettings = new CheckBox("Keep settings");
cbKeepDisplaySettings.selectedProperty().bindBidirectional(PathPrefs.keepDisplaySettingsProperty());
cbKeepDisplaySettings.setTooltip(new Tooltip("Retain same display settings where possible when opening similar images"));
FlowPane paneCheck = new FlowPane();
paneCheck.getChildren().add(cbShowGrayscale);
paneCheck.getChildren().add(cbKeepDisplaySettings);
paneCheck.setHgap(10);
paneCheck.setPadding(new Insets(5, 0, 0, 0));
panelColor.setBottom(paneCheck);
pane.setCenter(panelColor);
// Create brightness/contrast panel
BorderPane panelSliders = new BorderPane();
panelSliders.setTop(box);
GridPane panelButtons = PaneTools.createColumnGridControls(btnAuto, btnReset);
panelSliders.setBottom(panelButtons);
panelSliders.setPadding(new Insets(5, 0, 5, 0));
BorderPane panelMinMax = new BorderPane();
// panelMinMax.setBorder(BorderFactory.createTitledBorder("Brightness/Contrast"));
panelMinMax.setTop(panelSliders);
histogramPanel.setShowTickLabels(false);
histogramPanel.getChart().setAnimated(false);
// histogramPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
// panelMinMax.setCenter(histogramPanel.getChart());
panelMinMax.setCenter(chartWrapper.getPane());
chartWrapper.getPane().setPrefSize(200, 200);
// histogramPanel.getChart().setPrefSize(200, 200);
// histogramPanel.setPreferredSize(new Dimension(200, 120));
pane.setBottom(panelMinMax);
pane.setPadding(new Insets(10, 10, 10, 10));
Scene scene = new Scene(pane, 350, 500);
dialog.setScene(scene);
dialog.setMinWidth(300);
dialog.setMinHeight(400);
dialog.setMaxWidth(600);
dialog.setMaxHeight(800);
updateTable();
if (!table.getItems().isEmpty())
table.getSelectionModel().select(0);
updateDisplay(getCurrentInfo(), true);
updateHistogram();
updateSliders();
// Update sliders when receiving focus - in case the display has been updated elsewhere
dialog.focusedProperty().addListener((v, o, n) -> {
if (n)
updateSliders();
});
return dialog;
}
use of qupath.lib.display.ChannelDisplayInfo in project qupath by qupath.
the class ExtractRegionCommand method run.
@Override
public void run() {
QuPathViewer viewer = qupath.getViewer();
ImageServer<BufferedImage> server = null;
if (viewer != null)
server = viewer.getServer();
if (server == null)
return;
List<String> unitOptions = new ArrayList<>();
unitOptions.add(PIXELS_UNIT);
String unit = server.getPixelCalibration().getPixelWidthUnit();
if (unit.equals(server.getPixelCalibration().getPixelHeightUnit()) && !unit.equals(PixelCalibration.PIXEL))
unitOptions.add(unit);
if (!unitOptions.contains(resolutionUnit))
resolutionUnit = PIXELS_UNIT;
ParameterList params = new ParameterList().addDoubleParameter("resolution", "Resolution", resolution, null, "Resolution at which the image will be exported, defined as the 'pixel size' in Resolution units").addChoiceParameter("resolutionUnit", "Resolution unit", resolutionUnit, unitOptions, "Units defining the export resolution; if 'pixels' then the resolution is the same as a downsample value").addBooleanParameter("includeROI", "Include ROI", includeROI, "Include the primary object defining the exported region as an active ROI in ImageJ").addBooleanParameter("includeOverlay", "Include overlay", includeOverlay, "Include any objects overlapping the exported region as ROIs on an ImageJ overlay").addBooleanParameter("doTransforms", "Apply color transforms", doTransforms, "Optionally apply any color transforms when sending the pixels to ImageJ").addBooleanParameter("doZ", "All z-slices", doZ, "Optionally include all slices of a z-stack").addBooleanParameter("doT", "All timepoints", doT, "Optionally include all timepoints of a time series");
// params.setHiddenParameters(unitOptions.size() <= 1, "resolutionUnit");
params.setHiddenParameters(server.nZSlices() == 1, "doZ");
params.setHiddenParameters(server.nTimepoints() == 1, "doT");
if (!Dialogs.showParameterDialog("Send region to ImageJ", params))
return;
// Parse values
resolution = params.getDoubleParameterValue("resolution");
resolutionUnit = (String) params.getChoiceParameterValue("resolutionUnit");
includeROI = params.getBooleanParameterValue("includeROI");
includeOverlay = params.getBooleanParameterValue("includeOverlay");
doTransforms = params.getBooleanParameterValue("doTransforms");
doZ = params.getBooleanParameterValue("doZ");
doT = params.getBooleanParameterValue("doT");
// Calculate downsample
double downsample = resolution;
if (!resolutionUnit.equals(PIXELS_UNIT))
downsample = resolution / (server.getPixelCalibration().getPixelHeight().doubleValue() / 2.0 + server.getPixelCalibration().getPixelWidth().doubleValue() / 2.0);
// Color transforms are (currently) only applied for brightfield images - for fluorescence we always provide everything as unchanged as possible
List<ChannelDisplayInfo> selectedChannels = new ArrayList<>(viewer.getImageDisplay().selectedChannels());
List<ChannelDisplayInfo> channels = doTransforms && !selectedChannels.isEmpty() ? selectedChannels : null;
if (channels != null)
server = ChannelDisplayTransformServer.createColorTransformServer(server, channels);
// Loop through all selected objects
Collection<PathObject> pathObjects = viewer.getHierarchy().getSelectionModel().getSelectedObjects();
if (pathObjects.isEmpty())
pathObjects = Collections.singletonList(viewer.getHierarchy().getRootObject());
List<ImagePlus> imps = new ArrayList<>();
for (PathObject pathObject : pathObjects) {
if (Thread.currentThread().isInterrupted() || IJ.escapePressed())
return;
int width, height;
if (pathObject == null || !pathObject.hasROI()) {
width = server.getWidth();
height = server.getHeight();
} else {
Rectangle bounds = AwtTools.getBounds(pathObject.getROI());
width = bounds.width;
height = bounds.height;
}
RegionRequest region;
ROI roi = pathObject == null ? null : pathObject.getROI();
if (roi == null || PathObjectTools.hasPointROI(pathObject)) {
region = RegionRequest.createInstance(server.getPath(), downsample, 0, 0, server.getWidth(), server.getHeight(), viewer.getZPosition(), viewer.getTPosition());
} else
region = RegionRequest.createInstance(server.getPath(), downsample, roi);
// region = RegionRequest.createInstance(server.getPath(), downsample, pathObject.getROI(), viewer.getZPosition(), viewer.getTPosition());
// Minimum size has been removed (v0.2.0-m4); returned regions should be at least 1x1 pixels
// if (region.getWidth() / downsample < 8 || region.getHeight() / downsample < 8) {
// DisplayHelpers.showErrorMessage("Send region to ImageJ", "The width & height of the extracted image must both be >= 8 pixels");
// continue;
// }
// Calculate required z-slices and time-points
int zStart = doZ ? 0 : region.getZ();
int zEnd = doZ ? server.nZSlices() : region.getZ() + 1;
int tStart = doT ? 0 : region.getT();
int tEnd = doT ? server.nTimepoints() : region.getT() + 1;
long nZ = zEnd - zStart;
long nT = tEnd - tStart;
int bytesPerPixel = server.isRGB() ? 4 : server.getPixelType().getBytesPerPixel() * server.nChannels();
double memory = ((long) width * height * nZ * nT * bytesPerPixel) / (downsample * downsample);
// TODO: Perform calculation based on actual amount of available memory
long availableMemory = GeneralTools.estimateAvailableMemory();
if (memory >= availableMemory * 0.95) {
logger.error("Cannot extract region {} - estimated size is too large (approx. {} MB)", pathObject, GeneralTools.formatNumber(memory / (1024.0 * 1024.0), 2));
Dialogs.showErrorMessage("Send region to ImageJ error", "Selected region is too large to extract - please selected a smaller region or use a higher downsample factor");
continue;
}
if (memory / 1024 / 1024 > 100) {
if (pathObjects.size() == 1 && !Dialogs.showYesNoDialog("Send region to ImageJ", String.format("Attempting to extract this region is likely to require > %.2f MB - are you sure you want to continue?", memory / 1024 / 1024)))
return;
}
// We should switch to the event dispatch thread when interacting with ImageJ
try {
ImagePlus imp;
PathObjectHierarchy hierarchy = viewer.getHierarchy();
OverlayOptions options = viewer.getOverlayOptions();
if (zEnd - zStart > 1 || tEnd - tStart > 1) {
// TODO: Handle overlays
imp = IJTools.extractHyperstack(server, region, zStart, zEnd, tStart, tEnd);
if (includeROI && roi != null) {
Roi roiIJ = IJTools.convertToIJRoi(roi, imp.getCalibration(), region.getDownsample());
imp.setRoi(roiIJ);
}
if (includeOverlay) {
Overlay overlay = new Overlay();
for (int t = tStart; t < tEnd; t++) {
for (int z = zStart; z < zEnd; z++) {
RegionRequest request2 = RegionRequest.createInstance(region.getPath(), region.getDownsample(), region.getX(), region.getY(), region.getWidth(), region.getHeight(), z, t);
var regionPredicate = PathObjectTools.createImageRegionPredicate(request2);
Overlay temp = IJExtension.extractOverlay(hierarchy, request2, options, p -> p != pathObject && regionPredicate.test(p));
if (overlay == null)
overlay = temp;
for (int i = 0; i < temp.size(); i++) {
Roi roiIJ = temp.get(i);
roiIJ.setPosition(-1, z + 1, t + 1);
overlay.add(roiIJ);
}
}
}
if (overlay != null && overlay.size() > 0)
imp.setOverlay(overlay);
}
} else if (includeOverlay)
imp = IJExtension.extractROIWithOverlay(server, pathObject, hierarchy, region, includeROI, options).getImage();
else
imp = IJExtension.extractROIWithOverlay(server, pathObject, null, region, includeROI, options).getImage();
// Set display ranges if we can
if (viewer != null && imp instanceof CompositeImage) {
var availableChannels = viewer.getImageDisplay().availableChannels().stream().filter(c -> c instanceof SingleChannelDisplayInfo).map(c -> (SingleChannelDisplayInfo) c).collect(Collectors.toList());
CompositeImage impComp = (CompositeImage) imp;
if (availableChannels.size() == imp.getNChannels()) {
for (int c = 0; c < availableChannels.size(); c++) {
var channel = availableChannels.get(c);
imp.setPosition(c + 1, 1, 1);
impComp.setDisplayRange(channel.getMinDisplay(), channel.getMaxDisplay());
}
imp.setPosition(1);
}
} else if (selectedChannels.size() == 1 && imp.getType() != ImagePlus.COLOR_RGB) {
// Setting the display range for non-RGB images can give unexpected results (changing pixel values)
var channel = selectedChannels.get(0);
imp.setDisplayRange(channel.getMinDisplay(), channel.getMaxDisplay());
}
imps.add(imp);
} catch (IOException e) {
Dialogs.showErrorMessage("Send region to ImageJ", e);
return;
}
}
// Show all the images we've got
if (!imps.isEmpty()) {
SwingUtilities.invokeLater(() -> {
boolean batchMode = Interpreter.batchMode;
// Try to start an ImageJ instance, and return if this fails
try {
ImageJ ij = IJExtension.getImageJInstance();
if (ij == null)
return;
ij.setVisible(true);
// Make sure we aren't in batch mode, so that image will display
Interpreter.batchMode = false;
for (ImagePlus imp : imps) {
imp.show();
}
} finally {
Interpreter.batchMode = batchMode;
}
});
}
}
use of qupath.lib.display.ChannelDisplayInfo in project qupath by qupath.
the class QPEx method setChannelDisplayRange.
/**
* Set the minimum and maximum display range for the specified {@link ImageData} for a channel identified by name.
* @param imageData
* @param channelName
* @param minDisplay
* @param maxDisplay
*/
public static void setChannelDisplayRange(ImageData<BufferedImage> imageData, String channelName, double minDisplay, double maxDisplay) {
// Try to get an existing display if the image is currently open
var viewer = getQuPath().getViewers().stream().filter(v -> v.getImageData() == imageData).findFirst().orElse(null);
ImageDisplay display = viewer == null ? new ImageDisplay(imageData) : viewer.getImageDisplay();
var available = display.availableChannels();
ChannelDisplayInfo info = null;
var serverChannels = imageData.getServer().getMetadata().getChannels();
for (var c : available) {
if (channelName.equals(c.getName())) {
info = c;
break;
}
// We also need to check the channel names, since the info might have adjusted them (e.g. by adding (C1) at the end)
if (c instanceof DirectServerChannelInfo) {
int channelNumber = ((DirectServerChannelInfo) c).getChannel();
if (channelNumber >= 0 && channelNumber < serverChannels.size() && channelName.equals(serverChannels.get(channelNumber).getName())) {
info = c;
break;
}
}
}
if (info == null) {
logger.warn("No channel found with name {} - cannot set display range", channelName);
return;
}
display.setMinMaxDisplay(info, (float) minDisplay, (float) maxDisplay);
// Update the viewer is necessary
if (viewer != null)
viewer.repaintEntireImage();
}
Aggregations