Skip to content
This repository was archived by the owner on Jul 26, 2025. It is now read-only.

Commit f37d4e4

Browse files
authored
Stack: add sum and histogram (#420)
* feat: basic Stack API * feat(Stack): implement constructor Closes: #413 * feat(Stack): implement functions to get values * feat(Stack): implement minImage * feat(Stack): implement maxImage * feat(Stack): implement iterator * test(Stack): add min and max image tests * feat(Stack): implement getStackFromFolder for debug * fix(Stack): enhance Stack properties * feat(Stack): implement map and filter * feat(Stack): implement meanImage Closes: #414 * feat(Stack): implement medianImage * test: enhance stack coverage * test: fix test after merge * chore: fix eslint errors * feat(Stack): implement sum * feat(Stack): implement histogram Closes: #418 * refactor: move stack functions to subfolder * test: fix images path * fix: add check to sum * fix(stack): max nb of images for sum
1 parent 0c347c8 commit f37d4e4

19 files changed

+216
-37
lines changed

src/Stack.ts

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import { BitDepth } from 'fast-png';
22

33
import { Image } from './Image';
4-
import { maxImage } from './stack/maxImage';
5-
import { meanImage } from './stack/meanImage';
6-
import { medianImage } from './stack/medianImage';
7-
import { minImage } from './stack/minImage';
4+
import { HistogramOptions } from './compute';
5+
import { histogram } from './stack/compute/histogram';
6+
import { maxImage } from './stack/compute/maxImage';
7+
import { meanImage } from './stack/compute/meanImage';
8+
import { medianImage } from './stack/compute/medianImage';
9+
import { minImage } from './stack/compute/minImage';
10+
import { sum } from './stack/compute/sum';
811
import {
912
checkImagesValid,
1013
verifySameDimensions,
@@ -64,7 +67,7 @@ export class Stack {
6467
}
6568

6669
/**
67-
* Clone a stack.
70+
* Clone a stack. The images are a copy of the original images.
6871
* @returns A new stack with the same images.
6972
*/
7073
public clone(): Stack {
@@ -157,10 +160,22 @@ export class Stack {
157160
}
158161

159162
/**
160-
* Get the global histogram of the stack.
163+
* Return a 16 bits depth image containing the sum values of all the images in the stack for
164+
* each pixel.
165+
* @returns The sum image.
161166
*/
162-
// public getHistogram(): Uint32Array {}
167+
public sum(): Image {
168+
return sum(this);
169+
}
163170

171+
/**
172+
* Get the sum of all the histograms of the stack's images. If no channel is specified in the options, the images must be GREY.
173+
* @param options - Histogram options.
174+
* @returns The histogram of the stack.
175+
*/
176+
public histogram(options: HistogramOptions = {}): Uint32Array {
177+
return histogram(this, options);
178+
}
164179
/**
165180
* Align all the images of the stack on the image at the given index.
166181
* @param refIndex - The index of the reference image.
158 Bytes
Loading
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { Stack } from '../../../Stack';
2+
3+
test('two grey images, bitsDepth = 8', () => {
4+
const image = testUtils.createGreyImage([[1, 2, 3, 4]]);
5+
const stack = new Stack([image, image]);
6+
7+
const histogram = stack.histogram();
8+
9+
expect(histogram).toHaveLength(256);
10+
expect(histogram.subarray(0, 5)).toStrictEqual(
11+
new Uint32Array([0, 2, 2, 2, 2]),
12+
);
13+
});
14+
15+
test('two grey images, bitsDepth = 16', () => {
16+
const image1 = testUtils.createGreyImage([[1, 2, 3, 4]]);
17+
const image2 = testUtils.createGreyImage([[4, 5, 6, 7]]);
18+
const stack = new Stack([image1, image2]);
19+
20+
const histogram = stack.histogram();
21+
22+
expect(histogram).toHaveLength(256);
23+
expect(histogram.subarray(0, 8)).toStrictEqual(
24+
new Uint32Array([0, 1, 1, 1, 2, 1, 1, 1]),
25+
);
26+
});
27+
28+
test('two RGB images, channel = 2', () => {
29+
const image1 = testUtils.createRgbImage([[1, 2, 0]]);
30+
const image2 = testUtils.createRgbImage([[4, 5, 1]]);
31+
const stack = new Stack([image1, image2]);
32+
33+
const histogram = stack.histogram({ channel: 2 });
34+
35+
expect(histogram).toHaveLength(256);
36+
expect(histogram.subarray(0, 2)).toStrictEqual(new Uint32Array([1, 1]));
37+
});
38+
39+
test('two grey images, slots = 2', () => {
40+
const image1 = testUtils.createGreyImage([[1, 200, 3, 150]]);
41+
const image2 = testUtils.createGreyImage([[200, 5, 190, 7]]);
42+
const stack = new Stack([image1, image2]);
43+
44+
const histogram = stack.histogram({ slots: 2 });
45+
46+
expect(histogram).toHaveLength(2);
47+
expect(histogram).toStrictEqual(new Uint32Array([4, 4]));
48+
});

src/stack/__tests__/maxImage.test.ts renamed to src/stack/compute/__tests__/maxImage.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { join } from 'node:path';
22

3-
import { Image } from '../../Image';
4-
import { Stack } from '../../Stack';
5-
import { getStackFromFolder } from '../utils/getStackFromFolder';
3+
import { Image } from '../../../Image';
4+
import { Stack } from '../../../Stack';
5+
import { getStackFromFolder } from '../../utils/getStackFromFolder';
66

77
test('2 grey images', () => {
88
const image1 = testUtils.createGreyImage([[1, 2, 3, 4]]);
@@ -18,7 +18,7 @@ test('2 grey images', () => {
1818
});
1919

2020
test('more complex stack', () => {
21-
const folder = join(__dirname, '../../../test/img/correctColor');
21+
const folder = join(__dirname, '../../../../test/img/correctColor');
2222
const stack = getStackFromFolder(folder);
2323

2424
expect(stack.maxImage()).toMatchImageSnapshot();

src/stack/__tests__/meanImage.test.ts renamed to src/stack/compute/__tests__/meanImage.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { join } from 'node:path';
22

3-
import { Image } from '../../Image';
4-
import { Stack } from '../../Stack';
5-
import { getStackFromFolder } from '../utils/getStackFromFolder';
3+
import { Image } from '../../../Image';
4+
import { Stack } from '../../../Stack';
5+
import { getStackFromFolder } from '../../utils/getStackFromFolder';
66

77
test('2 grey images', () => {
88
const image1 = testUtils.createGreyImage([[1, 2, 3, 4]]);
@@ -31,7 +31,7 @@ test('2 RGB images', () => {
3131
});
3232

3333
test('more complex stack', () => {
34-
const folder = join(__dirname, '../../../test/img/correctColor');
34+
const folder = join(__dirname, '../../../../test/img/correctColor');
3535
const stack = getStackFromFolder(folder);
3636
expect(stack.meanImage()).toMatchImageSnapshot();
3737
});

src/stack/__tests__/medianImage.test.ts renamed to src/stack/compute/__tests__/medianImage.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { join } from 'node:path';
22

3-
import { Image } from '../../Image';
4-
import { Stack } from '../../Stack';
5-
import { getStackFromFolder } from '../utils/getStackFromFolder';
3+
import { Image } from '../../../Image';
4+
import { Stack } from '../../../Stack';
5+
import { getStackFromFolder } from '../../utils/getStackFromFolder';
66

77
test('3 grey images', () => {
88
const image1 = testUtils.createGreyImage([[1, 2, 3, 4]]);
@@ -33,7 +33,7 @@ test('3 RGB images', () => {
3333
});
3434

3535
test('more complex stack', () => {
36-
const folder = join(__dirname, '../../../test/img/correctColor');
36+
const folder = join(__dirname, '../../../../test/img/correctColor');
3737
const stack = getStackFromFolder(folder);
3838
expect(stack.medianImage()).toMatchImageSnapshot();
3939
});

0 commit comments

Comments
 (0)