Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save petebankhead/c696ffb62c5d11ebac299953f9fd6cce to your computer and use it in GitHub Desktop.
Save petebankhead/c696ffb62c5d11ebac299953f9fd6cce to your computer and use it in GitHub Desktop.
Script to transfer QuPath objects from one image to another, applying an AffineTransform to any ROIs
/**
* Script to transfer QuPath objects from one image to another, applying an AffineTransform to any ROIs.
*
* This is a based upon the script I posted on the epic forum thread https://forum.image.sc/t/interactive-image-alignment/23745/9?u=petebankhead
* It has been updated for QuPath v0.2.0 (and made quite a lot shorter along the way).
*
* @author Pete Bankhead
*/
// SET ME! Define transformation matrix
// You can determine this using the 'Interactive image alignment' command
def matrix = [
-0.998, -0.070, 127256.994,
0.070, -0.998, 72627.371
]
// SET ME! Define image containing the original objects (must be in the current project)
def otherImageName = 'type your image name here'
// SET ME! Delete existing objects
def deleteExisting = true
// SET ME! Change this if things end up in the wrong place
def createInverse = true
// SET ME! Change this to false if you want to discard existing measurements
// Note that things like area might be incorrect if the transform includes rescaling
boolean copyMeasurements = true
import qupath.lib.objects.PathObject
import qupath.lib.objects.PathObjectTools
import java.awt.geom.AffineTransform
import static qupath.lib.gui.scripting.QPEx.*
if (otherImageName == null) {
Dialogs.showErrorNotification("Transform objects", "Please specify an image name in the script!")
return
}
// Get the project & the requested image name
def project = getProject()
def entry = project.getImageList().find {it.getImageName() == otherImageName}
if (entry == null) {
print 'Could not find image with name ' + otherImageName
return
}
def otherHierarchy = entry.readHierarchy()
def pathObjects = otherHierarchy.getRootObject().getChildObjects()
// Define the transformation matrix
def transform = new AffineTransform(
matrix[0], matrix[3], matrix[1],
matrix[4], matrix[2], matrix[5]
)
if (createInverse)
transform = transform.createInverse()
if (deleteExisting)
clearAllObjects()
def newObjects = []
for (pathObject in pathObjects) {
newObjects << transformObject(pathObject, transform, copyMeasurements)
}
addObjects(newObjects)
print 'Done!'
/**
* Transform object, recursively transforming all child objects
*
* @param pathObject
* @param transform
* @return
*/
PathObject transformObject(PathObject pathObject, AffineTransform transform, boolean copyMeasurements) {
def newObject = PathObjectTools.transformObject(pathObject, transform, copyMeasurements)
// Handle child objects
if (pathObject.hasChildren()) {
newObject.addPathObjects(pathObject.getChildObjects().collect({transformObject(it, transform, copyMeasurements)}))
}
return newObject
}
@marco1108p
Copy link

marco1108p commented Feb 18, 2022

I have used the QuPath-Transform and transform objects.groovy and works great. However, I would need to transfer only the first order objects of the hierarchy and not all the child objects (eg the objects listed in the figure without all the child objects). I have made a few attempts without success.

Many thanks!

Screenshot 2022-02-18 at 19 32 11

@petebankhead
Copy link
Author

Hi @marco1108p if you don't need the other objects, you can just remove the lines

//    if (pathObject.hasChildren()) {
//        newObject.addPathObjects(pathObject.getChildObjects().collect({transformObject(it, transform, copyMeasurements)}))
//    }

If you need something more complicated, I'd suggest posting a question on the forum at http://forum.image.sc/tag/qupath

@marco1108p
Copy link

Many thanks Pete! This was exactly what I needed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment