@fnndsc/salsa 3.17.0 → 3.18.0
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/dist/dicom/index.d.ts +1 -0
- package/dist/dicom/index.js +1 -0
- package/dist/dicom/index.js.map +1 -1
- package/dist/dicom/pixels.d.ts +28 -0
- package/dist/dicom/pixels.js +129 -0
- package/dist/dicom/pixels.js.map +1 -0
- package/package.json +2 -2
package/dist/dicom/index.d.ts
CHANGED
package/dist/dicom/index.js
CHANGED
|
@@ -21,4 +21,5 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
21
21
|
__exportStar(require("./dictionary.js"), exports);
|
|
22
22
|
__exportStar(require("./tags.js"), exports);
|
|
23
23
|
__exportStar(require("./series.js"), exports);
|
|
24
|
+
__exportStar(require("./pixels.js"), exports);
|
|
24
25
|
//# sourceMappingURL=index.js.map
|
package/dist/dicom/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dicom/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;GAGG;AACH,kDAAgC;AAChC,4CAA0B;AAC1B,8CAA4B"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dicom/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;;GAGG;AACH,kDAAgC;AAChC,4CAA0B;AAC1B,8CAA4B;AAC5B,8CAA4B"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type Result } from '@fnndsc/cumin';
|
|
2
|
+
import { type DicomFolderIO } from './series.js';
|
|
3
|
+
/** A slice's luminance: `gray[y * width + x]`, 0 (black) to 255 (white). */
|
|
4
|
+
export interface DicomGray {
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
gray: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The outcome of reading a slice for a thumbnail: the grid, or the news that
|
|
11
|
+
* the pixels are compressed and this build cannot decode them. An unreadable
|
|
12
|
+
* or non-DICOM file is an error, not an outcome.
|
|
13
|
+
*/
|
|
14
|
+
export type DicomGrayOutcome = ({
|
|
15
|
+
kind: 'gray';
|
|
16
|
+
} & DicomGray) | {
|
|
17
|
+
kind: 'compressed';
|
|
18
|
+
transferSyntax: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Reads one DICOM slice as an 8-bit grayscale grid.
|
|
22
|
+
*
|
|
23
|
+
* @param path - The slice file's VFS path.
|
|
24
|
+
* @param io - The reader, injected for tests.
|
|
25
|
+
* @returns The grid, the compressed verdict, or an error when the bytes are
|
|
26
|
+
* not a readable DICOM file.
|
|
27
|
+
*/
|
|
28
|
+
export declare function dicomSlice_gray(path: string, io?: DicomFolderIO): Promise<Result<DicomGrayOutcome>>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.dicomSlice_gray = dicomSlice_gray;
|
|
7
|
+
/**
|
|
8
|
+
* @file One DICOM slice as an 8-bit grayscale grid, for a text thumbnail.
|
|
9
|
+
*
|
|
10
|
+
* The kernel reads a slice's pixels, applies the modality rescale and a
|
|
11
|
+
* window/level (the file's own, or an auto range when it declares none), and
|
|
12
|
+
* normalises to 0..255 with MONOCHROME1 inverted so bright always reads high.
|
|
13
|
+
* A surface then renders that grid — an ASCII ramp on any pipe, ANSI
|
|
14
|
+
* half-blocks on a colour terminal — without knowing anything about DICOM.
|
|
15
|
+
*
|
|
16
|
+
* Only uncompressed transfer syntaxes are read here; a compressed one (the
|
|
17
|
+
* JPEG family, RLE) needs a codec the kernel does not carry, and says so
|
|
18
|
+
* rather than pretending. dcmjs decodes the endianness of the uncompressed
|
|
19
|
+
* ones, so the pixels arrive native-endian in the naturalised dataset.
|
|
20
|
+
*
|
|
21
|
+
* @module
|
|
22
|
+
*/
|
|
23
|
+
const dcmjs_1 = __importDefault(require("dcmjs"));
|
|
24
|
+
const cumin_1 = require("@fnndsc/cumin");
|
|
25
|
+
const series_js_1 = require("./series.js");
|
|
26
|
+
/** Transfer syntaxes the kernel cannot decode without a codec: JPEG* and RLE. */
|
|
27
|
+
const COMPRESSED_SYNTAX = /^1\.2\.840\.10008\.1\.2\.[45]/;
|
|
28
|
+
/**
|
|
29
|
+
* Reads one DICOM slice as an 8-bit grayscale grid.
|
|
30
|
+
*
|
|
31
|
+
* @param path - The slice file's VFS path.
|
|
32
|
+
* @param io - The reader, injected for tests.
|
|
33
|
+
* @returns The grid, the compressed verdict, or an error when the bytes are
|
|
34
|
+
* not a readable DICOM file.
|
|
35
|
+
*/
|
|
36
|
+
async function dicomSlice_gray(path, io = series_js_1.dicomFolderIO_default) {
|
|
37
|
+
const bytes = await io.readBinary(path);
|
|
38
|
+
if (!bytes.ok)
|
|
39
|
+
return (0, cumin_1.Err)();
|
|
40
|
+
try {
|
|
41
|
+
const buffer = new Uint8Array(bytes.value).slice().buffer;
|
|
42
|
+
const message = dcmjs_1.default.data.DicomMessage.readFile(buffer, { ignoreErrors: true });
|
|
43
|
+
const meta = dcmjs_1.default.data.DicomMetaDictionary.naturalizeDataset(message.meta);
|
|
44
|
+
const ds = dcmjs_1.default.data.DicomMetaDictionary.naturalizeDataset(message.dict);
|
|
45
|
+
const transferSyntax = typeof meta.TransferSyntaxUID === 'string' ? meta.TransferSyntaxUID : '';
|
|
46
|
+
if (COMPRESSED_SYNTAX.test(transferSyntax))
|
|
47
|
+
return (0, cumin_1.Ok)({ kind: 'compressed', transferSyntax });
|
|
48
|
+
const height = numberOf(ds.Rows) ?? 0;
|
|
49
|
+
const width = numberOf(ds.Columns) ?? 0;
|
|
50
|
+
if (height === 0 || width === 0)
|
|
51
|
+
return (0, cumin_1.Err)();
|
|
52
|
+
const count = width * height;
|
|
53
|
+
const pixelData = Array.isArray(ds.PixelData) ? ds.PixelData[0] : ds.PixelData;
|
|
54
|
+
if (!(pixelData instanceof ArrayBuffer))
|
|
55
|
+
return (0, cumin_1.Err)();
|
|
56
|
+
const bits = numberOf(ds.BitsAllocated) ?? 16;
|
|
57
|
+
const signed = numberOf(ds.PixelRepresentation) === 1;
|
|
58
|
+
const src = new Float64Array(count);
|
|
59
|
+
if (bits === 16) {
|
|
60
|
+
const view = signed ? new Int16Array(pixelData, 0, count) : new Uint16Array(pixelData, 0, count);
|
|
61
|
+
for (let i = 0; i < count; i++)
|
|
62
|
+
src[i] = view[i];
|
|
63
|
+
}
|
|
64
|
+
else if (bits === 8) {
|
|
65
|
+
const view = new Uint8Array(pixelData, 0, count);
|
|
66
|
+
for (let i = 0; i < count; i++)
|
|
67
|
+
src[i] = view[i];
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
return (0, cumin_1.Err)();
|
|
71
|
+
}
|
|
72
|
+
// Modality rescale: stored value to a physical unit (HU for CT, etc.).
|
|
73
|
+
const slope = numberOf(ds.RescaleSlope) ?? 1;
|
|
74
|
+
const intercept = numberOf(ds.RescaleIntercept) ?? 0;
|
|
75
|
+
if (slope !== 1 || intercept !== 0)
|
|
76
|
+
for (let i = 0; i < count; i++)
|
|
77
|
+
src[i] = src[i] * slope + intercept;
|
|
78
|
+
// The file's own window/level, or an auto range when it declares none.
|
|
79
|
+
let center = firstNumber(ds.WindowCenter);
|
|
80
|
+
let width_wl = firstNumber(ds.WindowWidth);
|
|
81
|
+
if (center === undefined || width_wl === undefined || width_wl <= 0) {
|
|
82
|
+
const auto = autoWindow(src);
|
|
83
|
+
center = auto.center;
|
|
84
|
+
width_wl = auto.width;
|
|
85
|
+
}
|
|
86
|
+
const low = center - width_wl / 2;
|
|
87
|
+
const invert = ds.PhotometricInterpretation === 'MONOCHROME1';
|
|
88
|
+
const gray = new Uint8Array(count);
|
|
89
|
+
for (let i = 0; i < count; i++) {
|
|
90
|
+
let t = ((src[i] - low) / width_wl) * 255;
|
|
91
|
+
t = t < 0 ? 0 : t > 255 ? 255 : t;
|
|
92
|
+
gray[i] = invert ? 255 - t : t;
|
|
93
|
+
}
|
|
94
|
+
return (0, cumin_1.Ok)({ kind: 'gray', width, height, gray });
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return (0, cumin_1.Err)();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/** The first number of a DICOM value that may be a number, an array, or a DS string. */
|
|
101
|
+
function firstNumber(value) {
|
|
102
|
+
const one = Array.isArray(value) ? value[0] : value;
|
|
103
|
+
if (typeof one === 'number')
|
|
104
|
+
return Number.isFinite(one) ? one : undefined;
|
|
105
|
+
if (typeof one === 'string') {
|
|
106
|
+
const parsed = Number.parseFloat(one);
|
|
107
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
/** A whole number from a DICOM value that may arrive as a number or a string. */
|
|
112
|
+
function numberOf(value) {
|
|
113
|
+
if (typeof value === 'number')
|
|
114
|
+
return value;
|
|
115
|
+
if (typeof value === 'string') {
|
|
116
|
+
const parsed = Number.parseInt(value, 10);
|
|
117
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
120
|
+
}
|
|
121
|
+
/** A window from the robust range of the data: its 2nd to 98th percentile. */
|
|
122
|
+
function autoWindow(src) {
|
|
123
|
+
const sorted = src.slice().sort();
|
|
124
|
+
const low = sorted[Math.floor(sorted.length * 0.02)] ?? 0;
|
|
125
|
+
const high = sorted[Math.floor(sorted.length * 0.98)] ?? low + 1;
|
|
126
|
+
const width = Math.max(1, high - low);
|
|
127
|
+
return { center: low + width / 2, width };
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=pixels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixels.js","sourceRoot":"","sources":["../../src/dicom/pixels.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fnndsc/salsa",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0",
|
|
4
4
|
"description": "Shared Application Logic and Service Assets for ChRIS",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"adm-zip": "npm:empty-npm-package@1.0.0"
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
|
-
"@fnndsc/cumin": "^3.22.
|
|
63
|
+
"@fnndsc/cumin": "^3.22.1",
|
|
64
64
|
"axios": "^1.6.8",
|
|
65
65
|
"dcmjs": "^0.52.0",
|
|
66
66
|
"ws": "^8.21.0"
|