Search in sources :

Example 1 with ANN_MLP

use of org.bytedeco.opencv.opencv_ml.ANN_MLP in project qupath by qupath.

the class PixelClassifierPane method initialize.

private void initialize() {
    var imageData = qupath.getImageData();
    int row = 0;
    // Classifier
    pane = new GridPane();
    var labelClassifier = new Label("Classifier");
    var comboClassifier = new ComboBox<OpenCVStatModel>();
    labelClassifier.setLabelFor(comboClassifier);
    selectedClassifier = comboClassifier.getSelectionModel().selectedItemProperty();
    selectedClassifier.addListener((v, o, n) -> updateClassifier());
    var btnEditClassifier = new Button("Edit");
    btnEditClassifier.setOnAction(e -> editClassifierParameters());
    btnEditClassifier.disableProperty().bind(selectedClassifier.isNull());
    PaneTools.addGridRow(pane, row++, 0, "Choose classifier type (RTrees or ANN_MLP are generally good choices)", labelClassifier, comboClassifier, comboClassifier, btnEditClassifier);
    // Image resolution
    var labelResolution = new Label("Resolution");
    labelResolution.setLabelFor(comboResolutions);
    var btnResolution = new Button("Add");
    btnResolution.setOnAction(e -> addResolution());
    selectedResolution = comboResolutions.getSelectionModel().selectedItemProperty();
    PaneTools.addGridRow(pane, row++, 0, "Choose the base image resolution based upon required detail in the classification (see preview on the right)", labelResolution, comboResolutions, comboResolutions, btnResolution);
    // Features
    var labelFeatures = new Label("Features");
    var comboFeatures = new ComboBox<ImageDataTransformerBuilder>();
    comboFeatures.getItems().add(new ImageDataTransformerBuilder.DefaultFeatureCalculatorBuilder(imageData));
    // comboFeatures.getItems().add(new FeatureCalculatorBuilder.ExtractNeighborsFeatureCalculatorBuilder(viewer.getImageData()));
    labelFeatures.setLabelFor(comboFeatures);
    selectedFeatureCalculatorBuilder = comboFeatures.getSelectionModel().selectedItemProperty();
    // var labelFeaturesSummary = new Label("No features selected");
    var btnShowFeatures = new Button("Show");
    btnShowFeatures.setOnAction(e -> showFeatures());
    var btnCustomizeFeatures = new Button("Edit");
    btnCustomizeFeatures.disableProperty().bind(Bindings.createBooleanBinding(() -> {
        var calc = selectedFeatureCalculatorBuilder.get();
        return calc == null || !calc.canCustomize(imageData);
    }, selectedFeatureCalculatorBuilder));
    btnCustomizeFeatures.setOnAction(e -> {
        if (selectedFeatureCalculatorBuilder.get().doCustomize(imageData)) {
            updateFeatureCalculator();
        }
    });
    comboFeatures.getItems().addAll(defaultFeatureCalculatorBuilders);
    comboFeatures.getSelectionModel().select(0);
    comboFeatures.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> updateFeatureCalculator());
    // btnCustomizeFeatures.setOnAction(e -> showFeatures());
    PaneTools.addGridRow(pane, row++, 0, "Select features for the classifier", labelFeatures, comboFeatures, btnCustomizeFeatures, btnShowFeatures);
    // Output
    var labelOutput = new Label("Output");
    var comboOutput = new ComboBox<ImageServerMetadata.ChannelType>();
    comboOutput.getItems().addAll(ImageServerMetadata.ChannelType.CLASSIFICATION, ImageServerMetadata.ChannelType.PROBABILITY);
    selectedOutputType = comboOutput.getSelectionModel().selectedItemProperty();
    selectedOutputType.addListener((v, o, n) -> {
        updateClassifier();
    });
    comboOutput.getSelectionModel().clearAndSelect(0);
    var btnShowOutput = new Button("Show");
    btnShowOutput.setOnAction(e -> showOutput());
    PaneTools.addGridRow(pane, row++, 0, "Choose whether to output classifications only, or estimated probabilities per class (not all classifiers support probabilities, which also require more memory)", labelOutput, comboOutput, comboOutput, btnShowOutput);
    // Region
    var labelRegion = new Label("Region");
    var comboRegionFilter = PixelClassifierUI.createRegionFilterCombo(qupath.getOverlayOptions());
    // var nodeLimit = PixelClassifierTools.createLimitToAnnotationsControl(qupath.getOverlayOptions());
    PaneTools.addGridRow(pane, row++, 0, "Control where the pixel classification is applied during preview", labelRegion, comboRegionFilter, comboRegionFilter, comboRegionFilter);
    // Live predict
    var btnAdvancedOptions = new Button("Advanced options");
    btnAdvancedOptions.setTooltip(new Tooltip("Advanced options to customize preprocessing and classifier behavior"));
    btnAdvancedOptions.setOnAction(e -> {
        if (showAdvancedOptions())
            updateClassifier();
    });
    // Live predict
    var btnProject = new Button("Load training");
    btnProject.setTooltip(new Tooltip("Train using annotations from more images in the current project"));
    btnProject.setOnAction(e -> {
        if (promptToLoadTrainingImages()) {
            updateClassifier();
            int n = trainingEntries.size();
            if (n > 0)
                btnProject.setText("Load training (" + n + ")");
            else
                btnProject.setText("Load training");
        }
    });
    btnProject.disableProperty().bind(qupath.projectProperty().isNull());
    var btnLive = new ToggleButton("Live prediction");
    btnLive.selectedProperty().bindBidirectional(livePrediction);
    btnLive.setTooltip(new Tooltip("Toggle whether to calculate classification 'live' while viewing the image"));
    livePrediction.addListener((v, o, n) -> {
        if (overlay == null) {
            if (n) {
                updateClassifier(n);
                return;
            }
        } else {
            overlay.setLivePrediction(n);
        }
        if (featureOverlay != null)
            featureOverlay.setLivePrediction(n);
    });
    var panePredict = PaneTools.createColumnGridControls(btnProject, btnAdvancedOptions);
    pane.add(panePredict, 0, row++, pane.getColumnCount(), 1);
    // addGridRow(pane, row++, 0, btnPredict, btnPredict, btnPredict);
    // var btnUpdate = new Button("Update classifier");
    // btnUpdate.setMaxWidth(Double.MAX_VALUE);
    // btnUpdate.setOnAction(e -> updateClassifier(true));
    // btnUpdate.disableProperty().bind(qupath.imageDataProperty().isNull().or(btnLive.selectedProperty()));
    pane.add(btnLive, 0, row++, pane.getColumnCount(), 1);
    pieChart = new PieChart();
    // var hierarchy = viewer.getHierarchy();
    // Map<PathClass, List<PathObject>> map = hierarchy == null ? Collections.emptyMap() : PathClassificationLabellingHelper.getClassificationMap(hierarchy, false);
    pieChart.setLabelsVisible(false);
    pieChart.setLegendVisible(true);
    pieChart.setMinSize(40, 40);
    pieChart.setPrefSize(120, 120);
    // pieChart.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
    pieChart.setLegendSide(Side.RIGHT);
    // GridPane.setVgrow(pieChart, Priority.ALWAYS);
    // Tooltip.install(pieChart, new Tooltip("View training classes by proportion"));
    var paneChart = new BorderPane(pieChart);
    // paneChart.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
    // PaneTools.addGridRow(pane, row++, 0,
    // //				null,
    // "View information about the current classifier training",
    // paneChart, paneChart, paneChart);
    PaneTools.setFillWidth(Boolean.TRUE, paneChart);
    PaneTools.setFillHeight(Boolean.TRUE, paneChart);
    PaneTools.setVGrowPriority(Priority.ALWAYS, paneChart);
    PaneTools.setHGrowPriority(Priority.ALWAYS, paneChart);
    pane.add(paneChart, 0, row++, pane.getColumnCount(), 1);
    // Label showing cursor location
    var labelCursor = new Label();
    labelCursor.textProperty().bindBidirectional(cursorLocation);
    labelCursor.setAlignment(Pos.CENTER);
    labelCursor.setTextAlignment(TextAlignment.CENTER);
    labelCursor.setContentDisplay(ContentDisplay.CENTER);
    labelCursor.setWrapText(true);
    labelCursor.setMaxHeight(Double.MAX_VALUE);
    labelCursor.setMinWidth(100);
    labelCursor.setPrefWidth(390);
    labelCursor.setMaxWidth(390);
    labelCursor.setTooltip(new Tooltip("Prediction for current cursor location"));
    paneChart.setBottom(labelCursor);
    // This tends to make it harder to read the proportions as tooltips when putting the mouse over the pie chart
    // Tooltip.install(paneChart, new Tooltip("Relative proportion of training samples"));
    paneChart.setMaxWidth(400);
    // PaneTools.addGridRow(pane, row++, 0,
    // "Prediction for current cursor location",
    // labelCursor, labelCursor, labelCursor);
    comboClassifier.getItems().addAll(OpenCVClassifiers.createStatModel(RTrees.class), OpenCVClassifiers.createStatModel(ANN_MLP.class), OpenCVClassifiers.createStatModel(LogisticRegression.class), OpenCVClassifiers.createStatModel(KNearest.class));
    comboClassifier.getSelectionModel().clearAndSelect(1);
    PaneTools.setHGrowPriority(Priority.ALWAYS, comboResolutions, comboClassifier, comboFeatures);
    PaneTools.setFillWidth(Boolean.TRUE, comboResolutions, comboClassifier, comboFeatures);
    miniViewer = new MiniViewers.MiniViewerManager(qupath.getViewer(), 0);
    var viewerPane = miniViewer.getPane();
    Tooltip.install(viewerPane, new Tooltip("View image at classification resolution"));
    updateAvailableResolutions(imageData);
    selectedResolution.addListener((v, o, n) -> {
        updateResolution(n);
        updateClassifier();
        ensureOverlaySet();
    });
    if (!comboResolutions.getItems().isEmpty())
        comboResolutions.getSelectionModel().clearAndSelect(resolutions.size() / 2);
    pane.setHgap(5);
    pane.setVgap(6);
    var classifierName = new SimpleStringProperty(null);
    var panePostProcess = PaneTools.createRowGrid(PixelClassifierUI.createSavePixelClassifierPane(qupath.projectProperty(), currentClassifier, classifierName), PixelClassifierUI.createPixelClassifierButtons(qupath.imageDataProperty(), currentClassifier, classifierName));
    panePostProcess.setVgap(5);
    // var panePostProcess = PixelClassifierUI.createPixelClassifierButtons(qupath.imageDataProperty(), currentClassifier);
    pane.add(panePostProcess, 0, row++, pane.getColumnCount(), 1);
    PaneTools.setMaxWidth(Double.MAX_VALUE, pane.getChildren().stream().filter(p -> p instanceof Region).toArray(Region[]::new));
    var viewerBorderPane = new BorderPane(viewerPane);
    comboDisplayFeatures.getSelectionModel().selectedItemProperty().addListener((v, o, n) -> ensureOverlaySet());
    comboDisplayFeatures.setMaxWidth(Double.MAX_VALUE);
    spinFeatureMin.setPrefWidth(100);
    spinFeatureMax.setPrefWidth(100);
    spinFeatureMin.valueProperty().addListener((v, o, n) -> updateFeatureDisplayRange());
    spinFeatureMax.valueProperty().addListener((v, o, n) -> updateFeatureDisplayRange());
    sliderFeatureOpacity.valueProperty().addListener((v, o, n) -> {
        if (featureOverlay != null) {
            featureOverlay.setOpacity(n.doubleValue());
        }
        if (overlay != null)
            overlay.setOpacity(n.doubleValue());
        qupath.repaintViewers();
    });
    var btnFeatureAuto = new Button("Auto");
    btnFeatureAuto.setOnAction(e -> autoFeatureContrast());
    comboDisplayFeatures.getItems().setAll(DEFAULT_CLASSIFICATION_OVERLAY);
    comboDisplayFeatures.getSelectionModel().select(DEFAULT_CLASSIFICATION_OVERLAY);
    var featureDisableBinding = comboDisplayFeatures.valueProperty().isEqualTo(DEFAULT_CLASSIFICATION_OVERLAY).or(comboDisplayFeatures.valueProperty().isNull());
    btnFeatureAuto.disableProperty().bind(featureDisableBinding);
    btnFeatureAuto.setMaxHeight(Double.MAX_VALUE);
    spinFeatureMin.disableProperty().bind(featureDisableBinding);
    spinFeatureMin.setEditable(true);
    GuiTools.restrictTextFieldInputToNumber(spinFeatureMin.getEditor(), true);
    spinFeatureMax.disableProperty().bind(featureDisableBinding);
    spinFeatureMax.setEditable(true);
    GuiTools.restrictTextFieldInputToNumber(spinFeatureMax.getEditor(), true);
    var paneFeatures = new GridPane();
    comboDisplayFeatures.setTooltip(new Tooltip("Choose classification result or feature overlay to display (Warning: This requires a lot of memory & computation!)"));
    spinFeatureMin.setTooltip(new Tooltip("Min display value for feature overlay"));
    spinFeatureMax.setTooltip(new Tooltip("Max display value for feature overlay"));
    sliderFeatureOpacity.setTooltip(new Tooltip("Adjust classification/feature overlay opacity"));
    PaneTools.addGridRow(paneFeatures, 0, 0, null, comboDisplayFeatures, comboDisplayFeatures, comboDisplayFeatures, comboDisplayFeatures);
    PaneTools.addGridRow(paneFeatures, 1, 0, null, sliderFeatureOpacity, spinFeatureMin, spinFeatureMax, btnFeatureAuto);
    var factory = new Callback<ListView<String>, ListCell<String>>() {

        @Override
        public ListCell<String> call(ListView<String> param) {
            var listCell = new ListCell<String>() {

                @Override
                public void updateItem(String value, boolean empty) {
                    super.updateItem(value, empty);
                    if (value == null || empty)
                        setText(null);
                    else
                        setText(value);
                }
            };
            listCell.setTextOverrun(OverrunStyle.ELLIPSIS);
            return listCell;
        }
    };
    comboDisplayFeatures.setCellFactory(factory);
    comboDisplayFeatures.setButtonCell(factory.call(null));
    PaneTools.setMaxWidth(Double.MAX_VALUE, comboDisplayFeatures, sliderFeatureOpacity);
    PaneTools.setFillWidth(Boolean.TRUE, comboDisplayFeatures, sliderFeatureOpacity);
    PaneTools.setHGrowPriority(Priority.ALWAYS, comboDisplayFeatures, sliderFeatureOpacity);
    paneFeatures.setHgap(5);
    paneFeatures.setVgap(5);
    paneFeatures.setPadding(new Insets(5));
    paneFeatures.prefWidthProperty().bind(viewerBorderPane.prefWidthProperty());
    viewerBorderPane.setBottom(paneFeatures);
    var splitPane = new BorderPane(viewerBorderPane);
    splitPane.setLeft(pane);
    pane.setMinWidth(400);
    // pane.setPrefWidth(400);
    // pane.setMaxWidth(400);
    // new StackPane(splitPane);
    var fullPane = splitPane;
    pane.setPadding(new Insets(5));
    stage = new Stage();
    stage.setScene(new Scene(fullPane));
    stage.setMinHeight(400);
    stage.setMinWidth(600);
    stage.sizeToScene();
    stage.initOwner(QuPathGUI.getInstance().getStage());
    // stage.getScene().getRoot().disableProperty().bind(
    // QuPathGUI.getInstance().viewerProperty().isNotEqualTo(viewer)
    // );
    updateTitle();
    updateFeatureCalculator();
    PaneTools.setMinWidth(Region.USE_PREF_SIZE, PaneTools.getContentsOfType(stage.getScene().getRoot(), Region.class, true).toArray(Region[]::new));
    stage.show();
    stage.setOnCloseRequest(e -> destroy());
    qupath.getStage().addEventFilter(MouseEvent.MOUSE_MOVED, mouseListener);
    qupath.imageDataProperty().addListener(imageDataListener);
    if (qupath.getImageData() != null)
        qupath.getImageData().getHierarchy().addPathObjectListener(hierarchyListener);
    stage.focusedProperty().addListener((v, o, n) -> {
        if (n) {
            for (var viewer : qupath.getViewers()) {
                var currentOverlay = viewer.getCustomPixelLayerOverlay();
                if (currentOverlay != this.featureOverlay && currentOverlay != this.overlay) {
                    ensureOverlaySet();
                    break;
                }
            }
        }
    });
    nThreads.addListener((v, o, n) -> {
        if (n == null)
            return;
        if (overlay != null)
            overlay.setMaxThreads(n.intValue());
        if (featureOverlay != null)
            featureOverlay.setMaxThreads(n.intValue());
    });
}
Also used : ImageServerMetadata(qupath.lib.images.servers.ImageServerMetadata) BorderPane(javafx.scene.layout.BorderPane) Insets(javafx.geometry.Insets) ListCell(javafx.scene.control.ListCell) Label(javafx.scene.control.Label) KNearest(org.bytedeco.opencv.opencv_ml.KNearest) ListView(javafx.scene.control.ListView) PieChart(javafx.scene.chart.PieChart) Button(javafx.scene.control.Button) ToggleButton(javafx.scene.control.ToggleButton) ANN_MLP(org.bytedeco.opencv.opencv_ml.ANN_MLP) Stage(javafx.stage.Stage) LogisticRegression(org.bytedeco.opencv.opencv_ml.LogisticRegression) MiniViewers(qupath.lib.gui.commands.MiniViewers) RTrees(org.bytedeco.opencv.opencv_ml.RTrees) ToggleButton(javafx.scene.control.ToggleButton) GridPane(javafx.scene.layout.GridPane) ComboBox(javafx.scene.control.ComboBox) Tooltip(javafx.scene.control.Tooltip) SimpleStringProperty(javafx.beans.property.SimpleStringProperty) Scene(javafx.scene.Scene) Callback(javafx.util.Callback) Region(javafx.scene.layout.Region)

Aggregations

SimpleStringProperty (javafx.beans.property.SimpleStringProperty)1 Insets (javafx.geometry.Insets)1 Scene (javafx.scene.Scene)1 PieChart (javafx.scene.chart.PieChart)1 Button (javafx.scene.control.Button)1 ComboBox (javafx.scene.control.ComboBox)1 Label (javafx.scene.control.Label)1 ListCell (javafx.scene.control.ListCell)1 ListView (javafx.scene.control.ListView)1 ToggleButton (javafx.scene.control.ToggleButton)1 Tooltip (javafx.scene.control.Tooltip)1 BorderPane (javafx.scene.layout.BorderPane)1 GridPane (javafx.scene.layout.GridPane)1 Region (javafx.scene.layout.Region)1 Stage (javafx.stage.Stage)1 Callback (javafx.util.Callback)1 ANN_MLP (org.bytedeco.opencv.opencv_ml.ANN_MLP)1 KNearest (org.bytedeco.opencv.opencv_ml.KNearest)1 LogisticRegression (org.bytedeco.opencv.opencv_ml.LogisticRegression)1 RTrees (org.bytedeco.opencv.opencv_ml.RTrees)1