image-js 0.35.1 → 0.35.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.d.ts +99 -35
- package/lib/browser/environment.js +0 -6
- package/lib/browser/worker/extend.js +0 -3
- package/lib/browser/worker/process/background.js +0 -12
- package/lib/browser/worker/worker.js +0 -17
- package/lib/image/Image.js +18 -51
- package/lib/image/compute/colorHistogram.js +0 -8
- package/lib/image/compute/countAlphaPixels.js +0 -3
- package/lib/image/compute/extendedPoints.js +2 -7
- package/lib/image/compute/histogram.js +6 -25
- package/lib/image/compute/localMaxima.js +2 -10
- package/lib/image/compute/max.js +0 -5
- package/lib/image/compute/mean.js +0 -4
- package/lib/image/compute/median.js +0 -4
- package/lib/image/compute/min.js +0 -5
- package/lib/image/compute/minimalBoundingRectangle.js +5 -18
- package/lib/image/compute/moment.js +0 -3
- package/lib/image/compute/monotoneChainConvexHull.js +0 -3
- package/lib/image/compute/points.js +0 -3
- package/lib/image/compute/relativePosition.js +2 -8
- package/lib/image/compute/sum.js +0 -5
- package/lib/image/core/bitMethods.js +0 -11
- package/lib/image/core/checkProcessable.js +0 -13
- package/lib/image/core/environment.js +0 -8
- package/lib/image/core/export.js +0 -38
- package/lib/image/core/extend.js +0 -13
- package/lib/image/core/getRGBAData.js +0 -15
- package/lib/image/core/kind.js +4 -28
- package/lib/image/core/kindNames.js +1 -0
- package/lib/image/core/load.js +1 -53
- package/lib/image/core/mediaTypes.js +0 -2
- package/lib/image/core/valueMethods.js +0 -13
- package/lib/image/extend.js +7 -83
- package/lib/image/filter/abs.js +0 -3
- package/lib/image/filter/add.js +2 -8
- package/lib/image/filter/background.js +0 -9
- package/lib/image/filter/blurFilter.js +0 -6
- package/lib/image/filter/divide.js +0 -8
- package/lib/image/filter/flipX.js +0 -5
- package/lib/image/filter/flipY.js +0 -4
- package/lib/image/filter/gaussianFilter.js +0 -5
- package/lib/image/filter/gradientFilter.js +0 -10
- package/lib/image/filter/hypotenuse.js +0 -13
- package/lib/image/filter/invert.js +0 -9
- package/lib/image/filter/level.js +1 -19
- package/lib/image/filter/medianFilter.js +0 -15
- package/lib/image/filter/multiply.js +0 -10
- package/lib/image/filter/scharrFilter.js +0 -4
- package/lib/image/filter/sobelFilter.js +0 -4
- package/lib/image/filter/subtract.js +0 -8
- package/lib/image/filter/subtractImage.js +0 -10
- package/lib/image/internal/checks.js +1 -9
- package/lib/image/internal/clamp.js +0 -1
- package/lib/image/internal/copy.js +0 -2
- package/lib/image/internal/copyAlphaChannel.js +0 -1
- package/lib/image/internal/getImageParameters.js +0 -1
- package/lib/image/internal/getOutputImage.js +1 -13
- package/lib/image/model/model.js +1 -1
- package/lib/image/morphology/blackHat.js +0 -5
- package/lib/image/morphology/close.js +0 -5
- package/lib/image/morphology/dilate.js +0 -40
- package/lib/image/morphology/erode.js +0 -40
- package/lib/image/morphology/morphologicalGradient.js +0 -5
- package/lib/image/morphology/open.js +0 -5
- package/lib/image/morphology/topHat.js +0 -5
- package/lib/image/operator/cannyEdge.js +0 -3
- package/lib/image/operator/convolution.js +9 -34
- package/lib/image/operator/convolutionSeparable.js +0 -10
- package/lib/image/operator/extract.js +4 -13
- package/lib/image/operator/floodFill.js +2 -15
- package/lib/image/operator/getSeparatedKernel.js +0 -2
- package/lib/image/operator/paintLabels.js +8 -19
- package/lib/image/operator/paintMasks.js +2 -12
- package/lib/image/operator/paintPoints.js +0 -9
- package/lib/image/operator/paintPolygon.js +1 -15
- package/lib/image/operator/paintPolygons.js +2 -6
- package/lib/image/operator/paintPolyline.js +0 -7
- package/lib/image/operator/paintPolylines.js +1 -5
- package/lib/image/roi/Roi.js +27 -207
- package/lib/image/roi/RoiLayer.js +1 -22
- package/lib/image/roi/RoiMap.js +5 -38
- package/lib/image/roi/creator/fromMask.js +4 -22
- package/lib/image/roi/creator/fromMaskConnectedComponentLabelingAlgorithm.js +2 -20
- package/lib/image/roi/creator/fromMaxima.js +10 -43
- package/lib/image/roi/creator/fromPoints.js +2 -10
- package/lib/image/roi/creator/fromWaterShed.js +6 -17
- package/lib/image/roi/feretDiameters.js +9 -21
- package/lib/image/roi/manager.js +30 -119
- package/lib/image/roi/util/commonBorderLength.js +5 -16
- package/lib/image/roi/util/mergeRoi.js +2 -21
- package/lib/image/transform/cmyk.js +0 -9
- package/lib/image/transform/colorDepth.js +0 -12
- package/lib/image/transform/crop.js +0 -20
- package/lib/image/transform/cropAlpha.js +0 -11
- package/lib/image/transform/grey.js +0 -16
- package/lib/image/transform/greyAlgorithms.js +1 -24
- package/lib/image/transform/hsl.js +0 -16
- package/lib/image/transform/hsv.js +0 -15
- package/lib/image/transform/insert.js +0 -6
- package/lib/image/transform/mask/huang.js +3 -18
- package/lib/image/transform/mask/intermodes.js +3 -14
- package/lib/image/transform/mask/isodata.js +0 -14
- package/lib/image/transform/mask/li.js +12 -42
- package/lib/image/transform/mask/mask.js +1 -13
- package/lib/image/transform/mask/maxEntropy.js +8 -25
- package/lib/image/transform/mask/mean.js +1 -3
- package/lib/image/transform/mask/minError.js +10 -21
- package/lib/image/transform/mask/minimum.js +0 -20
- package/lib/image/transform/mask/moments.js +4 -17
- package/lib/image/transform/mask/otsu.js +1 -7
- package/lib/image/transform/mask/percentile.js +0 -8
- package/lib/image/transform/mask/renyiEntropy.js +8 -34
- package/lib/image/transform/mask/shanbhag.js +8 -25
- package/lib/image/transform/mask/thresholdAlgorithms.js +1 -17
- package/lib/image/transform/mask/triangle.js +13 -29
- package/lib/image/transform/mask/yen.js +2 -14
- package/lib/image/transform/pad.js +0 -20
- package/lib/image/transform/resize/nearestNeighbor.js +0 -6
- package/lib/image/transform/resize/resize.js +0 -16
- package/lib/image/transform/rgba.js +0 -9
- package/lib/image/transform/rgba8.js +0 -3
- package/lib/image/transform/rotate.js +0 -29
- package/lib/image/transform/rotateFree.js +0 -31
- package/lib/image/transform/warping.js +2 -35
- package/lib/image/utility/combineChannels.js +0 -11
- package/lib/image/utility/getBestMatch.js +2 -13
- package/lib/image/utility/getChannel.js +0 -10
- package/lib/image/utility/getClosestCommonParent.js +6 -23
- package/lib/image/utility/getColumn.js +0 -4
- package/lib/image/utility/getIntersection.js +2 -18
- package/lib/image/utility/getMatrix.js +0 -7
- package/lib/image/utility/getPixelsArray.js +0 -5
- package/lib/image/utility/getPixelsGrid.js +0 -11
- package/lib/image/utility/getRow.js +0 -4
- package/lib/image/utility/getSimilarity.js +3 -17
- package/lib/image/utility/getThreshold.js +0 -4
- package/lib/image/utility/setBorder.js +0 -16
- package/lib/image/utility/setChannel.js +0 -6
- package/lib/image/utility/setMatrix.js +0 -6
- package/lib/image/utility/split.js +2 -15
- package/lib/index.js +0 -12
- package/lib/kernel/kernel.js +0 -3
- package/lib/kernel/laplacianOfGaussian.js +1 -7
- package/lib/node_modules/test/binary.js +0 -3
- package/lib/node_modules/test/oneRoi.js +0 -2
- package/lib/stack/Stack.js +2 -35
- package/lib/stack/compute/histogram.js +0 -4
- package/lib/stack/compute/histograms.js +0 -4
- package/lib/stack/compute/max.js +0 -3
- package/lib/stack/compute/median.js +0 -4
- package/lib/stack/compute/min.js +0 -3
- package/lib/stack/extend.js +1 -11
- package/lib/stack/transform/matchAndCrop.js +6 -19
- package/lib/stack/utility/averageImage.js +0 -9
- package/lib/stack/utility/maxImage.js +0 -7
- package/lib/stack/utility/minImage.js +0 -7
- package/lib/util/Shape.js +3 -40
- package/lib/util/base64.js +3 -14
- package/lib/util/channel.js +1 -35
- package/lib/util/color.js +1 -25
- package/lib/util/converter.js +1 -13
- package/lib/util/deepValue.js +0 -3
- package/lib/util/histogram.js +1 -12
- package/lib/util/kernel.js +2 -12
- package/lib/util/matrix.js +0 -13
- package/lib/util/points.js +12 -35
- package/lib/util/value.js +0 -7
- package/lib/util/zerosMatrix.js +0 -3
- package/package.json +2 -2
- package/src/image/operator/paintLabels.js +6 -6
- package/src/image/operator/paintPolygon.js +1 -1
- package/src/image/operator/paintPolygons.js +2 -2
- package/src/image/operator/paintPolylines.js +1 -1
package/lib/image/roi/manager.js
CHANGED
|
@@ -4,29 +4,17 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
|
|
8
7
|
var _jsQuantities = _interopRequireDefault(require("js-quantities"));
|
|
9
|
-
|
|
10
8
|
var _deepValue = _interopRequireDefault(require("../../util/deepValue"));
|
|
11
|
-
|
|
12
9
|
var _Image = _interopRequireDefault(require("../Image"));
|
|
13
|
-
|
|
14
10
|
var _RoiLayer = _interopRequireDefault(require("./RoiLayer"));
|
|
15
|
-
|
|
16
11
|
var _RoiMap = _interopRequireDefault(require("./RoiMap"));
|
|
17
|
-
|
|
18
12
|
var _fromMask = _interopRequireDefault(require("./creator/fromMask"));
|
|
19
|
-
|
|
20
13
|
var _fromMaskConnectedComponentLabelingAlgorithm = _interopRequireDefault(require("./creator/fromMaskConnectedComponentLabelingAlgorithm"));
|
|
21
|
-
|
|
22
14
|
var _fromMaxima = _interopRequireDefault(require("./creator/fromMaxima"));
|
|
23
|
-
|
|
24
15
|
var _fromPoints = _interopRequireDefault(require("./creator/fromPoints"));
|
|
25
|
-
|
|
26
16
|
var _fromWaterShed = _interopRequireDefault(require("./creator/fromWaterShed"));
|
|
27
|
-
|
|
28
17
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
29
|
-
|
|
30
18
|
/**
|
|
31
19
|
* A manager of Regions of Interest. A RoiManager is related to a specific Image
|
|
32
20
|
* and may contain multiple layers. Each layer is characterized by a label whose is
|
|
@@ -39,130 +27,107 @@ class RoiManager {
|
|
|
39
27
|
constructor(image, options = {}) {
|
|
40
28
|
this._image = image;
|
|
41
29
|
this._options = options;
|
|
42
|
-
|
|
43
30
|
if (!this._options.label) {
|
|
44
31
|
this._options.label = 'default';
|
|
45
32
|
}
|
|
46
|
-
|
|
47
33
|
this._layers = {};
|
|
48
34
|
this._painted = null;
|
|
49
|
-
}
|
|
50
|
-
|
|
35
|
+
}
|
|
51
36
|
|
|
37
|
+
// docs is in the corresponding file
|
|
52
38
|
fromMaxima(options = {}) {
|
|
53
39
|
let opt = Object.assign({}, this._options, options);
|
|
54
|
-
|
|
55
40
|
let roiMap = _fromMaxima.default.call(this._image, options);
|
|
56
|
-
|
|
57
41
|
this._layers[opt.label] = new _RoiLayer.default(roiMap, opt);
|
|
58
|
-
}
|
|
59
|
-
|
|
42
|
+
}
|
|
60
43
|
|
|
44
|
+
// docs is in the corresponding file
|
|
61
45
|
fromPoints(points, options = {}) {
|
|
62
46
|
let opt = Object.assign({}, this._options, options);
|
|
63
|
-
|
|
64
47
|
let roiMap = _fromPoints.default.call(this._image, points, options);
|
|
65
|
-
|
|
66
48
|
this._layers[opt.label] = new _RoiLayer.default(roiMap, opt);
|
|
67
49
|
return this;
|
|
68
50
|
}
|
|
51
|
+
|
|
69
52
|
/**
|
|
70
53
|
* @param {number[]} map
|
|
71
54
|
* @param {object} [options]
|
|
72
55
|
* @return {this}
|
|
73
56
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
76
57
|
putMap(map, options = {}) {
|
|
77
58
|
let roiMap = new _RoiMap.default(this._image, map);
|
|
78
59
|
let opt = Object.assign({}, this._options, options);
|
|
79
60
|
this._layers[opt.label] = new _RoiLayer.default(roiMap, opt);
|
|
80
61
|
return this;
|
|
81
|
-
}
|
|
82
|
-
|
|
62
|
+
}
|
|
83
63
|
|
|
64
|
+
// docs is in the corresponding file
|
|
84
65
|
fromWaterShed(options = {}) {
|
|
85
66
|
let opt = Object.assign({}, this._options, options);
|
|
86
|
-
|
|
87
67
|
let roiMap = _fromWaterShed.default.call(this._image, options);
|
|
88
|
-
|
|
89
68
|
this._layers[opt.label] = new _RoiLayer.default(roiMap, opt);
|
|
90
|
-
}
|
|
91
|
-
|
|
69
|
+
}
|
|
92
70
|
|
|
71
|
+
// docs is in the corresponding file
|
|
93
72
|
fromMask(mask, options = {}) {
|
|
94
73
|
let opt = Object.assign({}, this._options, options);
|
|
95
|
-
|
|
96
74
|
let roiMap = _fromMask.default.call(this._image, mask, options);
|
|
97
|
-
|
|
98
75
|
this._layers[opt.label] = new _RoiLayer.default(roiMap, opt);
|
|
99
76
|
return this;
|
|
100
77
|
}
|
|
101
|
-
|
|
102
78
|
fromMaskConnectedComponentLabelingAlgorithm(mask, options = {}) {
|
|
103
79
|
let opt = Object.assign({}, this._options, options);
|
|
104
|
-
|
|
105
80
|
let roiMap = _fromMaskConnectedComponentLabelingAlgorithm.default.call(this._image, mask, options);
|
|
106
|
-
|
|
107
81
|
this._layers[opt.label] = new _RoiLayer.default(roiMap, opt);
|
|
108
82
|
return this;
|
|
109
83
|
}
|
|
84
|
+
|
|
110
85
|
/**
|
|
111
86
|
*
|
|
112
87
|
* @param {object} [options]
|
|
113
88
|
* @return {RoiMap}
|
|
114
89
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
117
90
|
getMap(options = {}) {
|
|
118
91
|
let opt = Object.assign({}, this._options, options);
|
|
119
|
-
|
|
120
92
|
this._assertLayerWithLabel(opt.label);
|
|
121
|
-
|
|
122
93
|
return this._layers[opt.label].roiMap;
|
|
123
94
|
}
|
|
95
|
+
|
|
124
96
|
/**
|
|
125
97
|
* Return statistics about rows
|
|
126
98
|
* @param {object} [options]
|
|
127
99
|
* @return {object[]}
|
|
128
100
|
*/
|
|
129
|
-
|
|
130
|
-
|
|
131
101
|
rowsInfo(options = {}) {
|
|
132
102
|
return this.getMap(options).rowsInfo();
|
|
133
103
|
}
|
|
104
|
+
|
|
134
105
|
/**
|
|
135
106
|
* Return statistics about columns
|
|
136
107
|
* @param {object} [options]
|
|
137
108
|
* @return {object[]}
|
|
138
109
|
*/
|
|
139
|
-
|
|
140
|
-
|
|
141
110
|
colsInfo(options = {}) {
|
|
142
111
|
return this.getMap(options).rowsInfo();
|
|
143
112
|
}
|
|
113
|
+
|
|
144
114
|
/**
|
|
145
115
|
* Return the IDs of the Regions Of Interest (Roi) as an array of number
|
|
146
116
|
* @param {object} [options]
|
|
147
117
|
* @return {number[]}
|
|
148
118
|
*/
|
|
149
|
-
|
|
150
|
-
|
|
151
119
|
getRoiIds(options = {}) {
|
|
152
120
|
let rois = this.getRois(options);
|
|
153
|
-
|
|
154
121
|
if (rois) {
|
|
155
122
|
let ids = new Array(rois.length);
|
|
156
|
-
|
|
157
123
|
for (let i = 0; i < rois.length; i++) {
|
|
158
124
|
ids[i] = rois[i].id;
|
|
159
125
|
}
|
|
160
|
-
|
|
161
126
|
return ids;
|
|
162
127
|
}
|
|
163
|
-
|
|
164
128
|
throw new Error('ROIs not found');
|
|
165
129
|
}
|
|
130
|
+
|
|
166
131
|
/**
|
|
167
132
|
* Allows to select ROI based on size, label and sign.
|
|
168
133
|
* @param {object} [options={}]
|
|
@@ -179,8 +144,6 @@ class RoiManager {
|
|
|
179
144
|
* @param {number} [options.maxRatio=Number.POSITIVE_INFINITY]
|
|
180
145
|
* @return {Roi[]}
|
|
181
146
|
*/
|
|
182
|
-
|
|
183
|
-
|
|
184
147
|
getRois(options = {}) {
|
|
185
148
|
let {
|
|
186
149
|
label = this._options.label,
|
|
@@ -195,22 +158,19 @@ class RoiManager {
|
|
|
195
158
|
minRatio = 0,
|
|
196
159
|
maxRatio = Number.POSITIVE_INFINITY
|
|
197
160
|
} = options;
|
|
198
|
-
|
|
199
161
|
if (!this._layers[label]) {
|
|
200
162
|
throw new Error(`this Roi layer (${label}) does not exist`);
|
|
201
163
|
}
|
|
202
|
-
|
|
203
164
|
const allRois = this._layers[label].roi;
|
|
204
165
|
const rois = [];
|
|
205
|
-
|
|
206
166
|
for (const roi of allRois) {
|
|
207
167
|
if ((roi.id < 0 && negative || roi.id > 0 && positive) && roi.surface >= minSurface && roi.surface <= maxSurface && roi.width >= minWidth && roi.width <= maxWidth && roi.height >= minHeight && roi.height <= maxHeight && roi.ratio >= minRatio && roi.ratio <= maxRatio) {
|
|
208
168
|
rois.push(roi);
|
|
209
169
|
}
|
|
210
170
|
}
|
|
211
|
-
|
|
212
171
|
return rois;
|
|
213
172
|
}
|
|
173
|
+
|
|
214
174
|
/**
|
|
215
175
|
* Get an ROI by its id.
|
|
216
176
|
* @param {number} roiId
|
|
@@ -218,51 +178,41 @@ class RoiManager {
|
|
|
218
178
|
* @param {string} [options.label='default'] Label of the layer containing the ROI
|
|
219
179
|
* @return {Roi}
|
|
220
180
|
*/
|
|
221
|
-
|
|
222
|
-
|
|
223
181
|
getRoi(roiId, options = {}) {
|
|
224
182
|
const {
|
|
225
183
|
label = this._options.label
|
|
226
184
|
} = options;
|
|
227
|
-
|
|
228
185
|
if (!this._layers[label]) {
|
|
229
186
|
throw new Error(`this Roi layer (${label}) does not exist`);
|
|
230
187
|
}
|
|
231
|
-
|
|
232
188
|
const roi = this._layers[label].roi.find(roi => roi.id === roiId);
|
|
233
|
-
|
|
234
189
|
if (!roi) {
|
|
235
190
|
throw new Error(`found no Roi with id ${roiId}`);
|
|
236
191
|
}
|
|
237
|
-
|
|
238
192
|
return roi;
|
|
239
193
|
}
|
|
194
|
+
|
|
240
195
|
/**
|
|
241
196
|
* Returns an array of masks
|
|
242
197
|
* See {@link Roi.getMask} for the options
|
|
243
198
|
* @param {object} [options]
|
|
244
199
|
* @return {Image[]} Retuns an array of masks (1 bit Image)
|
|
245
200
|
*/
|
|
246
|
-
|
|
247
|
-
|
|
248
201
|
getMasks(options = {}) {
|
|
249
202
|
let rois = this.getRois(options);
|
|
250
203
|
let masks = new Array(rois.length);
|
|
251
|
-
|
|
252
204
|
for (let i = 0; i < rois.length; i++) {
|
|
253
205
|
masks[i] = rois[i].getMask(options);
|
|
254
206
|
}
|
|
255
|
-
|
|
256
207
|
return masks;
|
|
257
208
|
}
|
|
209
|
+
|
|
258
210
|
/**
|
|
259
211
|
* Returns an array of masks
|
|
260
212
|
* See {@link Roi.getAnalysisMasks} for the options
|
|
261
213
|
* @param {object} [options]
|
|
262
214
|
* @return {Image[]} Retuns an array of masks (1 bit Image)
|
|
263
215
|
*/
|
|
264
|
-
|
|
265
|
-
|
|
266
216
|
getAnalysisMasks(options = {}) {
|
|
267
217
|
const {
|
|
268
218
|
analysisProperty
|
|
@@ -272,20 +222,18 @@ class RoiManager {
|
|
|
272
222
|
if (rois.length === 0 || !rois[0][maskProperty]) return [];
|
|
273
223
|
return rois.map(roi => roi[maskProperty]);
|
|
274
224
|
}
|
|
225
|
+
|
|
275
226
|
/**
|
|
276
227
|
*
|
|
277
228
|
* @param {object} [options]
|
|
278
229
|
* @return {number[]}
|
|
279
230
|
*/
|
|
280
|
-
|
|
281
|
-
|
|
282
231
|
getData(options = {}) {
|
|
283
232
|
let opt = Object.assign({}, this._options, options);
|
|
284
|
-
|
|
285
233
|
this._assertLayerWithLabel(opt.label);
|
|
286
|
-
|
|
287
234
|
return this._layers[opt.label].roiMap.data;
|
|
288
235
|
}
|
|
236
|
+
|
|
289
237
|
/**
|
|
290
238
|
* Paint the ROI on a copy of the image and return this image.
|
|
291
239
|
* For painting options {@link Image.paintMasks}
|
|
@@ -298,48 +246,37 @@ class RoiManager {
|
|
|
298
246
|
* @param {string} [options.unit="pixel"] Unit in which to display the values
|
|
299
247
|
* @return {Image} - The painted RGBA 8 bits image
|
|
300
248
|
*/
|
|
301
|
-
|
|
302
|
-
|
|
303
249
|
paint(options = {}) {
|
|
304
250
|
let {
|
|
305
251
|
labelProperty,
|
|
306
252
|
analysisProperty
|
|
307
253
|
} = options;
|
|
308
|
-
|
|
309
254
|
if (!this._painted) {
|
|
310
255
|
this._painted = this._image.rgba8();
|
|
311
256
|
}
|
|
312
|
-
|
|
313
257
|
let masks = this.getMasks(options);
|
|
314
|
-
|
|
315
258
|
if (labelProperty) {
|
|
316
259
|
const rois = this.getRois(options);
|
|
317
260
|
options.labels = rois.map(roi => (0, _deepValue.default)(roi, labelProperty));
|
|
318
261
|
const max = Math.max(...options.labels);
|
|
319
262
|
let isSurface = false;
|
|
320
263
|
let isDistance = false;
|
|
321
|
-
|
|
322
264
|
if (labelProperty.includes('surface')) {
|
|
323
265
|
isSurface = true;
|
|
324
266
|
} else if (/(?:perimeter|min|max|external|width|height|length)/.test(labelProperty)) {
|
|
325
267
|
isDistance = true;
|
|
326
268
|
}
|
|
327
|
-
|
|
328
269
|
if (isFinite(max)) {
|
|
329
270
|
let unitLabel = '';
|
|
330
|
-
|
|
331
271
|
if (options.unit !== 'pixel' && options.pixelSize && (isDistance || isSurface)) {
|
|
332
272
|
unitLabel = isSurface ? `${options.unit}^2` : options.unit;
|
|
333
273
|
let siLabel = isSurface ? 'm^2' : 'm';
|
|
334
274
|
let factor = isSurface ? options.pixelSize ** 2 : options.pixelSize;
|
|
335
|
-
|
|
336
275
|
const convert = _jsQuantities.default.swiftConverter(siLabel, unitLabel);
|
|
337
|
-
|
|
338
276
|
options.labels = options.labels.map(value => {
|
|
339
277
|
return convert(factor * value);
|
|
340
278
|
});
|
|
341
279
|
}
|
|
342
|
-
|
|
343
280
|
if (max > 50) {
|
|
344
281
|
options.labels = options.labels.map(number => Math.round(number) + unitLabel);
|
|
345
282
|
} else if (max > 10) {
|
|
@@ -348,33 +285,28 @@ class RoiManager {
|
|
|
348
285
|
options.labels = options.labels.map(number => number.toFixed(2) + unitLabel);
|
|
349
286
|
}
|
|
350
287
|
}
|
|
351
|
-
|
|
352
288
|
options.labelsPosition = rois.map(roi => [roi.meanX, roi.meanY]);
|
|
353
289
|
}
|
|
354
|
-
|
|
355
290
|
this._painted.paintMasks(masks, options);
|
|
356
|
-
|
|
357
291
|
if (analysisProperty) {
|
|
358
292
|
let analysisMasks = this.getAnalysisMasks(options);
|
|
359
|
-
|
|
360
293
|
this._painted.paintMasks(analysisMasks, {
|
|
361
294
|
color: options.analysisColor,
|
|
362
295
|
alpha: options.analysisAlpha
|
|
363
296
|
});
|
|
364
297
|
}
|
|
365
|
-
|
|
366
298
|
return this._painted;
|
|
367
|
-
}
|
|
368
|
-
|
|
299
|
+
}
|
|
369
300
|
|
|
301
|
+
// return a mask corresponding to all the selected masks
|
|
370
302
|
getMask(options = {}) {
|
|
371
303
|
let mask = new _Image.default(this._image.width, this._image.height, {
|
|
372
304
|
kind: 'BINARY'
|
|
373
305
|
});
|
|
374
306
|
let masks = this.getMasks(options);
|
|
375
|
-
|
|
376
307
|
for (let i = 0; i < masks.length; i++) {
|
|
377
|
-
let roi = masks[i];
|
|
308
|
+
let roi = masks[i];
|
|
309
|
+
// we need to find the parent image to calculate the relative position
|
|
378
310
|
|
|
379
311
|
for (let x = 0; x < roi.width; x++) {
|
|
380
312
|
for (let y = 0; y < roi.height; y++) {
|
|
@@ -384,9 +316,9 @@ class RoiManager {
|
|
|
384
316
|
}
|
|
385
317
|
}
|
|
386
318
|
}
|
|
387
|
-
|
|
388
319
|
return mask;
|
|
389
320
|
}
|
|
321
|
+
|
|
390
322
|
/**
|
|
391
323
|
* Reset the changes to the current painted iamge to the image that was
|
|
392
324
|
* used during the creation of the RoiManager except if a new image is
|
|
@@ -394,19 +326,17 @@ class RoiManager {
|
|
|
394
326
|
* @param {object} [options]
|
|
395
327
|
* @param {Image} [options.image] A new iamge that you would like to sue for painting over
|
|
396
328
|
*/
|
|
397
|
-
|
|
398
|
-
|
|
399
329
|
resetPainted(options = {}) {
|
|
400
330
|
const {
|
|
401
331
|
image
|
|
402
332
|
} = options;
|
|
403
|
-
|
|
404
333
|
if (image) {
|
|
405
334
|
this._painted = this.image.rgba8();
|
|
406
335
|
} else {
|
|
407
336
|
this._painted = this._image.rgba8();
|
|
408
337
|
}
|
|
409
338
|
}
|
|
339
|
+
|
|
410
340
|
/**
|
|
411
341
|
* In place modification of the roiMap that joins regions of interest
|
|
412
342
|
* @param {object} [options]
|
|
@@ -420,54 +350,46 @@ class RoiManager {
|
|
|
420
350
|
* @param {number} [options.maxCommonBorderRatio=1] maximal common border ratio for merging
|
|
421
351
|
* @return {this}
|
|
422
352
|
*/
|
|
423
|
-
|
|
424
|
-
|
|
425
353
|
mergeRoi(options = {}) {
|
|
426
354
|
const roiMap = this.getMap(options);
|
|
427
355
|
roiMap.mergeRoi(options);
|
|
428
356
|
this.putMap(roiMap.data, options);
|
|
429
357
|
return this;
|
|
430
358
|
}
|
|
359
|
+
|
|
431
360
|
/**
|
|
432
361
|
* Merge multiple rois into one.
|
|
433
362
|
* All rois in the provided array will be merged into the first one.
|
|
434
363
|
* @param {Array<number>} roiIds - A list of Roi ids to merge
|
|
435
364
|
* @param {object} [options]
|
|
436
365
|
*/
|
|
437
|
-
|
|
438
|
-
|
|
439
366
|
mergeRois(roiIds, options = {}) {
|
|
440
367
|
if (!Array.isArray(roiIds) || roiIds.some(id => !Number.isInteger(id))) {
|
|
441
368
|
throw new Error('Roi ids must be an array of integers');
|
|
442
369
|
}
|
|
443
|
-
|
|
444
370
|
if (roiIds.length < 2) {
|
|
445
371
|
throw new Error('Roi ids must have at least two elements');
|
|
446
372
|
}
|
|
447
|
-
|
|
448
373
|
if (new Set(roiIds).size !== roiIds.length) {
|
|
449
374
|
throw new Error('Roi ids must be all different');
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
|
|
375
|
+
}
|
|
376
|
+
// Throws if one of the ids is wrong
|
|
453
377
|
roiIds.forEach(roiId => this.getRoi(roiId));
|
|
454
378
|
const roiMap = this.getMap(options);
|
|
455
379
|
roiMap.mergeRois(roiIds);
|
|
456
380
|
this.putMap(roiMap.data, options);
|
|
457
381
|
return this;
|
|
458
382
|
}
|
|
383
|
+
|
|
459
384
|
/**
|
|
460
385
|
* Finds all corresponding ROIs for all ROIs in the manager
|
|
461
386
|
* @param {number[]} roiMap
|
|
462
387
|
* @param {object} [options]
|
|
463
388
|
* @return {Array} array of objects returned in correspondingRoisInformation
|
|
464
389
|
*/
|
|
465
|
-
|
|
466
|
-
|
|
467
390
|
findCorrespondingRoi(roiMap, options = {}) {
|
|
468
391
|
let allRois = this.getRois(options);
|
|
469
392
|
let allRelated = [];
|
|
470
|
-
|
|
471
393
|
for (let i = 0; i < allRois.length; i++) {
|
|
472
394
|
let currentRoi = allRois[i];
|
|
473
395
|
let x = currentRoi.minX;
|
|
@@ -477,17 +399,15 @@ class RoiManager {
|
|
|
477
399
|
let currentRelated = correspondingRoisInformation(x, y, allPoints, roiMap, roiSign);
|
|
478
400
|
allRelated.push(currentRelated);
|
|
479
401
|
}
|
|
480
|
-
|
|
481
402
|
return allRelated;
|
|
482
403
|
}
|
|
483
|
-
|
|
484
404
|
_assertLayerWithLabel(label) {
|
|
485
405
|
if (!this._layers[label]) {
|
|
486
406
|
throw new Error(`no layer with label ${label}`);
|
|
487
407
|
}
|
|
488
408
|
}
|
|
489
|
-
|
|
490
409
|
}
|
|
410
|
+
|
|
491
411
|
/**
|
|
492
412
|
* For a given ROI, find corresponding ROIs and properties in given ROIMap.
|
|
493
413
|
* Returns an object containing the ID of ROIs, the surface shared by given and corresponding ROIs,
|
|
@@ -501,10 +421,7 @@ class RoiManager {
|
|
|
501
421
|
* @return {object} {{id: Array, surface: Array, roiSurfaceCovered: Array, same: number, opposite: number, total: number}}
|
|
502
422
|
* @private
|
|
503
423
|
*/
|
|
504
|
-
|
|
505
|
-
|
|
506
424
|
exports.default = RoiManager;
|
|
507
|
-
|
|
508
425
|
function correspondingRoisInformation(x, y, points, roiMap, roiSign) {
|
|
509
426
|
let correspondingRois = {
|
|
510
427
|
id: [],
|
|
@@ -514,14 +431,12 @@ function correspondingRoisInformation(x, y, points, roiMap, roiSign) {
|
|
|
514
431
|
opposite: 0,
|
|
515
432
|
total: 0
|
|
516
433
|
};
|
|
517
|
-
|
|
518
434
|
for (let i = 0; i < points.length; i++) {
|
|
519
435
|
let currentPoint = points[i];
|
|
520
436
|
let currentX = currentPoint[0];
|
|
521
437
|
let currentY = currentPoint[1];
|
|
522
438
|
let correspondingRoiMapIndex = currentX + x + (currentY + y) * roiMap.width;
|
|
523
439
|
let value = roiMap.data[correspondingRoiMapIndex];
|
|
524
|
-
|
|
525
440
|
if (value > 0 || value < 0) {
|
|
526
441
|
if (correspondingRois.id.includes(value)) {
|
|
527
442
|
correspondingRois.surface[correspondingRois.id.indexOf(value)] += 1;
|
|
@@ -531,19 +446,15 @@ function correspondingRoisInformation(x, y, points, roiMap, roiSign) {
|
|
|
531
446
|
}
|
|
532
447
|
}
|
|
533
448
|
}
|
|
534
|
-
|
|
535
449
|
for (let i = 0; i < correspondingRois.id.length; i++) {
|
|
536
450
|
let currentSign = Math.sign(correspondingRois.id[i]);
|
|
537
|
-
|
|
538
451
|
if (currentSign === roiSign) {
|
|
539
452
|
correspondingRois.same += correspondingRois.surface[i];
|
|
540
453
|
} else {
|
|
541
454
|
correspondingRois.opposite += correspondingRois.surface[i];
|
|
542
455
|
}
|
|
543
|
-
|
|
544
456
|
correspondingRois.roiSurfaceCovered[i] = correspondingRois.surface[i] / points.length;
|
|
545
457
|
}
|
|
546
|
-
|
|
547
458
|
correspondingRois.total = correspondingRois.opposite + correspondingRois.same;
|
|
548
459
|
return correspondingRois;
|
|
549
460
|
}
|
|
@@ -4,8 +4,8 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = commonBorderLength;
|
|
7
|
-
|
|
8
7
|
// We calculate all the border length with the neighbours
|
|
8
|
+
|
|
9
9
|
function commonBorderLength(roiMap) {
|
|
10
10
|
let data = roiMap.data;
|
|
11
11
|
let dx = [+1, 0, -1, 0];
|
|
@@ -14,34 +14,26 @@ function commonBorderLength(roiMap) {
|
|
|
14
14
|
let shift = -minMax.min;
|
|
15
15
|
let max = minMax.max + shift;
|
|
16
16
|
let borderInfo = [];
|
|
17
|
-
|
|
18
17
|
for (let i = 0; i <= max; i++) {
|
|
19
18
|
borderInfo.push(Object.create(null));
|
|
20
19
|
}
|
|
21
|
-
|
|
22
20
|
for (let x = 0; x < roiMap.width; x++) {
|
|
23
21
|
for (let y = 0; y < roiMap.height; y++) {
|
|
24
22
|
let target = x + y * roiMap.width;
|
|
25
23
|
let currentRoiID = data[target];
|
|
26
|
-
|
|
27
24
|
if (currentRoiID !== 0) {
|
|
28
25
|
// each pixel may only contribute one time to a border
|
|
29
26
|
let used = Object.create(null);
|
|
30
27
|
let isBorder = false;
|
|
31
|
-
|
|
32
28
|
for (let dir = 0; dir < 4; dir++) {
|
|
33
29
|
let newX = x + dx[dir];
|
|
34
30
|
let newY = y + dy[dir];
|
|
35
|
-
|
|
36
31
|
if (newX >= 0 && newY >= 0 && newX < roiMap.width && newY < roiMap.height) {
|
|
37
32
|
let neighbourRoiID = data[newX + newY * roiMap.width];
|
|
38
|
-
|
|
39
33
|
if (currentRoiID !== neighbourRoiID) {
|
|
40
34
|
isBorder = true;
|
|
41
|
-
|
|
42
35
|
if (neighbourRoiID !== 0 && used[neighbourRoiID] === undefined) {
|
|
43
36
|
used[neighbourRoiID] = true;
|
|
44
|
-
|
|
45
37
|
if (!borderInfo[neighbourRoiID + shift][currentRoiID]) {
|
|
46
38
|
borderInfo[neighbourRoiID + shift][currentRoiID] = 1;
|
|
47
39
|
} else {
|
|
@@ -52,9 +44,8 @@ function commonBorderLength(roiMap) {
|
|
|
52
44
|
} else {
|
|
53
45
|
isBorder = true;
|
|
54
46
|
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
47
|
+
}
|
|
48
|
+
// we will also add an information to specify the border length
|
|
58
49
|
if (isBorder) {
|
|
59
50
|
if (!borderInfo[currentRoiID + shift][currentRoiID]) {
|
|
60
51
|
borderInfo[currentRoiID + shift][currentRoiID] = 1;
|
|
@@ -64,16 +55,14 @@ function commonBorderLength(roiMap) {
|
|
|
64
55
|
}
|
|
65
56
|
}
|
|
66
57
|
}
|
|
67
|
-
}
|
|
68
|
-
|
|
58
|
+
}
|
|
69
59
|
|
|
60
|
+
// we convert now the result to an object for fast lookup and we will reshift the result
|
|
70
61
|
let result = {};
|
|
71
|
-
|
|
72
62
|
for (let i = 0; i < borderInfo.length; i++) {
|
|
73
63
|
if (Object.keys(borderInfo[i]).length > 0) {
|
|
74
64
|
result[i - shift] = borderInfo[i];
|
|
75
65
|
}
|
|
76
66
|
}
|
|
77
|
-
|
|
78
67
|
return result;
|
|
79
68
|
}
|
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = mergeRoi;
|
|
7
|
-
|
|
8
7
|
/**
|
|
9
8
|
* In place modification of the roiMap that joins regions of interest
|
|
10
9
|
* @param {object} [options]
|
|
@@ -27,31 +26,25 @@ function mergeRoi(options = {}) {
|
|
|
27
26
|
minCommonBorderRatio = 0.3,
|
|
28
27
|
maxCommonBorderRatio = 1
|
|
29
28
|
} = options;
|
|
30
|
-
|
|
31
29
|
let checkFunction = function (currentInfo, currentID, neighbourID) {
|
|
32
30
|
return currentInfo[neighbourID] >= minCommonBorderLength && currentInfo[neighbourID] <= maxCommonBorderLength;
|
|
33
31
|
};
|
|
34
|
-
|
|
35
32
|
if (typeof algorithm === 'function') {
|
|
36
33
|
checkFunction = algorithm;
|
|
37
34
|
}
|
|
38
|
-
|
|
39
35
|
if (algorithm.toLowerCase() === 'commonborderratio') {
|
|
40
36
|
checkFunction = function (currentInfo, currentID, neighbourID) {
|
|
41
37
|
let ratio = Math.min(currentInfo[neighbourID] / currentInfo[currentID], 1);
|
|
42
38
|
return ratio >= minCommonBorderRatio && ratio <= maxCommonBorderRatio;
|
|
43
39
|
};
|
|
44
40
|
}
|
|
45
|
-
|
|
46
41
|
const roiMap = this;
|
|
47
42
|
const borderLengths = roiMap.commonBorderLength;
|
|
48
43
|
let newMap = {};
|
|
49
44
|
let oldToNew = {};
|
|
50
|
-
|
|
51
45
|
for (let currentID of Object.keys(borderLengths)) {
|
|
52
46
|
let currentInfo = borderLengths[currentID];
|
|
53
47
|
let neighbourIDs = Object.keys(currentInfo);
|
|
54
|
-
|
|
55
48
|
for (let neighbourID of neighbourIDs) {
|
|
56
49
|
if (neighbourID !== currentID) {
|
|
57
50
|
// it is not myself ...
|
|
@@ -61,25 +54,20 @@ function mergeRoi(options = {}) {
|
|
|
61
54
|
if (oldToNew[neighbourID]) newNeighbourID = oldToNew[neighbourID];
|
|
62
55
|
let newCurrentID = currentID;
|
|
63
56
|
if (oldToNew[currentID]) newCurrentID = oldToNew[currentID];
|
|
64
|
-
|
|
65
57
|
if (Number(newNeighbourID) !== newCurrentID) {
|
|
66
58
|
let smallerID = Math.min(newNeighbourID, newCurrentID);
|
|
67
59
|
let largerID = Math.max(newNeighbourID, newCurrentID);
|
|
68
|
-
|
|
69
60
|
if (!newMap[smallerID]) {
|
|
70
61
|
newMap[smallerID] = {};
|
|
71
62
|
}
|
|
72
|
-
|
|
73
63
|
newMap[smallerID][largerID] = true;
|
|
74
64
|
oldToNew[largerID] = smallerID;
|
|
75
|
-
|
|
76
65
|
if (newMap[largerID]) {
|
|
77
66
|
// need to put everything to smallerID and remove property
|
|
78
67
|
for (let id of Object.keys(newMap[largerID])) {
|
|
79
68
|
newMap[smallerID][id] = true;
|
|
80
69
|
oldToNew[id] = smallerID;
|
|
81
70
|
}
|
|
82
|
-
|
|
83
71
|
delete newMap[largerID];
|
|
84
72
|
}
|
|
85
73
|
}
|
|
@@ -87,31 +75,24 @@ function mergeRoi(options = {}) {
|
|
|
87
75
|
}
|
|
88
76
|
}
|
|
89
77
|
}
|
|
90
|
-
|
|
91
78
|
let minMax = roiMap.minMax;
|
|
92
79
|
let shift = -minMax.min;
|
|
93
80
|
let max = minMax.max + shift;
|
|
94
81
|
let oldToNewArray = new Array(max + 1).fill(0);
|
|
95
|
-
|
|
96
82
|
for (let key of Object.keys(oldToNew)) {
|
|
97
83
|
oldToNewArray[Number(key) + shift] = oldToNew[key];
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
|
|
84
|
+
}
|
|
85
|
+
// time to change the roiMap
|
|
101
86
|
let data = roiMap.data;
|
|
102
|
-
|
|
103
87
|
for (let i = 0; i < data.length; i++) {
|
|
104
88
|
let currentValue = data[i];
|
|
105
|
-
|
|
106
89
|
if (currentValue !== 0) {
|
|
107
90
|
let newValue = oldToNewArray[currentValue + shift];
|
|
108
|
-
|
|
109
91
|
if (newValue !== 0) {
|
|
110
92
|
data[i] = newValue;
|
|
111
93
|
}
|
|
112
94
|
}
|
|
113
95
|
}
|
|
114
|
-
|
|
115
96
|
roiMap.computed = {};
|
|
116
97
|
return roiMap;
|
|
117
98
|
}
|