Filter annotations by area

Hello,

I am trying to do some annotation clean-up after superpixel tiling -> applying classifier -> tile classifications to annotations.

I tried filtering the annotations by area using the script by Svidro (many thanks!):

// Remove Epidermis annotations with area less than 45,000 um^2
selectObjects { p -> p.getPathClass() == getPathClass('Epidermis') }
def notEpidermis1 = getSelectedObjects().findAll {it.getROI().getArea() < 45000}
removeObjects(notEpidermis1, true)

It does remove some annotations, but oddly enough not all of them even though they are within the criteria of <45,000 um^2.

Before filter:


After filter:


Any ideas on what’s going on?

Many thanks,
Yau

1 Like

I admi I haven’t tried this, but I’d suggest something like the following:

// Remove Epidermis annotations with area less than 45,000 um^2
def notEpidermis1 = getAnnotationObjects().findAll { p.getPathClass() == getPathClass('Epidermis') && it.getROI().getArea() < 45000 }
removeObjects(notEpidermis1, true)

Basically, interactive scrips (i.e. just run in the script editor for the image that is currently open) tend to work better if you can avoid relying on selecting/deselecting objects. So I’ve removed the selection step to try to get you annotations in one go.

Hi Pete,

Thanks for replying so quickly.

I’ve just tried your suggestion but the issue persisted.

I think I solved the issue with another method (also from Svidro) with some modifications for M5:

def server = getCurrentServer()
def cal = server.getPixelCalibration()
double pixelWidth = cal.getPixelWidthMicrons()
double pixelHeight = cal.getPixelHeightMicrons()
def notEpidermis = getSelectedObjects().findAll {it.getROI().getScaledArea(pixelWidth, pixelHeight) < 45000}
removeObjects(notEpidermis, true)

What is the difference between getArea() and getScaledArea()?

True, you’ll need scaled area if you want it defined in microns. Otherwise your area will be in pixels.

I don’t think the script you posted will work directly unless your annotations happen to be selected first. This may be more successful:

// Remove Epidermis annotations with area less than 45,000 um^2
def server = getCurrentServer()
def cal = server.getPixelCalibration()
double pixelWidth = cal.getPixelWidthMicrons()
double pixelHeight = cal.getPixelHeightMicrons()
def notEpidermis1 = getAnnotationObjects().findAll { it.getPathClass() == getPathClass('Epidermis') && it.getROI().getScaledArea(pixelWidth, pixelHeight) < 45000 }
removeObjects(notEpidermis1, true)
1 Like

Thanks for pointing that out, I’ll need to put some notes in there to emphasize that those are pixel counts and not square micron areas.

2 Likes

Thanks Pete!

The script I posted is a snippet where I have the desired annotations selected beforehand, so it worked in my case. I will mark your post as the solution for others who want a complete working script.

1 Like

Sorry to bring this up again but there seems to be an error in this line:

def notEpidermis1 = getAnnotationObjects().findAll { p.getPathClass() == getPathClass('Epidermis') && it.getROI().getScaledArea(pixelWidth, pixelHeight) < 45000 }

The error message that shows in the log is:

ERROR: I cannot find 'p'!

I can always go back to getSelectedObjects() but your method is one line shorter.

Oops, you’ll need to change p to it. I’ve edited the post above to include the change.

2 Likes

To explain, you can either use

getAnnotationObjects().findAll { p -> p.getPathClass() == getPathClass('Epidermis') && p.getROI().getScaledArea(pixelWidth, pixelHeight) < 45000 }

where p can be more or less any valid variable name that represents one of the annotation objects, or

getAnnotationObjects().findAll { it.getPathClass() == getPathClass('Epidermis') && it.getROI().getScaledArea(pixelWidth, pixelHeight) < 45000 }

because if you don’t bother specifying a name, it is the default.

1 Like

Thanks for the explanation.

Some of the scripts I found and adapted used p -> p.getPathClass(), and I have now replaced them with just it.getPathClass().

Yeah, one of the better reasons to use p-> or similar is when you have loops inside of loops, and it can be hard to keep track of what each “it” means. For simple stuff, it is good.

1 Like