Code Monkey home page Code Monkey logo

dicom-microscopy-viewer's Introduction

Build Status NPM version NPM downloads per month

DICOM Microscopy Viewer

Vanilla JS library for web-based visualization of DICOM VL Whole Slide Microscopy Image datasets and derived information.

The viewer allows visualization of slide microscopy images stored in a DICOMweb compatible archive. It leverages the dicomweb-client JavaScript library to retrieve data from the archive.

Features

  • Display of different image types: VOLUME/THUMBNAIL, OVERVIEW, LABEL
  • Annotation of regions of interest (ROI) as vector graphics based on 3-dimensional spatial coordinates (SCOORD3D): POINT, MULTIPOINT, POLYLINE, POLYGON, ELLIPSE, ELLIPSOID
  • Assembly of concatenations
  • Decoding of compressed pixel data, supporting baseline JPEG, JPEG 2000, and JPEG-LS codecs
  • Correction of color images using ICC profiles
  • Additive blending and coloring of monochromatic images of multiple optical paths (channels), supporting highly-multiplexed immunofluorescence imaging
  • Overlay of image analysis results in the form of DICOM Segmentation, Parametric Map, Comprehensive 3D SR, or Microscopy Bulk Simple Annotations

Documentation

Documentation of the JavaScript Application Programming Interface (API) is available online at imagingdatacommons.github.io/dicom-microscopy-viewer.

Getting started

Note that the dicom-microscopy-viewer package is not a viewer application, it is a library to build viewer applications.

Below is an example for the most basic usage: a web page that displays a collection of DICOM VL Whole Slide Microscopy Image instances of a digital slide. For more advanced usage, take a look at the Slim viewer.

Packaging

The library is packaged as two different builds, one using dynamic import, and the other bundling into one larger library. The dynamic import version uses a public path of /dicom-microscopy-viewer/ so that they can be used by simply adding an alias to the appropriate version, and then deploying that version. In a straight web application, this can be loaded as:

   const DICOMMicroscopyViewer = (await('/dicom-microscopy-viewer/dicomMicroscopyViewer.min.js')).default

The point of using the sub-directory here is to isolate the dependencies that unique to dicom-microscopy-viewer.

Basic usage

The viewer can be embedded in any website, one only needs to

  • Create an instance of VolumeImageViewer. The constructor requires an instance of DICOMwebClient for retrieving frames from the archive as well as the metadata for each DICOM image as an instance of VLWholeSlideMicroscopyImage.

  • Call the render() method, passing it the HTML element (or the name of the element), which shall contain the viewport.

import * as DICOMMicroscopyViewer from 'dicom-microscopy-viewer';
import * as DICOMwebClient from 'dicomweb-client';

// Construct client instance
const client = new DICOMwebClient.api.DICOMwebClient({
  url: 'http://localhost:8080/dicomweb'
});

// Retrieve metadata of a series of DICOM VL Whole Slide Microscopy Image instances
const retrieveOptions = {
  studyInstanceUID: '1.2.3.4',
  seriesInstanceUID: '1.2.3.5'
};
client.retrieveSeriesMetadata(retrieveOptions).then((metadata) => {
  // Parse, format, and filter metadata
  const volumeImages = []
  metadata.forEach(m => {
    const image = new DICOMMicroscopyViewer.metadata.VLWholeSlideMicroscopyImage({
      metadata: m
    })
    const imageFlavor = image.ImageType[2]
    if (imageFlavor === 'VOLUME' || imageFlavor === 'THUMBNAIL') {
      volumeImages.push(image)
    }
  })

  // Construct viewer instance
  const viewer = new DICOMMicroscopyViewer.viewer.VolumeViewer({
    client,
    metadata: volumeImages
  });

  // Render viewer instance in the "viewport" HTML element
  viewer.render({ container: 'viewport' });
});

Citation

Please cite the following article when using the viewer for scientific studies: Herrmann et al. J Path Inform. 2018:

@article{jpathinform-2018-9-37,
    Author={
        Herrmann, M. D. and Clunie, D. A. and Fedorov A. and Doyle, S. W. and Pieper, S. and
        Klepeis, V. and Le, L. P. and Mutter, G. L. and Milstone, D. S. and Schultz, T. J. and
        Kikinis, R. and Kotecha, G. K. and Hwang, D. H. and Andriole, K, P. and Iafrate, A. J. and
        Brink, J. A. and Boland, G. W. and Dreyer, K. J. and Michalski, M. and
        Golden, J. A. and Louis, D. N. and Lennerz, J. K.
    },
    Title={Implementing the {DICOM} standard for digital pathology},
    Journal={Journal of Pathology Informatics},
    Year={2018},
    Number={1},
    Volume={9},
    Number={37}
}

Installation

Install the dicom-microscopy-viewer package using the npm package manager:

npm install dicom-microscopy-viewer

Development & Testing

We use Babel to compile (transpile), webpack to bundle, and Jest to test JavaScript code.

Get the source code by cloning the git repository:

git clone https://github.com/imagingdatacommons/dicom-microscopy-viewer
cd dicom-microscopy-viewer

Install dependencies and build the package:

npm install
npm run build

Run tests:

npm run test

Build the API documentation:

npm run generateDocs

Support

The developers gratefully acknowledge their reseach support:

dicom-microscopy-viewer's People

Contributors

biharck avatar cgorman avatar dependabot[bot] avatar hackermd avatar igoroctaviano avatar pedrokohler avatar pieper avatar punzo avatar rodrigobasilio2022 avatar swederik avatar tfradicalimaging avatar wayfarer3130 avatar zia-chang avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

dicom-microscopy-viewer's Issues

Is dicom-microscopy-viewer support Z-stacks image?

Dear Sir,

As we know, Z-stacking(plane of focus) involves scanning a glass slide at different focal planes along the vertical z-axis and stacking the images on top of each other to produce a composite multiplane image.
Z-stack scanning, however, takes longer and produces larger digital files.

Is dicom-microscopy-viewer support Z-stacks image?
If dicom-microscopy-viewer can support, it will be easily used after feeding DICOM into viewer.
If not, how to display Z-stacks image? Any information we need to know from DICOM tags or scanner manufacturer?

Could you give some suggestions?

Thanks.

Deploy new version to npm

@hackermd the npm repo is currently tied to your account. Can you either make a deployment for 0.45.0 or somehow migrate it to an IDC account or something?

use symbols rather than hex to refer to data elements

I think this code (and all DICOM code) would be more readable and maintainable if we avoided the use of hex and use the dictionary. Since we have control over the dictionary class this is a well defined mapping even if the standard is updated and the terminology changes (this happens only very rarely).

So this code:

dict = dcmjs.data.DicomMetaDictionary
dict.nameMap.FrameOfReferenceUID.tag

Gives the string "(0020,0052)" which can be passed to dict.unpunctuateTag to get 00200052 for use with the metadata in code like this.

We can also look at maybe adding something like dict.rawTags.FrameOfReferenceUID to further simplify the code.

Hardcoded mimetype has quotation mark issue

Here we are passing the mimetype to dicomweb-client:

https://github.com/dcmjs-org/dicom-microscopy-viewer/blob/c706f918c0387c40f34cff922d6200e18d43b9ce/src/api.js#L498

We are passing image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.50.

dicomweb-client is wrapping this with type="${blah}"

This means that the quotation marks end up in the wrong place, and we end up with:
type="image/jpeg; transfer-syntax=1.2.840.10008.1.2.4.50" which is invalid. dcm4chee doesn't seem to care, but Google Cloud Healthcare API is more stringent and so we end up with 406 errors. It's weird because I swear I tested this before merging and it was working.

I'm not sure if this is something we should fix here or in dicomweb-client. We should probably just finish off this PR: dcmjs-org/dicomweb-client#16 which makes dwc act more like the Python version.

Just posting this for visibility.

IMS responds with octet-stream when image/jpeg is available

I'm currently working with a Slim deployment which is retrieving JPEG encoded SM instances. The request's accept header is as follows:

multipart/related; type="image/jls"; transfer-syntax=1.2.840.10008.1.2.4.80, multipart/related; type="image/jls"; transfer-syntax=1.2.840.10008.1.2.4.81, multipart/related; type="image/jp2"; transfer-syntax=1.2.840.10008.1.2.4.90, multipart/related; type="image/jp2"; transfer-syntax=1.2.840.10008.1.2.4.91, multipart/related; type="image/jpx"; transfer-syntax=1.2.840.10008.1.2.4.92, multipart/related; type="image/jpx"; transfer-syntax=1.2.840.10008.1.2.4.93, multipart/related; type="application/octet-stream"; transfer-syntax=*, multipart/related; type="image/jpeg"; transfer-syntax=1.2.840.10008.1.2.4.50

and the response's content type is:
multipart/related;start="<***@resteasy-multipart>";type="application/octet-stream"; boundary***

The source image is JPEG encoded, so I think that since JPEG appears after octet-stream, it chooses octet-stream instead. I assume the issue is introduced here:

},
{
mediaType: octetStreamMediaType,
transferSyntaxUID: octetStreamTransferSyntaxUID
}
])
if (bitsAllocated <= 8) {
mediaTypes.push({
mediaType: jpegMediaType,
transferSyntaxUID: jpegTransferSyntaxUID
})
}

Would it be sufficient to just put push the octet stream mediatype after the jpeg mediatype if statement?

Examples do not run properly with local built version due to random css issue

It appears that the viewer does not show up when you run the examples. It turns out that it is because the root element has no height or width and so it never gets displayed.

Not sure when this broke. We can just change it to <div id="root" style="height:1000px; width:1000px"></div> and it works fine.

Making an issue for anyone else that runs into it

Request: Target IE 11

All of the other dcmjs-org libraries appear to transpile/target IE 11 using babel. I believe, consumers of dicom-microscopy-viewer that target IE 11 will need to transpile this dependency on every build unless an IE 11 build is provided.

Please consider adopting the same approach used in the other repositories to make this package easier to add as a dependency ๐Ÿ™

https://github.com/dcmjs-org/dcmjs/blob/master/.babelrc

Use only one unit testing framework

The library currently uses two separate frameworks for defining and running test cases (mochify and jest) and there are two separate targets for testing defined in package.json ("test" and "test:unit").

@igoroctaviano you introduced jest and added unit tests using jest. Could we use jets for the other unit tests as well?

Add dicom-microscopy-viewer to OHIF

I have some questions. Is it possible to add this lib to OHIF?

I tested OHIF and i loaded images in a Orthanc Server and visualized with OHIF but there is not annotation tools for example.

Someone can explain the difference betwwen this dicom-microscopy-viewer and the other one that is in OHIF?

On the other hand. Can someone explain where i can modify and develop over OpenLayers. I saw that OpenLayers is installed to render WSI images.

Kind regards.

Unit tests failing

@Punzo it seems recent changes (#54) broke the unit tests:

Evaluation failed: Error: No channels or colorImage found.
      at new VolumeImageViewer (build/dicom-microscopy-viewer.js:141168)
      at Suite.<anonymous> (test/multichannel.spec.js:39)
      at Object.require.57.../build/dicom-microscopy-viewer.js (test/multichannel.spec.js:13)
Error: Exit 1

Unclear how to obtain referenced instance / frame from ROI / scoord

We might have missed it, but from an ROI, it wasn't obvious how to obtain the frame that the measurement was made upon. We have coordinates, but I'm not sure that's sufficient. It depends on how we encode the measurements in dcmjs (@hackermd mentioned scoord3d at one point, maybe @fedorov or @pieper have some other ideas).

Note: We started making a dcmjs adapter here:
https://github.com/dcmjs-org/dcmjs/tree/dicom-microscopy-adapter

getROI() does not work for Scoord3D objects of type Circle

Not all classes derived from Scoord3D have a coordinates attribute. For example, Circle doesn't.

This assumption currently breaks the getROI() viewer method:
https://github.com/dcmjs-org/dicom-microscopy-viewe/blob/0aa5e768029a5a5d6b9c8fa224081218dae76b6f/src/api.js#L731-L737

There are further issues with this approach:

  • The length of the value of the coordinates attribute of an object of type Point should no longer be 2, but 3 (since we switched from Scoord to Scoord3D)
  • The value of the coordinates attribute of an object of type Point should be an array of numbers of length 3. Changing the value to an array of arrays would break the graphicData() getter, which would return incorrect graphic data.
  • The coordinates returned by _geometry2Scoord3D() should already be in the correct format, i.e. in the slide coordinate system in millimeter unit. This line should thus not be necessary.

Revisit status checks for branch configuration

@hackermd we removed status checks to work around presumed blocking issue, since you renamed the task in github actions workflow, but we cannot figure out how to add new status checks that have the updated name. Maybe you can help with this? Related to #85.

Module Import Errors When Using [email protected]

The dicom-microscopy-viewer library is a valuable tool for displaying DICOM (Digital Imaging and Communications in Medicine) microscopy images within web applications. However, the recent release of version seems to have introduced module import issues for users, leading to two distinct error scenarios:

1. Using <script type="module">:

When developers include the library using the <script type="module"> tag like this:

<script type="module" src="https://unpkg.com/[email protected]"></script>

The error message Uncaught TypeError: Failed to resolve module specifier "mathjs". Relative references must start with either "/", "./", or "../". appears in the console. This error suggests an issue with module resolution and specifier handling.

2. Using <script> without type="module":

Alternatively, when developers attempt to include the library without the type="module" attribute:

<script src="https://unpkg.com/[email protected]"></script>

They encounter the error message Uncaught SyntaxError: Cannot use import statement outside a module. This error implies that the library's code is written as ES6 modules, and without the type="module" attribute, it cannot properly handle import statements.

I can't use it maybe there's a more specific example in the import section (a full example) and my dicomWeb works fine.

Add event handlers or callbacks for graphical annotations

We need some event listeners or callbacks that allow us to add custom behavior to the viewer. For example, an application may want to automatically compute measurements for each geometry that has been drawn by a user and update the measurements when a user modifies the geometry.

Openlayers exposes several types of events that we could leverage for this purpose. However, we don't want to rely on Openlayer events directly, but want to fully abstract the Openlayers API.

The following events fire for drawInteraction, selectInteraction, and modifyInteraction, respectively:

In addition, Openlayers fires an event whenever a geometry gets drawn and collected:

We may want to allow users of the library to provide callbacks, which could be specified as an option for the constructor and/or for activateDrawInteraction, activateSelectInteraction, and activateModifyInteraction.

Check constructor arguments of Scoord3D classes

Currently, there are no checks for the arguments of the constructor of classes derived from Scoord3D. This is dangerous because these values are returned by the graphicData() getter, which is indented to return the coordinates in the format required by DICOM.
We should check for the type (e.g. Array or Number) and as well as the values (e.g. whether first and last array element are identical for Polygon).

For example:

class Point extends Scoord3D {

  constructor(coordinates) {
    super()
    if (!Array.isArray(coordinates)) {
        console.error('coordinates of Point must be an array')
    }
    if (coordinates.length !== 3) {
        console.error('coordinates of Point must be an array of length 3')
    }
    this.coordinates = coordinates
  }

  get graphicData() {
    return this.coordinates;
  }

  get graphicType() {
    return 'POINT';
  }

}

Microscopy images (modality = SM) doesn't render on the viewer.

Similar to the issue #31 , but different reason.

The reason is: FrameOfReferenceUID of each layer of the image pyramid is often different ( usually not totally different but with some matches, for example, 2.16.840.1.113883.3.8467.132979444839078229637890676839078229.10 and 2.16.840.1.113883.3.8467.132979444839078229637890676839078229.9, only the last number 10 and 9 is different), when the DICOM is generated by aggregating the layer images.

I recognize if these numbers are different between layers, we would have problems when rendering annotations.
But on the condition that we are not using the annotations, it should at least render the image.

This is the line where it was blocked by throwing error:

if (metadata[0].FrameOfReferenceUID !== metadata[i].FrameOfReferenceUID) {

Add unit tests for addROI() and getROI()

We should add some simple tests for annotations, where we add ROIs to the viewer, retrieve them back and compare them for equality.

Something like:

const viewer = new DICOMMicroscopyViewer.api.VLWholeSlideMicroscopyImageViewer(...)
const circle = new DICOMMicroscopyViewer.scoord3d.Circle([1000, 1000, 1], 100);
const roi = new DICOMMicroscopyViewer.roi.ROI({scoord3d: circle});
viewer.addROI(roi);
const retrievedROIs = viewer.getAllROIs();
// assert that original and retrieved objects are equal, i.e. have identical coordinate values

Disable image rotation from Mobile Devices

The current bootstrap style allows users to pinch zoom and rotate the screen but sometimes is annoying when users are drawing geometric such as Polygons, where the geometry is composed by multiple points and by mistake users, could rotate the screen.

There are some related topics over StackOverflow about the same thing:

const controls = ol.control.defaults({rotate: false}); 
const interactions = ol.interaction.defaults({altShiftDragRotate:false, pinchRotate:false});

const map = new ol.Map {
    controls: controls,
    interactions: interactions
};

and

view: new ol.View({ ... enableRotation: false })

But none of them worked properly.

Assert code 17 - resolutions_ assertion

I am using OHIF as a viewer which is erroring out on this assertion when attempting to view WSI studies:

    assert(isSorted(this.resolutions_, function(a, b) {
      return b - a;
    }, true), 17);

I'm unclear on the purpose of this, or why it might be failing. Any insights would be much appreciated.

Thanks

Ability to customize style of controls and annotations

At the moment, regions of interest (geometries rendered by the viewer) and controls (buttons for the overview map, fullscreen mode, etc.) are rendered with the Openlayers default style. We want to allow an application to customize styling, such that the style of library elements can be matched to that of the application.

Openlayers provides a nice styling API (see Style). The following types may be worth looking into:

However, we don't want to expose the Openlayers API directly. We could either wrap Openlayers classes or implement our own styling logic.

Further, we may want to override some Openlayers CSS classes, such that an application can specify dicom-microscopy-viewer rather than Openlayers CSS.

How to zoom beyond the base image resolution?

I have a requirement where I am suppose to zoom an image upto 200% of its maximum resolution. The default behaviour doesn't allow beyond 100%. I couldn't find any configuration to change the same.
So, if, for eg. I have an SM modality whole slide image with 7 levels - I can zoom in to the last level, but not able to digitally zoom beyond that.
So, would like to know if there is any option to digitally zoom beyond the base image resolution if we want to?

Can give more details if required to understand the requirement better.

Apologies, if there is any mistake in the framing of the question.

Whole-slide Image Converted By Orthanc gives issue When Display in Viewer

Whole-slide Image Converted By Orthanc gives issue When Display in Viewer.

Screenshot from 2019-06-26 13-53-37

I am converting .tiff image to dicom using Orthanc Dicomizer. with the command having a dataset.

./OrthancWSIDicomizer Source.tif --dataset=dataset.json

My dataset.json are

`{
"PatientID": "C123456789",
"PatientName": "SOME^PATIENT",
"PatientBirthDate": "19700101",
"PatientSex": "M",
"StudyID": "NONE",
"SeriesNumber": "1",
"ReferringPhysicianName": "SOME^PHYSICIAN",
"AccessionNumber": "123456789",
"Manufacturer": "MyManufacturer",
"ManufacturerModelName": "MyModel",
"DeviceSerialNumber": "MySerialNumber",
"SoftwareVersions": "MyVersion",
"ImageType": "DERIVED\PRIMARY\VOLUME\NONE",
"FocusMethod": "AUTO",
"ExtendedDepthOfField": "NO",
"AcquisitionContextSequence": [],
"AcquisitionDuration": "100",
"ContainerIdentifier": "CI_12345",
"IssuerOfTheContainerIdentifierSequence": [],
"ContainerTypeCodeSequence": [],
"IssuerOfTheContainerIdentifierSequence": [],

"FrameOfReferenceUID": "",
"DerivationDescription": "Derived from a lossy compressed image in NDPi format (JPEG baseline, quality factor 80%, compression ratio 9.7:1)",
"SpecimenDescriptionSequence": [
{
"SpecimenIdentifier": "Specimen^Identifier",
"SpecimenUID": "1.2.276.0.7230010.3.1.4.3252829876.4112.1426166133.871",
"IssuerOfTheSpecimenIdentifierSequence": [],
"SpecimenPreparationSequence": []
}
],
"SpecimenLabelInImage": "NO",
"BurnedInAnnotation": "NO"
}
`

Offset issue / TotalPixelMatrixOriginSequence is different per layer

To summarise, the method coordinateFormatGeometry2Scoord was only considering the top-level TotalPixelMatrixOriginSequence attribute even if it was absent.

There is a case where a study didn't have this top-level attribute causing the viewer to break when attempting to add new ROIs. Although this attribute did exist nested inside the sequences.

  • I have updated the metadata provider to get the highest TotalPixelMatrixOriginSequence attribute among the sequences (under PerFrameFunctionalGroupsSequence) and assign it at the top level of the pyramid metadata to work as an approximation. With that, at least the viewer stopped breaking and I can add the annotations in certain places. The problem now is that the calculation is probably not correct, looks like there is a gap/offset between the image and the overlay causing the coordinates of the annotation to be negative in a small percentage of the viewer's (top).

  • @hackermd if TotalPixelMatrixOriginSequence is absent (at the top level of the pyramid metadata), how can we calculate it based on the nested sequence values?

  • Do we have to update here to consider the offsets under PerFrameFunctionalGroupsSequence?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.