ImageJ Rois and Rotated Image Server issue on QuPath

Hello everybody and especialy the qupath experts,

I need to export a RoiSet from ImageJ into QuPath. All Rois are defined in pixel coordinates of the original image (highest resolution level).

To convert from Roi to PathObject, this line is used:

PathObject object = PathObjects.createAnnotationObject(IJTools.convertToROI(roi, 0, 0, 1, null));

This works fine if the import is done in a ‘normal Image Server’. However, if I use a rotated image server (180 degrees for instance), then the converted Rois are upside down:

So : how can I import a Roi (defined in the original pixel coordinates in the highest resolution level) properly into a QuPath image, taking into account the rotations from the image server ?

ping @petebankhead, @Research_Associate , @oburri

Heya colleague who is definitely not on holidays, unlike me :slight_smile:

You could try using IJTools.convertToAnnotation( imp, server, roi, downsample, plane )

That way, you can use the calibration information from an ImagePlus (which can be blank as long as the origin and calibration are set). The plane can be null too. This way I hope that it uses the ImageServer to figure stuff out?

1 Like

Hi @NicoKiaru, a script like this could be used to transform objects after import (or parts extracted to perform the transform during import):

import java.awt.geom.AffineTransform

def server = getCurrentServer()

def transform = AffineTransform.getRotateInstance(Math.PI)
transform.translate(-server.getWidth(), -server.getHeight())

def annotations = getAnnotationObjects()
def transformedAnnotations = annotations.collect {PathObjectTools.transformObject(it, transform, true)}

removeObjects(annotations, true)
addObjects(transformedAnnotations)

(It can be a bit fiddly to get the transform exactly right, with the operations applied in the correct order, and I haven’t tested this much – but hopefully it’s on the right lines)

4 Likes

@oburri, I tried and unfortunately this doesn’t work :slightly_frowning_face:

@petebankhead, thanks! I think it will work soon. I’m modifying it a little bit because I want to make this to work for any rotation and also if there’s no rotation, so I’m testing if the server is an instance of RotatedImageServer. (Also I’m in java but the changes are easy)

Ok, so this seem to work in all cases:

 // Rotation for rotated servers
        ImageServer<?> server = imageData.getServer();

        AffineTransform transform = null;

        if (server instanceof RotatedImageServer) {
            // The roi will need to be transformed before being imported
            // First : get the rotation
            RotatedImageServer ris = (RotatedImageServer) server;
            switch (ris.getRotation()) {
                case ROTATE_NONE: // No rotation.
                    break;
                case ROTATE_90: // Rotate 90 degrees clockwise.
                    transform = AffineTransform.getRotateInstance(Math.PI/2.0);
                    transform.translate(0, -server.getWidth());//-server.getWidth());
                    break;
                case ROTATE_180: // Rotate 180 degrees.
                    transform = AffineTransform.getRotateInstance(Math.PI);
                    transform.translate(-server.getWidth(), -server.getHeight());
                    break;
                case ROTATE_270: // Rotate 270 degrees
                    transform = AffineTransform.getRotateInstance(Math.PI*3.0/2.0);
                    transform.translate(-server.getHeight(), 0);
                    break;
                default:
                    System.err.println("Unknow rotation for rotated image server: "+ris.getRotation());
            }
        }

I’m a bit puzzled by the required translations… It’s working but that was pure trial and errors.
Also : what is a good error log for QuPath (accessible in Java), I tried System.err.println but nothing shows up ?

Thanks!

Glad it’s working! The translation will handle the fact that the origin is around (0,0), which should be the top left corner of the image. But in practice trial and error usually sorts it out for me… since I tend to forget the details/order.

For a logging QuPath uses SLF4J – which as far as I can tell is the simplest / most common way to handle logging in Java applications. Almost all QuPath classes should have a logger in them.

2 Likes