Skip to content

Commit

Permalink
Created overlay interface so other types can be added (e.g. GSPS)
Browse files Browse the repository at this point in the history
  • Loading branch information
PantelisGeorgiadis committed May 3, 2023
1 parent 79258b9 commit e4b4bcf
Show file tree
Hide file tree
Showing 24 changed files with 1,596 additions and 692 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

strategy:
matrix:
node-version: [14.x, 16.x, 18.x]
node-version: [14.x, 16.x, 18.x, 20.x]

steps:
- name: Checkout
Expand Down
2 changes: 1 addition & 1 deletion examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ async function renderToBmp(dicomFile, bmpFile) {
// Optionally, provide the path to WebAssembly module.
// If not provided, the module is trying to be resolved within the same directory.
await NativePixelDecoder.initializeAsync({
webAssemblyModulePathOrUrl: path.resolve(__dirname, '../wasm/bin/native-pixel-decoder.wasm'),
webAssemblyModulePathOrUrl: path.resolve(__dirname, './../wasm/bin/native-pixel-decoder.wasm'),
});

const fileBuffer = fs.readFileSync(dicomFile);
Expand Down
2 changes: 1 addition & 1 deletion examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ async function renderToBmp(dicomFile: string, bmpFile: string) {
// Optionally, provide the path to WebAssembly module.
// If not provided, the module is trying to be resolved within the same directory.
await NativePixelDecoder.initializeAsync({
webAssemblyModulePathOrUrl: path.resolve(__dirname, '../wasm/bin/native-pixel-decoder.wasm'),
webAssemblyModulePathOrUrl: path.resolve(__dirname, './../wasm/bin/native-pixel-decoder.wasm'),
});

const fileBuffer = fs.readFileSync(dicomFile);
Expand Down
1,972 changes: 1,425 additions & 547 deletions package-lock.json

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dcmjs-imaging",
"version": "0.1.15",
"version": "0.1.16",
"description": "DICOM image and overlay rendering for Node.js and browser using dcmjs",
"main": "build/dcmjs-imaging.min.js",
"module": "build/dcmjs-imaging.min.js",
Expand All @@ -15,7 +15,7 @@
"doc:generate": "npm run clean:docs && jsdoc -c .jsdocrc.json",
"start:examples:node:js": "node examples/index.js",
"start:examples:node:ts": "ts-node examples/index.ts",
"start:examples:web": "webpack serve --port 8080 --open --no-client-overlay-warnings",
"start:examples:web": "webpack serve --port 8080 --open --no-client-overlay-warnings --no-client-overlay-errors",
"build": "npm run version && npm run lint && npm run test && npm run coverage && npm run webpack",
"start": "npm run webpack",
"version": "node -p -e \"'module.exports = \\'' + require('./package.json').version + '\\';'\" > src/version.js",
Expand Down Expand Up @@ -43,7 +43,7 @@
},
"homepage": "https://github.com/PantelisGeorgiadis/dcmjs-imaging",
"dependencies": {
"dcmjs": "^0.29.5",
"dcmjs": "^0.29.6",
"loglevel": "^1.8.1",
"loglevel-plugin-prefix": "^0.8.4"
},
Expand All @@ -56,27 +56,27 @@
"clang-format": "^1.8.0",
"copy-webpack-plugin": "^11.0.0",
"docdash": "^2.0.1",
"eslint": "^8.36.0",
"eslint": "^8.39.0",
"jsdoc": "^4.0.2",
"karma": "^6.4.1",
"karma": "^6.4.2",
"karma-browserify": "^8.1.0",
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^3.1.1",
"karma-chrome-launcher": "^3.2.0",
"karma-mocha": "^2.0.1",
"karma-mocha-reporter": "^2.2.5",
"karma-sinon": "^1.0.5",
"mocha": "^10.2.0",
"open-cli": "^7.1.0",
"open-cli": "^7.2.0",
"pako": "^2.1.0",
"prettier": "^2.8.4",
"prettier": "^2.8.8",
"shx": "^0.3.4",
"sinon": "^15.0.1",
"sinon": "^15.0.4",
"terser-webpack-plugin": "^5.3.7",
"ts-node": "^10.9.1",
"tsd": "^0.27.0",
"typescript": "^4.9.5",
"webpack": "^5.76.1",
"webpack-cli": "^5.0.1",
"webpack-dev-server": "^4.11.1"
"tsd": "^0.28.1",
"typescript": "^5.0.4",
"webpack": "^5.81.0",
"webpack-cli": "^5.0.2",
"webpack-dev-server": "^4.13.3"
}
}
4 changes: 2 additions & 2 deletions src/Cache.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { PixelPipeline } = require('./Pixel');
const { LutPipeline } = require('./Lut');
const { PixelPipeline } = require('./Pixel');

//#region LruCache
class LruCache {
Expand Down Expand Up @@ -132,5 +132,5 @@ class LutPipelineCache extends LruCache {
//#endregion

//#region Exports
module.exports = { PixelPipelineCache, LutPipelineCache };
module.exports = { LutPipelineCache, PixelPipelineCache };
//#endregion
8 changes: 4 additions & 4 deletions src/Constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,12 @@ Object.freeze(OverlayColor);

//#region Exports
module.exports = {
TransferSyntax,
RenderableTransferSyntaxes,
OverlayColor,
PhotometricInterpretation,
PlanarConfiguration,
PixelRepresentation,
PlanarConfiguration,
RenderableTransferSyntaxes,
StandardColorPalette,
OverlayColor,
TransferSyntax,
};
//#endregion
26 changes: 14 additions & 12 deletions src/DicomImage.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
const {
RenderableTransferSyntaxes,
TransferSyntax,
OverlayColor,
PhotometricInterpretation,
RenderableTransferSyntaxes,
StandardColorPalette,
TransferSyntax,
} = require('./Constants');
const { PixelPipelineCache, LutPipelineCache } = require('./Cache');
const { LutPipelineCache, PixelPipelineCache } = require('./Cache');
const {
LutPipeline,
GrayscaleLutPipeline,
RgbColorLutPipeline,
LutPipeline,
PaletteColorLutPipeline,
RgbColorLutPipeline,
} = require('./Lut');
const { Pixel, PixelPipeline } = require('./Pixel');
const { G60xxOverlay, Overlay } = require('./Overlay');
const WindowLevel = require('./WindowLevel');
const Overlay = require('./Overlay');

const dcmjs = require('dcmjs');
const { DicomMetaDictionary, DicomMessage, ReadBufferStream, WriteBufferStream } = dcmjs.data;
Expand Down Expand Up @@ -316,13 +316,15 @@ class DicomImage {
if (overlays.length > 0) {
for (let i = 0; i < overlays.length; i++) {
const overlay = overlays[i];
if (
frame + 1 < overlay.getFrameOrigin() ||
frame + 1 > overlay.getFrameOrigin() + overlay.getNumberOfFrames() - 1
) {
continue;
if (overlay instanceof G60xxOverlay) {
if (
frame + 1 < overlay.getFrameOrigin() ||
frame + 1 > overlay.getFrameOrigin() + overlay.getNumberOfFrames() - 1
) {
continue;
}
overlay.render(renderedPixels, pixel.getWidth(), pixel.getHeight(), OverlayColor);
}
overlay.render(renderedPixels, pixel.getWidth(), pixel.getHeight(), OverlayColor);
}
}
}
Expand Down
18 changes: 9 additions & 9 deletions src/Lut.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { PhotometricInterpretation, StandardColorPalette } = require('./Constants');
const ColorPalette = require('./ColorPalette');
const WindowLevel = require('./WindowLevel');
const { PhotometricInterpretation, StandardColorPalette } = require('./Constants');

//#region Lut
class Lut {
Expand Down Expand Up @@ -1137,18 +1137,18 @@ class PaletteColorLutPipeline extends LutPipeline {

//#region Exports
module.exports = {
CachedLut,
CompositeLut,
GrayscaleLutPipeline,
InvertLut,
Lut,
LutPipeline,
RescaleLut,
VoiLut,
InvertLut,
PaletteColorLut,
OutputLut,
CompositeLut,
PaletteColorLut,
PaletteColorLutPipeline,
PreCalculatedLut,
CachedLut,
GrayscaleLutPipeline,
RescaleLut,
RgbColorLutPipeline,
PaletteColorLutPipeline,
VoiLut,
};
//#endregion
4 changes: 2 additions & 2 deletions src/NativePixelDecoder.js
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,8 @@ class NativePixelDecoder {
throw new Error('NativePixelDecoder module is not initialized');
}

const heap8 = new Uint8Array(this.wasmApi.wasmMemory.buffer);
const stringData = new Uint8Array(heap8.buffer, pointer, len);
const heap = new Uint8Array(this.wasmApi.wasmMemory.buffer);
const stringData = new Uint8Array(heap.buffer, pointer, len);
let str = '';
for (let i = 0; i < len; i++) {
str += String.fromCharCode(stringData[i]);
Expand Down
75 changes: 50 additions & 25 deletions src/Overlay.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,62 @@
const { SingleBitPixelPipeline } = require('./Pixel');
const log = require('./log');

const dcmjs = require('dcmjs');
const { Tag } = dcmjs.data;

//#region Overlay
class Overlay {
/**
* Creates an instance of Overlay.
* Renders the overlay on top of a rendered image.
* @method
* @param {Int32Array} renderedPixels - Rendered ABGR image to be updated with the overlay.
* @param {number} width - Rendered image width.
* @param {number} height - Rendered image height.
* @param {number} color - Overlay color packed in an integer.
*/
// eslint-disable-next-line no-unused-vars
render(renderedPixels, width, height, color) {
throw new Error('render should be implemented');
}

/**
* Creates an array of overlay objects based on the image elements.
* @method
* @static
* @param {Object} elements - DICOM image elements.
* @returns {Array<Overlay>} Array of overlay objects.
*/
static fromDicomImageElements(elements) {
const ret = [];

// G60xx
const elementKeys = Object.keys(elements);
for (let i = 0; i < elementKeys.length; i++) {
const element = elementKeys[i];
const tag = Tag.fromString(element);
if (tag.element() === 0x0010) {
if (tag.group() >= 0x6000 && tag.group() <= 0x60ff && tag.group() % 2 === 0) {
ret.push(new G60xxOverlay(elements, tag.group()));
}
}
}

return ret;
}
}
//#endregion

//#region G60xxOverlay
class G60xxOverlay extends Overlay {
/**
* Creates an instance of G60xxOverlay.
* @constructor
* @param {Object} elements - DICOM image elements.
* @param {number} group - Overlay group.
*/
constructor(elements, group) {
super();

this.group = group;

this.height = this._getElement(elements, Tag.fromNumbers(group, 0x0010).toCleanString()) || 0;
Expand Down Expand Up @@ -175,12 +220,15 @@ class Overlay {
*/
render(renderedPixels, width, height, color) {
if (!this.getData()) {
log.warn('G60xx overlay: No data. Skipping...');
return;
}
if (!this.getWidth() || !this.getHeight()) {
log.warn('G60xx overlay: No width or height. Skipping...');
return;
}
if (!this.getBitsAllocated()) {
log.warn('G60xx overlay: No bits allocated. Skipping...');
return;
}

Expand Down Expand Up @@ -215,29 +263,6 @@ class Overlay {
}
}

/**
* Creates an array of overlay objects based on the image elements.
* @method
* @static
* @param {Object} elements - DICOM image elements.
* @returns {Array<Overlay>} Array of overlay objects.
*/
static fromDicomImageElements(elements) {
const ret = [];
const elementKeys = Object.keys(elements);
for (let i = 0; i < elementKeys.length; i++) {
const element = elementKeys[i];
const tag = Tag.fromString(element);
if (tag.element() === 0x0010) {
if (tag.group() >= 0x6000 && tag.group() <= 0x60ff && tag.group() % 2 === 0) {
ret.push(new Overlay(elements, tag.group()));
}
}
}

return ret;
}

//#region Private Methods
/**
* Gets element value.
Expand All @@ -254,5 +279,5 @@ class Overlay {
//#endregion

//#region Exports
module.exports = Overlay;
module.exports = { G60xxOverlay, Overlay };
//#endregion
8 changes: 4 additions & 4 deletions src/Pixel.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const {
PhotometricInterpretation,
PlanarConfiguration,
PixelRepresentation,
PlanarConfiguration,
TransferSyntax,
} = require('./Constants');
const Histogram = require('./Histogram');
Expand Down Expand Up @@ -1400,11 +1400,11 @@ class PixelConverter {

//#region Exports
module.exports = {
ColorPixelPipeline,
GrayscalePixelPipeline,
Pixel,
PixelConverter,
PixelPipeline,
GrayscalePixelPipeline,
SingleBitPixelPipeline,
ColorPixelPipeline,
PixelConverter,
};
//#endregion
20 changes: 3 additions & 17 deletions src/WindowLevel.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ class WindowLevel {
static fromDicomImageElements(elements) {
const ret = [];

let windowCenters = this._getElement(elements, 'WindowCenter');
let windowWidths = this._getElement(elements, 'WindowWidth');
let descriptions = this._getElement(elements, 'WindowCenterWidthExplanation');
let windowCenters = elements['WindowCenter'];
let windowWidths = elements['WindowWidth'];
let descriptions = elements['WindowCenterWidthExplanation'];

if (windowCenters === undefined || windowWidths === undefined) {
return ret;
Expand Down Expand Up @@ -120,20 +120,6 @@ class WindowLevel {

return ret;
}

//#region Private Methods
/**
* Gets element value.
* @method
* @static
* @param {Object} elements - Elements.
* @param {string} tag - Element tag.
* @returns {string|undefined} Element value or undefined if element doesn't exist.
*/
static _getElement(elements, tag) {
return elements[tag];
}
//#endregion
}
//#endregion

Expand Down
Loading

0 comments on commit e4b4bcf

Please sign in to comment.