use of qupath.lib.analysis.stats.Histogram 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.analysis.stats.Histogram in project qupath by qupath.
the class HistogramDisplay method setHistogram.
void setHistogram(final PathTableData<?> model, final String columnName) {
if (model != null && model.getMeasurementNames().contains(columnName)) {
double[] values = model.getDoubleValues(columnName);
int nBins = params.getIntParameterValue("nBins");
if (nBins < 2)
nBins = 2;
else if (nBins > 1000)
nBins = 1000;
// We can have values in the 'wrong' order to facilitate comparison...
Arrays.sort(values);
// Check if we've actually changed anything - if not, then abort
if (nBins == currentBins && currentValues != null && Arrays.equals(currentValues, values))
return;
Histogram histogram = new Histogram(values, nBins);
// histogram.setNormalizeCounts(params.getBooleanParameterValue("normalizeCounts"));
HistogramData histogramData = HistogramPanelFX.createHistogramData(histogram, false, (Integer) null);
histogramData.setNormalizeCounts(params.getBooleanParameterValue("normalizeCounts"));
panelHistogram.getHistogramData().setAll(histogramData);
AreaChart<Number, Number> chart = panelHistogram.getChart();
chart.setVerticalGridLinesVisible(true);
chart.setHorizontalGridLinesVisible(true);
chart.setLegendVisible(false);
// Can't stop them being orange...
chart.setCreateSymbols(false);
chart.getXAxis().setLabel("Values");
chart.getYAxis().setLabel("Counts");
chart.getYAxis().setTickLabelsVisible(true);
chart.getYAxis().setTickMarkVisible(true);
chart.getXAxis().setTickLabelsVisible(true);
chart.getXAxis().setTickMarkVisible(true);
chart.setAnimated(params.getBooleanParameterValue("animate"));
updateTable(histogram);
currentColumn = columnName;
currentBins = nBins;
currentValues = values;
this.model = model;
} else
panelHistogram.getHistogramData().clear();
}
use of qupath.lib.analysis.stats.Histogram in project qupath by qupath.
the class KaplanMeierDisplay method generatePlot.
@SuppressWarnings("unchecked")
private void generatePlot() {
KaplanMeierDisplay.ScoreData newScoreData = scoreData;
// If we have a hierarchy, update the scores with the most recent data
if (hierarchy != null) {
List<TMACoreObject> cores = PathObjectTools.getTMACoreObjects(hierarchy, false);
double[] survival = new double[cores.size()];
boolean[] censored = new boolean[cores.size()];
double[] scores = new double[cores.size()];
// scoreColumn = "RoughScore";
for (int i = 0; i < cores.size(); i++) {
TMACoreObject core = cores.get(i);
MeasurementList ml = core.getMeasurementList();
survival[i] = core.getMeasurementList().getMeasurementValue(survivalColumn);
double censoredValue = core.getMeasurementList().getMeasurementValue(censoredColumn);
boolean hasCensoredValue = !Double.isNaN(censoredValue) && (censoredValue == 0 || censoredValue == 1);
censored[i] = censoredValue != 0;
if (!hasCensoredValue) {
// If we don't have a censored value, ensure we mask out everything else
scores[i] = Double.NaN;
survival[i] = Double.NaN;
} else if (ml.containsNamedMeasurement(scoreColumn))
// Get the score if we can
scores[i] = ml.getMeasurementValue(scoreColumn);
else {
// // Try to compute score if we need to
// Map<String, Number> map = ROIMeaningfulMeasurements.getPathClassSummaryMeasurements(core.getChildObjects(), true);
// Number value = map.get(scoreColumn);
// if (value == null)
scores[i] = Double.NaN;
// else
// scores[i] = value.doubleValue();
}
}
// Mask out any scores that don't have associated survival data
for (int i = 0; i < survival.length; i++) {
if (Double.isNaN(survival[i]))
scores[i] = Double.NaN;
}
newScoreData = new ScoreData(scores, survival, censored);
}
if (newScoreData == null || newScoreData.scores.length == 0)
return;
// KaplanMeier kmHigh = new KaplanMeier("Above threshold");
// KaplanMeier kmLow = new KaplanMeier("Below threshold");
double[] quartiles = StatisticsHelper.getQuartiles(newScoreData.scores);
double q1 = quartiles[0];
double median = quartiles[1];
double q3 = quartiles[2];
double[] thresholds;
if (params != null) {
Object thresholdMethod = params.getChoiceParameterValue("scoreThresholdMethod");
if (thresholdMethod.equals("Median")) {
// panelParams.setNumericParameterValue("scoreThreshold", median);
// ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
thresholds = new double[] { median };
} else if (thresholdMethod.equals("Tertiles")) {
// ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
thresholds = StatisticsHelper.getTertiles(newScoreData.scores);
} else if (thresholdMethod.equals("Quartiles")) {
// ((DoubleParameter)params.getParameters().get("scoreThreshold")).setValue(median); // TODO: UPDATE DIALOG!
thresholds = new double[] { q1, median, q3 };
} else if (thresholdMethod.equals("Manual (1)")) {
thresholds = new double[] { params.getDoubleParameterValue("threshold1") };
} else if (thresholdMethod.equals("Manual (2)")) {
thresholds = new double[] { params.getDoubleParameterValue("threshold1"), params.getDoubleParameterValue("threshold2") };
} else
// if (thresholdMethod.equals("Manual (3)")) {
thresholds = new double[] { params.getDoubleParameterValue("threshold1"), params.getDoubleParameterValue("threshold2"), params.getDoubleParameterValue("threshold3") };
} else
thresholds = new double[] { median };
double minVal = Double.POSITIVE_INFINITY;
double maxVal = Double.NEGATIVE_INFINITY;
int numNonNaN = 0;
for (double d : newScoreData.scores) {
if (Double.isNaN(d))
continue;
if (d < minVal)
minVal = d;
if (d > maxVal)
maxVal = d;
numNonNaN++;
}
// If not this, we don't have valid scores that we can work with
boolean scoresValid = maxVal > minVal;
double maxTimePoint = 0;
for (double d : newScoreData.survival) {
if (Double.isNaN(d))
continue;
if (d > maxTimePoint)
maxTimePoint = d;
}
if (panelParams != null && maxTimePoint > ((IntParameter) params.getParameters().get("censorTimePoints")).getUpperBound()) {
panelParams.setNumericParameterValueRange("censorTimePoints", 0, Math.ceil(maxTimePoint));
}
// Optionally censor at specified time
double censorThreshold = params == null ? maxTimePoint : params.getIntParameterValue("censorTimePoints");
// Compute log-rank p-values for *all* possible thresholds
// Simultaneously determine the threshold that yields the lowest p-value,
// resolving ties in favour of a more even split between high/low numbers of events
boolean pValuesChanged = false;
if (calculateAllPValues) {
if (!(pValues != null && pValueThresholds != null && newScoreData.equals(scoreData) && censorThreshold == lastPValueCensorThreshold)) {
Map<Double, Double> mapLogRank = new TreeMap<>();
Set<Double> setObserved = new HashSet<>();
for (int i = 0; i < newScoreData.scores.length; i++) {
Double d = newScoreData.scores[i];
boolean observed = !newScoreData.censored[i] && newScoreData.survival[i] < censorThreshold;
if (observed)
setObserved.add(d);
if (mapLogRank.containsKey(d))
continue;
List<KaplanMeierData> kmsTemp = splitByThresholds(newScoreData, new double[] { d }, censorThreshold, false);
// if (kmsTemp.get(1).nObserved() == 0 || kmsTemp.get(1).nObserved() == 0)
// continue;
LogRankResult test = LogRankTest.computeLogRankTest(kmsTemp.get(0), kmsTemp.get(1));
double pValue = test.getPValue();
// double pValue = test.hazardRatio < 1 ? test.hazardRatio : 1.0/test.hazardRatio; // Checking usefulness of Hazard ratios...
if (!Double.isFinite(pValue))
continue;
// if (!Double.isFinite(test.getHazardRatio())) {
// // continue;
// pValue = Double.NaN;
// }
mapLogRank.put(d, pValue);
}
pValueThresholds = new double[mapLogRank.size()];
pValues = new double[mapLogRank.size()];
pValueThresholdsObserved = new boolean[mapLogRank.size()];
int count = 0;
for (Entry<Double, Double> entry : mapLogRank.entrySet()) {
pValueThresholds[count] = entry.getKey();
pValues[count] = entry.getValue();
if (setObserved.contains(entry.getKey()))
pValueThresholdsObserved[count] = true;
count++;
}
// Find the longest 'significant' stretch
int maxSigCount = 0;
int maxSigInd = -1;
int sigCurrent = 0;
int[] sigCount = new int[pValues.length];
for (int i = 0; i < pValues.length; i++) {
if (pValues[i] < 0.05) {
sigCurrent++;
sigCount[i] = sigCurrent;
if (sigCurrent > maxSigCount) {
maxSigCount = sigCurrent;
maxSigInd = i;
}
} else
sigCurrent = 0;
}
if (maxSigCount == 0) {
logger.info("No p-values < 0.05");
} else {
double minThresh = maxSigInd - maxSigCount < 0 ? pValueThresholds[0] - 0.0000001 : pValueThresholds[maxSigInd - maxSigCount];
double maxThresh = pValueThresholds[maxSigInd];
int nBetween = 0;
int nBetweenObserved = 0;
for (int i = 0; i < newScoreData.scores.length; i++) {
if (newScoreData.scores[i] > minThresh && newScoreData.scores[i] <= maxThresh) {
nBetween++;
if (newScoreData.survival[i] < censorThreshold && !newScoreData.censored[i])
nBetweenObserved++;
}
}
logger.info("Longest stretch of p-values < 0.05: {} - {} ({} entries, {} observed)", minThresh, maxThresh, nBetween, nBetweenObserved);
}
pValuesSmoothed = new double[pValues.length];
Arrays.fill(pValuesSmoothed, Double.NaN);
int n = (pValues.length / 20) * 2 + 1;
logger.info("Smoothing log-rank test p-values by " + n);
for (int i = n / 2; i < pValues.length - n / 2; i++) {
double sum = 0;
for (int k = i - n / 2; k < i - n / 2 + n; k++) {
sum += pValues[k];
}
pValuesSmoothed[i] = sum / n;
}
// for (int i = 0; i < pValues.length; i++) {
// double sum = 0;
// for (int k = Math.max(0, i-n/2); k < Math.min(pValues.length, i-n/2+n); k++) {
// sum += pValues[k];
// }
// pValuesSmoothed[i] = sum/n;
// }
// pValues = pValuesSmoothed;
lastPValueCensorThreshold = censorThreshold;
pValuesChanged = true;
}
} else {
lastPValueCensorThreshold = Double.NaN;
pValueThresholds = null;
pValues = null;
}
// if (params != null && !Double.isNaN(bestThreshold) && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value")))
if (params != null && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value"))) {
int bestIdx = -1;
double bestPValue = Double.POSITIVE_INFINITY;
for (int i = pValueThresholds.length / 10; i < pValueThresholds.length * 9 / 10; i++) {
if (pValues[i] < bestPValue) {
bestIdx = i;
bestPValue = pValues[i];
}
}
thresholds = bestIdx >= 0 ? new double[] { pValueThresholds[bestIdx] } : new double[0];
} else if (params != null && (params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest smoothed p-value"))) {
int bestIdx = -1;
double bestPValue = Double.POSITIVE_INFINITY;
for (int i = pValueThresholds.length / 10; i < pValueThresholds.length * 9 / 10; i++) {
if (pValuesSmoothed[i] < bestPValue) {
bestIdx = i;
bestPValue = pValuesSmoothed[i];
}
}
thresholds = bestIdx >= 0 ? new double[] { pValueThresholds[bestIdx] } : new double[0];
}
// Split into different curves using the provided thresholds
List<KaplanMeierData> kms = splitByThresholds(newScoreData, thresholds, censorThreshold, params != null && "Quartiles".equals(params.getChoiceParameterValue("scoreThresholdMethod")));
if (plotter == null) {
plotter = new KaplanMeierChartWrapper(survivalColumn + " time");
// plotter.setBorder(BorderFactory.createTitledBorder("Survival plot"));
// plotter.getCanvas().setWidth(300);
// plotter.getCanvas().setHeight(300);
}
KaplanMeierData[] kmArray = new KaplanMeierData[kms.size()];
plotter.setKaplanMeierCurves(survivalColumn + " time", kms.toArray(kmArray));
tableModel.setSurvivalCurves(thresholds, params != null && params.getChoiceParameterValue("scoreThresholdMethod").equals("Lowest p-value"), kmArray);
// Bar width determined using 'Freedman and Diaconis' rule' (but overridden if this gives < 16 bins...)
double barWidth = (2 * q3 - q1) * Math.pow(numNonNaN, -1.0 / 3.0);
int nBins = 100;
if (!Double.isNaN(barWidth))
barWidth = (int) Math.max(16, Math.ceil((maxVal - minVal) / barWidth));
Histogram histogram = scoresValid ? new Histogram(newScoreData.scores, nBins) : null;
if (histogramPanel == null) {
GridPane paneHistogram = new GridPane();
histogramPanel = new HistogramPanelFX();
histogramPanel.getChart().setAnimated(false);
histogramWrapper = new ThresholdedChartWrapper(histogramPanel.getChart());
for (ObservableNumberValue val : threshProperties) histogramWrapper.addThreshold(val, ColorToolsFX.getCachedColor(240, 0, 0, 128));
histogramWrapper.getPane().setPrefHeight(150);
paneHistogram.add(histogramWrapper.getPane(), 0, 0);
Tooltip.install(histogramPanel.getChart(), new Tooltip("Distribution of scores"));
GridPane.setHgrow(histogramWrapper.getPane(), Priority.ALWAYS);
GridPane.setVgrow(histogramWrapper.getPane(), Priority.ALWAYS);
NumberAxis xAxis = new NumberAxis();
xAxis.setLabel("Score threshold");
NumberAxis yAxis = new NumberAxis();
yAxis.setLowerBound(0);
yAxis.setUpperBound(1);
yAxis.setTickUnit(0.1);
yAxis.setAutoRanging(false);
yAxis.setLabel("P-value");
chartPValues = new LineChart<>(xAxis, yAxis);
chartPValues.setAnimated(false);
chartPValues.setLegendVisible(false);
// Make chart so it can be navigated
ChartTools.makeChartInteractive(chartPValues, xAxis, yAxis);
pValuesChanged = true;
Tooltip.install(chartPValues, new Tooltip("Distribution of p-values (log-rank test) comparing low vs. high for all possible score thresholds"));
// chartPValues.getYAxis().setAutoRanging(false);
pValuesWrapper = new ThresholdedChartWrapper(chartPValues);
for (ObservableNumberValue val : threshProperties) pValuesWrapper.addThreshold(val, ColorToolsFX.getCachedColor(240, 0, 0, 128));
pValuesWrapper.getPane().setPrefHeight(150);
paneHistogram.add(pValuesWrapper.getPane(), 0, 1);
GridPane.setHgrow(pValuesWrapper.getPane(), Priority.ALWAYS);
GridPane.setVgrow(pValuesWrapper.getPane(), Priority.ALWAYS);
ContextMenu popup = new ContextMenu();
ChartTools.addChartExportMenu(chartPValues, popup);
RadioMenuItem miZoomY1 = new RadioMenuItem("0-1");
miZoomY1.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(1);
yAxis.setTickUnit(0.2);
});
RadioMenuItem miZoomY05 = new RadioMenuItem("0-0.5");
miZoomY05.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.5);
yAxis.setTickUnit(0.1);
});
RadioMenuItem miZoomY02 = new RadioMenuItem("0-0.2");
miZoomY02.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.2);
yAxis.setTickUnit(0.05);
});
RadioMenuItem miZoomY01 = new RadioMenuItem("0-0.1");
miZoomY01.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.1);
yAxis.setTickUnit(0.05);
});
RadioMenuItem miZoomY005 = new RadioMenuItem("0-0.05");
miZoomY005.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.05);
yAxis.setTickUnit(0.01);
});
RadioMenuItem miZoomY001 = new RadioMenuItem("0-0.01");
miZoomY001.setOnAction(e -> {
yAxis.setAutoRanging(false);
yAxis.setUpperBound(0.01);
yAxis.setTickUnit(0.005);
});
ToggleGroup tgZoom = new ToggleGroup();
miZoomY1.setToggleGroup(tgZoom);
miZoomY05.setToggleGroup(tgZoom);
miZoomY02.setToggleGroup(tgZoom);
miZoomY01.setToggleGroup(tgZoom);
miZoomY005.setToggleGroup(tgZoom);
miZoomY001.setToggleGroup(tgZoom);
Menu menuZoomY = new Menu("Set y-axis range");
menuZoomY.getItems().addAll(miZoomY1, miZoomY05, miZoomY02, miZoomY01, miZoomY005, miZoomY001);
MenuItem miCopyData = new MenuItem("Copy chart data");
miCopyData.setOnAction(e -> {
String dataString = ChartTools.getChartDataAsString(chartPValues);
ClipboardContent content = new ClipboardContent();
content.putString(dataString);
Clipboard.getSystemClipboard().setContent(content);
});
popup.getItems().addAll(miCopyData, menuZoomY);
chartPValues.setOnContextMenuRequested(e -> {
popup.show(chartPValues, e.getScreenX(), e.getScreenY());
});
for (int col = 0; col < tableModel.getColumnCount(); col++) {
TableColumn<Integer, String> column = new TableColumn<>(tableModel.getColumnName(col));
int colNumber = col;
column.setCellValueFactory(new Callback<CellDataFeatures<Integer, String>, ObservableValue<String>>() {
@Override
public ObservableValue<String> call(CellDataFeatures<Integer, String> p) {
return new SimpleStringProperty((String) tableModel.getValueAt(p.getValue(), colNumber));
}
});
column.setCellFactory(new Callback<TableColumn<Integer, String>, TableCell<Integer, String>>() {
@Override
public TableCell<Integer, String> call(TableColumn<Integer, String> param) {
TableCell<Integer, String> cell = new TableCell<Integer, String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(item);
setTooltip(new Tooltip(item));
}
};
return cell;
}
});
table.getColumns().add(column);
}
table.setPrefHeight(250);
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
table.maxHeightProperty().bind(table.prefHeightProperty());
params = new ParameterList();
// maxTimePoint = 0;
// for (TMACoreObject core : hierarchy.getTMAGrid().getTMACoreList()) {
// double os = core.getMeasurementList().getMeasurementValue(TMACoreObject.KEY_OVERALL_SURVIVAL);
// double rfs = core.getMeasurementList().getMeasurementValue(TMACoreObject.KEY_RECURRENCE_FREE_SURVIVAL);
// if (os > maxTimePoint)
// maxTimePoint = os;
// if (rfs > maxTimePoint)
// maxTimePoint = rfs;
// }
params.addIntParameter("censorTimePoints", "Max censored time", (int) (censorThreshold + 0.5), null, 0, (int) Math.ceil(maxTimePoint), "Latest time point beyond which data will be censored");
// params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Manual", Arrays.asList("Manual", "Median", "Log-rank test"));
if (calculateAllPValues)
// Don't include "Lowest smoothed p-value" - it's not an established method and open to misinterpretation...
params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles", "Lowest p-value"));
else
// params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles", "Lowest p-value", "Lowest smoothed p-value"));
params.addChoiceParameter("scoreThresholdMethod", "Threshold method", "Median", Arrays.asList("Manual (1)", "Manual (2)", "Manual (3)", "Median", "Tertiles", "Quartiles"));
params.addDoubleParameter("threshold1", "Threshold 1", thresholds.length > 0 ? thresholds[0] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
params.addDoubleParameter("threshold2", "Threshold 2", thresholds.length > 1 ? thresholds[1] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
params.addDoubleParameter("threshold3", "Threshold 3", thresholds.length > 2 ? thresholds[2] : (minVal + maxVal) / 2, null, "Threshold to distinguish between patient groups");
params.addBooleanParameter("showAtRisk", "Show at risk", plotter.getShowAtRisk(), "Show number of patients at risk below the plot");
params.addBooleanParameter("showTicks", "Show censored ticks", plotter.getShowCensoredTicks(), "Show ticks to indicate censored data");
params.addBooleanParameter("showKey", "Show key", plotter.getShowKey(), "Show key indicating display of each curve");
// Hide threshold parameters if threshold can't be used
if (!scoresValid) {
// params.setHiddenParameters(true, "scoreThresholdMethod", "scoreThreshold");
histogramPanel.getChart().setVisible(false);
}
panelParams = new ParameterPanelFX(params);
panelParams.addParameterChangeListener(this);
updateThresholdsEnabled();
for (int i = 0; i < threshProperties.length; i++) {
String p = "threshold" + (i + 1);
threshProperties[i].addListener((v, o, n) -> {
if (interactiveThresholds()) {
// Need to do a decent double check with tolerance to text field value changing while typing
if (!GeneralTools.almostTheSame(params.getDoubleParameterValue(p), n.doubleValue(), 0.0001))
panelParams.setNumericParameterValue(p, n);
}
});
}
BorderPane paneBottom = new BorderPane();
TitledPane paneOptions = new TitledPane("Options", panelParams.getPane());
// paneOptions.setCollapsible(false);
Pane paneCanvas = new StackPane();
paneCanvas.getChildren().add(plotter.getCanvas());
GridPane paneLeft = new GridPane();
paneLeft.add(paneOptions, 0, 0);
paneLeft.add(table, 0, 1);
GridPane.setHgrow(paneOptions, Priority.ALWAYS);
GridPane.setHgrow(table, Priority.ALWAYS);
paneBottom.setLeft(paneLeft);
paneBottom.setCenter(paneHistogram);
paneMain.setCenter(paneCanvas);
paneMain.setBottom(paneBottom);
paneMain.setPadding(new Insets(10, 10, 10, 10));
} else if (thresholds.length > 0) {
// Ensure the sliders/text fields are set sensibly
if (!GeneralTools.almostTheSame(thresholds[0], params.getDoubleParameterValue("threshold1"), 0.0001)) {
panelParams.setNumericParameterValue("threshold1", thresholds[0]);
}
if (thresholds.length > 1 && !GeneralTools.almostTheSame(thresholds[1], params.getDoubleParameterValue("threshold2"), 0.0001)) {
panelParams.setNumericParameterValue("threshold2", thresholds[1]);
}
if (thresholds.length > 2 && !GeneralTools.almostTheSame(thresholds[2], params.getDoubleParameterValue("threshold3"), 0.0001)) {
panelParams.setNumericParameterValue("threshold3", thresholds[2]);
}
}
if (histogram != null) {
histogramPanel.getHistogramData().setAll(HistogramPanelFX.createHistogramData(histogram, false, (Color) null));
histogramPanel.getChart().getXAxis().setLabel(scoreColumn);
histogramPanel.getChart().getYAxis().setLabel("Count");
ChartTools.addChartExportMenu(histogramPanel.getChart(), null);
// histogramWrapper.setVerticalLines(thresholds, ColorToolsFX.getCachedColor(240, 0, 0, 128));
// Deal with threshold adjustment
// histogramWrapper.getThresholds().addListener((Observable o) -> generatePlot());
}
if (pValues != null) {
// TODO: Raise earlier where p-value calculation is
if (pValuesChanged) {
ObservableList<XYChart.Data<Number, Number>> data = FXCollections.observableArrayList();
for (int i = 0; i < pValueThresholds.length; i++) {
double pValue = pValues[i];
if (Double.isNaN(pValue))
continue;
data.add(new XYChart.Data<>(pValueThresholds[i], pValue, pValueThresholdsObserved[i]));
}
ObservableList<XYChart.Data<Number, Number>> dataSmoothed = null;
if (pValuesSmoothed != null) {
dataSmoothed = FXCollections.observableArrayList();
for (int i = 0; i < pValueThresholds.length; i++) {
double pValueSmoothed = pValuesSmoothed[i];
if (Double.isNaN(pValueSmoothed))
continue;
dataSmoothed.add(new XYChart.Data<>(pValueThresholds[i], pValueSmoothed));
}
}
// Don't bother showing the smoothed data... it tends to get in the way...
// if (dataSmoothed != null)
// chartPValues.getData().setAll(new XYChart.Series<>("P-values", data), new XYChart.Series<>("Smoothed P-values", dataSmoothed));
// else
chartPValues.getData().setAll(new XYChart.Series<>("P-values", data));
// Add line to show 0.05 significance threshold
if (pValueThresholds.length > 1) {
Data<Number, Number> sigData1 = new Data<>(pValueThresholds[0], 0.05);
Data<Number, Number> sigData2 = new Data<>(pValueThresholds[pValueThresholds.length - 1], 0.05);
XYChart.Series<Number, Number> dataSignificant = new XYChart.Series<>("Significance 0.05", FXCollections.observableArrayList(sigData1, sigData2));
chartPValues.getData().add(dataSignificant);
sigData1.getNode().setVisible(false);
sigData2.getNode().setVisible(false);
}
// pValuesWrapper.clearThresholds();
for (XYChart.Data<Number, Number> dataPoint : data) {
if (!Boolean.TRUE.equals(dataPoint.getExtraValue()))
dataPoint.getNode().setVisible(false);
}
// if (dataSmoothed != null) {
// for (XYChart.Data<Number, Number> dataPoint : dataSmoothed) {
// dataPoint.getNode().setVisible(false);
// }
// chartPValues.getData().get(1).getNode().setOpacity(0.5);
// }
// int count = 0;
// for (int i = 0; i < pValueThresholds.length; i++) {
// double pValue = pValues[i];
// if (Double.isNaN(pValue))
// continue;
// boolean observed = pValueThresholdsObserved[i];
// // if (observed)
// // pValuesWrapper.addThreshold(new ReadOnlyDoubleWrapper(pValueThresholds[i]), Color.rgb(0, 0, 0, 0.05));
//
// if (!observed) {
// // StackPane pane = (StackPane)data.get(count).getNode();
// // pane.setEffect(new DropShadow());
// data.get(count).getNode().setVisible(false);
// }
// count++;
// }
}
for (int i = 0; i < threshProperties.length; i++) {
if (i < thresholds.length)
threshProperties[i].set(thresholds[i]);
else
threshProperties[i].set(Double.NaN);
}
boolean isInteractive = interactiveThresholds();
histogramWrapper.setIsInteractive(isInteractive);
pValuesWrapper.setIsInteractive(isInteractive);
chartPValues.setVisible(true);
}
// else
// chartPValues.setVisible(false);
// Store values for next time
scoreData = newScoreData;
}
use of qupath.lib.analysis.stats.Histogram in project qupath by qupath.
the class CellIntensityClassificationCommand method run.
@Override
public void run() {
// Dialogs.showErrorNotification(title, "Not implemented yet!");
var imageData = qupath.getImageData();
if (imageData == null) {
Dialogs.showNoImageError(title);
return;
}
var hierarchy = imageData.getHierarchy();
// Try to operate on cells, but accept operating on all detections if necessary
var cells = imageData.getHierarchy().getCellObjects();
boolean allDetections = cells.isEmpty();
if (allDetections)
logger.debug("No cells found - will try using all detections");
var detections = allDetections ? imageData.getHierarchy().getDetectionObjects() : cells;
if (detections.isEmpty()) {
Dialogs.showErrorMessage(title, "No cells found in the current image!");
return;
}
var measurements = PathClassifierTools.getAvailableFeatures(detections);
if (measurements.isEmpty()) {
Dialogs.showErrorMessage(title, "No cell measurements found in the current image!");
return;
}
var currentClassifications = PathClassifierTools.createClassificationMap(detections);
var comboMeasurements = new ComboBox<String>();
comboMeasurements.getItems().setAll(measurements);
PaneTools.setToExpandGridPaneWidth(comboMeasurements);
var selectedMeasurement = comboMeasurements.getSelectionModel().selectedItemProperty();
var cbSingleThreshold = new CheckBox("Single threshold");
cbSingleThreshold.setSelected(true);
var singleThreshold = cbSingleThreshold.selectedProperty();
var sliders = new ArrayList<Slider>();
var textFields = new ArrayList<TextField>();
for (int i = 0; i < 3; i++) {
var slider = new Slider();
var tf = new TextField();
tf.setPrefColumnCount(6);
textFields.add(tf);
GuiTools.bindSliderAndTextField(slider, tf, true);
GuiTools.installRangePrompt(slider);
slider.valueProperty().addListener((v, o, n) -> {
updateClassifications(hierarchy, allDetections, selectedMeasurement.get(), parseValues(sliders, singleThreshold.get()));
});
PaneTools.setToExpandGridPaneWidth(slider);
sliders.add(slider);
}
var map = new HashMap<String, double[]>();
var histogramPanel = new HistogramPanelFX();
var chartWrapper = new ThresholdedChartWrapper(histogramPanel.getChart());
chartWrapper.setIsInteractive(true);
singleThreshold.addListener((v, o, n) -> {
chartWrapper.clearThresholds();
Color color = Color.rgb(0, 0, 0, 0.2);
if (!n) {
for (int i = 0; i < sliders.size(); i++) {
chartWrapper.addThreshold(sliders.get(i).valueProperty(), color);
}
} else
chartWrapper.addThreshold(sliders.get(0).valueProperty(), color);
});
selectedMeasurement.addListener((v, o, n) -> {
if (o != null)
map.put(o, parseValues(sliders));
double[] measurementValues = detections.stream().mapToDouble(p -> p.getMeasurementList().getMeasurementValue(n)).filter(d -> Double.isFinite(d)).toArray();
var stats = new DescriptiveStatistics(measurementValues);
var histogram = new Histogram(measurementValues, 100, stats.getMin(), stats.getMax());
histogramPanel.getHistogramData().setAll(HistogramPanelFX.createHistogramData(histogram, false, ColorTools.packARGB(100, 200, 20, 20)));
double[] values = map.get(n);
for (int i = 0; i < sliders.size(); i++) {
var slider = sliders.get(i);
slider.setMin(stats.getMin());
slider.setMax(stats.getMax());
double val = values == null ? stats.getMean() + stats.getStandardDeviation() * i : values[i];
slider.setValue(val);
// Add first threshold to histogram
if (i == 0) {
Color color = Color.rgb(0, 0, 0, 0.2);
chartWrapper.addThreshold(sliders.get(i).valueProperty(), color);
}
}
});
selectedMeasurement.addListener((v, o, n) -> updateClassifications(hierarchy, allDetections, n, parseValues(sliders, singleThreshold.get())));
singleThreshold.addListener((v, o, n) -> updateClassifications(hierarchy, allDetections, selectedMeasurement.get(), parseValues(sliders, singleThreshold.get())));
var pane = new GridPane();
int row = 0;
var labelMeasurements = new Label("Measurement");
PaneTools.addGridRow(pane, row++, 0, "Select measurement to threshold", labelMeasurements, comboMeasurements, comboMeasurements);
for (int i = 0; i < sliders.size(); i++) {
var labelThreshold = new Label("Threshold " + (i + 1) + "+");
var slider = sliders.get(i);
var tf = textFields.get(i);
if (i > 0) {
slider.disableProperty().bind(singleThreshold);
tf.disableProperty().bind(singleThreshold);
}
PaneTools.addGridRow(pane, row++, 0, "Select threshold value", labelThreshold, slider, tf);
}
PaneTools.addGridRow(pane, row++, 0, "Toggle between using a single threshold (Negative/Positive) or three threshold Negative/1+/2+/3+)", cbSingleThreshold, cbSingleThreshold, cbSingleThreshold);
pane.setHgap(5.0);
pane.setVgap(5.0);
PaneTools.setToExpandGridPaneHeight(chartWrapper.getPane());
PaneTools.setToExpandGridPaneWidth(chartWrapper.getPane());
histogramPanel.getChart().getYAxis().setTickLabelsVisible(false);
histogramPanel.getChart().setAnimated(false);
chartWrapper.getPane().setPrefSize(200, 80);
pane.add(chartWrapper.getPane(), pane.getColumnCount(), 0, 1, pane.getRowCount());
var dialog = new Dialog<ButtonType>();
dialog.initOwner(qupath.getStage());
dialog.setTitle(title);
dialog.getDialogPane().setContent(pane);
dialog.getDialogPane().getButtonTypes().setAll(ButtonType.APPLY, ButtonType.CANCEL);
var response = dialog.showAndWait().orElse(ButtonType.CANCEL);
if (pool != null) {
pool.shutdown();
try {
pool.awaitTermination(5000L, TimeUnit.SECONDS);
} catch (InterruptedException e) {
logger.debug("Exception waiting for classification to complete: " + e.getLocalizedMessage(), e);
}
}
// Check if we did anything, if not return
if (nextRequest == null)
return;
if (ButtonType.APPLY.equals(response)) {
// Make sure we ran the last command, then log it in the workflow
if (!nextRequest.isComplete())
nextRequest.doClassification();
imageData.getHistoryWorkflow().addStep(nextRequest.toWorkflowStep());
} else {
// Restore classifications if the user cancelled
var changed = PathClassifierTools.restoreClassificationsFromMap(currentClassifications);
if (!changed.isEmpty())
hierarchy.fireObjectClassificationsChangedEvent(this, changed);
}
}
use of qupath.lib.analysis.stats.Histogram in project qupath by qupath.
the class PathIntensityClassifierPane method updateIntensityHistogram.
private void updateIntensityHistogram() {
String selected = comboIntensities.getSelectionModel().getSelectedItem();
PathObjectHierarchy hierarchy = getHierarchy();
// if (!"None".equals(selected) || hierarchy == null)
if ("None".equals(selected) || hierarchy == null) {
if (panelHistogram != null)
panelHistogram.getHistogramData().clear();
return;
}
// Try to make a histogram & set it in the panel
// PathObject pathObjectSelected = hierarchy.getSelectionModel().getSelectedPathObject();
// For now, always use all objects (not direct descendants only)
Collection<PathObject> pathObjects = null;
// if (pathObjectSelected == null || !pathObjectSelected.hasChildren())
pathObjects = hierarchy.getDetectionObjects();
// else
// pathObjects = hierarchy.getDescendantObjects(pathObjectSelected, pathObjects, PathDetectionObject.class);
// Histogram histogram = Histogram.makeMeasurementHistogram(pathObjects, (String)selected, 256);
double[] values = Histogram.getMeasurementValues(pathObjects, (String) selected);
Histogram histogram = new Histogram(values, 128);
// Compute quartile values
Arrays.sort(values);
int nNaNs = 0;
// NaNs should be at the end of the list
for (int i = values.length - 1; i >= 0; i--) {
if (Double.isNaN(values[i]))
nNaNs++;
else
break;
}
// Should be same as histogram.getCountSum() ?
int nValues = values.length - nNaNs;
assert nValues == histogram.getCountSum();
if (nValues > 0) {
double median = values[nValues / 2];
double quartile1 = values[(int) (nValues / 4 + .5)];
double quartile3 = values[(int) (nValues * 3 / 4 + .5)];
logger.info(String.format("%s Quartile 1: %.4f", selected, quartile1));
logger.info(String.format("%s Median: %.4f", selected, median));
logger.info(String.format("%s Quartile 3: %.4f", selected, quartile3));
RunningStatistics stats = StatisticsHelper.computeRunningStatistics(values);
logger.info(String.format("%s Mean: %.4f", selected, stats.getMean()));
logger.info(String.format("%s Std.Dev.: %.4f", selected, stats.getStdDev()));
panelHistogram.getHistogramData().setAll(HistogramPanelFX.createHistogramData(histogram, true, (Integer) null));
} else
panelHistogram.getHistogramData().clear();
updateHistogramThresholdLines();
}
Aggregations