Skip to content

Commit e4d5ff9

Browse files
Merge branch 'main' into 246-new-roi-property-ellipse
2 parents 7e48ea3 + fc6c3d5 commit e4d5ff9

File tree

1 file changed

+261
-31
lines changed

1 file changed

+261
-31
lines changed

src/roi/Roi.ts

Lines changed: 261 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,50 @@ export class Roi {
163163
/**
164164
* Return an array of ROIs IDs that are included in the current ROI.
165165
* This will be useful to know if there are some holes in the ROI.
166+
*
167+
* @returns internalIDs
166168
*/
167169
get internalIDs() {
168170
return this.#getComputed('internalIDs', () => {
169-
return getInternalIDs(this);
171+
let internal = [this.id];
172+
let roiMap = this.map;
173+
let data = roiMap.data;
174+
175+
if (this.height > 2) {
176+
for (let column = 0; column < this.width; column++) {
177+
let target = this.computeIndex(0, column);
178+
if (internal.includes(data[target])) {
179+
let id = data[target + roiMap.width];
180+
if (!internal.includes(id) && !this.boxIDs.includes(id)) {
181+
internal.push(id);
182+
}
183+
}
184+
}
185+
}
186+
187+
let array = new Array(4);
188+
for (let column = 1; column < this.width - 1; column++) {
189+
for (let row = 1; row < this.height - 1; row++) {
190+
let target = this.computeIndex(row, column);
191+
if (internal.includes(data[target])) {
192+
// we check if one of the neighbour is not yet in
193+
194+
array[0] = data[target - 1];
195+
array[1] = data[target + 1];
196+
array[2] = data[target - roiMap.width];
197+
array[3] = data[target + roiMap.width];
198+
199+
for (let i = 0; i < 4; i++) {
200+
let id = array[i];
201+
if (!internal.includes(id) && !this.boxIDs.includes(id)) {
202+
internal.push(id);
203+
}
204+
}
205+
}
206+
}
207+
}
208+
209+
return internal;
170210
});
171211
}
172212
//TODO externalIds should be an array of {id: number, length: number}
@@ -176,12 +216,91 @@ export class Roi {
176216
*/
177217
get externalBorders(): Border[] {
178218
return this.#getComputed('externalBorders', () => {
179-
return this.getExternalBorders();
219+
// take all the borders and remove the internal one ...
220+
let borders = this.borders;
221+
222+
let externalBorders = [];
223+
let externalIDs = [];
224+
let internals = this.internalIDs;
225+
226+
for (let border of borders) {
227+
if (!internals.includes(border.connectedID)) {
228+
const element: Border = {
229+
connectedID: border.connectedID,
230+
length: border.length,
231+
};
232+
externalIDs.push(element.connectedID);
233+
externalBorders.push(element);
234+
}
235+
}
236+
237+
return externalBorders;
180238
});
181239
}
240+
/**
241+
* Calculates and caches the number of sides by which each pixel is touched externally
242+
*
243+
* @param roi -ROI
244+
* @returns object which tells how many pixels are exposed externally to how many sides
245+
*/
182246
get perimeterInfo() {
183247
return this.#getComputed('perimeterInfo', () => {
184-
return getPerimeterInfo(this);
248+
const roiMap = this.map;
249+
const data = roiMap.data;
250+
let one = 0;
251+
let two = 0;
252+
let three = 0;
253+
let four = 0;
254+
let externalIDs = this.externalBorders.map(
255+
(element) => element.connectedID,
256+
);
257+
for (let column = 0; column < this.width; column++) {
258+
for (let row = 0; row < this.height; row++) {
259+
let target = this.computeIndex(row, column);
260+
if (data[target] === this.id) {
261+
let nbAround = 0;
262+
if (column === 0) {
263+
nbAround++;
264+
} else if (externalIDs.includes(data[target - 1])) {
265+
nbAround++;
266+
}
267+
268+
if (column === roiMap.width - 1) {
269+
nbAround++;
270+
} else if (externalIDs.includes(data[target + 1])) {
271+
nbAround++;
272+
}
273+
274+
if (row === 0) {
275+
nbAround++;
276+
} else if (externalIDs.includes(data[target - roiMap.width])) {
277+
nbAround++;
278+
}
279+
280+
if (row === roiMap.height - 1) {
281+
nbAround++;
282+
} else if (externalIDs.includes(data[target + roiMap.width])) {
283+
nbAround++;
284+
}
285+
switch (nbAround) {
286+
case 1:
287+
one++;
288+
break;
289+
case 2:
290+
two++;
291+
break;
292+
case 3:
293+
three++;
294+
break;
295+
case 4:
296+
four++;
297+
break;
298+
default:
299+
}
300+
}
301+
}
302+
}
303+
return { one, two, three, four };
185304
});
186305
}
187306

@@ -225,7 +344,58 @@ export class Roi {
225344
}
226345
get boxIDs() {
227346
return this.#getComputed('boxIDs', () => {
228-
return getBoxIDs(this);
347+
let surroundingIDs = new Set<number>(); // allows to get a unique list without indexOf
348+
349+
const roiMap = this.map;
350+
const data = roiMap.data;
351+
352+
// we check the first line and the last line
353+
for (let row of [0, this.height - 1]) {
354+
for (let column = 0; column < this.width; column++) {
355+
let target = this.computeIndex(row, column);
356+
if (
357+
column - this.origin.column > 0 &&
358+
data[target] === this.id &&
359+
data[target - 1] !== this.id
360+
) {
361+
let value = data[target - 1];
362+
surroundingIDs.add(value);
363+
}
364+
if (
365+
roiMap.width - column - this.origin.column > 1 &&
366+
data[target] === this.id &&
367+
data[target + 1] !== this.id
368+
) {
369+
let value = data[target + 1];
370+
surroundingIDs.add(value);
371+
}
372+
}
373+
}
374+
375+
// we check the first column and the last column
376+
for (let column of [0, this.width - 1]) {
377+
for (let row = 0; row < this.height; row++) {
378+
let target = this.computeIndex(row, column);
379+
if (
380+
row - this.origin.row > 0 &&
381+
data[target] === this.id &&
382+
data[target - roiMap.width] !== this.id
383+
) {
384+
let value = data[target - roiMap.width];
385+
surroundingIDs.add(value);
386+
}
387+
if (
388+
roiMap.height - row - this.origin.row > 1 &&
389+
data[target] === this.id &&
390+
data[target + roiMap.width] !== this.id
391+
) {
392+
let value = data[target + roiMap.width];
393+
surroundingIDs.add(value);
394+
}
395+
}
396+
}
397+
398+
return Array.from(surroundingIDs); // the selection takes the whole rectangle
229399
});
230400
}
231401

@@ -252,44 +422,101 @@ export class Roi {
252422
/**
253423
* Number of holes in the ROI and their total surface.
254424
* Used to calculate fillRatio.
425+
*
426+
* @returns the surface of holes in ROI
255427
*/
256428
get holesInfo() {
257429
return this.#getComputed('holesInfo', () => {
258-
return getHolesInfo(this);
259-
});
260-
}
261-
262-
getExternalBorders(): Border[] {
263-
// take all the borders and remove the internal one ...
264-
let borders = this.borders;
265-
266-
let externalBorders = [];
267-
let externalIDs = [];
268-
let internals = this.internalIDs;
269-
270-
for (let border of borders) {
271-
if (!internals.includes(border.connectedID)) {
272-
const element: Border = {
273-
connectedID: border.connectedID,
274-
length: border.length,
275-
};
276-
externalIDs.push(element.connectedID);
277-
externalBorders.push(element);
430+
let surface = 0;
431+
const data = this.map.data;
432+
for (let column = 1; column < this.width - 1; column++) {
433+
for (let row = 1; row < this.height - 1; row++) {
434+
let target = this.computeIndex(row, column);
435+
if (
436+
this.internalIDs.includes(data[target]) &&
437+
data[target] !== this.id
438+
) {
439+
surface++;
440+
}
441+
}
278442
}
279-
}
280-
281-
return externalBorders;
443+
return {
444+
number: this.internalIDs.length - 1,
445+
surface,
446+
};
447+
});
282448
}
283449

284450
/**
285-
*Calculates the borders' IDs and lengths
451+
*Calculates and caches border's length and their IDs
452+
*
453+
* @returns borders' length and their IDs
286454
*/
287455
get borders() {
288456
return this.#getComputed('borders', () => {
289-
return getBorders(this);
457+
const roiMap = this.map;
458+
const data = roiMap.data;
459+
let surroudingIDs = new Set<number>(); // allows to get a unique list without indexOf
460+
let surroundingBorders = new Map();
461+
let visitedData = new Set();
462+
let dx = [+1, 0, -1, 0];
463+
let dy = [0, +1, 0, -1];
464+
465+
for (
466+
let column = this.origin.column;
467+
column <= this.origin.column + this.width;
468+
column++
469+
) {
470+
for (
471+
let row = this.origin.row;
472+
row <= this.origin.row + this.height;
473+
row++
474+
) {
475+
let target = column + row * roiMap.width;
476+
if (data[target] === this.id) {
477+
for (let dir = 0; dir < 4; dir++) {
478+
let newX = column + dx[dir];
479+
let newY = row + dy[dir];
480+
if (
481+
newX >= 0 &&
482+
newY >= 0 &&
483+
newX < roiMap.width &&
484+
newY < roiMap.height
485+
) {
486+
let neighbour = newX + newY * roiMap.width;
487+
488+
if (
489+
data[neighbour] !== this.id &&
490+
!visitedData.has(neighbour)
491+
) {
492+
visitedData.add(neighbour);
493+
surroudingIDs.add(data[neighbour]);
494+
let surroundingBorder = surroundingBorders.get(
495+
data[neighbour],
496+
);
497+
if (!surroundingBorder) {
498+
surroundingBorders.set(data[neighbour], 1);
499+
} else {
500+
surroundingBorders.set(
501+
data[neighbour],
502+
++surroundingBorder,
503+
);
504+
}
505+
}
506+
}
507+
}
508+
}
509+
}
510+
}
511+
let id: number[] = Array.from(surroudingIDs);
512+
return id.map((id) => {
513+
return {
514+
connectedID: id,
515+
length: surroundingBorders.get(id),
516+
};
517+
});
290518
});
291519
}
292-
293520
/**
294521
* Calculates fill ratio of the ROI
295522
*/
@@ -366,7 +593,7 @@ export class Roi {
366593

367594
get centroid() {
368595
return this.#getComputed('centroid', () => {
369-
const roiMap = this.getMap();
596+
const roiMap = this.map;
370597
const data = roiMap.data;
371598
let sumColumn = 0;
372599
let sumRow = 0;
@@ -400,6 +627,7 @@ export class Roi {
400627
return this.#computed[property] as Computed[T];
401628
}
402629
//TODO Make this private
630+
403631
/**
404632
* Calculates the correct index on the map of ROI
405633
*
@@ -412,6 +640,7 @@ export class Roi {
412640
}
413641
}
414642

643+
415644
/**
416645
*
417646
* @param roi -ROI
@@ -754,3 +983,4 @@ function getEllipse(roi: Roi, scale: number): Ellipse {
754983
surface: ellipseSurface,
755984
};
756985
}
986+

0 commit comments

Comments
 (0)