Search in sources :

Example 1 with ImageTypeSetting

use of qupath.lib.gui.prefs.PathPrefs.ImageTypeSetting in project qupath by qupath.

the class ImageDetailsPane method promptToSetImageType.

// /**
// * Prompt the user to set the {@link ImageType} for the image.
// * @param imageData
// * @return
// */
// public static boolean promptToSetImageType(ImageData<BufferedImage> imageData) {
// List<ImageType> values = Arrays.asList(ImageType.values());
// if (!imageData.getServer().isRGB()) {
// values = new ArrayList<>(values);
// values.remove(ImageType.BRIGHTFIELD_H_DAB);
// values.remove(ImageType.BRIGHTFIELD_H_E);
// values.remove(ImageType.BRIGHTFIELD_OTHER);
// }
// 
// var dialog = new ChoiceDialog<>(imageData.getImageType(), values);
// dialog.setTitle("Image type");
// if (QuPathGUI.getInstance() != null)
// dialog.initOwner(QuPathGUI.getInstance().getStage());
// dialog.getDialogPane().setHeaderText(null);
// dialog.getDialogPane().setContentText("Set image type");
// dialog.getDialogPane().setPrefWidth(400);
// 
// var labelExplain = new Label("The image type influences some commands (e.g. cell detection) and should be set for every image. "
// + "\n\nSelect an option below or in the preferences to customize how QuPath handles setting the image type when opening an image."
// + "\n\n'Auto-estimate' is convenient to reduce annoying prompts, but the estimates are sometimes wrong. "
// + "When this happens you can correct them by double-clicking "
// + "the type under the 'Image' tab.");
// labelExplain.setWrapText(true);
// labelExplain.setPrefWidth(400);
// labelExplain.setMinHeight(Label.USE_PREF_SIZE);
// 
// var comboSetType = new ComboBox<ImageTypeSetting>();
// comboSetType.getItems().setAll(ImageTypeSetting.values());
// comboSetType.getSelectionModel().select(PathPrefs.imageTypeSettingProperty().get());
// comboSetType.setMaxWidth(Double.MAX_VALUE);
// labelExplain.setPadding(new Insets(0, 0, 10, 0));
// var expandablePane = new BorderPane(labelExplain);
// expandablePane.setBottom(comboSetType);
// 
// dialog.getDialogPane().setExpandableContent(expandablePane);
// 
// var result = dialog.showAndWait();
// ImageType type = result.orElse(null);
// if (type == null)
// return false;
// 
// if (comboSetType.getSelectionModel().getSelectedItem() != null)
// PathPrefs.imageTypeSettingProperty().set(comboSetType.getSelectionModel().getSelectedItem());
// 
// if (type != imageData.getImageType()) {
// imageData.setImageType(type);
// return true;
// }
// return false;
// }
/**
 * Prompt the user to set the {@link ImageType} for the image.
 * @param imageData the image data for which the type should be set
 * @param defaultType the default type (selected when the dialog is shown)
 * @return true if the type was changed, false otherwise
 */
public static boolean promptToSetImageType(ImageData<BufferedImage> imageData, ImageType defaultType) {
    double size = 32;
    var group = new ToggleGroup();
    boolean isRGB = imageData.getServer().isRGB();
    if (defaultType == null)
        defaultType = ImageType.UNSET;
    var buttonMap = new LinkedHashMap<ImageType, ToggleButton>();
    // TODO: Create a nicer icon for unspecified type
    var iconUnspecified = (Group) createImageTypeCell(Color.GRAY, null, null, size);
    if (isRGB) {
        buttonMap.put(ImageType.BRIGHTFIELD_H_E, createImageTypeButton(ImageType.BRIGHTFIELD_H_E, "Brightfield\nH&E", createImageTypeCell(Color.WHITE, Color.PINK, Color.DARKBLUE, size), "Brightfield image with hematoylin & eosin stains\n(8-bit RGB only)", isRGB));
        buttonMap.put(ImageType.BRIGHTFIELD_H_DAB, createImageTypeButton(ImageType.BRIGHTFIELD_H_DAB, "Brightfield\nH-DAB", createImageTypeCell(Color.WHITE, Color.rgb(200, 200, 220), Color.rgb(120, 50, 20), size), "Brightfield image with hematoylin & DAB stains\n(8-bit RGB only)", isRGB));
        buttonMap.put(ImageType.BRIGHTFIELD_OTHER, createImageTypeButton(ImageType.BRIGHTFIELD_OTHER, "Brightfield\nOther", createImageTypeCell(Color.WHITE, Color.ORANGE, Color.FIREBRICK, size), "Brightfield image with other chromogenic stains\n(8-bit RGB only)", isRGB));
    }
    buttonMap.put(ImageType.FLUORESCENCE, createImageTypeButton(ImageType.FLUORESCENCE, "Fluorescence", createImageTypeCell(Color.BLACK, Color.LIGHTGREEN, Color.BLUE, size), "Fluorescence or fluorescence-like image with a dark background\n" + "Also suitable for imaging mass cytometry", true));
    buttonMap.put(ImageType.OTHER, createImageTypeButton(ImageType.OTHER, "Other", createImageTypeCell(Color.BLACK, Color.WHITE, Color.GRAY, size), "Any other image type", true));
    buttonMap.put(ImageType.UNSET, createImageTypeButton(ImageType.UNSET, "Unspecified", iconUnspecified, "Do not set the image type (not recommended for analysis)", true));
    var buttons = buttonMap.values().toArray(ToggleButton[]::new);
    for (var btn : buttons) {
        if (btn.isDisabled()) {
            btn.getTooltip().setText("Image type is not supported because image is not RGB");
        }
    }
    var buttonList = Arrays.asList(buttons);
    group.getToggles().setAll(buttons);
    group.selectedToggleProperty().addListener((v, o, n) -> {
        // Ensure that we can't deselect all buttons
        if (n == null)
            o.setSelected(true);
    });
    PaneTools.setMaxWidth(Double.MAX_VALUE, buttons);
    PaneTools.setMaxHeight(Double.MAX_VALUE, buttons);
    var selectedButton = buttonMap.get(defaultType);
    group.selectToggle(selectedButton);
    var grid = new GridPane();
    int nHorizontal = 3;
    int nVertical = (int) Math.ceil(buttons.length / (double) nHorizontal);
    grid.getColumnConstraints().setAll(IntStream.range(0, nHorizontal).mapToObj(i -> {
        var c = new ColumnConstraints();
        c.setPercentWidth(100.0 / nHorizontal);
        return c;
    }).collect(Collectors.toList()));
    grid.getRowConstraints().setAll(IntStream.range(0, nVertical).mapToObj(i -> {
        var c = new RowConstraints();
        c.setPercentHeight(100.0 / nVertical);
        return c;
    }).collect(Collectors.toList()));
    grid.setVgap(5);
    // grid.setHgap(5);
    grid.setMaxWidth(Double.MAX_VALUE);
    for (int i = 0; i < buttons.length; i++) {
        grid.add(buttons[i], i % nHorizontal, i / nHorizontal);
    }
    // grid.getChildren().setAll(buttons);
    var content = new BorderPane(grid);
    var comboOptions = new ComboBox<ImageTypeSetting>();
    comboOptions.getItems().setAll(ImageTypeSetting.values());
    var prompts = Map.of(ImageTypeSetting.AUTO_ESTIMATE, "Always auto-estimate type (don't prompt)", ImageTypeSetting.PROMPT, "Always prompt me to set type", ImageTypeSetting.NONE, "Don't set the image type");
    comboOptions.setButtonCell(GuiTools.createCustomListCell(p -> prompts.get(p)));
    comboOptions.setCellFactory(c -> GuiTools.createCustomListCell(p -> prompts.get(p)));
    comboOptions.setTooltip(new Tooltip("Choose whether you want to see these prompts " + "when opening an image for the first time"));
    comboOptions.setMaxWidth(Double.MAX_VALUE);
    // comboOptions.prefWidthProperty().bind(grid.widthProperty().subtract(100));
    comboOptions.getSelectionModel().select(PathPrefs.imageTypeSettingProperty().get());
    if (nVertical > 1)
        BorderPane.setMargin(comboOptions, new Insets(5, 0, 0, 0));
    else
        BorderPane.setMargin(comboOptions, new Insets(10, 0, 0, 0));
    content.setBottom(comboOptions);
    var labelDetails = new Label("The image type is used for stain separation " + "by some commands, e.g. 'Cell detection'.\n" + "Brightfield types are only available for 8-bit RGB images.");
    // + "For 'Brightfield' images you can set the color stain vectors.");
    labelDetails.setWrapText(true);
    labelDetails.prefWidthProperty().bind(grid.widthProperty().subtract(10));
    labelDetails.setMaxHeight(Double.MAX_VALUE);
    labelDetails.setPrefHeight(Label.USE_COMPUTED_SIZE);
    labelDetails.setPrefHeight(100);
    labelDetails.setAlignment(Pos.CENTER);
    labelDetails.setTextAlignment(TextAlignment.CENTER);
    var dialog = Dialogs.builder().title("Set image type").headerText("What type of image is this?").content(content).buttons(ButtonType.APPLY, ButtonType.CANCEL).expandableContent(labelDetails).build();
    // Try to make it easier to dismiss the dialog in a variety of ways
    var btnApply = dialog.getDialogPane().lookupButton(ButtonType.APPLY);
    Platform.runLater(() -> selectedButton.requestFocus());
    for (var btn : buttons) {
        btn.setOnMouseClicked(e -> {
            if (!btn.isDisabled() && e.getClickCount() == 2) {
                btnApply.fireEvent(new ActionEvent());
                e.consume();
            }
        });
    }
    var enterPressed = new KeyCodeCombination(KeyCode.ENTER);
    var spacePressed = new KeyCodeCombination(KeyCode.SPACE);
    dialog.getDialogPane().addEventFilter(KeyEvent.KEY_PRESSED, e -> {
        if (enterPressed.match(e) || spacePressed.match(e)) {
            btnApply.fireEvent(new ActionEvent());
            e.consume();
        } else if (e.getCode() == KeyCode.UP || e.getCode() == KeyCode.DOWN || e.getCode() == KeyCode.LEFT || e.getCode() == KeyCode.RIGHT) {
            var selected = (ToggleButton) group.getSelectedToggle();
            var ind = buttonList.indexOf(selected);
            var newSelected = selected;
            if (e.getCode() == KeyCode.UP && ind >= nHorizontal) {
                newSelected = buttonList.get(ind - nHorizontal);
            }
            if (e.getCode() == KeyCode.LEFT && ind > 0) {
                newSelected = buttonList.get(ind - 1);
            }
            if (e.getCode() == KeyCode.RIGHT && ind < buttonList.size() - 1) {
                newSelected = buttonList.get(ind + 1);
            }
            if (e.getCode() == KeyCode.DOWN && ind < buttonList.size() - nHorizontal) {
                newSelected = buttonList.get(ind + nHorizontal);
            }
            newSelected.requestFocus();
            group.selectToggle(newSelected);
            e.consume();
        }
    });
    var response = dialog.showAndWait();
    if (response.orElse(ButtonType.CANCEL) == ButtonType.APPLY) {
        PathPrefs.imageTypeSettingProperty().set(comboOptions.getSelectionModel().getSelectedItem());
        var selectedType = (ImageType) group.getSelectedToggle().getUserData();
        if (selectedType != imageData.getImageType()) {
            imageData.setImageType(selectedType);
            return true;
        }
    }
    return false;
}
Also used : Arrays(java.util.Arrays) ServerTools(qupath.lib.images.servers.ServerTools) StackPane(javafx.scene.layout.StackPane) Category(java.util.Locale.Category) ParameterList(qupath.lib.plugins.parameters.ParameterList) MasterDetailPane(org.controlsfx.control.MasterDetailPane) ReadOnlyObjectWrapper(javafx.beans.property.ReadOnlyObjectWrapper) Map(java.util.Map) ColorTools(qupath.lib.common.ColorTools) Rectangle(javafx.scene.shape.Rectangle) KeyEvent(javafx.scene.input.KeyEvent) WorkflowStep(qupath.lib.plugins.workflow.WorkflowStep) Group(javafx.scene.Group) StandardCharsets(java.nio.charset.StandardCharsets) Platform(javafx.application.Platform) PropertyChangeListener(java.beans.PropertyChangeListener) Clipboard(javafx.scene.input.Clipboard) BorderPane(javafx.scene.layout.BorderPane) RectangleROI(qupath.lib.roi.RectangleROI) ColumnConstraints(javafx.scene.layout.ColumnConstraints) Bindings(javafx.beans.binding.Bindings) ArrayList(java.util.ArrayList) LinkedHashMap(java.util.LinkedHashMap) TextAlignment(javafx.scene.text.TextAlignment) GridPane(javafx.scene.layout.GridPane) Color(javafx.scene.paint.Color) GeneralTools(qupath.lib.common.GeneralTools) RegionRequest(qupath.lib.regions.RegionRequest) Node(javafx.scene.Node) IOException(java.io.IOException) Background(javafx.scene.layout.Background) File(java.io.File) Menu(javafx.scene.control.Menu) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) ROI(qupath.lib.roi.interfaces.ROI) ParameterPanelFX(qupath.lib.gui.dialogs.ParameterPanelFX) ImageView(javafx.scene.image.ImageView) PixelCalibration(qupath.lib.images.servers.PixelCalibration) ObservableValue(javafx.beans.value.ObservableValue) QP(qupath.lib.scripting.QP) Image(javafx.scene.image.Image) PathPrefs(qupath.lib.gui.prefs.PathPrefs) ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) PaneTools(qupath.lib.gui.tools.PaneTools) Pos(javafx.geometry.Pos) ImageServer(qupath.lib.images.servers.ImageServer) URLDecoder(java.net.URLDecoder) LoggerFactory(org.slf4j.LoggerFactory) Side(javafx.geometry.Side) KeyCombination(javafx.scene.input.KeyCombination) ComboBox(javafx.scene.control.ComboBox) Locale(java.util.Locale) ImageIO(javax.imageio.ImageIO) BufferedImageTools(qupath.lib.awt.common.BufferedImageTools) URI(java.net.URI) TableView(javafx.scene.control.TableView) ImageType(qupath.lib.images.ImageData.ImageType) QuPathGUI(qupath.lib.gui.QuPathGUI) Pane(javafx.scene.layout.Pane) MenuItem(javafx.scene.control.MenuItem) BufferedImage(java.awt.image.BufferedImage) Ellipse(javafx.scene.shape.Ellipse) Collection(java.util.Collection) ChannelDisplayInfo(qupath.lib.display.ChannelDisplayInfo) Collectors(java.util.stream.Collectors) StainVector(qupath.lib.color.StainVector) Objects(java.util.Objects) List(java.util.List) ImageTypeSetting(qupath.lib.gui.prefs.PathPrefs.ImageTypeSetting) DefaultScriptableWorkflowStep(qupath.lib.plugins.workflow.DefaultScriptableWorkflowStep) ToggleButton(javafx.scene.control.ToggleButton) GuiTools(qupath.lib.gui.tools.GuiTools) ClipboardContent(javafx.scene.input.ClipboardContent) IntStream(java.util.stream.IntStream) Scene(javafx.scene.Scene) ListView(javafx.scene.control.ListView) ButtonType(javafx.scene.control.ButtonType) RowConstraints(javafx.scene.layout.RowConstraints) ColorDeconvolutionHelper(qupath.lib.color.ColorDeconvolutionHelper) ImageDisplay(qupath.lib.display.ImageDisplay) TableColumn(javafx.scene.control.TableColumn) Dialogs(qupath.lib.gui.dialogs.Dialogs) ColorDeconvolutionStains(qupath.lib.color.ColorDeconvolutionStains) TableCell(javafx.scene.control.TableCell) Insets(javafx.geometry.Insets) BackgroundFill(javafx.scene.layout.BackgroundFill) Callback(javafx.util.Callback) Tooltip(javafx.scene.control.Tooltip) PropertyChangeEvent(java.beans.PropertyChangeEvent) ImageData(qupath.lib.images.ImageData) KeyCode(javafx.scene.input.KeyCode) Modality(javafx.stage.Modality) Logger(org.slf4j.Logger) Label(javafx.scene.control.Label) MenuBar(javafx.scene.control.MenuBar) ReadOnlyStringWrapper(javafx.beans.property.ReadOnlyStringWrapper) DropShadow(javafx.scene.effect.DropShadow) ActionEvent(javafx.event.ActionEvent) ToggleGroup(javafx.scene.control.ToggleGroup) Stage(javafx.stage.Stage) SwingFXUtils(javafx.embed.swing.SwingFXUtils) WrappedBufferedImageServer(qupath.lib.images.servers.WrappedBufferedImageServer) ChangeListener(javafx.beans.value.ChangeListener) Collections(java.util.Collections) ContentDisplay(javafx.scene.control.ContentDisplay) Group(javafx.scene.Group) ToggleGroup(javafx.scene.control.ToggleGroup) BorderPane(javafx.scene.layout.BorderPane) GridPane(javafx.scene.layout.GridPane) Insets(javafx.geometry.Insets) ColumnConstraints(javafx.scene.layout.ColumnConstraints) ComboBox(javafx.scene.control.ComboBox) ActionEvent(javafx.event.ActionEvent) Tooltip(javafx.scene.control.Tooltip) Label(javafx.scene.control.Label) KeyCodeCombination(javafx.scene.input.KeyCodeCombination) LinkedHashMap(java.util.LinkedHashMap) RowConstraints(javafx.scene.layout.RowConstraints) ImageType(qupath.lib.images.ImageData.ImageType) ToggleGroup(javafx.scene.control.ToggleGroup)

Aggregations

BufferedImage (java.awt.image.BufferedImage)1 PropertyChangeEvent (java.beans.PropertyChangeEvent)1 PropertyChangeListener (java.beans.PropertyChangeListener)1 File (java.io.File)1 IOException (java.io.IOException)1 URI (java.net.URI)1 URLDecoder (java.net.URLDecoder)1 StandardCharsets (java.nio.charset.StandardCharsets)1 ArrayList (java.util.ArrayList)1 Arrays (java.util.Arrays)1 Collection (java.util.Collection)1 Collections (java.util.Collections)1 LinkedHashMap (java.util.LinkedHashMap)1 List (java.util.List)1 Locale (java.util.Locale)1 Category (java.util.Locale.Category)1 Map (java.util.Map)1 Objects (java.util.Objects)1 Collectors (java.util.stream.Collectors)1 IntStream (java.util.stream.IntStream)1