Skip to content

docs: write a tutorial on stack roi average extraction #116

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 126 additions & 0 deletions docs/Tutorials/Finding ROI average in Stack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
The point of this tutorial is to show how to decode a stack of images and how to do some basic analysis with it.

## Synopsis

This tutorial demonstrates how to use ImageJS to decode a TIFF stack of images with similar properties and analyze frame-by-frame changes, particularly focusing on regions of interest (ROIs) and their changes in pulsar images.

- We read and decode the TIFF stack.
- Then, utilizing the `maxImage()` function, we get the image with the maximum pixel values across the stack.
- We identify regions of interest from the maximum value image. First we segment an image using a global threshold algorithm ([`otsu`](https://en.wikipedia.org/wiki/Otsu%27s_method 'wikipedia link on otsu thresholding') algorithm by default). From obtained binary image we get coordinates of ROIs.
- Finally we compute the average pixel value for each ROI across all images in the stack. We store results of each ROI across all the images in the stack.

We can use this data to analyze changes in intensity over time or compare changes in its position.

```ts
import * as fs from 'node:fs';

const buffer = fs.readFileSync('./path/to/file.tiff');
const stack = decodeStack(buffer);

let maxValueImage = stack.maxImage();
//We will segment image through
// `threshold()` to find ROIs,
//therefore color model has to be "GREY".
if (maxValueImage.colorModel !== 'GREY') {
maxValueImage = maxValueImage.grey();
}
const maxValueMask = maxValueImage.threshold();
const roiMap = fromMask(maxValueMask);
//Provides all the regions of interest.
const rois = roiMap.getRois();
const stackGrays = new Map<number, number[][]>();
for (const roi of rois) {
const stackAvgs = [];
const roiPoints = roi.absolutePoints;
for (const image of stack) {
const avgValue = image.mean({ points: roiPoints });
stackAvgs.push(avgValue);
}
stackGrays.set(roi.id, stackAvgs);
}
```

Here is a more detailed review of these steps.

## Introduction

ImageJS has the ability to decode a TIFF stack of images. A TIFF stack is a TIFF file that contains multiple images. In our specific case here, we have a stack of pulsar kind of images. They represent dynamic changes that happen to regions of interest frame-by-frame.
We can use ImageJS to figure out when the region is visible and when it isn't by looking at the average value of said region compared to its maximum value in the stack.

Let's go step by step together to figure out how it works.

## Decode the Stack

Just like any image, our stack needs to be decoded for us to work with data.

```ts
import * as fs from 'node:fs';

const buffer = fs.readFileSync('/path/to/file.tiff');
const stack = decodeStack(buffer);
```

:::warning
`Stack` class works only with images that share same properties. Particularly, values for [bit depth](../Glossary.md#bit-depth 'internal link on bit depth'), [color model](../Glossary.md#color-model 'internal link on color model'), width and height must be the same.
:::

## Find the image with maximum values:

Stack class has a function called `maxImage()`. It will give us the maximum value of each pixel throughout the stack. In our particular example we use `maxImage()`. The reason for it is that we want to look at the evaluation of pulsing particles. Therefore we need to detect all the appeared particles throughout the stack. We will use this image as a reference for all other images to locate their ROIs.

```ts
const maxValueImage = stack.maxImage();
```

![Image](./images/stackAvg/maxImage.png)

## Locate ROIs

From our `maxValueImage` we can find all regions of interest. To be precise we need their coordinates to apply them to other images.

```ts
//We will segment image through
// `threshold()` to find ROIs,
//therefore color model has to be "GREY".
if (maxValueImage.colorModel !== 'GREY') {
maxValueImage = maxValueImage.grey();
}
const maxValueMask = maxValueImage.threshold();
const roiMap = fromMask(maxValueMask);
```

![Mask](./images/stackAvg/maxMask.png)

```ts
//Provides all the regions of interest.
const rois = roiMap.getRois();
```

![Painted ROIs](./images/stackAvg/paintedROIs.png)

## Find average value of each ROI on each image

After we found all the ROIs on the `maxValueImage`, we take each region and check its average intensity on every image in the stack.
It will look something like this:

```ts
const stackGrays = new Map<number, number[][]>();
for (const roi of rois) {
const stackAvgs = [];
const roiPoints = roi.absolutePoints;
for (const image of stack) {
const avgValue = image.mean({ points: roiPoints });
//Gets value from one channel since it is grayscaled.
stackAvgs.push(avgValue);
}
stackGrays.set(roi.id, stackAvgs);
}
```

This will create a map where each key is an ROI ID, and each value is an array of average ROI intensities across the image stack.
This way we can take a look at the changes in intensity of ROI from one image to another.
For instance here we have a graph of intensity of region with ID equal to 9.
We can see that there is a rising pulse between images 1 and 4, but then it disappears.

![ROI 9](./images/stackAvg/ROI9.png)
![ROI 9 graph](./images/stackAvg/graphROI9.png)
Binary file added docs/Tutorials/images/stackAvg/ROI9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Tutorials/images/stackAvg/graphROI9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Tutorials/images/stackAvg/maxImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Tutorials/images/stackAvg/maxMask.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/Tutorials/images/stackAvg/paintedROIs.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ grayscaled
exif
electrometric
Grayscaling

Avgs

Loading