|
1 | | -# QuPath extension template |
| 1 | +# QuPath XGBoost Classifier Extension |
2 | 2 |
|
3 | | -This repo contains a template and instructions to help create a new extension for [QuPath](https://qupath.github.io). |
| 3 | +A [QuPath](https://qupath.github.io) extension that brings [XGBoost](https://xgboost.readthedocs.io/) gradient-boosted tree classification to object classification workflows. |
| 4 | +Train a model from point or area annotations across multiple project images, then run inference across any selection of images — all from a GUI, no scripting required. |
4 | 5 |
|
5 | | -It already contains two minimal extensions - one using Java, one using Groovy - so the first task is to make sure that they work. |
6 | | -Then, it's a matter of customizing the code to make it more useful. |
| 6 | +--- |
7 | 7 |
|
8 | | -> **Update!** |
9 | | -> For QuPath v0.6.0 this repo switched to use Kotlin DSL for Gradle build files - |
10 | | -> and also to use the [QuPath Gradle Plugin](https://github.com/qupath/qupath-gradle-plugin). |
11 | | -> |
12 | | -> The outcome is that the build files are _much_ simpler. |
| 8 | +## How it works |
13 | 9 |
|
| 10 | +### Training |
14 | 11 |
|
15 | | -## Build the extension |
| 12 | +The extension looks for **annotated regions** (point or area ROIs) on top of existing detections to build training data. |
16 | 13 |
|
17 | | -Building the extension with Gradle should be pretty easy - you don't even need to install Gradle separately, because the |
18 | | -[Gradle Wrapper](https://docs.gradle.org/current/userguide/gradle_wrapper.html) will take care of that. |
| 14 | +1. For each selected project entry it reads the object hierarchy. |
| 15 | +2. Every detection that falls inside an annotation whose class is one of the selected output classes becomes a training sample. If a detection is covered by annotations of more than one class, it is discarded to avoid label ambiguity. |
| 16 | +3. Measurement values for the selected features are extracted from each training detection into a feature matrix. |
| 17 | +4. An initial XGBoost model is trained on the full feature set to rank features by **gain importance**. |
| 18 | +5. A final model is retrained using only the top-N selected features (configurable; 0 = keep all). |
| 19 | +6. Feature names and class names are embedded directly in the saved model JSON so no external metadata file is needed at inference time. |
19 | 20 |
|
20 | | -Open a command prompt, navigate to where the code lives, and use |
21 | | -```bash |
22 | | -gradlew build |
23 | | -``` |
| 21 | +### Inference |
24 | 22 |
|
25 | | -The built extension should be found inside `build/libs`. |
26 | | -You can drag this onto QuPath to install it. |
27 | | -You'll be prompted to create a user directory if you don't already have one. |
| 23 | +1. The saved model JSON is loaded; feature names and class names are read from its metadata. |
| 24 | +2. For each selected project entry, measurements are extracted from all detections using the embedded feature list. |
| 25 | +3. Each detection is assigned the class with the highest predicted probability. |
| 26 | +4. Image data is saved back to the project. |
28 | 27 |
|
29 | | -The minimal extension here doesn't do much, but it should at least install a new command under the 'Extensions' menu in |
30 | | -QuPath. |
| 28 | +--- |
31 | 29 |
|
32 | | -> In case your extension contains external dependencies beyond what QuPath already includes, you can create a |
33 | | -> [single jar file](https://imperceptiblethoughts.com/shadow/introduction/#benefits-of-shadow) that bundles these along |
34 | | -> with your extension by using |
35 | | -> ```bash |
36 | | -> gradlew shadowJar |
37 | | -> ``` |
38 | | -> If you don't do that, you'll need to drag *all* the extra dependences onto QuPath to install them as well. |
| 30 | +## Requirements |
39 | 31 |
|
| 32 | +- **QuPath** 0.7.0 or later |
| 33 | +- **Java** 25 |
| 34 | +- Detections must already exist in the project images (e.g. from cell detection or tile classification) |
| 35 | +- Training images must have point or area annotations placed on top of detections, with a `PathClass` set to one of the desired output classes |
40 | 36 |
|
41 | | -## Configure the extension |
| 37 | +--- |
42 | 38 |
|
43 | | -Edit `settings.gradle.kts` to specify which version of QuPath your extension should be compatible with, e.g. |
| 39 | +## Build |
44 | 40 |
|
45 | | -```kotlin |
46 | | -qupath { |
47 | | - version = "0.6.0" |
48 | | -} |
49 | | -``` |
| 41 | +### Standalone shadow jar (recommended for distribution) |
50 | 42 |
|
51 | | -Edit `build.gradle.kts` to specify the details of your extension |
52 | | - |
53 | | -```kotlin |
54 | | -qupathExtension { |
55 | | - name = "qupath-extension-template" |
56 | | - group = "io.github.qupath" |
57 | | - version = "0.1.0-SNAPSHOT" |
58 | | - description = "A simple QuPath extension" |
59 | | - automaticModule = "io.github.qupath.extension.template" |
60 | | -} |
| 43 | +```bash |
| 44 | +gradlew shadowJar |
61 | 45 | ``` |
62 | 46 |
|
| 47 | +The output is in `build/libs/` with `-all` in the filename. |
| 48 | +Drag this onto QuPath to install it — you will be prompted to create a user directory if you don't have one yet. |
63 | 49 |
|
64 | | -## Run QuPath + the extension |
| 50 | +### During development (composite build) |
65 | 51 |
|
66 | | -During development, your probably want to run QuPath easily with your extension installed for debugging. |
| 52 | +Create a file called `include-extra` in the **root of the QuPath source directory** (not this repo): |
67 | 53 |
|
68 | | -### 0. Make sure you have Java installed |
69 | | -You'll need to install Java first. |
70 | | - |
71 | | -At the time of writing, we use a Java 21 JDK downloaded from https://adoptium.net/ |
72 | | - |
73 | | -> Java 21 is a 'Long Term Support' release - which is why we use it instead of the very latest version. |
74 | | -
|
75 | | -### 1. Get QuPath's source code |
76 | | -You can find instructions at https://qupath.readthedocs.io/en/stable/docs/reference/building.html |
77 | | - |
78 | | -### 2. Create an `include-extra` file |
79 | | -Create a file called `include-extra` in the root directory of the QuPath source code (*not* the extension code!). |
80 | | - |
81 | | -Set the contents of this file to: |
82 | 54 | ``` |
83 | 55 | [includeBuild] |
84 | | -/path/to/your/extension |
| 56 | +../qupath-extension-xgboost |
85 | 57 |
|
86 | 58 | [dependencies] |
87 | | -extension-group:extension-name |
| 59 | +io.github.qupath:qupath-extension-xgboost |
88 | 60 | ``` |
89 | | -replacing the default lines where needed. |
90 | 61 |
|
91 | | -For example, to build the extension with the names given above you'd use |
92 | | -``` |
93 | | -[includeBuild] |
94 | | -../qupath-extension-template |
95 | | -
|
96 | | -[dependencies] |
97 | | -io.github.qupath:qupath-extension-template |
98 | | -``` |
| 62 | +Then run QuPath from the QuPath source directory: |
99 | 63 |
|
100 | | -### 3. Run QuPath |
101 | | -Run QuPath from the command line using |
102 | | -``` |
| 64 | +```bash |
103 | 65 | gradlew run |
104 | 66 | ``` |
105 | | -If all goes well, QuPath should launch and you can check the *Extensions* mention to confirm the extension is installed. |
106 | 67 |
|
| 68 | +When using composite build, add `implementation` alongside `shadow` for XGBoost4J in `build.gradle.kts` so the dependency is available on the runtime classpath: |
107 | 69 |
|
108 | | -## Set up in an IDE (optional) |
109 | | - |
110 | | -During development, things are likely to be much easier if you work within an IDE. |
111 | | - |
112 | | -QuPath itself is developed using IntelliJ, and you can import the extension template there. |
113 | | - |
114 | | -The setup process is as above, and you'll need a a [Run configuration](https://www.jetbrains.com/help/idea/run-debug-configuration.html) |
115 | | -to call `gradlew run`. |
| 70 | +```kotlin |
| 71 | +implementation("ml.dmlc:xgboost4j_2.13:3.2.0") |
| 72 | +shadow("ml.dmlc:xgboost4j_2.13:3.2.0") |
| 73 | +``` |
116 | 74 |
|
| 75 | +--- |
117 | 76 |
|
118 | | -## Customize the extension |
| 77 | +## Usage |
119 | 78 |
|
120 | | -Now you're ready for the creative part. |
| 79 | +After installation the extension adds two commands under **Extensions → XGBoost Classifier**. |
121 | 80 |
|
122 | | -You can develop the extension using either Java or Groovy - the template includes examples of both. |
| 81 | +--- |
123 | 82 |
|
124 | | -### Create the extension Java or Groovy file(s) |
| 83 | +### Train XGBoost Classifier |
125 | 84 |
|
126 | | -For the extension to work, you need to create at least one file that extends `qupath.lib.gui.extensions.QuPathExtension`. |
| 85 | +Opens the training dialog. |
127 | 86 |
|
128 | | -There are two examples in the template, in two languages: |
129 | | -* **Java:** `qupath.ext.template.DemoExtension.java`. |
130 | | -* **Groovy:** `qupath.ext.template.DemoGroovyExtension.java`. |
| 87 | +#### Project Entries |
131 | 88 |
|
132 | | -You can pick the one that corresponds to the language you want to use, and delete the other. |
| 89 | +Select which project images to use for training. Only images with annotations on top of detections will contribute training data. |
133 | 90 |
|
134 | | -Then take your chosen file and rename it, edit it, move it to another package... basically, make it your own. |
| 91 | +- **Filter field** — type to search image names |
| 92 | +- **Available / Selected / Both** — toggle which side of the list the filter applies to |
| 93 | +- **With data file only** — hide images that have no saved data file |
135 | 94 |
|
136 | | -> Please **don't neglect this step!** |
137 | | -> If you do, there's a chance of multiple extensions being created with the same class names... and causing confusion later. |
| 95 | +#### Features |
138 | 96 |
|
139 | | -### Update the `META-INF/services` file |
| 97 | +Select which object measurements to offer to the model. Use the **↺ Refresh** button to discover available measurements from the currently open image. |
140 | 98 |
|
141 | | -For QuPath to *find* the extension later, the full class name needs to be available in `resources/META-INFO/services/qupath.lib.gui.extensions.QuPathExtensions`. |
| 99 | +- **Filter field** — type to narrow the list; toggle **Available / Selected / Both** to target either side |
| 100 | +- All features moved to the right will be used as the initial feature set; the model then ranks and optionally reduces them further by gain importance |
142 | 101 |
|
143 | | -So remember to edit that file to include the class name that you actually used for your extension. |
| 102 | +#### Output Classes |
144 | 103 |
|
145 | | -### Specify your license |
| 104 | +Select which annotation classes define the training labels. At least two classes are required. |
146 | 105 |
|
147 | | -Add a license file to your GitHub repo so that others know what they can and can't do with your extension. |
| 106 | +- **Filter field** — works the same as the features filter |
148 | 107 |
|
149 | | -This should be compatible with QuPath's license -- see https://github.com/qupath/qupath |
| 108 | +#### Parameters |
150 | 109 |
|
151 | | -## Repository configuration |
| 110 | +| Parameter | Default | Description | |
| 111 | +|-----------|---------|-------------| |
| 112 | +| Model name | `my_xgboost` | Base filename; saved as `<name>.json` | |
| 113 | +| Output dir | Project `classifiers/object_classifiers/` | Directory for the saved model | |
| 114 | +| Rounds | 150 | Number of boosting rounds | |
| 115 | +| Max depth | 5 | Maximum depth of each tree | |
| 116 | +| Learning rate (η) | 0.2 | Step size shrinkage; lower = more conservative | |
| 117 | +| Subsample | 0.8 | Fraction of training rows sampled per round | |
| 118 | +| Top-N features | 10 | Keep only the N highest-gain features after the first pass; set to 0 to keep all | |
152 | 119 |
|
153 | | -### Easy install |
| 120 | +The **Log** panel streams progress during training, including per-image object counts, the top-20 feature importances, and the final saved model path. |
154 | 121 |
|
155 | | -If you follow some conventions in naming your extension and making releases, then other QuPath users will find it easy to automatically |
156 | | -install and update your extension! |
| 122 | +--- |
157 | 123 |
|
158 | | -First, we suggest you name your extension `qupath-extension-[something]`, and keep it in its own repository (named the same as the extension), |
159 | | -separate from other projects. |
| 124 | +### Run XGBoost Classifier |
160 | 125 |
|
161 | | -Next, when you want to publish a new version of your extension, use the `github_release.yml` workflow included in this repository. |
| 126 | +Opens the inference dialog. |
162 | 127 |
|
163 | | -To do so, you'd need to navigate to `Actions -> Make draft release -> Run workflow -> Run workflow` as shown in the following screenshot: |
| 128 | +#### Project Entries |
164 | 129 |
|
165 | | - |
| 130 | +Same filter controls as the training dialog. Select the images you want to classify. |
166 | 131 |
|
167 | | -This will automatically build the extension, and create a draft release containing the extension jar (and its associated sources and javadoc). |
168 | | -You can then navigate to `Releases` and fill out information about the release --- the version, any significant changes, etc. |
169 | | -Once published, users will be able to automatically install the extension as described here: |
170 | | -https://qupath.readthedocs.io/en/0.5/docs/intro/extensions.html#installing-extensions |
| 132 | +#### Model |
171 | 133 |
|
172 | | -### Catalogs |
| 134 | +Pick the `.json` model file produced by the training step. |
| 135 | +The file chooser defaults to the project's `classifiers/object_classifiers/` folder. |
173 | 136 |
|
174 | | -QuPath's extension manager can easily install an extension if it is referenced in a **catalog**. |
175 | | -A catalog is a JSON file hosted on a GitHub repository containing information about extensions, making it possible to easily manage them from QuPath. |
| 137 | +The **Log** panel reports the number of detections classified per image. |
176 | 138 |
|
177 | | -To create a catalog, follow the [extension catalog model documentation](https://qupath.github.io/extension-catalog-model/). |
178 | | -You will need to create a JSON file containing specific information about your extension and host it on a dedicated GitHub repository. |
179 | | -Once the catalog is created, any user will be able to easily install your catalog by: |
| 139 | +--- |
180 | 140 |
|
181 | | -* Opening QuPath's extension manager by clicking on `Extensions` -> `Manage extensions` in QuPath. |
182 | | -* Adding the URL to your catalog by clicking on `Manage extension catalogs` -> `Add` in the extension manager. |
183 | | -* Clicking on the `+` symbol next to your extension in the extension manager. |
| 141 | +## Model file format |
184 | 142 |
|
185 | | -QuPath will then make it easy to manage your extension and automatically inform users when an update is available. |
| 143 | +Models are saved as XGBoost native JSON. Feature names and class names are embedded as model attributes: |
186 | 144 |
|
187 | | -### Replace this readme |
| 145 | +- `feature_names` — the ordered list of measurement names the model expects |
| 146 | +- `class_names` — comma-separated list of output class names in label index order |
188 | 147 |
|
189 | | -Don't forget to replace the contents of this readme with your own! |
| 148 | +This makes a model file fully self-contained — it can be shared across projects or used in scripts as long as the same measurements exist in the target images. |
190 | 149 |
|
| 150 | +--- |
191 | 151 |
|
192 | | -## Getting help |
| 152 | +## Tips |
193 | 153 |
|
194 | | -For questions about QuPath and/or creating new extensions, please use the forum at https://forum.image.sc/tag/qupath |
| 154 | +- **More training data is better.** Collect annotations across multiple images to cover the range of imaging conditions in your dataset. |
| 155 | +- **Start with Top-N = 0** to see the full importance ranking in the log, then re-train with a smaller Top-N to reduce overfitting on noisy measurements. |
| 156 | +- **Conflicting annotations** (a detection covered by two different classes) are silently discarded. If you see unexpectedly low training counts, check for overlapping annotations of different classes. |
| 157 | +- The final model is trained from scratch on the reduced feature set, so Rounds and Max depth apply to both the ranking pass and the final pass. |
195 | 158 |
|
196 | | ------- |
| 159 | +--- |
197 | 160 |
|
198 | 161 | ## License |
199 | 162 |
|
200 | | -This is just a template, you're free to use it however you like. |
201 | | -You can treat the contents of *this repository only* as being under [the Unlicense](https://unlicense.org) (except for the Gradle wrapper, which has its own license included). |
| 163 | +GPL v3 — consistent with QuPath's own license. |
| 164 | + |
| 165 | +--- |
202 | 166 |
|
203 | | -If you use it to create a new QuPath extension, I'd strongly encourage you to select a suitable open-source license for the extension. |
| 167 | +## Acknowledgements |
204 | 168 |
|
205 | | -Note that *QuPath itself* is available under the GPL, so you do have to abide by those terms: see https://github.com/qupath/qupath for more. |
| 169 | +- [QuPath](https://github.com/qupath/qupath) — Pete Bankhead and contributors |
| 170 | +- [XGBoost4J](https://xgboost.readthedocs.io/en/stable/jvm/index.html) — DMLC / XGBoost contributors |
0 commit comments