-
-
Save NicoKiaru/f45f56e3ff2d1fb708821c110fbdee62 to your computer and use it in GitHub Desktop.
/** | |
* ABBA Script : performs a cell detection on the root node annotation | |
* imported from ABBA and restores the child objects after the detection has been performed | |
* To use and modify like you need / want | |
* You need to have imported the regions before running this script. See https://gist.github.com/NicoKiaru/723d8e628a3bb03902bb3f0f2f0fa466 for instance | |
* See https://biop.github.io/ijp-imagetoatlas/ | |
* Author: Olivier Burri, Nicolas Chiaruttini, BIOP, EPFL | |
* Date: 2022-01-26 | |
*/ | |
// setImageType('FLUORESCENCE'); // if your image is fluorescent | |
// If you just want to clear the detections and keep the previously ABBA imported annotations | |
clearDetections() | |
// or, if you want to reset and re-import all annotations from ABBA | |
// clearAllObjects(); | |
// qupath.ext.biop.abba.AtlasTools.loadWarpedAtlasAnnotations(getCurrentImageData(), "acronym", true); | |
def project = getProject() | |
// We want to run cell detection only once on the entire brain, which means we need to select the Root node. We can find it by name | |
def root = getAnnotationObjects().find{ it.getName().equals( 'Root' ) } | |
// Duplicate root node. It is a bit contrived but this is how you can copy an annotation | |
def regionMeasured = PathObjectTools.transformObject( root, null, true ) | |
// Temporary name | |
regionMeasured.setName( "Measured Region" ) | |
// Need to add it to the current hierarchy | |
addObject( regionMeasured ) | |
// So that we can select it | |
setSelectedObject( regionMeasured ) | |
// RUN CELL DETECTION | |
// Modify the line below by inserting your cell detection plugin line | |
// ********************************************************************* | |
// ********************************************************************* | |
runPlugin( 'qupath.imagej.detect.cells.WatershedCellDetection', | |
'{"detectionImage": "FL CY3", "requestedPixelSizeMicrons": 2.0, "backgroundRadiusMicrons": 8.0, "medianRadiusMicrons": 0.0, "sigmaMicrons": 2.0, "minAreaMicrons": 20.0, "maxAreaMicrons": 400.0, "threshold": 200.0, "watershedPostProcess": true, "cellExpansionMicrons": 0.0, "includeNuclei": true, "smoothBoundaries": true, "makeMeasurements": true}') | |
// ********************************************************************* | |
// ********************************************************************* | |
// We no longer need the object we used for detection. We remove it but keep the child objects, which are the cells | |
removeObject( regionMeasured, true ) | |
// We can now add the cells to the original root object | |
def cells = getDetectionObjects() | |
root.addPathObjects( cells ) | |
// Update the view for the user | |
fireHierarchyUpdate() | |
// From here, the rest of the script adds to all cells their coordinates in the reference atlas | |
// to their measurement list | |
// Get ABBA transform file located in entry path | |
def targetEntry = getProjectEntry() | |
def targetEntryPath = targetEntry.getEntryPath() | |
def fTransform = new File(targetEntryPath.toFile(), "ABBA-Transform-Adult Mouse Brain - Allen Brain Atlas V3p1.json" ) | |
if ( !fTransform.exists() ) { | |
logger.error( "ABBA transformation file not found for entry {}", targetEntry ) | |
return | |
} | |
def pixelToCCFTransform = Warpy.getRealTransform( fTransform ).inverse() // Needs the inverse transform | |
getDetectionObjects().forEach{ detection -> | |
def ccfCoordinates = new RealPoint(3) | |
def ml = detection.getMeasurementList() | |
ccfCoordinates.setPosition( [detection.getROI().getCentroidX(),detection.getROI().getCentroidY(), 0] as double[] ) | |
// The Z=0 pixel coordinate is automatically transformed to the right atlas position in Z thanks to the fact that the slice | |
// position is already known by the transform | |
// This applies the transform in place to cffCoordinates | |
pixelToCCFTransform.apply(ccfCoordinates, ccfCoordinates) | |
// Fianlly: Add the coordinates as measurements | |
ml.addMeasurement("Allen CCFv3 X mm", ccfCoordinates.getDoublePosition(0) ) | |
ml.addMeasurement("Allen CCFv3 Y mm", ccfCoordinates.getDoublePosition(1) ) | |
ml.addMeasurement("Allen CCFv3 Z mm", ccfCoordinates.getDoublePosition(2) ) | |
ml.addMeasurement("Count: Num Spots", 1 ) | |
} | |
// imports | |
import qupath.ext.biop.warpy.* | |
import net.imglib2.RealPoint | |
import qupath.lib.measurements.MeasurementList | |
import qupath.lib.objects.PathObjectTools | |
import qupath.lib.objects.PathObjects | |
import qupath.lib.objects.PathCellObject | |
import qupath.lib.objects.PathObject | |
import qupath.lib.objects.PathDetectionObject | |
import qupath.lib.roi.ROIs | |
import qupath.lib.regions.ImagePlane |
And there's no error message when you ecport from Fiji ? Are you sure you're looking at the right QuPath project and not a duplicated one ?
It is really tricky somehow. I reopen my project, re-export on ABBA, and re-import using codes on the document:
setImageType('FLUORESCENCE'); clearAllObjects(); qupath.ext.biop.abba.AtlasTools.loadWarpedAtlasAnnotations(getCurrentImageData(), "acronym", false);
The entry folder this time shows the ABBA files. Strangely
But this time it raise a new error:
ERROR:` Cannot invoke "qupath.lib.objects.PathObject.getROI()" because "pathObject" is null in DetectCellsABBA.groovy at line number 19
ERROR: qupath.lib.objects.PathObjectTools.transformObjectImpl(PathObjectTools.java:1255)
This could be sovled by changing false
into true
within this code
qupath.ext.biop.abba.AtlasTools.loadWarpedAtlasAnnotations(getCurrentImageData(), "acronym", false);
but again when going to run 'DetectCellsABBA.groovy' code, the bug is still here:
ERROR: Error processing Polygon (-1016, 661, 1266, 732)
java.io.IOException: java.lang.InterruptedException
at qupath.lib.images.servers.bioformats.BioFormatsImageServer.readTile(BioFormatsImageServer.java:917)
at qupath.lib.images.servers.AbstractTileableImageServer.lambda$prerequestTiles$2(AbstractTileableImageServer.java:462)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at qupath.lib.images.servers.AbstractTileableImageServer.prerequestTiles(AbstractTileableImageServer.java:464)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:295)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:60)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:863)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:902)
at qupath.imagej.detect.cells.WatershedCellDetection$CellDetector.runDetection(WatershedCellDetection.java:216)
at qupath.lib.plugins.DetectionPluginTools$DetectionRunnable.run(DetectionPluginTools.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by null at java.base/java.util.concurrent.locks.ReentrantLock$Sync.lockInterruptibly(Unknown Source)
at java.base/java.util.concurrent.locks.ReentrantLock.lockInterruptibly(Unknown Source)
at java.base/java.util.concurrent.ArrayBlockingQueue.put(Unknown Source)
at qupath.lib.images.servers.bioformats.BioFormatsImageServer$ReaderPool.openImage(BioFormatsImageServer.java:1354)
at qupath.lib.images.servers.bioformats.BioFormatsImageServer.readTile(BioFormatsImageServer.java:915)
at qupath.lib.images.servers.AbstractTileableImageServer.lambda$prerequestTiles$2(AbstractTileableImageServer.java:462)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at qupath.lib.images.servers.AbstractTileableImageServer.prerequestTiles(AbstractTileableImageServer.java:464)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:295)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:60)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:863)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:902)
at qupath.imagej.detect.cells.WatershedCellDetection$CellDetector.runDetection(WatershedCellDetection.java:216)
at qupath.lib.plugins.DetectionPluginTools$DetectionRunnable.run(DetectionPluginTools.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
INFO: ParallelTileObject (Tile-Processing) (processing time: 0.00 seconds)
ERROR: Error running plugin: java.lang.NullPointerException: Cannot invoke "java.awt.image.BufferedImage.getSampleModel()" because "img" is null
java.util.concurrent.ExecutionException: java.lang.NullPointerException: Cannot invoke "java.awt.image.BufferedImage.getSampleModel()" because "img" is null
at java.base/java.util.concurrent.FutureTask.report(Unknown Source)
at java.base/java.util.concurrent.FutureTask.get(Unknown Source)
at qupath.lib.plugins.AbstractPluginRunner.awaitCompletion(AbstractPluginRunner.java:139)
at qupath.lib.plugins.AbstractPluginRunner.runTasks(AbstractPluginRunner.java:107)
at qupath.lib.gui.PluginRunnerFX.runTasks(PluginRunnerFX.java:98)
at qupath.lib.plugins.AbstractPlugin.runPlugin(AbstractPlugin.java:169)
at qupath.lib.gui.QuPathGUI.runPlugin(QuPathGUI.java:3961)
at qupath.lib.gui.scripting.QPEx.runPlugin(QPEx.java:236)
at qupath.lib.gui.scripting.QPEx.runPlugin(QPEx.java:258)
at org.codehaus.groovy.vmplugin.v8.IndyInterface.fromCache(IndyInterface.java:321)
at QuPathScript.run(QuPathScript:35)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:331)
at org.codehaus.groovy.jsr223.GroovyScriptEngineImpl.eval(GroovyScriptEngineImpl.java:161)
at qupath.lib.gui.scripting.languages.DefaultScriptLanguage.execute(DefaultScriptLanguage.java:233)
at qupath.lib.gui.scripting.DefaultScriptEditor.executeScript(DefaultScriptEditor.java:1113)
at qupath.lib.gui.scripting.DefaultScriptEditor$3.run(DefaultScriptEditor.java:1478)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by Cannot invoke "java.awt.image.BufferedImage.getSampleModel()" because "img" is null at qupath.imagej.tools.IJTools.convertToUncalibratedImagePlus(IJTools.java:791)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:864)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:902)
at qupath.imagej.detect.cells.WatershedCellDetection$CellDetector.runDetection(WatershedCellDetection.java:216)
at qupath.lib.plugins.DetectionPluginTools$DetectionRunnable.run(DetectionPluginTools.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
ERROR: Error processing Geometry (128, 319, 3045, 1074)
java.io.IOException: java.lang.InterruptedException
at qupath.lib.images.servers.bioformats.BioFormatsImageServer.readTile(BioFormatsImageServer.java:917)
at qupath.lib.images.servers.AbstractTileableImageServer.lambda$prerequestTiles$2(AbstractTileableImageServer.java:462)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at qupath.lib.images.servers.AbstractTileableImageServer.prerequestTiles(AbstractTileableImageServer.java:464)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:295)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:60)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:863)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:902)
at qupath.imagej.detect.cells.WatershedCellDetection$CellDetector.runDetection(WatershedCellDetection.java:216)
at qupath.lib.plugins.DetectionPluginTools$DetectionRunnable.run(DetectionPluginTools.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by null at java.base/java.util.concurrent.locks.ReentrantLock$Sync.lockInterruptibly(Unknown Source)
at java.base/java.util.concurrent.locks.ReentrantLock.lockInterruptibly(Unknown Source)
at java.base/java.util.concurrent.ArrayBlockingQueue.put(Unknown Source)
at qupath.lib.images.servers.bioformats.BioFormatsImageServer$ReaderPool.openImage(BioFormatsImageServer.java:1354)
at qupath.lib.images.servers.bioformats.BioFormatsImageServer.readTile(BioFormatsImageServer.java:915)
at qupath.lib.images.servers.AbstractTileableImageServer.lambda$prerequestTiles$2(AbstractTileableImageServer.java:462)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at qupath.lib.images.servers.AbstractTileableImageServer.prerequestTiles(AbstractTileableImageServer.java:464)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:295)
at qupath.lib.images.servers.AbstractTileableImageServer.readRegion(AbstractTileableImageServer.java:60)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:863)
at qupath.imagej.tools.IJTools.convertToImagePlus(IJTools.java:902)
at qupath.imagej.detect.cells.WatershedCellDetection$CellDetector.runDetection(WatershedCellDetection.java:216)
at qupath.lib.plugins.DetectionPluginTools$DetectionRunnable.run(DetectionPluginTools.java:112)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
INFO: ParallelTileObject (Tile-Processing) (processing time: 0.04 seconds)
INFO: Processing complete in 0.14 seconds
INFO: Completed with error java.lang.NullPointerException: Cannot invoke "java.awt.image.BufferedImage.getSampleModel()" because "img" is null
ERROR: ABBA transformation file not found for entry 449#3_Syt2Ephys_Day6205.vsi - 10x_DAPI, GFP, DiI, Cy5_08
Hi, I found there are some mistakes in my coding.
1, during ABBA annotation importing: qupath.ext.biop.abba.AtlasTools.loadWarpedAtlasAnnotations(getCurrentImageData(), "acronym", false);
, false
should be changed to true
2, during cell detection: in my case, "ABBA-Transform-Adult Mouse Brain - Allen Brain Atlas V3.json"
should be changed to "ABBA-Transform-Adult Mouse Brain - Allen Brain Atlas V3p1.json"
3, I by order ran the script 0,1,2 and happened to meet some problems, but i simple integrated all the script with some codes deleted, thing went more smooth.
My workflow code like this, basically they are based on NicoKiaru's scripts:
// imports
import qupath.ext.biop.warpy.*
import net.imglib2.RealPoint
import qupath.lib.measurements.MeasurementList
import qupath.lib.objects.PathObjectTools
// import annotation from ABBA
setImageType('FLUORESCENCE');
clearAllObjects();
qupath.ext.biop.abba.AtlasTools.loadWarpedAtlasAnnotations(getCurrentImageData(), "acronym", true);
// cell detection
clearDetections()
def project = getProject()
// We want to run cell detection only once on the entire brain, which means we need to select the Root node. We can find it by name
def root = getAnnotationObjects().find{ it.getName().equals( 'Root' ) }
// Duplicate root node. It is a bit contrived but this is how you can copy an annotation
def regionMeasured = PathObjectTools.transformObject( root, null, true )
// Temporary name
regionMeasured.setName( "Measured Region" )
// Need to add it to the current hierarchy
addObject( regionMeasured )
// So that we can select it
setSelectedObject( regionMeasured )
// RUN CELL DETECTION
// Modify the line below by inserting your cell detection plugin line
// *********************************************************************
// *********************************************************************
runPlugin( 'qupath.imagej.detect.cells.WatershedCellDetection',
'{"detectionImage": "Cy3", "requestedPixelSizeMicrons": 1.0, "backgroundRadiusMicrons": 8.0, "medianRadiusMicrons": 0.0, "sigmaMicrons": 2.0, "minAreaMicrons": 20.0, "maxAreaMicrons": 200.0, "threshold": 50.0, "watershedPostProcess": true, "cellExpansionMicrons": 10.0, "includeNuclei": True, "smoothBoundaries": true, "makeMeasurements": true}')
// *********************************************************************
// *********************************************************************
// re-loading annotation
qupath.ext.biop.abba.AtlasTools.loadWarpedAtlasAnnotations(getCurrentImageData(), "acronym", true);
// Get ABBA transform file located in entry path
def targetEntry = getProjectEntry()
def targetEntryPath = targetEntry.getEntryPath()
def fTransform = new File(targetEntryPath.toFile(), "ABBA-Transform-Adult Mouse Brain - Allen Brain Atlas V3p1.json" )
if ( !fTransform.exists() ) {
logger.error( "ABBA transformation file not found for entry {}", targetEntry )
return
}
def pixelToCCFTransform = Warpy.getRealTransform( fTransform ).inverse() // Needs the inverse transform
getDetectionObjects().forEach{ detection ->
def ccfCoordinates = new RealPoint(3)
def ml = detection.getMeasurementList()
ccfCoordinates.setPosition( [detection.getROI().getCentroidX(),detection.getROI().getCentroidY(), 0] as double[] )
// The Z=0 pixel coordinate is automatically transformed to the right atlas position in Z thanks to the fact that the slice
// position is already known by the transform
// This applies the transform in place to cffCoordinates
pixelToCCFTransform.apply(ccfCoordinates, ccfCoordinates)
// Fianlly: Add the coordinates as measurements
ml.addMeasurement("Allen CCFv3 X mm", ccfCoordinates.getDoublePosition(0) )
ml.addMeasurement("Allen CCFv3 Y mm", ccfCoordinates.getDoublePosition(1) )
ml.addMeasurement("Allen CCFv3 Z mm", ccfCoordinates.getDoublePosition(2) )
ml.addMeasurement("Count: Num Spots", 1 )
}
// To execute before:
// 0 - https://gist.github.com/NicoKiaru/723d8e628a3bb03902bb3f0f2f0fa466
// 1 - https://gist.github.com/NicoKiaru/f45f56e3ff2d1fb708821c110fbdee62
// save annotations
File directory = new File(buildFilePath(PROJECT_BASE_DIR,'export'));
directory.mkdirs();
def imageData = getCurrentImageData();
imageName = ServerTools.getDisplayableImageName(imageData.getServer())
def filename = imageName//.take(imageName.indexOf('.'))
saveMeasurements(
imageData,
PathDetectionObject.class,
buildFilePath(directory.toString(),filename + '.tsv'),
"Class",
"Allen CCFv3 X mm",
"Allen CCFv3 Y mm",
"Allen CCFv3 Z mm",
"Count: Num Spots"
);
import qupath.lib.objects.PathObjects
import qupath.lib.objects.PathObject
import qupath.lib.objects.PathDetectionObject
import qupath.lib.objects.PathCellObject
import qupath.lib.roi.ROIs
import qupath.lib.regions.ImagePlane
import qupath.lib.measurements.MeasurementList
import qupath.lib.objects.PathCellObject
Thanks a lot @AthiemoneZero , I'll update my script based on your modifications, indeed some parts are not up to date
Normally you need to run
export registration to QuPath
from ABBA in Fiji.If the process is successful, you can, in QuPath, right click on the image entry and click
Open directory... > Project Entry...
You should see a json and a zip file which are the result of ABBA's registration: