use of qupath.lib.images.servers.ImageChannel in project qupath by qupath.
the class CreateChannelTrainingImagesCommand method run.
@Override
public void run() {
var project = qupath.getProject();
var imageData = qupath.getImageData();
var entry = project != null && imageData != null ? project.getEntry(imageData) : null;
if (entry == null) {
Dialogs.showErrorMessage(title, "This command requires an open image within a project!");
return;
}
var channels = new ArrayList<>(imageData.getServer().getMetadata().getChannels());
var list = new CheckListView<ImageChannel>();
list.getItems().setAll(channels);
list.getCheckModel().checkAll();
list.setCellFactory(v -> new CheckBoxListCell<>(item -> list.getItemBooleanProperty(item)) {
@Override
public void updateItem(ImageChannel item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
return;
}
setText(item.getName());
}
});
var label = new Label("Create duplicate images for each of the following channels?");
if (channels.size() == 1)
label.setText("Create a duplicate image for the one and only channel?");
var labelName = new Label("Root name");
var tfName = new TextField(entry.getImageName().trim());
labelName.setLabelFor(tfName);
String namePrompt = "Enter root image name for duplicate images";
tfName.setPromptText(namePrompt);
tfName.setTooltip(new Tooltip(namePrompt));
var cbInitializePoints = new CheckBox("Initialize Points annotations");
var pane = new GridPane();
int row = 0;
PaneTools.addGridRow(pane, row++, 0, null, label, label);
PaneTools.addGridRow(pane, row++, 0, namePrompt, labelName, tfName);
PaneTools.addGridRow(pane, row++, 0, "Channels to duplicate", list, list);
PaneTools.addGridRow(pane, row++, 0, "Create Points annotations for the corresponding channel", cbInitializePoints, cbInitializePoints);
PaneTools.setFillWidth(Boolean.TRUE, label, tfName, list, cbInitializePoints);
PaneTools.setHGrowPriority(Priority.ALWAYS, label, tfName, list, cbInitializePoints);
PaneTools.setVGrowPriority(Priority.ALWAYS, list);
PaneTools.setMaxWidth(Double.MAX_VALUE, label, tfName, list, cbInitializePoints);
list.setPrefHeight(240);
pane.setHgap(5.0);
pane.setVgap(5.0);
if (!Dialogs.showConfirmDialog(title, pane))
return;
var name = tfName.getText().trim();
boolean initializePoints = cbInitializePoints.isSelected();
try {
for (var channel : list.getCheckModel().getCheckedItems()) {
var entry2 = project.addDuplicate(entry, true);
String channelName = channel.getName();
entry2.setImageName(name.trim() + " - " + channelName);
if (initializePoints) {
var imageData2 = entry2.readImageData();
imageData2.getHierarchy().addPathObjects(Arrays.asList(PathObjects.createAnnotationObject(ROIs.createPointsROI(ImagePlane.getDefaultPlane()), PathClassFactory.getPathClass(channelName, channel.getColor())), PathObjects.createAnnotationObject(ROIs.createPointsROI(ImagePlane.getDefaultPlane()), PathClassFactory.getPathClass(PathClassFactory.StandardPathClasses.IGNORE))));
entry2.saveImageData(imageData2);
}
}
project.syncChanges();
} catch (Exception e) {
Dialogs.showErrorMessage(title, e);
}
qupath.refreshProject();
}
use of qupath.lib.images.servers.ImageChannel in project qupath by qupath.
the class DensityMapDataOp method buildOpAndChannels.
private void buildOpAndChannels() {
logger.trace("Building density map op with type {}", densityType);
String baseChannelName;
ImageChannel lastChannel = null;
List<ImageOp> sequentialOps = new ArrayList<>();
switch(densityType) {
case GAUSSIAN:
if (radius > 0) {
double sigma = radius;
sequentialOps.add(ImageOps.Filters.gaussianBlur(sigma));
// Scale so that central value ~1 - this is a closer match to the alternative sum filter
sequentialOps.add(ImageOps.Core.multiply(2 * Math.PI * sigma * sigma));
}
baseChannelName = "Gaussian weighted counts ";
break;
case PERCENT:
int[] extractInds = IntStream.range(0, primaryObjects.size()).toArray();
if (radius > 0) {
sequentialOps.add(ImageOps.Filters.sum(radius));
sequentialOps.add(ImageOps.Core.round());
}
if (extractInds.length > 0) {
// Duplicate the image, then
// - On the first duplicate, remove the last channel and divide all the other channels by its values
// - On the second duplicate, extract the last channel (so as to retain its values)
// Finally merge the last channel back
// The outcome should be that the last channel is unchanged, while the other channels are divided by the last
// channel elementwise.
sequentialOps.addAll(Arrays.asList(ImageOps.Core.splitMerge(ImageOps.Core.sequential(ImageOps.Core.splitDivide(ImageOps.Channels.extract(extractInds), ImageOps.Core.sequential(ImageOps.Channels.extract(extractInds.length), ImageOps.Channels.repeat(extractInds.length))), // Convert to percent
ImageOps.Core.multiply(100.0)), ImageOps.Channels.extract(extractInds.length))));
} else {
// Values will be 1 and NaN (a possibly-zero value being divided by itself)
sequentialOps.add(ImageOps.Core.splitDivide(null, null));
}
baseChannelName = "";
if (!primaryObjects.isEmpty())
lastChannel = ImageChannel.getInstance(DensityMaps.CHANNEL_ALL_OBJECTS, null);
break;
case SUM:
default:
if (radius > 0) {
sequentialOps.add(ImageOps.Filters.sum(radius));
sequentialOps.add(ImageOps.Core.round());
}
baseChannelName = "";
}
var channelNames = primaryObjects.keySet().stream().map(n -> baseChannelName + n).toArray(String[]::new);
var channels = new ArrayList<>(ImageChannel.getChannelList(channelNames));
if (lastChannel != null)
channels.add(lastChannel);
if (channels.isEmpty())
this.channels = Collections.singletonList(ImageChannel.getInstance(DensityMaps.CHANNEL_ALL_OBJECTS, null));
else
this.channels = Collections.unmodifiableList(channels);
sequentialOps.add(ImageOps.Core.ensureType(PixelType.FLOAT32));
this.op = ImageOps.Core.sequential(sequentialOps);
}
use of qupath.lib.images.servers.ImageChannel in project qupath by qupath.
the class WatershedCellDetection method buildParameterList.
private ParameterList buildParameterList(final ImageData<BufferedImage> imageData) {
ParameterList params = new ParameterList();
// TODO: Use a better way to determining if pixel size is available in microns
// params.addEmptyParameter("detectionParameters", "Detection parameters", true);
String microns = IJ.micronSymbol + "m";
params.addTitleParameter("Setup parameters");
String defaultChannel = null;
List<String> channelNames = new ArrayList<>();
String[] nucleusGuesses = new String[] { "dapi", "hoechst", "nucleus", "nuclei", "nuclear", "hematoxylin", "haematoxylin" };
for (ImageChannel channel : imageData.getServer().getMetadata().getChannels()) {
String name = channel.getName();
channelNames.add(name);
if (defaultChannel == null) {
String lower = name.toLowerCase();
for (String guess : nucleusGuesses) {
if (lower.contains(guess))
defaultChannel = name;
}
}
}
if (defaultChannel == null)
defaultChannel = channelNames.get(0);
if (channelNames.size() != new HashSet<>(channelNames).size())
logger.warn("Image contains duplicate channel names! This may be confusing for detection and analysis.");
params.addChoiceParameter("detectionImage", "Detection channel", defaultChannel, channelNames, "Choose the channel that should be used for nucleus detection (e.g. DAPI)");
params.addChoiceParameter("detectionImageBrightfield", "Detection image", IMAGE_HEMATOXYLIN, Arrays.asList(IMAGE_HEMATOXYLIN, IMAGE_OPTICAL_DENSITY), "Transformed image to which to apply the detection");
params.addDoubleParameter("requestedPixelSizeMicrons", "Requested pixel size", .5, microns, "Choose pixel size at which detection will be performed - higher values are likely to be faster, but may be less accurate; set <= 0 to use the full image resolution");
// params.addDoubleParameter("requestedPixelSize", "Requested downsample factor", 1, "");
params.addTitleParameter("Nucleus parameters");
params.addDoubleParameter("backgroundRadiusMicrons", "Background radius", 8, microns, "Radius for background estimation, should be > the largest nucleus radius, or <= 0 to turn off background subtraction");
params.addDoubleParameter("medianRadiusMicrons", "Median filter radius", 0, microns, "Radius of median filter used to reduce image texture (optional)");
params.addDoubleParameter("sigmaMicrons", "Sigma", 1.5, microns, "Sigma value for Gaussian filter used to reduce noise; increasing the value stops nuclei being fragmented, but may reduce the accuracy of boundaries");
params.addDoubleParameter("minAreaMicrons", "Minimum area", 10, microns + "^2", "Detected nuclei with an area < minimum area will be discarded");
params.addDoubleParameter("maxAreaMicrons", "Maximum area", 400, microns + "^2", "Detected nuclei with an area > maximum area will be discarded");
params.addDoubleParameter("backgroundRadius", "Background radius", 15, "px", "Radius for background estimation, should be > the largest nucleus radius, or <= 0 to turn off background subtraction");
params.addDoubleParameter("medianRadius", "Median filter radius", 0, "px", "Radius of median filter used to reduce image texture (optional)");
params.addDoubleParameter("sigma", "Sigma", 3, "px", "Sigma value for Gaussian filter used to reduce noise; increasing the value stops nuclei being fragmented, but may reduce the accuracy of boundaries");
params.addDoubleParameter("minArea", "Minimum area", 10, "px^2", "Detected nuclei with an area < minimum area will be discarded");
params.addDoubleParameter("maxArea", "Maximum area", 1000, "px^2", "Detected nuclei with an area > maximum area will be discarded");
params.addTitleParameter("Intensity parameters");
params.addDoubleParameter("threshold", "Threshold", 0.1, null, "Intensity threshold - detected nuclei must have a mean intensity >= threshold");
// params.addDoubleParameter("threshold", "Threshold", 0.1, null, 0, 2.5,
// "Intensity threshold - detected nuclei must have a mean intensity >= threshold");
params.addDoubleParameter("maxBackground", "Max background intensity", 2, null, "If background radius > 0, detected nuclei occurring on a background > max background intensity will be discarded");
// params.addBooleanParameter("mergeAll", "Merge all", true);
params.addBooleanParameter("watershedPostProcess", "Split by shape", true, "Split merged detected nuclei based on shape ('roundness')");
params.addBooleanParameter("excludeDAB", "Exclude DAB (membrane staining)", false, "Set to 'true' if regions of high DAB staining should not be considered nuclei; useful if DAB stains cell membranes");
params.addTitleParameter("Cell parameters");
params.addDoubleParameter("cellExpansionMicrons", "Cell expansion", 5, microns, 0, 25, "Amount by which to expand detected nuclei to approximate the full cell area");
params.addDoubleParameter("cellExpansion", "Cell expansion", 5, "px", "Amount by which to expand detected nuclei to approximate the full cell area");
// params.addBooleanParameter("limitExpansionByNucleusSize", "Limit cell expansion by nucleus size", false, "If checked, nuclei will not be expanded by more than their (estimated) smallest diameter in any direction - may give more realistic results for smaller, or 'thinner' nuclei");
params.addBooleanParameter("includeNuclei", "Include cell nucleus", true, "If cell expansion is used, optionally include/exclude the nuclei within the detected cells");
params.addTitleParameter("General parameters");
params.addBooleanParameter("smoothBoundaries", "Smooth boundaries", true, "Smooth the detected nucleus/cell boundaries");
params.addBooleanParameter("makeMeasurements", "Make measurements", true, "Add default shape & intensity measurements during detection");
return params;
}
Aggregations