Get pixel-by-pixel intensities in QuPath


I have managed to get the mean intensity values for annotations in Brightfield (H-DAB) images using the Add intensity features command (see the relevant post below). But…

Is it possible to access (and export as a CSV or similar) the individual pixel ‘intensity’ values in annotations in QuPath via a Groovy script (and without sending to ImageJ)?


You may want to look into something like this…

But effectively, I think most of the pixel value stuff I have seen in scripts goes through ImageJ (ImagePlus) collections of pixels. I don’t have a great handle on it, so the scripts I played with got done just about what I needed them (R^2 values between channels, colocalization quantification) to and sometimes errored out on other image types.

1 Like

Can you describe more what you want to do?

Within a Groovy script you can potentially access pixels as a Java BufferedImage, an ImageJ ImagePlus or an OpenCV Mat – although you might also need an accompanying binary mask for the region of interest, for which the process would be different in each case. All of these are also limited by the size of a Java array / available memory, so can’t be applied for arbitrarily large regions.

1 Like

Hello @Pete,

Taking care not to overinterpret this, we have been asked to compare the staining intensity of peroxidase-based IHC signals for various antigens, both within the stroma and within cells, for diseased and control samples.

The pathologist has made annotations of various sizes using the brush tool (aiming to limit their size/number of vertices to avoid a drop in performance). While some of these are not as large as a full image annotation, they are still much larger than the average detection of say nuclei.

We would like to plot the pixel values from within these ROIs (and likely the inverse of these, although they may be too large) outside of QuPath, let’s say in R?

Just to add: the workstation this is running on has ~ 96 GB RAM allocated to QuPath.

Thank you!

@Research_Associate I like this, Thank you!

@petebankhead do you think this would be an option, provided we don’t encounter a 2 gigapixel ImagePlus?

I think I’m missing something… what’s wrong with Add intensity features?

It may be that I’m not sure what ‘plot the pixel values’ means here. Histogram, scatterplot? For individual pixels at full resolution, or mean values per annotation?

I haven’t explained this well enough because I don’t know the answer to this question either.

Mean values per annotation would achieve the same result as Add intensity features, right? We would like to export the individual pixels at full resolution, no downsampling, and create histograms of frequency vs intensity.


1 Like

Yes, should be.

For which image channels? Red/Green/Blue, or after color deconvolution?

It would be the color deconvolved Haematoxylin and DAB if possible, please!

Here’s part of a potential solution:

import qupath.lib.images.servers.TransformedServerBuilder

// Create an ImageServer that applies color deconvolution to the current image, using the current stains
def imageData = getCurrentImageData()
def server = new TransformedServerBuilder(imageData.getServer())
    .deconvolveStains(imageData.getColorDeconvolutionStains(), 1, 2)
for (annotation in getAnnotationObjects()) {
  def region = RegionRequest.createInstance(server.getPath(), 1.0, annotation.getROI())
  def pathImage = IJTools.convertToImagePlus(server, region)
  def imp = pathImage.getImage()
  def roiIJ = IJTools.convertToIJRoi(annotation.getROI(), pathImage)
  new ij.gui.HistogramWindow(imp)
  new ij.gui.HistogramWindow(imp)
// // show image to check if you want

It delegates the histogram computation to ImageJ, and assumes everything will fit in RAM. You can find the ImageJ source (and alternative constructors) here:

Potentially you could create the histogram some other way, but doing so would probably cost quite a lot more code – especially since the image will be 32-bit and so require binning.

(I think it will pay attention to the ROI, but everything needs to be checked…)


Quick note, either open an IJ instance within QuPath or add

import qupath.imagej.gui.IJExtension

to the top to make sure an IJ window is open - which will create the histograms.


@Research_Associate perfect, thank you!

1 Like

So, it looks like for an image with 14 ROIs it creates 28 histograms (14 for the Hematoxylin and 14 for the DAB channel). Is that expected behaviour?

Is it possible to pool these and create one histogram per channel per image?

Thank you once again This is a great help!

You could select annotations and Merge them, unless you also needed to keep them separate? Commands are in the Object menu, and should show up in the Workflow.

In other words, turn all of the separate annotations into one annotation, rather than merging the collections of pixels.


@Research_Associate oh, but of course. We did this originally… Thanks for your patience.

1 Like

This has been excellent so far, thank you @petebankhead and @Research_Associate.

Looking at the the Javadoc, do you think it is possible to modify these lines in your script:

to something like: new ij.gui.HistogramWindow(imp, bins, histMin, histMax, adding these extra parameters so that the histograms for images across the project can be standardised?


Sure, HistogramWindow is from ImageJ1 – source is here:

I only suggested it as it was the quickest way I could think of to generate a histogram using an existing QuPath dependency, and without writing a lot of custom code.

Yes, that’s where I got the idea from, but I couldn’t get it to run. Turns out I forgot to include a title… Then it worked perfectly.

Thank you!