Skip to content

Instantly share code, notes, and snippets.

@petebankhead
Last active May 3, 2023 08:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save petebankhead/0b14beef131312042686c01056104b85 to your computer and use it in GitHub Desktop.
Save petebankhead/0b14beef131312042686c01056104b85 to your computer and use it in GitHub Desktop.
Script to export annotated regions as binary masks (revised for v0.2.0-m6)
/**
* Script to export binary masks corresponding to all annotations of an image,
* optionally along with extracted image regions.
*
* Note: Pay attention to the 'downsample' value to control the export resolution!
*
* @author Pete Bankhead
*/
import qupath.lib.images.servers.ImageServer
import qupath.lib.objects.PathObject
import javax.imageio.ImageIO
import java.awt.Color
import java.awt.image.BufferedImage
// Get the main QuPath data structures
def imageData = getCurrentImageData()
def hierarchy = imageData.getHierarchy()
def server = imageData.getServer()
// Request all objects from the hierarchy & filter only the annotations
def annotations = hierarchy.getAnnotationObjects()
// Define downsample value for export resolution & output directory, creating directory if necessary
def downsample = 4.0
def pathOutput = buildFilePath(QPEx.PROJECT_BASE_DIR, 'masks')
mkdirs(pathOutput)
// Define image export type; valid values are JPG, PNG or null (if no image region should be exported with the mask)
// Note: masks will always be exported as PNG
def imageExportType = 'JPG'
// Export each annotation
annotations.each {
saveImageAndMask(pathOutput, server, it, downsample, imageExportType)
}
print 'Done!'
/**
* Save extracted image region & mask corresponding to an object ROI.
*
* @param pathOutput Directory in which to store the output
* @param server ImageServer for the relevant image
* @param pathObject The object to export
* @param downsample Downsample value for the export of both image region & mask
* @param imageExportType Type of image (original pixels, not mask!) to export ('JPG', 'PNG' or null)
* @return
*/
def saveImageAndMask(String pathOutput, ImageServer server, PathObject pathObject, double downsample, String imageExportType) {
// Extract ROI & classification name
def roi = pathObject.getROI()
def pathClass = pathObject.getPathClass()
def classificationName = pathClass == null ? 'None' : pathClass.toString()
if (roi == null) {
print 'Warning! No ROI for object ' + pathObject + ' - cannot export corresponding region & mask'
return
}
// Create a region from the ROI
def region = RegionRequest.createInstance(server.getPath(), downsample, roi)
// Create a name
String name = String.format('%s_%s_(%.2f,%d,%d,%d,%d)',
server.getMetadata().getName(),
classificationName,
region.getDownsample(),
region.getX(),
region.getY(),
region.getWidth(),
region.getHeight()
)
// Request the BufferedImage
def img = server.readBufferedImage(region)
// Create a mask using Java2D functionality
// (This involves applying a transform to a graphics object, so that none needs to be applied to the ROI coordinates)
def shape = RoiTools.getShape(roi)
def imgMask = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_BYTE_GRAY)
def g2d = imgMask.createGraphics()
g2d.setColor(Color.WHITE)
g2d.scale(1.0/downsample, 1.0/downsample)
g2d.translate(-region.getX(), -region.getY())
g2d.fill(shape)
g2d.dispose()
// Create filename & export
if (imageExportType != null) {
def fileImage = new File(pathOutput, name + '.' + imageExportType.toLowerCase())
ImageIO.write(img, imageExportType, fileImage)
}
// Export the mask
def fileMask = new File(pathOutput, name + '-mask.png')
ImageIO.write(imgMask, 'PNG', fileMask)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment