use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class DragDropImportListener method handleFileDropImpl.
private void handleFileDropImpl(final QuPathViewer viewer, final List<File> list) throws IOException {
// Shouldn't occur... but keeps FindBugs happy to check
if (list == null) {
logger.warn("No files given!");
return;
}
// Check if we have only jar files
int nJars = 0;
for (File file : list) {
if (file.getName().toLowerCase().endsWith(".jar"))
nJars++;
}
if (nJars == list.size()) {
if (qupath.canInstallExtensions())
qupath.installExtensions(list);
else
Dialogs.showErrorMessage("Install extensions", "Sorry, extensions can only be installed when QuPath is run as a standalone application.");
return;
}
// Try to get a hierarchy for importing ROIs
ImageData<BufferedImage> imageData = viewer == null ? null : viewer.getImageData();
PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
// Some consumers can only handle one file
boolean singleFile = list.size() == 1;
// Gather together the extensions - if this has length one, we know all the files have the same extension
Set<String> allExtensions = list.stream().map(f -> GeneralTools.getExtension(f).orElse("")).collect(Collectors.toSet());
// If we have a zipped file, create a set that includes the files within the zip image
// This helps us determine whether or not a zip file contains an image or objects, for example
Set<String> allUnzippedExtensions = allExtensions;
if (allExtensions.contains(".zip")) {
allUnzippedExtensions = list.stream().flatMap(f -> {
try {
return PathIO.unzippedExtensions(f.toPath()).stream();
} catch (IOException e) {
logger.debug(e.getLocalizedMessage(), e);
return Arrays.stream(new String[0]);
}
}).collect(Collectors.toSet());
}
// Extract the first (and possibly only) file
File file = list.get(0);
String fileName = file.getName().toLowerCase();
// Check if this is a hierarchy file
if (singleFile && (fileName.endsWith(PathPrefs.getSerializationExtension()))) {
// If we have a different path, open as a new image
if (viewer == null) {
Dialogs.showErrorMessage("Load data", "Please drag the file onto a specific viewer to open!");
return;
}
try {
// Check if we should be importing objects or opening the file
if (imageData != null) {
var dialog = new Dialog<ButtonType>();
var btOpen = new ButtonType("Open image");
var btImport = new ButtonType("Import objects");
dialog.getDialogPane().getButtonTypes().setAll(btOpen, btImport, ButtonType.CANCEL);
dialog.setTitle("Open data");
dialog.setHeaderText("What do you want to do with the data file?");
dialog.setContentText("You can\n" + " 1. Open the image in the current viewer\n" + " 2. Import objects and add them to the current image");
// dialog.setHeaderText("What do you want to do?");
var choice = dialog.showAndWait().orElse(ButtonType.CANCEL);
if (choice == ButtonType.CANCEL)
return;
if (choice == btImport) {
var pathObjects = PathIO.readObjects(file);
hierarchy.addPathObjects(pathObjects);
return;
}
}
qupath.openSavedData(viewer, file, false, true);
} catch (Exception e) {
Dialogs.showErrorMessage("Load data", e);
}
return;
}
// Check if this is a directory - if so, look for a single project file
if (singleFile && file.isDirectory()) {
// Identify all files in the directory, and also all potential project files
File[] filesInDirectory = file.listFiles(f -> !f.isHidden());
List<File> projectFiles = Arrays.stream(filesInDirectory).filter(f -> f.isFile() && f.getAbsolutePath().toLowerCase().endsWith(ProjectIO.getProjectExtension())).collect(Collectors.toList());
if (projectFiles.size() == 1) {
file = projectFiles.get(0);
logger.warn("Selecting project file {}", file);
} else if (projectFiles.size() > 1) {
// Prompt to select which project file to open
logger.debug("Multiple project files found in directory {}", file);
String[] fileNames = projectFiles.stream().map(f -> f.getName()).toArray(n -> new String[n]);
String selectedName = Dialogs.showChoiceDialog("Select project", "Select project to open", fileNames, fileNames[0]);
if (selectedName == null)
return;
file = new File(file, selectedName);
} else if (filesInDirectory.length == 0) {
// If we have an empty directory, offer to set it as a project
if (Dialogs.showYesNoDialog("Create project", "Create project for empty directory?")) {
Project<BufferedImage> project = Projects.createProject(file, BufferedImage.class);
qupath.setProject(project);
if (!project.isEmpty())
project.syncChanges();
return;
} else
// Can't do anything else with an empty folder
return;
}
}
// Check if this is a project
if (singleFile && (fileName.endsWith(ProjectIO.getProjectExtension()))) {
try {
Project<BufferedImage> project = ProjectIO.loadProject(file, BufferedImage.class);
qupath.setProject(project);
} catch (Exception e) {
// Dialogs.showErrorMessage("Project error", e);
logger.error("Could not open as project file: {}, opening in the Script Editor instead", e);
qupath.getScriptEditor().showScript(file);
}
return;
}
// Check if it is an object file in GeoJSON format (.geojson)
if (PathIO.getObjectFileExtensions(false).containsAll(allUnzippedExtensions)) {
if (imageData == null || hierarchy == null) {
qupath.getScriptEditor().showScript(file);
logger.info("Opening the dragged file in the Script Editor as there is no currently opened image in the viewer");
// Dialogs.showErrorMessage("Open object file", "Please open an image first to import objects!");
return;
}
List<PathObject> pathObjects = new ArrayList<>();
List<WorkflowStep> steps = new ArrayList<>();
for (var tempFile : list) {
try {
var tempObjects = PathIO.readObjects(tempFile);
if (tempObjects.isEmpty()) {
logger.warn("No objects found in {}, opening the dragged file in the Script Editor instead", tempFile.getAbsolutePath());
qupath.getScriptEditor().showScript(file);
return;
}
pathObjects.addAll(tempObjects);
// Add step to workflow
Map<String, String> map = new HashMap<>();
map.put("path", file.getPath());
String method = "Import objects";
String methodString = String.format("%s(%s%s%s)", "importObjectsFromFile", "\"", GeneralTools.escapeFilePath(tempFile.getPath()), "\"");
steps.add(new DefaultScriptableWorkflowStep(method, map, methodString));
} catch (IOException | IllegalArgumentException e) {
Dialogs.showErrorNotification("Object import", e.getLocalizedMessage());
return;
}
}
// Ask confirmation to user
int nObjects = pathObjects.size();
String message = nObjects == 1 ? "Add object to the hierarchy?" : String.format("Add %d objects to the hierarchy?", nObjects);
var confirm = Dialogs.showConfirmDialog("Add to hierarchy", message);
if (!confirm)
return;
// Add objects to hierarchy
hierarchy.addPathObjects(pathObjects);
imageData.getHistoryWorkflow().addSteps(steps);
return;
}
// Check if this is TMA dearraying data file
if (singleFile && (fileName.endsWith(TMADataIO.TMA_DEARRAYING_DATA_EXTENSION))) {
if (hierarchy == null)
Dialogs.showErrorMessage("TMA grid import", "Please open an image first before importing a dearrayed TMA grid!");
else {
TMAGrid tmaGrid = TMADataIO.importDearrayedTMAData(file);
if (tmaGrid != null) {
if (hierarchy.isEmpty() || Dialogs.showYesNoDialog("TMA grid import", "Set TMA grid for existing hierarchy?"))
hierarchy.setTMAGrid(tmaGrid);
} else
Dialogs.showErrorMessage("TMA grid import", "Could not parse TMA grid from " + file.getName());
}
return;
}
// Open file with an extension supported by the Script Editor
ScriptEditor scriptEditor = qupath.getScriptEditor();
if (scriptEditor instanceof DefaultScriptEditor && ((DefaultScriptEditor) scriptEditor).supportsFile(file)) {
scriptEditor.showScript(file);
return;
}
// Check handlers
for (DropHandler<File> handler : dropHandlers) {
if (handler.handleDrop(viewer, list))
return;
}
// Assume we have images
if (singleFile && file.isFile()) {
// Try to open as an image, if the extension is known
if (viewer == null) {
Dialogs.showErrorMessage("Open image", "Please drag the file only a specific viewer to open!");
return;
}
qupath.openImage(viewer, file.getAbsolutePath(), true, true);
return;
} else if (qupath.getProject() != null) {
// Try importing multiple images to a project
String[] potentialFiles = list.stream().filter(f -> f.isFile()).map(f -> f.getAbsolutePath()).toArray(String[]::new);
if (potentialFiles.length > 0) {
ProjectCommands.promptToImportImages(qupath, potentialFiles);
return;
}
}
if (qupath.getProject() == null) {
if (list.size() > 1) {
Dialogs.showErrorMessage("Drag & drop", "Could not handle multiple file drop - if you want to handle multiple images, you need to create a project first");
return;
}
}
if (list.size() > 1)
Dialogs.showErrorMessage("Drag & drop", "Sorry, I couldn't figure out what to do with these files - try opening one at a time");
else
Dialogs.showErrorMessage("Drag & drop", "Sorry, I couldn't figure out what to do with " + list.get(0).getName());
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class TMAGridOverlay method paintOverlay.
@Override
public void paintOverlay(final Graphics2D g, final ImageRegion imageRegion, final double downsampleFactor, final ImageData<BufferedImage> imageData, final boolean paintCompletely) {
if (!isVisible())
return;
PathObjectHierarchy hierarchy = imageData == null ? null : imageData.getHierarchy();
if (hierarchy == null)
return;
TMAGrid tmaGrid = hierarchy.getTMAGrid();
if (tmaGrid == null)
return;
Graphics2D g2d = (Graphics2D) g.create();
// Set alpha composite if needed
setAlphaComposite(g2d);
// Rectangle serverBounds = imageRegion.getBounds();
// Ensure antialias is on...?
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
PathHierarchyPaintingHelper.paintTMAGrid(g2d, tmaGrid, getOverlayOptions(), hierarchy.getSelectionModel(), downsampleFactor);
g2d.dispose();
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class AbstractPathROITool method mousePressed.
@Override
public void mousePressed(MouseEvent e) {
super.mousePressed(e);
if (!e.isPrimaryButtonDown() || e.isConsumed()) {
return;
}
var viewer = getViewer();
PathObjectHierarchy hierarchy = viewer.getHierarchy();
if (hierarchy == null)
return;
PathObject currentObject = viewer.getSelectedObject();
ROI currentROI = currentObject == null ? null : currentObject.getROI();
RoiEditor editor = viewer.getROIEditor();
boolean adjustingPolygon = (currentROI instanceof PolygonROI || currentROI instanceof PolylineROI) && editor.getROI() == currentROI && (editor.isTranslating() || editor.hasActiveHandle());
// If we're adjusting a polygon/polyline with an appropriate tool, return at leave it up to the tool to handle the custom things
if (adjustingPolygon) {
if (viewer.getActiveTool() == PathTools.POLYGON || viewer.getActiveTool() == PathTools.POLYLINE)
return;
else {
viewer.getHierarchy().getSelectionModel().clearSelection();
viewer.getHierarchy().fireHierarchyChangedEvent(currentObject);
}
}
// Find out the coordinates in the image domain
Point2D p2 = mouseLocationToImage(e, false, requestPixelSnapping());
double xx = p2.getX();
double yy = p2.getY();
if (xx < 0 || yy < 0 || xx >= viewer.getServerWidth() || yy >= viewer.getServerHeight())
return;
// If we are double-clicking & we don't have a polygon, see if we can access a ROI
if (!PathPrefs.selectionModeProperty().get() && e.getClickCount() > 1) {
// Reset parent... for now
resetConstrainedAreaParent();
tryToSelect(xx, yy, e.getClickCount() - 2, false);
e.consume();
return;
}
// Set the current parent object based on the first click
setConstrainedAreaParent(hierarchy, xx, yy, Collections.emptyList());
// Create a new annotation
PathObject pathObject = createNewAnnotation(e, xx, yy);
if (pathObject == null)
return;
// Start editing the ROI immediately
editor.setROI(pathObject.getROI());
editor.grabHandle(xx, yy, viewer.getMaxROIHandleSize() * 1.5, e.isShiftDown());
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class PathIO method readHierarchy.
/**
* Read a {@link PathObjectHierarchy} from a saved data file (omitting all other contents).
*
* @param fileIn
* @return
* @throws IOException
*/
public static PathObjectHierarchy readHierarchy(final InputStream fileIn) throws IOException {
Locale locale = Locale.getDefault(Category.FORMAT);
boolean localeChanged = false;
try (ObjectInputStream inStream = new ObjectInputStream(new BufferedInputStream(fileIn))) {
if (!inStream.readUTF().startsWith("Data file version")) {
logger.error("Input stream is not from a valid QuPath data file!");
}
while (true) {
// logger.debug("Starting read: " + inStream.available());
try {
// Try to read a relevant object from the stream
Object input = inStream.readObject();
logger.debug("Read: {}", input);
// Set locale - may be needed (although probably isn't...)
if (input instanceof Locale) {
if (input != locale) {
Locale.setDefault(Category.FORMAT, (Locale) input);
localeChanged = true;
}
} else if (input instanceof PathObjectHierarchy) {
/* This would ideally be unnecessary, but it's needed to ensure that the PathObjectHierarchy
* has been property initialized. We can't count on the deserialized hierarchy being immediately functional.
*/
PathObjectHierarchy hierarchy = new PathObjectHierarchy();
hierarchy.setHierarchy((PathObjectHierarchy) input);
return hierarchy;
}
} catch (ClassNotFoundException e) {
logger.error("Unable to find class", e);
} catch (EOFException e) {
logger.error("Reached end of file unexpectedly...");
}
}
} finally {
if (localeChanged)
Locale.setDefault(Category.FORMAT, locale);
}
}
use of qupath.lib.objects.hierarchy.PathObjectHierarchy in project qupath by qupath.
the class ClassifierBuilderPane method updateRetainedObjectsMap.
/**
* Update the retained objects map using the data from the current image.
*/
private void updateRetainedObjectsMap() {
PathObjectHierarchy hierarchy = getHierarchy();
if (hierarchy != null) {
Map<PathClass, List<PathObject>> mapCurrent = PathClassificationLabellingHelper.getClassificationMap(hierarchy, paramsUpdate.getBooleanParameterValue("trainFromPoints"));
// Add in any retained objects, if we have some
PathClassificationLabellingHelper.countObjectsInMap(mapCurrent);
// int retainedImageCount = retainedObjectsMap.addToTrainingMap(map, getImageData().getServerPath());
retainedObjectsMap.put(getMapKey(getImageData()), mapCurrent);
updateRetainedObjectsLabel();
}
}
Aggregations