Easy and consistent measurement based cell coloring

New script time! I recommend clicking the Raw button on the right and then selecting all-> copy/paste into QuPath.

This one was motivated by a recent post by @komzy. I hadn’t considered adding directly to the GUI since way back when I took a brief and unsuccessful foray into extensions.

This script creates a set of buttons on the top of the QuPath GUI bar that allow a user to quickly flip between pre-selected Measurement Maps settings. Not only does this make it much quicker to select a measurement of interest in what may otherwise be quite a long list (8 channel IF images have 125 by default!), it allows/forces you to set multiple fixed thresholds for the color map. That last bit makes it much easier to compare measurement colors between images. Normally the color map automatically selects thresholds based on the highest and lowest value in current image’s list of detections… which is less useful if, for example, one entire image is your negative control.

To demonstrate, I went right back to the LuCa 7 color image from the multiplex classification script guide, generated some cells, and then went through the actual Measurement Maps to pick out some good min and max values for my measurements of interest. Next, you need to manually enter the list of measurements, minimum and maximum values, and names for your buttons. I suggest keeping the button names to one or two characters so they fit in your window, though you can expand the buttons if you want longer names. All variables can be entered at the top of the script.
Mine ended up looking like:

//Keep these labels fairly short, or increase the button size. Be careful about having enough room!
int buttonSize = 40
String[] buttonLabels = ["1C","2C", "3N", "4C", "5C", "6C"] as String[]

String[] names = ["Cytoplasm: Channel 1 mean", "Cytoplasm: Channel 2 mean", "Nucleus: Channel 3 mean", "Cytoplasm: Channel 4 mean", "Cytoplasm: Channel 5 mean", "Cytoplasm: Channel 6 mean"] as String[]

double[] minValue= [0,0,0,0,0,0]
double[] maxValue= [18, 1,6,1,20,10]

The only checks for whether everything is typed correctly is a quick test to make sure all lists are the same length, so make sure you type everything correctly.
Once you run the script, you should get a string of buttons including a “Clear” button to the right of the other buttons in your normal GUI. Mine looked like the following:


The only way to remove the buttons currently is to close that instance of QuPath, and the buttons do not persist when closing and reopening the program.
image
image image
Clicking on any of the buttons sets the measurement map to the selected measurement, and Clear returns you to the default view. Note this is ONLY an overlay coloring, it does not affect or interfere with cell classification coloring. Even if you want it to :slight_smile: That could be another script, potentially.

Enjoy! And as usual let me know if you have any questions, comments, concerns or more importantly, find any bugs! So far it seems to be compatible with both 0.2.0m2 and earlier versions.

2 Likes

Ack, my apologies to either of the two people who clicked on the link. I added a quick comment to the top line after posting the script… and placing anything on the first line other than guiscript=true breaks the entire thing!

It has been fixed.

Still working on fixing it up to access user defined color maps, but here is a version that can be used in M4.


guiscript=true
//Script sets up some buttons to allow easy viewing of a fixed measurement map. Buttons are only removed when closing QuPath
//Keep these labels fairly short, or increase the button size. Be careful about having enough room!
int buttonSize = 40
String[] buttonLabels = ["1C","2C", "3N", "4C", "5C", "6C"] as String[]

String[] names = ["Cytoplasm: PDL1 (Opal 520) mean", "Cytoplasm: CD8 (Opal 540) mean", "Nucleus: FoxP3 (Opal 570) mean", "Cytoplasm: CD68 (Opal 620) mean", "Cytoplasm: PD1 (Opal 650) mean", "Cytoplasm: CK (Opal 690) mean"] as String[]
double[] minValue= [0,0,0,0,0,0]
double[] maxValue= [18, 1,6,1,20,10]

//import javafx.application.Platform
import javafx.scene.control.Button
import javafx.scene.control.Tooltip
import qupath.lib.gui.QuPathGUI
import qupath.lib.gui.helpers.MeasurementMapper
import qupath.lib.gui.helpers.MeasurementMapper.ColorMapper
import qupath.lib.gui.helpers.MeasurementMapper.PseudoColorMapper

def qupath = QuPathGUI.getInstance()
/*List<ColorMapper> DEFAULT_COLOR_MAPS;
DEFAULT_COLOR_MAPS.loadDefaultColorMaps()*/
ColorMapper LEGACY_COLOR_MAP = new PseudoColorMapper();

int size = names.size()
buttons = new Button [size]

//check some things
if (minValue.size()!= size || maxValue.size()!= size || buttonLabels.size() != size ) {println("All lists not same size"); return;}
if (getDetectionObjects().size() < 1){println("Detections NEED to be present before running this script"); return;}

maps = new MeasurementMapper [size]
def unColor = new Button('Clear')
unColor.setPrefSize(50, QuPathGUI.iconSize)
unColor.setTooltip(new Tooltip("Remove coloring"));
unColor.setOnAction {

        print 'Resetting measurement map'
        getCurrentViewer().getOverlayOptions().setMeasurementMapper(null)

}
qupath.addToolbarButton(unColor);
for (i = 0; i<size; i++){
    maps[i] = new MeasurementMapper(LEGACY_COLOR_MAP, names[i], getDetectionObjects())
}

//println(maps)

for (i = 0; i<size; i++){

    buttons[i] = new Button(buttonLabels[i])
    buttons[i].setPrefSize(buttonSize, QuPathGUI.iconSize)
    buttons[i].setTooltip(new Tooltip("Measurement Maps "+names[i]));

    buttons[i].setOnAction {e->
        
        source = e.getSource().getText()
        //println(source)
        //println(buttons[0].getText())
        //println(buttons[1].getText())
        //println(size)
        int j= 255
        for (k = 0; k<size; k++){
            //println("k " + k)
            if (source == buttons[k].getText()){j=k; print "please";}else{print "NotA"}
        }
        // Update the display
        //println(j)
        //println(buttons[j].getText())
        if (names[j]) {
            print String.format('Setting measurement map: %s (%.2f - %.2f)', names[j], minValue[j], maxValue[j])
            maps[j].setDisplayMinValue(minValue[j])
            maps[j].setDisplayMaxValue(maxValue[j])
            getCurrentViewer().getOverlayOptions().setMeasurementMapper(maps[j])
        } else {
            print 'Resetting measurement map'
            getCurrentViewer().getOverlayOptions().setMeasurementMapper(null)
        }

    }
}
for (n = 0; n<size; n++){
    qupath.addToolbarButton(buttons[n]);
}