use of qupath.lib.objects.classes.PathClass in project qupath by qupath.
the class DefaultProject method writePathClasses.
void writePathClasses(Collection<PathClass> pathClasses) throws IOException {
var path = Paths.get(ensureDirectoryExists(getClassifiersPath()).toString(), "classes.json");
if (pathClasses == null || pathClasses.isEmpty()) {
Files.deleteIfExists(path);
return;
}
JsonArray pathClassArray = new JsonArray();
for (PathClass pathClass : pathClasses) {
JsonObject jsonEntry = new JsonObject();
jsonEntry.addProperty("name", pathClass.toString());
jsonEntry.addProperty("color", pathClass.getColor());
pathClassArray.add(jsonEntry);
}
var element = new JsonObject();
element.add("pathClasses", pathClassArray);
try (var writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
GsonTools.getInstance(true).toJson(element, writer);
}
}
use of qupath.lib.objects.classes.PathClass in project qupath by qupath.
the class QuPathGUI method savePathClasses.
/**
* Save available PathClasses to preferences.
*/
private void savePathClasses() {
ByteArrayOutputStream stream = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(stream)) {
List<PathClass> pathClasses = new ArrayList<>(availablePathClasses);
out.writeObject(pathClasses);
out.flush();
} catch (IOException e) {
logger.error("Error saving classes", e);
}
byte[] bytes = stream.toByteArray();
if (bytes.length < 0.75 * Preferences.MAX_VALUE_LENGTH)
PathPrefs.getUserPreferences().putByteArray("defaultPathClasses", bytes);
else
logger.error("Classification list too long ({} bytes) - cannot save it to the preferences.", bytes.length);
}
use of qupath.lib.objects.classes.PathClass in project qupath by qupath.
the class QuPathGUI method updateSetAnnotationPathClassMenu.
void updateSetAnnotationPathClassMenu(final ObservableList<MenuItem> menuSetClassItems, final QuPathViewer viewer, final boolean useFancyIcons) {
// We need a viewer and an annotation, as well as some PathClasses, otherwise we just need to ensure the menu isn't visible
if (viewer == null || !(viewer.getSelectedObject() instanceof PathAnnotationObject) || availablePathClasses.isEmpty()) {
menuSetClassItems.clear();
return;
}
PathObject mainPathObject = viewer.getSelectedObject();
PathClass currentClass = mainPathObject.getPathClass();
ToggleGroup group = new ToggleGroup();
List<MenuItem> itemList = new ArrayList<>();
RadioMenuItem selected = null;
for (PathClass pathClass : availablePathClasses) {
PathClass pathClassToSet = pathClass.getName() == null ? null : pathClass;
String name = pathClass.getName() == null ? "None" : pathClass.toString();
Action actionSetClass = new Action(name, e -> {
List<PathObject> changed = new ArrayList<>();
for (PathObject pathObject : viewer.getAllSelectedObjects()) {
if (!pathObject.isAnnotation() || pathObject.getPathClass() == pathClassToSet)
continue;
pathObject.setPathClass(pathClassToSet);
changed.add(pathObject);
}
if (!changed.isEmpty())
viewer.getHierarchy().fireObjectClassificationsChangedEvent(this, changed);
});
Node shape;
if (useFancyIcons) {
Ellipse r = new Ellipse(TOOLBAR_ICON_SIZE / 2.0, TOOLBAR_ICON_SIZE / 2.0, TOOLBAR_ICON_SIZE, TOOLBAR_ICON_SIZE);
if ("None".equals(name)) {
r.setFill(Color.rgb(255, 255, 255, 0.75));
} else
r.setFill(ColorToolsFX.getCachedColor(pathClass.getColor()));
r.setOpacity(0.8);
DropShadow effect = new DropShadow(6, -3, 3, Color.GRAY);
r.setEffect(effect);
shape = r;
} else {
Rectangle r = new Rectangle(0, 0, 8, 8);
r.setFill("None".equals(name) ? Color.TRANSPARENT : ColorToolsFX.getCachedColor(pathClass.getColor()));
shape = r;
}
// actionSetClass.setGraphic(r);
RadioMenuItem item = ActionUtils.createRadioMenuItem(actionSetClass);
item.graphicProperty().unbind();
item.setGraphic(shape);
item.setToggleGroup(group);
itemList.add(item);
if (pathClassToSet == currentClass)
selected = item;
}
group.selectToggle(selected);
menuSetClassItems.setAll(itemList);
}
use of qupath.lib.objects.classes.PathClass in project qupath by qupath.
the class ColorModelFactory method getIndexedClassificationColorModel.
/**
* Get a ColorModel suitable for showing output pixel classifications, using an 8-bit or 16-bit labeled image.
*
* @param channels
* @return
*/
public static IndexColorModel getIndexedClassificationColorModel(Map<Integer, PathClass> channels) {
var stats = channels.keySet().stream().mapToInt(c -> c).summaryStatistics();
if (stats.getMin() < 0)
throw new IllegalArgumentException("Minimum label must be >= 0");
int length = stats.getMax() + 1;
int[] cmap = new int[length];
for (var entry : channels.entrySet()) {
var pathClass = entry.getValue();
if (pathClass == null || pathClass == PathClassFactory.getPathClassUnclassified()) {
cmap[entry.getKey()] = ColorTools.packARGB(0, 255, 255, 255);
} else if (PathClassTools.isIgnoredClass(entry.getValue())) {
var color = pathClass == null ? 0 : pathClass.getColor();
int alpha = 192;
if (pathClass == PathClassFactory.getPathClass(StandardPathClasses.IGNORE))
alpha = 32;
cmap[entry.getKey()] = ColorTools.packARGB(alpha, ColorTools.red(color), ColorTools.green(color), ColorTools.blue(color));
} else
cmap[entry.getKey()] = entry.getValue().getColor();
}
if (cmap.length <= 256)
return new IndexColorModel(8, length, cmap, 0, true, -1, DataBuffer.TYPE_BYTE);
else if (cmap.length <= 65536)
return new IndexColorModel(16, length, cmap, 0, true, -1, DataBuffer.TYPE_USHORT);
else
throw new IllegalArgumentException("Only 65536 possible classifications supported!");
}
use of qupath.lib.objects.classes.PathClass in project qupath by qupath.
the class PathIntensityClassifier method classifyPathObjects.
/**
* Classify the intensity of a collection of objects, based on the current thresholds. <p>
* If an object is missing the required measurement, its {@link PathClass} remains unchanged.
*
* @param pathObjects
*/
@Override
public int classifyPathObjects(Collection<PathObject> pathObjects) {
int counter = 0;
// If there is no class specified, apply to all
if (classSelected == null) {
if (singleThreshold)
PathClassifierTools.setIntensityClassifications(pathObjects, intensityMeasurement, t1);
else
PathClassifierTools.setIntensityClassifications(pathObjects, intensityMeasurement, t1, t2, t3);
return pathObjects.size();
}
// Ensure we have the correct singleton class so we can do an equality check
classSelected = PathClassFactory.getSingletonPathClass(classSelected);
PathClass classPositive = PathClassFactory.getPositive(classSelected);
PathClass classNegative = PathClassFactory.getNegative(classSelected);
PathClass classOnePlus = PathClassFactory.getOnePlus(classSelected);
PathClass classTwoPlus = PathClassFactory.getTwoPlus(classSelected);
PathClass classThreePlus = PathClassFactory.getThreePlus(classSelected);
// Because the classifications are really sub-classifications, retain the same probability
for (PathObject pathObjectTemp : pathObjects) {
if (classSelected == null || pathObjectTemp.getPathClass() == null || !(pathObjectTemp.getPathClass().isDerivedFrom(classSelected) || pathObjectTemp.getPathClass().getName().equals(classSelected.getName())))
// if (classSelected == null || pathObjectTemp.getPathClass() == null || !pathObjectTemp.getPathClass().getName().equals(classSelected.getName()))
continue;
Object value = pathObjectTemp.getMeasurementList().getMeasurementValue(intensityMeasurement);
if (!(value instanceof Number))
continue;
double val = ((Number) value).doubleValue();
// If measurement is missing, do not change it
if (Double.isNaN(val))
continue;
else if (singleThreshold) {
if (val > t1)
pathObjectTemp.setPathClass(classPositive, pathObjectTemp.getClassProbability());
else
pathObjectTemp.setPathClass(classNegative, pathObjectTemp.getClassProbability());
} else {
if (val > t3)
pathObjectTemp.setPathClass(classThreePlus, pathObjectTemp.getClassProbability());
else if (val > t2)
pathObjectTemp.setPathClass(classTwoPlus, pathObjectTemp.getClassProbability());
else if (val > t1)
pathObjectTemp.setPathClass(classOnePlus, pathObjectTemp.getClassProbability());
else
pathObjectTemp.setPathClass(classNegative, pathObjectTemp.getClassProbability());
}
counter++;
}
return counter;
}
Aggregations