@aics/vole-core 4.4.0 → 4.4.1
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/es/Channel.js +2 -2
- package/es/Histogram.js +11 -15
- package/es/MeshVolume.js +2 -1
- package/es/VectorArrows3d.js +2 -3
- package/es/loaders/OmeZarrLoader.js +3 -14
- package/es/loaders/TiffLoader.js +7 -5
- package/es/types/BaseDrawableMeshObject.d.ts +1 -1
- package/es/types/Histogram.d.ts +1 -1
- package/es/types/ImageInfo.d.ts +1 -1
- package/es/types/MeshVolume.d.ts +1 -1
- package/es/types/VectorArrows3d.d.ts +2 -2
- package/es/types/Volume.d.ts +1 -1
- package/es/types/loaders/TiffLoader.d.ts +1 -0
- package/es/utils/num_utils.js +6 -2
- package/es/workers/FetchTiffWorker.js +2 -11
- package/package.json +1 -1
package/es/Channel.js
CHANGED
|
@@ -157,7 +157,7 @@ export default class Channel {
|
|
|
157
157
|
};
|
|
158
158
|
this.rebuildDataTexture(this.imgData.data, w, h);
|
|
159
159
|
this.loaded = true;
|
|
160
|
-
this.histogram = new Histogram(bitsArray);
|
|
160
|
+
this.histogram = new Histogram(bitsArray, rawMin, rawMax);
|
|
161
161
|
this.frame = frame;
|
|
162
162
|
|
|
163
163
|
// reuse old lut but auto-remap it to new data range
|
|
@@ -205,7 +205,7 @@ export default class Channel {
|
|
|
205
205
|
this.loaded = true;
|
|
206
206
|
// update from current histogram?
|
|
207
207
|
this.setRawDataRange(rawMin, rawMax);
|
|
208
|
-
this.histogram = new Histogram(this.volumeData);
|
|
208
|
+
this.histogram = new Histogram(this.volumeData, rawMin, rawMax);
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
// given this.volumeData, let's unpack it into a flat textureatlas and fill up this.imgData.
|
package/es/Histogram.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isFloatTypeArray } from "./types.js";
|
|
2
|
+
import { getDataRange } from "./utils/num_utils.js";
|
|
2
3
|
const NBINS = 256;
|
|
3
4
|
/**
|
|
4
5
|
* Builds a histogram with 256 bins from a data array. Assume data is 8 bit single channel grayscale.
|
|
@@ -18,7 +19,7 @@ export default class Histogram {
|
|
|
18
19
|
|
|
19
20
|
/** Index of the last bin (other than 0) with at least 1 value. */
|
|
20
21
|
|
|
21
|
-
constructor(data) {
|
|
22
|
+
constructor(data, dataMin = undefined, dataMax = undefined) {
|
|
22
23
|
this.dataMinBin = 0;
|
|
23
24
|
this.dataMaxBin = 0;
|
|
24
25
|
this.maxBin = 0;
|
|
@@ -28,7 +29,7 @@ export default class Histogram {
|
|
|
28
29
|
this.binSize = 0;
|
|
29
30
|
|
|
30
31
|
// build up the histogram
|
|
31
|
-
const hinfo = Histogram.calculateHistogram(data, NBINS);
|
|
32
|
+
const hinfo = Histogram.calculateHistogram(data, NBINS, dataMin, dataMax);
|
|
32
33
|
this.bins = hinfo.bins;
|
|
33
34
|
this.min = hinfo.min;
|
|
34
35
|
this.max = hinfo.max;
|
|
@@ -233,24 +234,19 @@ export default class Histogram {
|
|
|
233
234
|
}
|
|
234
235
|
return [b, e];
|
|
235
236
|
}
|
|
236
|
-
static calculateHistogram(arr, numBins = 1) {
|
|
237
|
+
static calculateHistogram(arr, numBins = 1, dataMin = undefined, dataMax = undefined) {
|
|
237
238
|
if (numBins < 1) {
|
|
238
239
|
numBins = 1;
|
|
239
240
|
}
|
|
240
241
|
|
|
241
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
// need to be careful about computing over chunks or whole ready-to-display volume
|
|
242
|
+
// ASSUMPTION: we will trust the min and max if provided.
|
|
243
|
+
let min = dataMin !== undefined ? dataMin : arr[0];
|
|
244
|
+
let max = dataMax !== undefined ? dataMax : arr[0];
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
min = arr[i];
|
|
251
|
-
} else if (arr[i] > max) {
|
|
252
|
-
max = arr[i];
|
|
253
|
-
}
|
|
246
|
+
// Find min and max in the array if the user did not provide them.
|
|
247
|
+
// Note that this is a completely separate walk through the data array which could be expensive.
|
|
248
|
+
if (dataMin === undefined || dataMax === undefined) {
|
|
249
|
+
[min, max] = getDataRange(arr);
|
|
254
250
|
}
|
|
255
251
|
const bins = new Uint32Array(numBins).fill(0);
|
|
256
252
|
|
package/es/MeshVolume.js
CHANGED
|
@@ -250,11 +250,12 @@ export default class MeshVolume {
|
|
|
250
250
|
this.meshrep[channel] = null;
|
|
251
251
|
}
|
|
252
252
|
}
|
|
253
|
-
saveChannelIsosurface(channelIndex, type,
|
|
253
|
+
saveChannelIsosurface(channelIndex, type, name) {
|
|
254
254
|
const meshrep = this.meshrep[channelIndex];
|
|
255
255
|
if (!meshrep) {
|
|
256
256
|
return;
|
|
257
257
|
}
|
|
258
|
+
const namePrefix = name !== undefined ? `${name}_` : "";
|
|
258
259
|
if (type === "STL") {
|
|
259
260
|
this.exportSTL(meshrep, namePrefix + "_" + this.volume.channelNames[channelIndex]);
|
|
260
261
|
} else if (type === "GLTF") {
|
package/es/VectorArrows3d.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
+
import BaseDrawableMeshObject from "./BaseDrawableMeshObject.js";
|
|
2
|
+
import { MESH_NO_PICK_OCCLUSION_LAYER } from "./ThreeJsPanel.js";
|
|
1
3
|
import { InstancedMesh, CylinderGeometry, ConeGeometry, Object3D, Vector3, MeshBasicMaterial, Color, DynamicDrawUsage, Matrix4 } from "three";
|
|
2
|
-
import BaseDrawableMeshObject from "./BaseDrawableMeshObject";
|
|
3
|
-
import { MESH_NO_PICK_OCCLUSION_LAYER } from "./ThreeJsPanel";
|
|
4
|
-
|
|
5
4
|
// Unscaled arrowhead dimensions.
|
|
6
5
|
const SHAFT_BASE_RADIUS = 0.5;
|
|
7
6
|
const HEAD_BASE_RADIUS = 1.5;
|
|
@@ -3,6 +3,7 @@ import * as zarr from "zarrita";
|
|
|
3
3
|
const {
|
|
4
4
|
slice
|
|
5
5
|
} = zarr;
|
|
6
|
+
import { getDataRange } from "../utils/num_utils.js";
|
|
6
7
|
import SubscribableRequestQueue from "../utils/SubscribableRequestQueue.js";
|
|
7
8
|
import { ThreadableVolumeLoader } from "./IVolumeLoader.js";
|
|
8
9
|
import { composeSubregion, computePackedAtlasDims, convertSubregionToPixels, pickLevelToLoad, unitNameToSymbol } from "./VolumeLoaderUtils.js";
|
|
@@ -16,18 +17,7 @@ const CHUNK_REQUEST_CANCEL_REASON = "chunk request cancelled";
|
|
|
16
17
|
// returns the converted data and the original min and max values
|
|
17
18
|
function convertChannel(channelData, dtype) {
|
|
18
19
|
// get min and max
|
|
19
|
-
|
|
20
|
-
let min = channelData[0];
|
|
21
|
-
let max = channelData[0];
|
|
22
|
-
for (let i = 0; i < channelData.length; i++) {
|
|
23
|
-
const val = channelData[i];
|
|
24
|
-
if (val < min) {
|
|
25
|
-
min = val;
|
|
26
|
-
}
|
|
27
|
-
if (val > max) {
|
|
28
|
-
max = val;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
20
|
+
const [min, max] = getDataRange(channelData);
|
|
31
21
|
if (dtype === "float64") {
|
|
32
22
|
// convert to float32
|
|
33
23
|
const f32 = new Float32Array(channelData.length);
|
|
@@ -315,7 +305,7 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
|
|
|
315
305
|
return dims;
|
|
316
306
|
});
|
|
317
307
|
const imgdata = {
|
|
318
|
-
name: source0.omeroMetadata?.name
|
|
308
|
+
name: source0.omeroMetadata?.name,
|
|
319
309
|
atlasTileDims: [atlasTileDims.x, atlasTileDims.y],
|
|
320
310
|
subregionSize: [pxSizeLv.x, pxSizeLv.y, pxSizeLv.z],
|
|
321
311
|
subregionOffset: [0, 0, 0],
|
|
@@ -468,7 +458,6 @@ class OMEZarrLoader extends ThreadableVolumeLoader {
|
|
|
468
458
|
const level = this.sources[sourceIdx].scaleLevels[multiscaleLevel];
|
|
469
459
|
const sliceSpec = this.orderByDimension(unorderedSpec, sourceIdx);
|
|
470
460
|
const reportChunk = (coords, sub) => reportChunkBase(sourceIdx, coords, sub);
|
|
471
|
-
console.log(level);
|
|
472
461
|
const result = await zarr.get(level, sliceSpec, {
|
|
473
462
|
opts: {
|
|
474
463
|
subscriber,
|
package/es/loaders/TiffLoader.js
CHANGED
|
@@ -21,6 +21,7 @@ function getOME(xml) {
|
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
class OMEDims {
|
|
24
|
+
name = undefined;
|
|
24
25
|
sizex = 0;
|
|
25
26
|
sizey = 0;
|
|
26
27
|
sizez = 1;
|
|
@@ -63,14 +64,15 @@ function getAttributeOrError(el, attr) {
|
|
|
63
64
|
function getOMEDims(imageEl) {
|
|
64
65
|
const dims = new OMEDims();
|
|
65
66
|
const pixelsEl = imageEl.getElementsByTagName("Pixels")[0];
|
|
67
|
+
dims.name = imageEl.getAttribute("Name") ?? "";
|
|
66
68
|
dims.sizex = Number(getAttributeOrError(pixelsEl, "SizeX"));
|
|
67
69
|
dims.sizey = Number(getAttributeOrError(pixelsEl, "SizeY"));
|
|
68
70
|
dims.sizez = Number(pixelsEl.getAttribute("SizeZ"));
|
|
69
71
|
dims.sizec = Number(pixelsEl.getAttribute("SizeC"));
|
|
70
72
|
dims.sizet = Number(pixelsEl.getAttribute("SizeT"));
|
|
71
|
-
dims.unit = pixelsEl.getAttribute("PhysicalSizeXUnit")
|
|
72
|
-
dims.pixeltype = pixelsEl.getAttribute("Type")
|
|
73
|
-
dims.dimensionorder = pixelsEl.getAttribute("DimensionOrder")
|
|
73
|
+
dims.unit = pixelsEl.getAttribute("PhysicalSizeXUnit") ?? "";
|
|
74
|
+
dims.pixeltype = pixelsEl.getAttribute("Type") ?? "";
|
|
75
|
+
dims.dimensionorder = pixelsEl.getAttribute("DimensionOrder") ?? "XYZCT";
|
|
74
76
|
dims.pixelsizex = Number(pixelsEl.getAttribute("PhysicalSizeX"));
|
|
75
77
|
dims.pixelsizey = Number(pixelsEl.getAttribute("PhysicalSizeY"));
|
|
76
78
|
dims.pixelsizez = Number(pixelsEl.getAttribute("PhysicalSizeZ"));
|
|
@@ -178,7 +180,7 @@ class TiffLoader extends ThreadableVolumeLoader {
|
|
|
178
180
|
// load tiff and check metadata
|
|
179
181
|
const numChannelsPerSource = this.url.length > 1 ? Array(this.url.length).fill(1) : [dims.sizec];
|
|
180
182
|
const imgdata = {
|
|
181
|
-
name:
|
|
183
|
+
name: dims.name,
|
|
182
184
|
atlasTileDims: [atlasDims.x, atlasDims.y],
|
|
183
185
|
subregionSize: [tilesizex, tilesizey, dims.sizez],
|
|
184
186
|
subregionOffset: [0, 0, 0],
|
|
@@ -188,7 +190,7 @@ class TiffLoader extends ThreadableVolumeLoader {
|
|
|
188
190
|
multiscaleLevelDims: [{
|
|
189
191
|
shape: [dims.sizet, dims.sizec, dims.sizez, tilesizey, tilesizex],
|
|
190
192
|
spacing: [1, 1, dims.pixelsizez, dims.pixelsizey * dims.sizey / tilesizey, dims.pixelsizex * dims.sizex / tilesizex],
|
|
191
|
-
spaceUnit: dims.unit
|
|
193
|
+
spaceUnit: dims.unit ?? "",
|
|
192
194
|
timeUnit: "",
|
|
193
195
|
dataType: getDtype(dims.pixeltype)
|
|
194
196
|
}],
|
package/es/types/Histogram.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export default class Histogram {
|
|
|
18
18
|
private dataMaxBin;
|
|
19
19
|
private pixelCount;
|
|
20
20
|
maxBin: number;
|
|
21
|
-
constructor(data: TypedArray<NumberType
|
|
21
|
+
constructor(data: TypedArray<NumberType>, dataMin?: number | undefined, dataMax?: number | undefined);
|
|
22
22
|
private static findBin;
|
|
23
23
|
/**
|
|
24
24
|
* Returns the integer bin index for the given value. If a value is outside
|
package/es/types/ImageInfo.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type VolumeDims } from "./VolumeDims.js";
|
|
2
2
|
import { Vector3, Vector2 } from "three";
|
|
3
3
|
export type ImageInfo = Readonly<{
|
|
4
|
-
name: string;
|
|
4
|
+
name: string | undefined;
|
|
5
5
|
/**
|
|
6
6
|
* XY dimensions of the texture atlas used by `RayMarchedAtlasVolume` and
|
|
7
7
|
* `Atlas2DSlice`, in number of z-slice tiles (not pixels). Chosen by the
|
package/es/types/MeshVolume.d.ts
CHANGED
|
@@ -34,7 +34,7 @@ export default class MeshVolume implements IDrawableObject {
|
|
|
34
34
|
hasIsosurface(channel: number): boolean;
|
|
35
35
|
createIsosurface(channel: number, color: [number, number, number], value?: number, alpha?: number, transp?: boolean): void;
|
|
36
36
|
destroyIsosurface(channel: number): void;
|
|
37
|
-
saveChannelIsosurface(channelIndex: number, type: string,
|
|
37
|
+
saveChannelIsosurface(channelIndex: number, type: string, name?: string): void;
|
|
38
38
|
exportSTL(input: Object3D, fname: string): void;
|
|
39
39
|
exportGLTF(input: Object3D, fname: string): void;
|
|
40
40
|
generateIsosurfaceGeometry(channelIndex: number, isovalue: number): BufferGeometry[];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import BaseDrawableMeshObject from "./BaseDrawableMeshObject.js";
|
|
1
2
|
import { Vector3, Color } from "three";
|
|
2
|
-
import { IDrawableObject } from "./types";
|
|
3
|
-
import BaseDrawableMeshObject from "./BaseDrawableMeshObject";
|
|
3
|
+
import { IDrawableObject } from "./types.js";
|
|
4
4
|
/**
|
|
5
5
|
* A drawable vector arrow field, which uses instanced meshes for performance.
|
|
6
6
|
*/
|
package/es/types/Volume.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export default class Volume {
|
|
|
25
25
|
loadSpecRequired: Required<LoadSpec>;
|
|
26
26
|
channelLoadCallback?: PerChannelCallback;
|
|
27
27
|
imageMetadata: Record<string, unknown>;
|
|
28
|
-
name: string;
|
|
28
|
+
name: string | undefined;
|
|
29
29
|
channels: Channel[];
|
|
30
30
|
numChannels: number;
|
|
31
31
|
channelNames: string[];
|
package/es/utils/num_utils.js
CHANGED
|
@@ -224,8 +224,12 @@ export function getDataRange(data) {
|
|
|
224
224
|
let min = data[0];
|
|
225
225
|
let max = data[0];
|
|
226
226
|
for (let i = 1; i < data.length; i++) {
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
const value = data[i];
|
|
228
|
+
if (value < min) {
|
|
229
|
+
min = value;
|
|
230
|
+
} else if (value > max) {
|
|
231
|
+
max = value;
|
|
232
|
+
}
|
|
229
233
|
}
|
|
230
234
|
return [min, max];
|
|
231
235
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { fromUrl } from "geotiff";
|
|
2
2
|
import { serializeError } from "serialize-error";
|
|
3
3
|
import { VolumeLoadError, VolumeLoadErrorType } from "../loaders/VolumeLoadError.js";
|
|
4
|
+
import { getDataRange } from "../utils/num_utils.js";
|
|
4
5
|
// from TIFF
|
|
5
6
|
const SAMPLEFORMAT_UINT = 1;
|
|
6
7
|
const SAMPLEFORMAT_INT = 2;
|
|
@@ -121,17 +122,7 @@ async function loadTiffChannel(e) {
|
|
|
121
122
|
// all slices collected, now resample to 8 bits full data range
|
|
122
123
|
const src = castToArray(buffer, bytesPerPixel, sampleFormat);
|
|
123
124
|
const dtype = getDtype(sampleFormat, bytesPerPixel);
|
|
124
|
-
|
|
125
|
-
let chmax = src[0];
|
|
126
|
-
for (let j = 0; j < src.length; ++j) {
|
|
127
|
-
const val = src[j];
|
|
128
|
-
if (val < chmin) {
|
|
129
|
-
chmin = val;
|
|
130
|
-
}
|
|
131
|
-
if (val > chmax) {
|
|
132
|
-
chmax = val;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
125
|
+
const [chmin, chmax] = getDataRange(src);
|
|
135
126
|
return {
|
|
136
127
|
data: src,
|
|
137
128
|
channel: channelIndex,
|