UNPKG

cornerstone-nifti-image-loader

Version:
394 lines (356 loc) 18.7 kB
<!doctype html> <html lang="en"> <head> <meta name="charset" content="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- twitter bootstrap CSS stylesheet - included to make things pretty, not needed or used by cornerstone --> <link href="bootstrap.min.css" rel="stylesheet"> <link href="cornerstone.min.css" rel="stylesheet"> <link href="orientation-check.css" rel="stylesheet"> </head> <body> <div class="container-fluid"> <header id="main-header"> <h1>NIFTI loading</h1> <h2><small class="text-muted">Example of displaying a NIFTI image using Cornerstone</small></h2> <p class="lead"> Enter a URL for a NIFTI file to view it using cornerstone. <button id='toggle-more-info' class="btn btn-outline-info" type="button"> More info </button> </p> </header> <div id="more-info" class="collapse"> <p> This example illustrates how to use the cornerstone-nifti-image-loader to get a NIFTI file and display it in your web browser using cornerstone. </p> <p> Use the hash fragment (<code>#</code>) to specify which frame (slice) to display from an object (defaults to the first frame if not specified). Example: <dl class="row"> <dt class="col-sm-3 col-md-5 text-right">URL: <code>brain.nii</code></dt> <dd class="col-sm-9 col-md-7">Loads the first frame (slice) of the data</dd> <dt class="col-sm-3 col-md-5 text-right">URL: <code>brain.nii#15</code></dt> <dd class="col-sm-9 col-md-7">Loads the 15th frame (slice) of the data</dd> </dl> </p> <strong>If you get an HTTP error and your URL is correct, it is probably because the server is not configured to allow <a href="http://en.wikipedia.org/wiki/Cross-origin_resource_sharing">Cross Origin Requests</a>. Most browsers will allow you to enable cross domain requests via settings or command line switches, you can start chrome with the command line switch <code>--disable-web-security</code> to allow cross origin requests. See the <a href="http://enable-cors.org/">Enable CORS site</a> for information about CORS. </strong> <br> <br> <p> Looking for a CORS proxy? Try <a href="https://www.npmjs.com/package/corsproxy">CORSProxy</a> </p> <strong>Use of this example require IE10+ or any other modern browser.</strong> </div> <div id="load-progress">Image Load Progress:</div> <div class="row"> <div class="col"> <form id="form"> <div class="form-group form-row"> <label class="col-form-label col-sm-1" for="nifti-URL">URL</label> <div class="col-sm-7"> <div class="input-group"> <div class="input-group-prepend"> <div class="input-group-text">nifti:</div> </div> <input class="form-control" type="text" id="nifti-URL" placeholder="Enter URL to NIFTI file or pick from list" list="sample-nifti-files"> <datalist id="sample-nifti-files"> <option value="data/SC-N-3.nii">data/SC-N-3.nii</option> <option value="data/avg152T1_LR_nifti.nii">data/avg152T1_LR_nifti.nii (uncompressed, 903 KB)</option> <option value="data/avg152T1_RL_nifti.nii.gz">data/avg152T1_RL_nifti.nii.gz (compressed, 531 KB)</option> <option value="data/avg152T1_LR_nifti2.nii.gz">data/avg152T1_LR_nifti2.nii.gz (compressed, 764 KB)</option> <option value="data/sample_image.nii.gz">data/sample_image.nii.gz (compressed, 1.5 MB)</option> <option value="data/fMRI_Ret_bars.nii.gz">data/fMRI_Ret_bars.nii.gz (compressed, 3.0 MB)</option> <option value="data/dti_FA.nii.gz">data/dti_FA.nii.gz (compressed, 395.6 KB)</option> <option value="data/dti_V1.nii.gz">data/dti_V1.nii.gz (compressed, 1.2 MB)</option> <option value="data/mni152_2009_256.nii.gz">data/mni152_2009_256.nii.gz (compressed, 4.5 MB)</option> <option value="data/2878_1_1_localizera.nii.gz">data/2878_1_1_localizera.nii.gz (YZX pixel order, compressed, 1.6 MB)</option> </datalist> </div> </div> <div class="col-sm-3"> <button class="btn btn-primary" type="button" id="download-and-view">Download and View</button> </div> </div> </form> </div> </div> <section id="image-and-data-info"> <div class="nifti-image-container" oncontextmenu="return false" class='disable-selection noIbar' unselectable='on' onselectstart='return false;' onmousedown='return false;'> <div id="nifti-image-z"></div> <button data-dimension="z" class="display-metadata btn btn-sm btn-secondary">Display meta data</button> </div> <div class="nifti-image-container" oncontextmenu="return false" class='disable-selection noIbar' unselectable='on' onselectstart='return false;' onmousedown='return false;'> <div id="nifti-image-x"></div> <button data-dimension="x" class="display-metadata btn btn-sm btn-secondary">Display meta data</button> </div> <div class="nifti-image-container" oncontextmenu="return false" class='disable-selection noIbar' unselectable='on' onselectstart='return false;' onmousedown='return false;'> <div id="nifti-image-y"></div> <button data-dimension="y" class="display-metadata btn btn-sm btn-secondary">Display meta data</button> </div> <div id="nifti-image-data"> <div class="metadata"> <span class="badge badge-pill badge-light">Columns:</span> <span class="badge badge-pill badge-primary" id="columns"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Rows:</span> <span class="badge badge-pill badge-primary" id="rows"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Number of Frames:</span> <span class="badge badge-pill badge-primary" id="numberOfFrames"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Pixel Spacing:</span> <span class="badge badge-pill badge-primary" id="pixelSpacing"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Samples per Pixel:</span> <span class="badge badge-pill badge-primary" id="samplesPerPixel"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Photometric Interpretation:</span> <span class="badge badge-pill badge-primary" id="photometricInterpretation"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Planar Configuration:</span> <span class="badge badge-pill badge-primary" id="planarConfiguration"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Pixel Aspect Ratio:</span> <span class="badge badge-pill badge-primary" id="pixelAspectRatio"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Bits Allocated:</span> <span class="badge badge-pill badge-primary" id="bitsAllocated"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Bits Stored:</span> <span class="badge badge-pill badge-primary" id="bitsStored"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">High Bit:</span> <span class="badge badge-pill badge-primary" id="highBit"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Pixel Representation:</span> <span class="badge badge-pill badge-primary" id="pixelRepresentation"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">WindowCenter:</span> <span class="badge badge-pill badge-primary" id="windowCenter"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">WindowWidth:</span> <span class="badge badge-pill badge-primary" id="windowWidth"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">RescaleIntercept:</span> <span class="badge badge-pill badge-primary" id="rescaleIntercept"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">RescaleSlope:</span> <span class="badge badge-pill badge-primary" id="rescaleSlope"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Min Stored Pixel Value:</span> <span class="badge badge-pill badge-primary" id="minStoredPixelValue"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Max Stored Pixel Value:</span> <span class="badge badge-pill badge-primary" id="maxStoredPixelValue"></span> </div> <div class="metadata"> <span class="badge badge-pill badge-light">Timepoints:</span> <span class="badge badge-pill badge-primary" id="timepoints"></span> </div> <hr /> <div> <button class="btn btn-secondary btn-sm" type="button" id="prev-timepoint">Prev. Timepoint</button> <button class="btn btn-secondary btn-sm" type="button" id="next-timepoint">Next Timepoint</button> <div class="metadata"> <span class="badge badge-pill badge-light">Current Timepoint:</span> <span class="badge badge-pill badge-primary" id="currentTimepoint"></span> </div> </div> </div> </section> </div> </body> <!-- include the cornerstone library --> <script src="cornerstone.js"></script> <script src="cornerstoneMath.js"></script> <script src="cornerstoneTools.js"></script> <!-- include the cornerstoneNIFTIImageLoader library --> <script src="../../dist/cornerstoneNIFTIImageLoader.js"></script> <script> cornerstoneNIFTIImageLoader.external.cornerstone = cornerstone; const ImageId = cornerstoneNIFTIImageLoader.nifti.ImageId; cornerstoneNIFTIImageLoader.nifti.streamingMode = true; let loaded = false; var synchronizer = new cornerstoneTools.Synchronizer("cornerstonenewimage", cornerstoneTools.updateImageSynchronizer); function loadAndViewImage(element, imageId) { const imageIdObject = ImageId.fromURL(imageId); element.dataset.imageId = imageIdObject.url; //cornerstoneNIFTIImageLoader.nifti.loadVolumeTimepoint(imageIdObject).then((data) => { // console.info(data); //}); //return; try { cornerstone.loadAndCacheImage(imageIdObject.url).then(function(image) { const numberOfSlices = cornerstone.metaData.get('multiFrameModule', imageIdObject.url).numberOfFrames; const stack = { currentImageIdIndex: imageIdObject.slice.index, imageIds: Array.from(Array(numberOfSlices), (_, i) => `nifti:${imageIdObject.filePath}#${imageIdObject.slice.dimension}-${i},t-0`) }; const viewport = cornerstone.getDefaultViewportForImage(element, image); cornerstone.displayImage(element, image, viewport); cornerstone.resize(element, true); cornerstoneTools.addStackStateManager(element, ['stack']); cornerstoneTools.addToolState(element, 'stack', stack); cornerstoneTools.mouseInput.enable(element); cornerstoneTools.mouseWheelInput.enable(element); cornerstoneTools.pan.activate(element, 2); cornerstoneTools.zoom.activate(element, 4); cornerstoneTools.stackScrollWheel.activate(element); cornerstoneTools.orientationMarkers.enable(element); synchronizer.add(element); cornerstoneTools.stackPrefetch.enable(element); cornerstoneTools.referenceLines.tool.enable(element, synchronizer); cornerstoneTools.crosshairs.enable(element, 1, synchronizer); }, function(err) { throw err; alert(err); }); } catch(err) { throw err; alert(err); } } const elementZ = document.getElementById('nifti-image-z'); const elementX = document.getElementById('nifti-image-x'); const elementY = document.getElementById('nifti-image-y'); let timepoint = 0; function downloadAndView(elements) { let url = document.getElementById('nifti-URL').value; let imageIdObject = ImageId.fromURL(`nifti:${url}`); document.getElementById('currentTimepoint').textContent = timepoint; loadAndViewImage(elementZ, `nifti:${imageIdObject.filePath}#z,t-${timepoint}`); loadAndViewImage(elementX, `nifti:${imageIdObject.filePath}#x,t-${timepoint}`); loadAndViewImage(elementY, `nifti:${imageIdObject.filePath}#y,t-${timepoint}`); } cornerstone.events.addEventListener('cornerstoneimageloadprogress', function(event) { const eventData = event.detail; const loadProgress = document.getElementById('load-progress'); loadProgress.textContent = `Image Load Progress: ${eventData.percentComplete}%`; }); cornerstone.enable(elementZ); cornerstone.enable(elementX); cornerstone.enable(elementY); document.getElementById('download-and-view').addEventListener('click', function(e) { let url = document.getElementById('nifti-URL').value; downloadAndView([elementZ, elementX, elementY]); }); document.getElementById('next-timepoint').addEventListener('click', function (e) { timepoint++; downloadAndView([elementZ, elementX, elementY]); }); document.getElementById('prev-timepoint').addEventListener('click', function (e) { timepoint--; downloadAndView([elementZ, elementX, elementY]); }); const form = document.getElementById('form'); form.addEventListener('submit', function(e) { downloadAndView(); e.preventDefault(); }); document.getElementById('toggle-more-info').addEventListener('click', function() { const moreInfoEl = document.querySelector('#more-info'); let justAppeared = !moreInfoEl.classList.toggle('collapse'); if (justAppeared) { moreInfoEl.classList.add('highlighted'); setTimeout(() => moreInfoEl.classList.remove('highlighted'), 100); } }); document.addEventListener('DOMContentLoaded', () => { // check if there is a query parameter pointing to a file to be preloaded const fileToPreload = (getParameterByName('file') || '').trim(); if (fileToPreload !== '') { document.getElementById('nifti-URL').value = fileToPreload; downloadAndView(); } }); document.querySelectorAll('.display-metadata').forEach(button => { button.addEventListener('click', (e) => { const imageId = e.currentTarget.previousElementSibling.dataset.imageId; if (typeof imageId !== 'undefined') { displayMetaData(imageId); } }); }); function displayMetaData (imageId) { const metaData = (type) => cornerstone.metaData.get(type, imageId); document.getElementById('columns').textContent = metaData('imagePixelModule').columns; document.getElementById('rows').textContent = metaData('imagePixelModule').rows; document.getElementById('numberOfFrames').textContent = metaData('multiFrameModule').numberOfFrames; document.getElementById('pixelSpacing').textContent = metaData('imagePlaneModule').columnPixelSpacing.toFixed(2) + ' / ' + metaData('imagePlaneModule').rowPixelSpacing.toFixed(2); document.getElementById('samplesPerPixel').textContent = metaData('imagePixelModule').samplesPerPixel; document.getElementById('photometricInterpretation').textContent = metaData('imagePixelModule').photometricInterpretation; document.getElementById('planarConfiguration').textContent = getPlanarConfiguration(metaData('imagePixelModule').planarConfiguration); document.getElementById('pixelAspectRatio').textContent = metaData('imagePixelModule').pixelAspectRatio; document.getElementById('bitsAllocated').textContent = metaData('imagePixelModule').bitsAllocated; document.getElementById('bitsStored').textContent = metaData('imagePixelModule').bitsStored; document.getElementById('highBit').textContent = metaData('imagePixelModule').highBit; document.getElementById('pixelRepresentation').textContent = getPixelRepresentation(metaData('imagePixelModule').pixelRepresentation); document.getElementById('windowCenter').textContent = metaData('voiLutModule').windowCenter; document.getElementById('windowWidth').textContent = metaData('voiLutModule').windowWidth; document.getElementById('rescaleIntercept').textContent = metaData('modalityLutModule').rescaleIntercept; document.getElementById('rescaleSlope').textContent = metaData('modalityLutModule').rescaleSlope; document.getElementById('minStoredPixelValue').textContent = metaData('imagePixelModule').smallestPixelValue; document.getElementById('maxStoredPixelValue').textContent = metaData('imagePixelModule').largestPixelValue; document.getElementById('timepoints').textContent = metaData('functional').timeSlices; function getPixelRepresentation(value) { return value === '0000H' ? '(unsigned)' : '(signed)'; } function getPlanarConfiguration(value) { return (typeof value === 'undefined' ? 'unspecified' : (value === 0 ? '(pixel)' : '(plane)')); } } function getParameterByName (name) { const url = window.location.href; name = name.replace(/[\[\]]/g, '\\$&'); const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`); const results = regex.exec(url); if (!results) { return null; } if (!results[2]) { return ''; } return decodeURIComponent(results[2].replace(/\+/g, ' ')); } </script> </html>