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

Commit 6adde86

Browse files
authored
colorMap (#390)
* chore: change colorMap to have only the length of the number of ROIs * fix: bug preventing over 32767 ROIs
1 parent bc5437a commit 6adde86

File tree

8 files changed

+68
-93
lines changed

8 files changed

+68
-93
lines changed

src/roi/__tests__/fromMask.test.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,8 @@ test('6x6 mask, allowCorners true', () => {
164164
);
165165
});
166166

167-
test('exceed max number of ROIs error', () => {
168-
const size = 513;
167+
test('no way to exceed max number of ROIs error', () => {
168+
const size = 4097;
169169
const mask = new Mask(size, size);
170170
let pos = true;
171171
for (let row = 0; row < size; row++) {
@@ -174,7 +174,10 @@ test('exceed max number of ROIs error', () => {
174174
pos = !pos;
175175
}
176176
}
177-
expect(() => {
178-
fromMask(mask);
179-
}).toThrow(/too many regions of interest/);
177+
178+
const roiMap = fromMask(mask);
179+
//@ts-expect-error This is a test
180+
expect(roiMap.map.nbNegative).toBe(8392704);
181+
//@ts-expect-error This is a test
182+
expect(roiMap.map.nbPositive).toBe(8392705);
180183
});

src/roi/colorRois.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { Image } from '..';
22

33
import { RoiKind } from './getRois';
4-
import { colorMapCenter } from './utils/constants';
54
import { getColorMap } from './utils/getColorMap';
65

76
import { RoiMapManager } from '.';
@@ -63,7 +62,7 @@ export function colorRois(
6362
const data32 = new Uint32Array(image.getRawImage().data.buffer);
6463

6564
for (let index = 0; index < image.size; index++) {
66-
data32[index] = colorMap[map.data[index] + colorMapCenter];
65+
data32[index] = colorMap[map.data[index] + map.nbNegative];
6766
}
6867

6968
return image;

src/roi/fromMask.ts

Lines changed: 33 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import { Mask } from '..';
22
import { assert } from '../utils/validators/assert';
33

44
import { RoiMapManager } from './RoiMapManager';
5-
import { maxNumberRois, maxRoiId } from './utils/constants';
65

76
export interface FromMaskOptions {
87
/**
@@ -24,10 +23,10 @@ export function fromMask(
2423
): RoiMapManager {
2524
const { allowCorners = false } = options;
2625

27-
const MAX_ARRAY = maxNumberRois - 1; // 65535 should be enough for most of the cases
26+
const MAX_TODO_ARRAY_FILTER = 65535; // 65535 should be enough for most of the cases
2827

29-
const maxPositiveId = maxRoiId - 1;
30-
const maxNegativeId = -maxRoiId;
28+
const MAX_POSITIVE_ID = 2 ** 31 - 1;
29+
const MAX_NEGATIVE_ID = -(2 ** 31 - 1);
3130

3231
// based on a binary image we will create plenty of small images
3332
const data = new Int32Array(mask.size); // maxValue: maxPositiveId, minValue: maxNegativeId
@@ -36,8 +35,8 @@ export function fromMask(
3635
let positiveId = 0;
3736
let negativeId = 0;
3837

39-
const columnToProcess = new Uint16Array(maxNumberRois);
40-
const rowToProcess = new Uint16Array(maxNumberRois);
38+
const columnToProcess = new Uint16Array(MAX_TODO_ARRAY_FILTER + 1);
39+
const rowToProcess = new Uint16Array(MAX_TODO_ARRAY_FILTER + 1);
4140

4241
for (let column = 0; column < mask.width; column++) {
4342
for (let row = 0; row < mask.height; row++) {
@@ -55,14 +54,14 @@ export function fromMask(
5554
const targetState = mask.getBit(column, row);
5655
const id = targetState ? ++positiveId : --negativeId;
5756
assert(
58-
positiveId <= maxPositiveId && negativeId >= maxNegativeId,
57+
positiveId <= MAX_POSITIVE_ID && negativeId >= MAX_NEGATIVE_ID,
5958
'too many regions of interest',
6059
);
6160
columnToProcess[0] = column;
6261
rowToProcess[0] = row;
6362
while (from <= to) {
64-
const currentColumn = columnToProcess[from & MAX_ARRAY];
65-
const currentRow = rowToProcess[from & MAX_ARRAY];
63+
const currentColumn = columnToProcess[from & MAX_TODO_ARRAY_FILTER];
64+
const currentRow = rowToProcess[from & MAX_TODO_ARRAY_FILTER];
6665
data[currentRow * mask.width + currentColumn] = id;
6766
// need to check all around mask pixel
6867
if (
@@ -72,9 +71,9 @@ export function fromMask(
7271
) {
7372
// LEFT
7473
to++;
75-
columnToProcess[to & MAX_ARRAY] = currentColumn - 1;
76-
rowToProcess[to & MAX_ARRAY] = currentRow;
77-
data[currentRow * mask.width + currentColumn - 1] = maxNegativeId;
74+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn - 1;
75+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow;
76+
data[currentRow * mask.width + currentColumn - 1] = MAX_NEGATIVE_ID;
7877
}
7978
if (
8079
currentRow > 0 &&
@@ -83,9 +82,9 @@ export function fromMask(
8382
) {
8483
// TOP
8584
to++;
86-
columnToProcess[to & MAX_ARRAY] = currentColumn;
87-
rowToProcess[to & MAX_ARRAY] = currentRow - 1;
88-
data[(currentRow - 1) * mask.width + currentColumn] = maxNegativeId;
85+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn;
86+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow - 1;
87+
data[(currentRow - 1) * mask.width + currentColumn] = MAX_NEGATIVE_ID;
8988
}
9089
if (
9190
currentColumn < mask.width - 1 &&
@@ -94,9 +93,9 @@ export function fromMask(
9493
) {
9594
// RIGHT
9695
to++;
97-
columnToProcess[to & MAX_ARRAY] = currentColumn + 1;
98-
rowToProcess[to & MAX_ARRAY] = currentRow;
99-
data[currentRow * mask.width + currentColumn + 1] = maxNegativeId;
96+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn + 1;
97+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow;
98+
data[currentRow * mask.width + currentColumn + 1] = MAX_NEGATIVE_ID;
10099
}
101100
if (
102101
currentRow < mask.height - 1 &&
@@ -105,9 +104,9 @@ export function fromMask(
105104
) {
106105
// BOTTOM
107106
to++;
108-
columnToProcess[to & MAX_ARRAY] = currentColumn;
109-
rowToProcess[to & MAX_ARRAY] = currentRow + 1;
110-
data[(currentRow + 1) * mask.width + currentColumn] = maxNegativeId;
107+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn;
108+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow + 1;
109+
data[(currentRow + 1) * mask.width + currentColumn] = MAX_NEGATIVE_ID;
111110
}
112111
if (allowCorners) {
113112
if (
@@ -118,10 +117,10 @@ export function fromMask(
118117
) {
119118
// TOP LEFT
120119
to++;
121-
columnToProcess[to & MAX_ARRAY] = currentColumn - 1;
122-
rowToProcess[to & MAX_ARRAY] = currentRow - 1;
120+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn - 1;
121+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow - 1;
123122
data[(currentRow - 1) * mask.width + currentColumn - 1] =
124-
maxNegativeId;
123+
MAX_NEGATIVE_ID;
125124
}
126125
if (
127126
currentColumn < mask.width - 1 &&
@@ -131,10 +130,10 @@ export function fromMask(
131130
) {
132131
// TOP RIGHT
133132
to++;
134-
columnToProcess[to & MAX_ARRAY] = currentColumn + 1;
135-
rowToProcess[to & MAX_ARRAY] = currentRow - 1;
133+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn + 1;
134+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow - 1;
136135
data[(currentRow - 1) * mask.width + currentColumn + 1] =
137-
maxNegativeId;
136+
MAX_NEGATIVE_ID;
138137
}
139138
if (
140139
currentColumn > 0 &&
@@ -144,10 +143,10 @@ export function fromMask(
144143
) {
145144
// BOTTOM LEFT
146145
to++;
147-
columnToProcess[to & MAX_ARRAY] = currentColumn - 1;
148-
rowToProcess[to & MAX_ARRAY] = currentRow + 1;
146+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn - 1;
147+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow + 1;
149148
data[(currentRow + 1) * mask.width + currentColumn - 1] =
150-
maxNegativeId;
149+
MAX_NEGATIVE_ID;
151150
}
152151
if (
153152
currentColumn < mask.width - 1 &&
@@ -157,17 +156,17 @@ export function fromMask(
157156
) {
158157
// BOTTOM RIGHT
159158
to++;
160-
columnToProcess[to & MAX_ARRAY] = currentColumn + 1;
161-
rowToProcess[to & MAX_ARRAY] = currentRow + 1;
159+
columnToProcess[to & MAX_TODO_ARRAY_FILTER] = currentColumn + 1;
160+
rowToProcess[to & MAX_TODO_ARRAY_FILTER] = currentRow + 1;
162161
data[(currentRow + 1) * mask.width + currentColumn + 1] =
163-
maxNegativeId;
162+
MAX_NEGATIVE_ID;
164163
}
165164
}
166165

167166
from++;
168167

169168
assert(
170-
to - from <= MAX_ARRAY,
169+
to - from <= MAX_TODO_ARRAY_FILTER,
171170
'fromMask can not finish, the array to manage internal data is not big enough.' +
172171
'You could improve mask by changing MAX_ARRAY',
173172
);
Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,17 @@
11
import { getBinaryMap } from '../colorMaps/getBinaryMap';
2-
import { maxNumberRois, colorMapCenter } from '../constants';
32
import { getColorMap } from '../getColorMap';
43

54
test('default options', () => {
65
const colorMap = getBinaryMap({ nbNegative: 1, nbPositive: 1 });
7-
8-
expect(colorMap).toHaveLength(maxNumberRois);
9-
expect(colorMap[colorMapCenter - 1]).toBe(0xff0000ff); // red
10-
expect(colorMap[colorMapCenter + 1]).toBe(0xff00ff00); // green
6+
expect(Array.from(colorMap)).toStrictEqual([4278190335, 0, 4278255360]);
117
});
128

139
test('binary, BW', () => {
1410
const colorMap = getColorMap({
1511
nbNegative: 1,
1612
nbPositive: 1,
1713
});
18-
19-
expect(colorMap).toHaveLength(maxNumberRois);
20-
expect(colorMap[colorMapCenter - 1]).toBe(0xff0000ff); // red
21-
expect(colorMap[colorMapCenter + 1]).toBe(0xff00ff00); // green
14+
expect(Array.from(colorMap)).toStrictEqual([4278190335, 0, 4278255360]);
2215
});
2316

2417
test('binary, WHITE', () => {
@@ -27,9 +20,7 @@ test('binary, WHITE', () => {
2720
nbPositive: 1,
2821
roiKind: 'white',
2922
});
30-
31-
expect(colorMap[colorMapCenter - 1]).toBe(0);
32-
expect(colorMap[colorMapCenter + 1]).toBe(0xff00ff00);
23+
expect(Array.from(colorMap)).toStrictEqual([0, 0, 4278255360]);
3324
});
3425

3526
test('binary, BLACK', () => {
@@ -38,9 +29,7 @@ test('binary, BLACK', () => {
3829
nbPositive: 1,
3930
roiKind: 'black',
4031
});
41-
42-
expect(colorMap[colorMapCenter - 1]).toBe(0xff0000ff);
43-
expect(colorMap[colorMapCenter + 1]).toBe(0);
32+
expect(Array.from(colorMap)).toStrictEqual([4278190335, 0, 0]);
4433
});
4534

4635
test('SATURATION, 1 negative and 1 positive ROIs', () => {
@@ -49,9 +38,7 @@ test('SATURATION, 1 negative and 1 positive ROIs', () => {
4938
nbNegative: 1,
5039
nbPositive: 1,
5140
});
52-
53-
expect(colorMap[colorMapCenter - 1]).toBe(0xffff0000); // blue
54-
expect(colorMap[colorMapCenter + 1]).toBe(0xff0000ff); // red
41+
expect(Array.from(colorMap)).toStrictEqual([4294901760, 0, 4278190335]);
5542
});
5643

5744
test('RAINBOW, 1 negative and 2 positive ROIs, WHITE', () => {
@@ -61,10 +48,7 @@ test('RAINBOW, 1 negative and 2 positive ROIs, WHITE', () => {
6148
nbPositive: 2,
6249
roiKind: 'white',
6350
});
64-
65-
expect(colorMap[colorMapCenter - 1]).toBe(0); // transparent
66-
expect(colorMap[colorMapCenter + 1]).toBe(0xff0000ff); // red
67-
expect(colorMap[colorMapCenter + 2]).toBe(0xffffff00); // turquoise
51+
expect(Array.from(colorMap)).toStrictEqual([0, 0, 4278190335, 4294967040]);
6852
});
6953

7054
test('RAINBOW, 1 negative and 2 positive ROIs, BLACK', () => {
@@ -74,21 +58,17 @@ test('RAINBOW, 1 negative and 2 positive ROIs, BLACK', () => {
7458
nbPositive: 2,
7559
roiKind: 'black',
7660
});
77-
78-
expect(colorMap[colorMapCenter - 1]).toBe(0xff0000ff);
79-
expect(colorMap[colorMapCenter + 1]).toBe(0);
80-
expect(colorMap[colorMapCenter + 2]).toBe(0);
61+
expect(Array.from(colorMap)).toStrictEqual([4278190335, 0, 0, 0]);
8162
});
8263

8364
test('RAINBOW, 1 negative and 1 positive ROIs, BW', () => {
8465
const colorMap = getColorMap({
8566
mode: 'rainbow',
86-
nbNegative: 1,
67+
nbNegative: 2,
8768
nbPositive: 1,
8869
roiKind: 'bw',
8970
});
90-
91-
expect(colorMap[colorMapCenter - 1]).toBe(0xff0000ff);
92-
expect(colorMap[colorMapCenter + 1]).toBe(0xffffff00);
93-
expect(colorMap[colorMapCenter + 2]).toBe(0);
71+
expect(Array.from(colorMap)).toStrictEqual([
72+
4278190335, 4278255360, 0, 4294901760,
73+
]);
9474
});

src/roi/utils/colorMaps/getBinaryMap.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { RoiKind } from '../../getRois';
2-
import { maxNumberRois, colorMapCenter } from '../constants';
32
import { hsvToRgb } from '../hsvToRgb';
43
import { rgbToNumber } from '../rgbToNumber';
54

@@ -47,20 +46,20 @@ export function getBinaryMap(options: GetBinaryMapOptions): Uint32Array {
4746
roiKind = 'bw',
4847
} = options;
4948

50-
const colorMap = new Uint32Array(maxNumberRois);
49+
const colorMap = new Uint32Array(nbNegative + nbPositive + 1);
5150

5251
// negative values
5352
if (roiKind === 'bw' || roiKind === 'black') {
54-
for (let i = colorMapCenter - nbNegative; i < colorMapCenter; i++) {
53+
for (let i = -nbNegative; i < 0; i++) {
5554
const hsv = [blackHue, 255, 255];
56-
colorMap[i] = rgbToNumber(hsvToRgb(hsv));
55+
colorMap[i + nbNegative] = rgbToNumber(hsvToRgb(hsv));
5756
}
5857
}
5958
if (roiKind === 'bw' || roiKind === 'white') {
6059
// positive values
61-
for (let i = colorMapCenter + 1; i < colorMapCenter + 1 + nbPositive; i++) {
60+
for (let i = 1; i <= nbPositive; i++) {
6261
const hsv = [whiteHue, 255, 255];
63-
colorMap[i] = rgbToNumber(hsvToRgb(hsv));
62+
colorMap[i + nbNegative] = rgbToNumber(hsvToRgb(hsv));
6463
}
6564
}
6665

src/roi/utils/colorMaps/getRainbowMap.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { RoiKind } from '../../getRois';
2-
import { maxNumberRois, colorMapCenter } from '../constants';
32
import { hsvToRgb } from '../hsvToRgb';
43
import { rgbToNumber } from '../rgbToNumber';
54

@@ -27,7 +26,7 @@ export interface GetRainbowMapOptions {
2726
export function getRainbowMap(options: GetRainbowMapOptions): Uint32Array {
2827
const { nbNegative, nbPositive, roiKind = 'bw' } = options;
2928

30-
const colorMap = new Uint32Array(maxNumberRois);
29+
const colorMap = new Uint32Array(nbNegative + nbPositive + 1);
3130

3231
const hueRange = 360;
3332

@@ -53,17 +52,17 @@ export function getRainbowMap(options: GetRainbowMapOptions): Uint32Array {
5352
// negative values
5453
let hue = 0;
5554
if (roiKind === 'bw' || roiKind === 'black') {
56-
for (let i = colorMapCenter - nbNegative; i < colorMapCenter; i++) {
55+
for (let i = -nbNegative; i < 0; i++) {
5756
const hsv = [hue, 255, 255];
58-
colorMap[i] = rgbToNumber(hsvToRgb(hsv));
57+
colorMap[i + nbNegative] = rgbToNumber(hsvToRgb(hsv));
5958
hue += step;
6059
}
6160
}
6261
// positive values
6362
if (roiKind === 'bw' || roiKind === 'white') {
64-
for (let i = colorMapCenter + 1; i < colorMapCenter + 1 + nbPositive; i++) {
63+
for (let i = 1; i <= nbPositive; i++) {
6564
const hsv = [hue, 255, 255];
66-
colorMap[i] = rgbToNumber(hsvToRgb(hsv));
65+
colorMap[i + nbNegative] = rgbToNumber(hsvToRgb(hsv));
6766
hue += step;
6867
}
6968
}

0 commit comments

Comments
 (0)