Search in sources :

Example 1 with ImageDisplay

use of qupath.lib.display.ImageDisplay 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;
}
Also used : WrappedBufferedImageServer(qupath.lib.images.servers.WrappedBufferedImageServer) ImageData(qupath.lib.images.ImageData) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo) ImageDisplay(qupath.lib.display.ImageDisplay)

Example 2 with ImageDisplay

use of qupath.lib.display.ImageDisplay 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;
}
Also used : EventHandler(javafx.event.EventHandler) Button(javafx.scene.control.Button) ImageServer(qupath.lib.images.servers.ImageServer) LoggerFactory(org.slf4j.LoggerFactory) KeyCombination(javafx.scene.input.KeyCombination) ContextMenu(javafx.scene.control.ContextMenu) DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) TableView(javafx.scene.control.TableView) QuPathGUI(qupath.lib.gui.QuPathGUI) HistogramData(qupath.lib.gui.charts.HistogramPanelFX.HistogramData) TextField(javafx.scene.control.TextField) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Predicate(java.util.function.Predicate) ObjectBinding(javafx.beans.binding.ObjectBinding) FilteredList(javafx.collections.transformation.FilteredList) Set(java.util.Set) Rectangle(javafx.scene.shape.Rectangle) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo) KeyEvent(javafx.scene.input.KeyEvent) Collectors(java.util.stream.Collectors) CellDataFeatures(javafx.scene.control.TableColumn.CellDataFeatures) Platform(javafx.application.Platform) Text(javafx.scene.text.Text) QuPathViewer(qupath.lib.gui.viewer.QuPathViewer) Priority(javafx.scene.layout.Priority) List(java.util.List) BooleanProperty(javafx.beans.property.BooleanProperty) PropertyChangeListener(java.beans.PropertyChangeListener) Clipboard(javafx.scene.input.Clipboard) FlowPane(javafx.scene.layout.FlowPane) CheckBoxTableCell(javafx.scene.control.cell.CheckBoxTableCell) ColorToolsFX(qupath.lib.gui.tools.ColorToolsFX) Optional(java.util.Optional) NumberAxis(javafx.scene.chart.NumberAxis) ClipboardContent(javafx.scene.input.ClipboardContent) BorderPane(javafx.scene.layout.BorderPane) StringProperty(javafx.beans.property.StringProperty) Scene(javafx.scene.Scene) TextArea(javafx.scene.control.TextArea) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) ButtonType(javafx.scene.control.ButtonType) MouseEvent(javafx.scene.input.MouseEvent) FXCollections(javafx.collections.FXCollections) ImageChannel(qupath.lib.images.servers.ImageChannel) Bindings(javafx.beans.binding.Bindings) ImageDisplay(qupath.lib.display.ImageDisplay) ArrayList(java.util.ArrayList) TableColumn(javafx.scene.control.TableColumn) HashSet(java.util.HashSet) Dialogs(qupath.lib.gui.dialogs.Dialogs) TableCell(javafx.scene.control.TableCell) Insets(javafx.geometry.Insets) Slider(javafx.scene.control.Slider) HistogramPanelFX(qupath.lib.gui.charts.HistogramPanelFX) Callback(javafx.util.Callback) Tooltip(javafx.scene.control.Tooltip) PropertyChangeEvent(java.beans.PropertyChangeEvent) LinkedHashSet(java.util.LinkedHashSet) GridPane(javafx.scene.layout.GridPane) ColorPicker(javafx.scene.control.ColorPicker) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) Color(javafx.scene.paint.Color) Logger(org.slf4j.Logger) Dialog(javafx.scene.control.Dialog) Label(javafx.scene.control.Label) TableRow(javafx.scene.control.TableRow) DecimalFormat(java.text.DecimalFormat) CheckBox(javafx.scene.control.CheckBox) ThresholdedChartWrapper(qupath.lib.gui.charts.HistogramPanelFX.ThresholdedChartWrapper) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) SelectionMode(javafx.scene.control.SelectionMode) Stage(javafx.stage.Stage) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) ObservableValue(javafx.beans.value.ObservableValue) Histogram(qupath.lib.analysis.stats.Histogram) ChangeListener(javafx.beans.value.ChangeListener) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PaneTools(qupath.lib.gui.tools.PaneTools) CellDataFeatures(javafx.scene.control.TableColumn.CellDataFeatures) Label(javafx.scene.control.Label) ObservableValue(javafx.beans.value.ObservableValue) Rectangle(javafx.scene.shape.Rectangle) ArrayList(java.util.ArrayList) DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) Button(javafx.scene.control.Button) Dialog(javafx.scene.control.Dialog) Stage(javafx.stage.Stage) TextField(javafx.scene.control.TextField) FlowPane(javafx.scene.layout.FlowPane) ButtonType(javafx.scene.control.ButtonType) GridPane(javafx.scene.layout.GridPane) Tooltip(javafx.scene.control.Tooltip) Color(javafx.scene.paint.Color) Scene(javafx.scene.Scene) TableColumn(javafx.scene.control.TableColumn) CheckBox(javafx.scene.control.CheckBox) TableRow(javafx.scene.control.TableRow) BorderPane(javafx.scene.layout.BorderPane) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) Insets(javafx.geometry.Insets) CheckBoxTableCell(javafx.scene.control.cell.CheckBoxTableCell) TableCell(javafx.scene.control.TableCell) CheckBoxTableCell(javafx.scene.control.cell.CheckBoxTableCell) Text(javafx.scene.text.Text) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo)

Example 3 with ImageDisplay

use of qupath.lib.display.ImageDisplay in project qupath by qupath.

the class QuPathViewer method setImageData.

/**
 * Set the current image for this viewer.
 * @param imageDataNew
 */
public void setImageData(ImageData<BufferedImage> imageDataNew) {
    if (this.imageDataProperty.get() == imageDataNew)
        return;
    imageDataChanging.set(true);
    // Remove listeners for previous hierarchy
    ImageData<BufferedImage> imageDataOld = this.imageDataProperty.get();
    if (imageDataOld != null) {
        imageDataOld.getHierarchy().removePathObjectListener(this);
        imageDataOld.getHierarchy().getSelectionModel().removePathObjectSelectionListener(this);
    }
    // Determine if the server has remained the same, so we can avoid shifting the viewer
    boolean sameServer = false;
    if (imageDataOld != null && imageDataNew != null && imageDataOld.getServerPath().equals(imageDataNew.getServerPath()))
        sameServer = true;
    this.imageDataProperty.set(imageDataNew);
    ImageServer<BufferedImage> server = imageDataNew == null ? null : imageDataNew.getServer();
    PathObjectHierarchy hierarchy = imageDataNew == null ? null : imageDataNew.getHierarchy();
    long startTime = System.currentTimeMillis();
    if (imageDisplay != null) {
        boolean keepDisplay = PathPrefs.keepDisplaySettingsProperty().get();
        // This is a bit of a hack to avoid calling internal methods for ImageDisplay
        // See https://github.com/qupath/qupath/issues/601
        boolean displaySet = false;
        if (imageDataNew != null && keepDisplay) {
            if (imageDisplay.getImageData() != null && serversCompatible(imageDataNew.getServer(), imageDisplay.getImageData().getServer())) {
                imageDisplay.setImageData(imageDataNew, keepDisplay);
                displaySet = true;
            } else {
                for (var viewer : QuPathGUI.getInstance().getViewers()) {
                    if (this == viewer || viewer.getImageData() == null)
                        continue;
                    var tempServer = viewer.getServer();
                    var currentServer = imageDataNew.getServer();
                    if (serversCompatible(tempServer, currentServer)) {
                        var json = viewer.getImageDisplay().toJSON(false);
                        imageDataNew.setProperty(ImageDisplay.class.getName(), json);
                        imageDisplay.setImageData(imageDataNew, false);
                        displaySet = true;
                        break;
                    }
                }
            }
        }
        if (!displaySet)
            imageDisplay.setImageData(imageDataNew, keepDisplay);
        // See https://github.com/qupath/qupath/issues/843
        if (server != null && !server.isRGB()) {
            var colors = imageDisplay.availableChannels().stream().filter(c -> c instanceof DirectServerChannelInfo).map(c -> c.getColor()).collect(Collectors.toList());
            if (server.nChannels() == colors.size())
                updateServerChannels(server, colors);
        }
    }
    long endTime = System.currentTimeMillis();
    logger.debug("Setting ImageData time: {} ms", endTime - startTime);
    initializeForServer(server);
    if (!sameServer) {
        setDownsampleFactorImpl(getZoomToFitDownsampleFactor(), -1, -1);
        centerImage();
    }
    fireImageDataChanged(imageDataOld, imageDataNew);
    if (imageDataNew != null) {
        // hierarchyPainter = new PathHierarchyPainter(hierarchy);
        hierarchy.addPathObjectListener(this);
        hierarchy.getSelectionModel().addPathObjectSelectionListener(this);
    }
    setSelectedObject(null);
    // TODO: Consider shifting, fixing magnification, repainting etc.
    if (isShowing())
        repaint();
    logger.info("Image data set to {}", imageDataNew);
}
Also used : Color(java.awt.Color) GridOverlay(qupath.lib.gui.viewer.overlays.GridOverlay) Change(javafx.collections.ListChangeListener.Change) Rectangle2D(java.awt.geom.Rectangle2D) StackPane(javafx.scene.layout.StackPane) ColorConvertOp(java.awt.image.ColorConvertOp) ListChangeListener(javafx.collections.ListChangeListener) ImageRegion(qupath.lib.regions.ImageRegion) SimpleIntegerProperty(javafx.beans.property.SimpleIntegerProperty) ColorToolsAwt(qupath.lib.color.ColorToolsAwt) ColorTools(qupath.lib.common.ColorTools) GraphicsContext(javafx.scene.canvas.GraphicsContext) Set(java.util.Set) Canvas(javafx.scene.canvas.Canvas) KeyEvent(javafx.scene.input.KeyEvent) AffineTransform(java.awt.geom.AffineTransform) Observable(javafx.beans.Observable) AbstractOverlay(qupath.lib.gui.viewer.overlays.AbstractOverlay) ImageInputStream(javax.imageio.stream.ImageInputStream) Platform(javafx.application.Platform) RoiEditor(qupath.lib.roi.RoiEditor) BooleanProperty(javafx.beans.property.BooleanProperty) SimpleDoubleProperty(javafx.beans.property.SimpleDoubleProperty) ObservableList(javafx.collections.ObservableList) RectangleROI(qupath.lib.roi.RectangleROI) FXCollections(javafx.collections.FXCollections) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) IntegerProperty(javafx.beans.property.IntegerProperty) ArrayList(java.util.ArrayList) AlphaComposite(java.awt.AlphaComposite) TextAlignment(javafx.scene.text.TextAlignment) Stroke(java.awt.Stroke) PathOverlay(qupath.lib.gui.viewer.overlays.PathOverlay) ImageReader(javax.imageio.ImageReader) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) AwtTools(qupath.lib.awt.common.AwtTools) PathObjectSelectionListener(qupath.lib.objects.hierarchy.events.PathObjectSelectionListener) IOException(java.io.IOException) File(java.io.File) PathObjectTools(qupath.lib.objects.PathObjectTools) Cursor(javafx.scene.Cursor) ROI(qupath.lib.roi.interfaces.ROI) PathTools(qupath.lib.gui.viewer.tools.PathTools) SimpleObjectProperty(javafx.beans.property.SimpleObjectProperty) PixelCalibration(qupath.lib.images.servers.PixelCalibration) ObservableValue(javafx.beans.value.ObservableValue) PathObjectHierarchyListener(qupath.lib.objects.hierarchy.events.PathObjectHierarchyListener) TMAGrid(qupath.lib.objects.hierarchy.TMAGrid) PathPrefs(qupath.lib.gui.prefs.PathPrefs) LookupOp(java.awt.image.LookupOp) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) EventHandler(javafx.event.EventHandler) ImageServer(qupath.lib.images.servers.ImageServer) Point2D(java.awt.geom.Point2D) IIOMetadata(javax.imageio.metadata.IIOMetadata) LoggerFactory(org.slf4j.LoggerFactory) RenderingHints(java.awt.RenderingHints) TMAGridOverlay(qupath.lib.gui.viewer.overlays.TMAGridOverlay) PixelClassificationOverlay(qupath.lib.gui.viewer.overlays.PixelClassificationOverlay) InvalidationListener(javafx.beans.InvalidationListener) DefaultImageRegionStore(qupath.lib.gui.images.stores.DefaultImageRegionStore) ObservableMeasurementTableData(qupath.lib.gui.measure.ObservableMeasurementTableData) PathObjectHierarchyEvent(qupath.lib.objects.hierarchy.events.PathObjectHierarchyEvent) DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) ImageIO(javax.imageio.ImageIO) NoninvertibleTransformException(java.awt.geom.NoninvertibleTransformException) Method(java.lang.reflect.Method) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) MoveTool(qupath.lib.gui.viewer.tools.MoveTool) Shape(java.awt.Shape) Composite(java.awt.Composite) Image(java.awt.Image) BufferedImage(java.awt.image.BufferedImage) Collection(java.util.Collection) Collectors(java.util.stream.Collectors) PathDetectionObject(qupath.lib.objects.PathDetectionObject) PathObject(qupath.lib.objects.PathObject) Objects(java.util.Objects) List(java.util.List) GuiTools(qupath.lib.gui.tools.GuiTools) ColorToolsFX(qupath.lib.gui.tools.ColorToolsFX) Graphics(java.awt.Graphics) ImagePlane(qupath.lib.regions.ImagePlane) SimpleLongProperty(javafx.beans.property.SimpleLongProperty) ReadOnlyLongProperty(javafx.beans.property.ReadOnlyLongProperty) Rectangle(java.awt.Rectangle) ReadOnlyObjectProperty(javafx.beans.property.ReadOnlyObjectProperty) MouseEvent(javafx.scene.input.MouseEvent) ImageChannel(qupath.lib.images.servers.ImageChannel) DoubleProperty(javafx.beans.property.DoubleProperty) ImageRenderer(qupath.lib.gui.images.stores.ImageRenderer) ByteLookupTable(java.awt.image.ByteLookupTable) ColorSpace(java.awt.color.ColorSpace) ImageDisplay(qupath.lib.display.ImageDisplay) ImageRegionStoreHelpers(qupath.lib.gui.images.stores.ImageRegionStoreHelpers) HashSet(java.util.HashSet) Graphics2D(java.awt.Graphics2D) Tooltip(javafx.scene.control.Tooltip) ImageData(qupath.lib.images.ImageData) ICC_Profile(java.awt.color.ICC_Profile) KeyCode(javafx.scene.input.KeyCode) ObjectProperty(javafx.beans.property.ObjectProperty) Logger(org.slf4j.Logger) Iterator(java.util.Iterator) WritableImage(javafx.scene.image.WritableImage) HierarchyOverlay(qupath.lib.gui.viewer.overlays.HierarchyOverlay) LongProperty(javafx.beans.property.LongProperty) PathHierarchyImageServer(qupath.lib.gui.images.servers.PathHierarchyImageServer) TMACoreObject(qupath.lib.objects.TMACoreObject) SimpleBooleanProperty(javafx.beans.property.SimpleBooleanProperty) SwingFXUtils(javafx.embed.swing.SwingFXUtils) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) TileListener(qupath.lib.gui.images.stores.TileListener) PathTool(qupath.lib.gui.viewer.tools.PathTool) PathObjectHierarchy(qupath.lib.objects.hierarchy.PathObjectHierarchy) DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) BufferedImage(java.awt.image.BufferedImage) ImageDisplay(qupath.lib.display.ImageDisplay)

Example 4 with ImageDisplay

use of qupath.lib.display.ImageDisplay 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 number.
 * @param imageData
 * @param channel channel number (0-based index)
 * @param minDisplay
 * @param maxDisplay
 */
public static void setChannelDisplayRange(ImageData<BufferedImage> imageData, int channel, 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();
    if (channel < 0 || channel >= available.size()) {
        logger.warn("Channel {} is out of range ({}-{}) - cannot set display range", channel, 0, available.size() - 1);
        return;
    }
    var info = display.availableChannels().get(channel);
    display.setMinMaxDisplay(info, (float) minDisplay, (float) maxDisplay);
    // Update the viewer is necessary
    if (viewer != null)
        viewer.repaintEntireImage();
}
Also used : ImageDisplay(qupath.lib.display.ImageDisplay)

Example 5 with ImageDisplay

use of qupath.lib.display.ImageDisplay 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();
}
Also used : DirectServerChannelInfo(qupath.lib.display.DirectServerChannelInfo) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo) ImageDisplay(qupath.lib.display.ImageDisplay)

Aggregations

ImageDisplay (qupath.lib.display.ImageDisplay)6 BufferedImage (java.awt.image.BufferedImage)3 ChannelDisplayInfo (qupath.lib.display.ChannelDisplayInfo)3 DirectServerChannelInfo (qupath.lib.display.DirectServerChannelInfo)3 ImageData (qupath.lib.images.ImageData)3 ArrayList (java.util.ArrayList)2 HashSet (java.util.HashSet)2 List (java.util.List)2 Set (java.util.Set)2 Collectors (java.util.stream.Collectors)2 Platform (javafx.application.Platform)2 BooleanProperty (javafx.beans.property.BooleanProperty)2 SimpleBooleanProperty (javafx.beans.property.SimpleBooleanProperty)2 SimpleObjectProperty (javafx.beans.property.SimpleObjectProperty)2 ChangeListener (javafx.beans.value.ChangeListener)2 ObservableValue (javafx.beans.value.ObservableValue)2 FXCollections (javafx.collections.FXCollections)2 EventHandler (javafx.event.EventHandler)2 Tooltip (javafx.scene.control.Tooltip)2 KeyCode (javafx.scene.input.KeyCode)2